mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-10-05 17:53:54 +02:00
Better assistant interactions + UI (#2029)
* add assistnat re-ordering, selections, etc. * squash * remove unnecessary comment * squash * adapt dragging for all IDs + smoother animation + consistency * fix minor typing issue * fix minor typing issue * remove logs
This commit is contained in:
@@ -248,7 +248,6 @@ def create_initial_default_connector(db_session: Session) -> None:
|
|||||||
default_connector_id = 0
|
default_connector_id = 0
|
||||||
default_connector = fetch_connector_by_id(default_connector_id, db_session)
|
default_connector = fetch_connector_by_id(default_connector_id, db_session)
|
||||||
if default_connector is not None:
|
if default_connector is not None:
|
||||||
# Check if the existing connector has the correct values
|
|
||||||
if (
|
if (
|
||||||
default_connector.source != DocumentSource.INGESTION_API
|
default_connector.source != DocumentSource.INGESTION_API
|
||||||
or default_connector.input_type != InputType.LOAD_STATE
|
or default_connector.input_type != InputType.LOAD_STATE
|
||||||
|
@@ -211,7 +211,7 @@ export default function AddConnector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Without credential
|
// Without credential
|
||||||
if (isSuccess && response && credentialActivated) {
|
if (credentialActivated && isSuccess && response) {
|
||||||
const credential =
|
const credential =
|
||||||
currentCredential || liveGDriveCredential || liveGmailCredential;
|
currentCredential || liveGDriveCredential || liveGmailCredential;
|
||||||
const linkCredentialResponse = await linkCredential(
|
const linkCredentialResponse = await linkCredential(
|
||||||
@@ -228,7 +228,19 @@ export default function AddConnector({
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.open("/admin/indexing/status", "_self");
|
window.open("/admin/indexing/status", "_self");
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
const errorData = await linkCredentialResponse.json();
|
||||||
|
setPopup({
|
||||||
|
message: errorData.message,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else if (isSuccess) {
|
||||||
|
setPopup({
|
||||||
|
message:
|
||||||
|
"Credential created succsfully! Redirecting to connector home page",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setPopup({ message: message, type: "error" });
|
setPopup({ message: message, type: "error" });
|
||||||
}
|
}
|
||||||
|
@@ -250,10 +250,21 @@ const DynamicConnectionForm: React.FC<DynamicConnectionFormProps> = ({
|
|||||||
{field.description}
|
{field.description}
|
||||||
</CredentialSubText>
|
</CredentialSubText>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
onChange={(
|
||||||
|
e: React.ChangeEvent<HTMLSelectElement>
|
||||||
|
) =>
|
||||||
|
updateValue(setFieldValue)(
|
||||||
|
field.name,
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
as="select"
|
as="select"
|
||||||
|
value={values[field.name]}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
className="w-full p-2 border bg-input border-border-medium rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
|
className="w-full p-2 border bg-input border-border-medium rounded-md bg-black
|
||||||
|
focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
|
||||||
>
|
>
|
||||||
<option value="">Select an option</option>
|
<option value="">Select an option</option>
|
||||||
{field.options?.map((option) => (
|
{field.options?.map((option) => (
|
||||||
|
@@ -102,6 +102,7 @@ export function AssistantsGallery({
|
|||||||
my-auto
|
my-auto
|
||||||
ml-2
|
ml-2
|
||||||
text-strong
|
text-strong
|
||||||
|
line-clamp-2
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{assistant.name}
|
{assistant.name}
|
||||||
|
@@ -4,25 +4,10 @@ import { useState } from "react";
|
|||||||
import { MinimalUserSnapshot, User } from "@/lib/types";
|
import { MinimalUserSnapshot, User } from "@/lib/types";
|
||||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
import { Divider, Text } from "@tremor/react";
|
import { Divider, Text } from "@tremor/react";
|
||||||
import {
|
import { FiEdit2, FiPlus, FiSearch, FiShare2 } from "react-icons/fi";
|
||||||
FiArrowDown,
|
|
||||||
FiArrowUp,
|
|
||||||
FiEdit2,
|
|
||||||
FiMoreHorizontal,
|
|
||||||
FiPlus,
|
|
||||||
FiSearch,
|
|
||||||
FiX,
|
|
||||||
FiShare2,
|
|
||||||
FiImage,
|
|
||||||
} from "react-icons/fi";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
||||||
import {
|
import { updateUserAssistantList } from "@/lib/assistants/updateAssistantPreferences";
|
||||||
addAssistantToList,
|
|
||||||
moveAssistantDown,
|
|
||||||
moveAssistantUp,
|
|
||||||
removeAssistantFromList,
|
|
||||||
} from "@/lib/assistants/updateAssistantPreferences";
|
|
||||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||||
import { DefaultPopover } from "@/components/popover/DefaultPopover";
|
import { DefaultPopover } from "@/components/popover/DefaultPopover";
|
||||||
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
|
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
|
||||||
@@ -34,8 +19,56 @@ import { AssistantSharingModal } from "./AssistantSharingModal";
|
|||||||
import { AssistantSharedStatusDisplay } from "../AssistantSharedStatus";
|
import { AssistantSharedStatusDisplay } from "../AssistantSharedStatus";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { errorHandlingFetcher } from "@/lib/fetcher";
|
import { errorHandlingFetcher } from "@/lib/fetcher";
|
||||||
import { AssistantTools, ToolsDisplay } from "../ToolsDisplay";
|
import { AssistantTools } from "../ToolsDisplay";
|
||||||
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
|
|
||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
closestCenter,
|
||||||
|
KeyboardSensor,
|
||||||
|
PointerSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
DragEndEvent,
|
||||||
|
} from "@dnd-kit/core";
|
||||||
|
import {
|
||||||
|
arrayMove,
|
||||||
|
SortableContext,
|
||||||
|
sortableKeyboardCoordinates,
|
||||||
|
verticalListSortingStrategy,
|
||||||
|
} from "@dnd-kit/sortable";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
|
||||||
|
import { DragHandle } from "@/components/table/DragHandle";
|
||||||
|
|
||||||
|
function DraggableAssistantListItem(props: any) {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
isDragging,
|
||||||
|
} = useSortable({ id: props.assistant.id.toString() });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
|
opacity: isDragging ? 0.9 : 1,
|
||||||
|
zIndex: isDragging ? 1000 : "auto",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={setNodeRef} style={style} className="flex items-center">
|
||||||
|
<div {...attributes} {...listeners} className="mr-2 cursor-grab">
|
||||||
|
<DragHandle />
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow">
|
||||||
|
<AssistantListItem {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function AssistantListItem({
|
function AssistantListItem({
|
||||||
assistant,
|
assistant,
|
||||||
@@ -50,7 +83,7 @@ function AssistantListItem({
|
|||||||
assistant: Persona;
|
assistant: Persona;
|
||||||
user: User | null;
|
user: User | null;
|
||||||
allUsers: MinimalUserSnapshot[];
|
allUsers: MinimalUserSnapshot[];
|
||||||
allAssistantIds: number[];
|
allAssistantIds: string[];
|
||||||
isFirst: boolean;
|
isFirst: boolean;
|
||||||
isLast: boolean;
|
isLast: boolean;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
@@ -59,7 +92,6 @@ function AssistantListItem({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [showSharingModal, setShowSharingModal] = useState(false);
|
const [showSharingModal, setShowSharingModal] = useState(false);
|
||||||
|
|
||||||
const currentChosenAssistants = user?.preferences?.chosen_assistants;
|
|
||||||
const isOwnedByUser = checkUserOwnsAssistant(user, assistant);
|
const isOwnedByUser = checkUserOwnsAssistant(user, assistant);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -83,7 +115,6 @@ function AssistantListItem({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="
|
className="
|
||||||
|
|
||||||
flex
|
flex
|
||||||
justify-between
|
justify-between
|
||||||
items-center
|
items-center
|
||||||
@@ -92,13 +123,13 @@ function AssistantListItem({
|
|||||||
<div className="w-3/4">
|
<div className="w-3/4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<AssistantIcon assistant={assistant} />
|
<AssistantIcon assistant={assistant} />
|
||||||
<h2 className="text-xl font-semibold my-auto ml-2">
|
<h2 className="text-xl line-clamp-2 font-semibold my-auto ml-2">
|
||||||
{assistant.name}
|
{assistant.name}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-sm mt-2">{assistant.description}</div>
|
<div className="text-sm mt-2">{assistant.description}</div>
|
||||||
<div className="mt-2 flex items-center gap-x-3">
|
<div className="mt-2 flex items-start gap-y-2 flex-col gap-x-3">
|
||||||
<AssistantSharedStatusDisplay assistant={assistant} user={user} />
|
<AssistantSharedStatusDisplay assistant={assistant} user={user} />
|
||||||
{assistant.tools.length != 0 && (
|
{assistant.tools.length != 0 && (
|
||||||
<AssistantTools list assistant={assistant} />
|
<AssistantTools list assistant={assistant} />
|
||||||
@@ -111,7 +142,10 @@ function AssistantListItem({
|
|||||||
{!assistant.is_public && (
|
{!assistant.is_public && (
|
||||||
<div
|
<div
|
||||||
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
||||||
onClick={() => setShowSharingModal(true)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowSharingModal(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<FiShare2 size={16} />
|
<FiShare2 size={16} />
|
||||||
</div>
|
</div>
|
||||||
@@ -124,158 +158,31 @@ function AssistantListItem({
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DefaultPopover
|
|
||||||
content={
|
|
||||||
<div className="hover:bg-hover rounded p-2 cursor-pointer">
|
|
||||||
<FiMoreHorizontal size={16} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
side="bottom"
|
|
||||||
align="start"
|
|
||||||
sideOffset={5}
|
|
||||||
>
|
|
||||||
{[
|
|
||||||
...(!isFirst
|
|
||||||
? [
|
|
||||||
<div
|
|
||||||
key="move-up"
|
|
||||||
className="flex items-center gap-x-2"
|
|
||||||
onClick={async () => {
|
|
||||||
const success = await moveAssistantUp(
|
|
||||||
assistant.id,
|
|
||||||
currentChosenAssistants || allAssistantIds
|
|
||||||
);
|
|
||||||
if (success) {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" has been moved up.`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
router.refresh();
|
|
||||||
} else {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" could not be moved up.`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiArrowUp /> Move Up
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(!isLast
|
|
||||||
? [
|
|
||||||
<div
|
|
||||||
key="move-down"
|
|
||||||
className="flex items-center gap-x-2"
|
|
||||||
onClick={async () => {
|
|
||||||
const success = await moveAssistantDown(
|
|
||||||
assistant.id,
|
|
||||||
currentChosenAssistants || allAssistantIds
|
|
||||||
);
|
|
||||||
if (success) {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" has been moved down.`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
router.refresh();
|
|
||||||
} else {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" could not be moved down.`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiArrowDown /> Move Down
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
isVisible ? (
|
|
||||||
<div
|
|
||||||
key="remove"
|
|
||||||
className="flex items-center gap-x-2"
|
|
||||||
onClick={async () => {
|
|
||||||
if (
|
|
||||||
currentChosenAssistants &&
|
|
||||||
currentChosenAssistants.length === 1
|
|
||||||
) {
|
|
||||||
setPopup({
|
|
||||||
message: `Cannot remove "${assistant.name}" - you must have at least one assistant.`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const success = await removeAssistantFromList(
|
|
||||||
assistant.id,
|
|
||||||
currentChosenAssistants || allAssistantIds
|
|
||||||
);
|
|
||||||
if (success) {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" has been removed from your list.`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
router.refresh();
|
|
||||||
} else {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" could not be removed from your list.`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiX /> {isOwnedByUser ? "Hide" : "Remove"}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
key="add"
|
|
||||||
className="flex items-center gap-x-2"
|
|
||||||
onClick={async () => {
|
|
||||||
const success = await addAssistantToList(
|
|
||||||
assistant.id,
|
|
||||||
currentChosenAssistants || allAssistantIds
|
|
||||||
);
|
|
||||||
if (success) {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" has been added to your list.`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
router.refresh();
|
|
||||||
} else {
|
|
||||||
setPopup({
|
|
||||||
message: `"${assistant.name}" could not be added to your list.`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FiPlus /> Add
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
]}
|
|
||||||
</DefaultPopover>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
export function AssistantsList({
|
||||||
interface AssistantsListProps {
|
user,
|
||||||
|
assistants,
|
||||||
|
}: {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
assistants: Persona[];
|
assistants: Persona[];
|
||||||
}
|
}) {
|
||||||
|
const [filteredAssistants, setFilteredAssistants] = useState(
|
||||||
|
orderAssistantsForUser(assistants, user)
|
||||||
|
);
|
||||||
|
|
||||||
export function AssistantsList({ user, assistants }: AssistantsListProps) {
|
|
||||||
const filteredAssistants = orderAssistantsForUser(assistants, user);
|
|
||||||
const ownedButHiddenAssistants = assistants.filter(
|
const ownedButHiddenAssistants = assistants.filter(
|
||||||
(assistant) =>
|
(assistant) =>
|
||||||
checkUserOwnsAssistant(user, assistant) &&
|
checkUserOwnsAssistant(user, assistant) &&
|
||||||
user?.preferences?.chosen_assistants &&
|
user?.preferences?.chosen_assistants &&
|
||||||
!user?.preferences?.chosen_assistants?.includes(assistant.id)
|
!user?.preferences?.chosen_assistants?.includes(assistant.id)
|
||||||
);
|
);
|
||||||
const allAssistantIds = assistants.map((assistant) => assistant.id);
|
const allAssistantIds = assistants.map((assistant) =>
|
||||||
|
assistant.id.toString()
|
||||||
|
);
|
||||||
|
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
|
|
||||||
@@ -284,6 +191,32 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
|
|||||||
errorHandlingFetcher
|
errorHandlingFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const sensors = useSensors(
|
||||||
|
useSensor(PointerSensor),
|
||||||
|
useSensor(KeyboardSensor, {
|
||||||
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
async function handleDragEnd(event: DragEndEvent) {
|
||||||
|
const { active, over } = event;
|
||||||
|
|
||||||
|
if (over && active.id !== over.id) {
|
||||||
|
setFilteredAssistants((assistants) => {
|
||||||
|
const oldIndex = assistants.findIndex(
|
||||||
|
(a) => a.id.toString() === active.id
|
||||||
|
);
|
||||||
|
const newIndex = assistants.findIndex(
|
||||||
|
(a) => a.id.toString() === over.id
|
||||||
|
);
|
||||||
|
const newAssistants = arrayMove(assistants, oldIndex, newIndex);
|
||||||
|
|
||||||
|
updateUserAssistantList(newAssistants.map((a) => a.id));
|
||||||
|
return newAssistants;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{popup}
|
{popup}
|
||||||
@@ -323,24 +256,35 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
|
|||||||
<Text>
|
<Text>
|
||||||
The order the assistants appear below will be the order they appear in
|
The order the assistants appear below will be the order they appear in
|
||||||
the Assistants dropdown. The first assistant listed will be your
|
the Assistants dropdown. The first assistant listed will be your
|
||||||
default assistant when you start a new chat.
|
default assistant when you start a new chat. Drag and drop to reorder.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
<DndContext
|
||||||
|
sensors={sensors}
|
||||||
|
collisionDetection={closestCenter}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
>
|
||||||
|
<SortableContext
|
||||||
|
items={filteredAssistants.map((a) => a.id.toString())}
|
||||||
|
strategy={verticalListSortingStrategy}
|
||||||
|
>
|
||||||
<div className="w-full p-4 mt-3">
|
<div className="w-full p-4 mt-3">
|
||||||
{filteredAssistants.map((assistant, index) => (
|
{filteredAssistants.map((assistant, index) => (
|
||||||
<AssistantListItem
|
<DraggableAssistantListItem
|
||||||
key={assistant.id}
|
key={assistant.id}
|
||||||
assistant={assistant}
|
assistant={assistant}
|
||||||
user={user}
|
user={user}
|
||||||
allAssistantIds={allAssistantIds}
|
allAssistantIds={allAssistantIds}
|
||||||
allUsers={users || []}
|
allUsers={users || []}
|
||||||
isFirst={index === 0}
|
isFirst={false}
|
||||||
isLast={index === filteredAssistants.length - 1}
|
isLast={index === filteredAssistants.length - 1}
|
||||||
isVisible
|
isVisible
|
||||||
setPopup={setPopup}
|
setPopup={setPopup}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
|
||||||
{ownedButHiddenAssistants.length > 0 && (
|
{ownedButHiddenAssistants.length > 0 && (
|
||||||
<>
|
<>
|
||||||
@@ -362,7 +306,7 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
|
|||||||
allAssistantIds={allAssistantIds}
|
allAssistantIds={allAssistantIds}
|
||||||
allUsers={users || []}
|
allUsers={users || []}
|
||||||
isFirst={index === 0}
|
isFirst={index === 0}
|
||||||
isLast={index === filteredAssistants.length - 1}
|
isLast={index === ownedButHiddenAssistants.length - 1}
|
||||||
isVisible={false}
|
isVisible={false}
|
||||||
setPopup={setPopup}
|
setPopup={setPopup}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,16 +1,9 @@
|
|||||||
import { HistorySidebar } from "@/app/chat/sessionSidebar/HistorySidebar";
|
|
||||||
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||||
import { UserDropdown } from "@/components/UserDropdown";
|
|
||||||
import { ChatProvider } from "@/components/context/ChatContext";
|
import { ChatProvider } from "@/components/context/ChatContext";
|
||||||
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
|
||||||
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
|
||||||
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
import { fetchChatData } from "@/lib/chat/fetchChatData";
|
||||||
import { unstable_noStore as noStore } from "next/cache";
|
import { unstable_noStore as noStore } from "next/cache";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { AssistantsList } from "./AssistantsList";
|
|
||||||
import { Logo } from "@/components/Logo";
|
|
||||||
import FixedLogo from "@/app/chat/shared_chat_search/FixedLogo";
|
|
||||||
import SidebarWrapper from "../SidebarWrapper";
|
|
||||||
import WrappedAssistantsMine from "./WrappedAssistantsMine";
|
import WrappedAssistantsMine from "./WrappedAssistantsMine";
|
||||||
|
|
||||||
export default async function GalleryPage({
|
export default async function GalleryPage({
|
||||||
|
@@ -55,7 +55,6 @@ import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
|
|||||||
import { FiArrowDown } from "react-icons/fi";
|
import { FiArrowDown } from "react-icons/fi";
|
||||||
import { ChatIntro } from "./ChatIntro";
|
import { ChatIntro } from "./ChatIntro";
|
||||||
import { AIMessage, HumanMessage } from "./message/Messages";
|
import { AIMessage, HumanMessage } from "./message/Messages";
|
||||||
import { ThreeDots } from "react-loader-spinner";
|
|
||||||
import { StarterMessage } from "./StarterMessage";
|
import { StarterMessage } from "./StarterMessage";
|
||||||
import { AnswerPiecePacket, DanswerDocument } from "@/lib/search/interfaces";
|
import { AnswerPiecePacket, DanswerDocument } from "@/lib/search/interfaces";
|
||||||
import { buildFilters } from "@/lib/search/utils";
|
import { buildFilters } from "@/lib/search/utils";
|
||||||
@@ -1169,6 +1168,7 @@ export function ChatPage({
|
|||||||
>
|
>
|
||||||
<div className="w-full relative">
|
<div className="w-full relative">
|
||||||
<HistorySidebar
|
<HistorySidebar
|
||||||
|
reset={() => setMessage("")}
|
||||||
page="chat"
|
page="chat"
|
||||||
ref={innerSidebarElementRef}
|
ref={innerSidebarElementRef}
|
||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
@@ -1192,6 +1192,7 @@ export function ChatPage({
|
|||||||
<div className="flex h-full flex-col w-full">
|
<div className="flex h-full flex-col w-full">
|
||||||
{liveAssistant && (
|
{liveAssistant && (
|
||||||
<FunctionalHeader
|
<FunctionalHeader
|
||||||
|
reset={() => setMessage("")}
|
||||||
page="chat"
|
page="chat"
|
||||||
setSharingModalVisible={
|
setSharingModalVisible={
|
||||||
chatSessionIdRef.current !== null
|
chatSessionIdRef.current !== null
|
||||||
|
@@ -1,35 +1,83 @@
|
|||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
closestCenter,
|
||||||
|
KeyboardSensor,
|
||||||
|
PointerSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
DragEndEvent,
|
||||||
|
} from "@dnd-kit/core";
|
||||||
|
import {
|
||||||
|
arrayMove,
|
||||||
|
SortableContext,
|
||||||
|
sortableKeyboardCoordinates,
|
||||||
|
verticalListSortingStrategy,
|
||||||
|
} from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
import { LLMProviderDescriptor } from "@/app/admin/models/llm/interfaces";
|
import { LLMProviderDescriptor } from "@/app/admin/models/llm/interfaces";
|
||||||
import { AssistantTools } from "@/app/assistants/ToolsDisplay";
|
|
||||||
import { Bubble } from "@/components/Bubble";
|
|
||||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
|
||||||
import { getDisplayNameForModel } from "@/lib/hooks";
|
|
||||||
import { getFinalLLM } from "@/lib/llm/utils";
|
import { getFinalLLM } from "@/lib/llm/utils";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FiBookmark, FiPlus } from "react-icons/fi";
|
import { updateUserAssistantList } from "@/lib/assistants/updateAssistantPreferences";
|
||||||
|
import { DraggableAssistantCard } from "@/components/assistants/AssistantCards";
|
||||||
interface AssistantsTabProps {
|
|
||||||
selectedAssistant: Persona;
|
|
||||||
availableAssistants: Persona[];
|
|
||||||
llmProviders: LLMProviderDescriptor[];
|
|
||||||
onSelect: (assistant: Persona) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AssistantsTab({
|
export function AssistantsTab({
|
||||||
selectedAssistant,
|
selectedAssistant,
|
||||||
availableAssistants,
|
availableAssistants,
|
||||||
llmProviders,
|
llmProviders,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: AssistantsTabProps) {
|
}: {
|
||||||
|
selectedAssistant: Persona;
|
||||||
|
availableAssistants: Persona[];
|
||||||
|
llmProviders: LLMProviderDescriptor[];
|
||||||
|
onSelect: (assistant: Persona) => void;
|
||||||
|
}) {
|
||||||
const [_, llmName] = getFinalLLM(llmProviders, null, null);
|
const [_, llmName] = getFinalLLM(llmProviders, null, null);
|
||||||
|
const [assistants, setAssistants] = useState(availableAssistants);
|
||||||
|
|
||||||
|
const sensors = useSensors(
|
||||||
|
useSensor(PointerSensor),
|
||||||
|
useSensor(KeyboardSensor, {
|
||||||
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleDragEnd(event: DragEndEvent) {
|
||||||
|
const { active, over } = event;
|
||||||
|
|
||||||
|
if (over && active.id !== over.id) {
|
||||||
|
setAssistants((items) => {
|
||||||
|
const oldIndex = items.findIndex(
|
||||||
|
(item) => item.id.toString() === active.id
|
||||||
|
);
|
||||||
|
const newIndex = items.findIndex(
|
||||||
|
(item) => item.id.toString() === over.id
|
||||||
|
);
|
||||||
|
const updatedAssistants = arrayMove(items, oldIndex, newIndex);
|
||||||
|
|
||||||
|
updateUserAssistantList(updatedAssistants.map((a) => a.id));
|
||||||
|
|
||||||
|
return updatedAssistants;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<h3 className="px-4 text-lg font-semibold">Change Assistant</h3>
|
<h3 className="px-4 text-lg font-semibold">Change Assistant</h3>
|
||||||
<div className="px-2 pb-2 mx-2 max-h-[500px] overflow-y-scroll my-3 grid grid-cols-1 gap-4">
|
<DndContext
|
||||||
{availableAssistants.map((assistant) => (
|
sensors={sensors}
|
||||||
<AssistantCard
|
collisionDetection={closestCenter}
|
||||||
key={assistant.id}
|
onDragEnd={handleDragEnd}
|
||||||
|
>
|
||||||
|
<SortableContext
|
||||||
|
items={assistants.map((a) => a.id.toString())}
|
||||||
|
strategy={verticalListSortingStrategy}
|
||||||
|
>
|
||||||
|
<div className="px-2 pb-2 mx-2 max-h-[500px] miniscroll overflow-y-scroll my-3 grid grid-cols-1 gap-4">
|
||||||
|
{assistants.map((assistant) => (
|
||||||
|
<DraggableAssistantCard
|
||||||
|
key={assistant.id.toString()}
|
||||||
assistant={assistant}
|
assistant={assistant}
|
||||||
isSelected={selectedAssistant.id === assistant.id}
|
isSelected={selectedAssistant.id === assistant.id}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
@@ -37,72 +85,8 @@ export function AssistantsTab({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssistantCard = ({
|
|
||||||
assistant,
|
|
||||||
isSelected,
|
|
||||||
onSelect,
|
|
||||||
llmName,
|
|
||||||
}: {
|
|
||||||
assistant: Persona;
|
|
||||||
isSelected: boolean;
|
|
||||||
onSelect: (assistant: Persona) => void;
|
|
||||||
llmName: string;
|
|
||||||
}) => {
|
|
||||||
const [hovering, setHovering] = useState(false);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onClick={() => onSelect(assistant)}
|
|
||||||
key={assistant.id}
|
|
||||||
className={`
|
|
||||||
p-4
|
|
||||||
cursor-pointer
|
|
||||||
border
|
|
||||||
hover:bg-hover
|
|
||||||
shadow-md
|
|
||||||
rounded
|
|
||||||
rounded-lg
|
|
||||||
border-border
|
|
||||||
`}
|
|
||||||
onMouseEnter={() => setHovering(true)}
|
|
||||||
onMouseLeave={() => setHovering(false)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center mb-2">
|
|
||||||
<AssistantIcon assistant={assistant} />
|
|
||||||
<div className="ml-2 line-clamp-1 ellipsis font-bold text-sm text-emphasis">
|
|
||||||
{assistant.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-xs text-subtle mb-2 text-wrap mt-2 line-clamp-3 py-1">
|
|
||||||
{assistant.description}
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 flex flex-col gap-y-1">
|
|
||||||
{assistant.document_sets.length > 0 && (
|
|
||||||
<div className="text-xs text-subtle flex flex-wrap gap-2">
|
|
||||||
<p className="my-auto font-medium">Document Sets:</p>
|
|
||||||
{assistant.document_sets.map((set) => (
|
|
||||||
<Bubble key={set.id} isSelected={false}>
|
|
||||||
<div className="flex flex-row gap-1">
|
|
||||||
<FiBookmark className="mr-1 my-auto" />
|
|
||||||
{set.name}
|
|
||||||
</div>
|
|
||||||
</Bubble>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="text-xs text-subtle">
|
|
||||||
<span className="font-semibold">Default model:</span>{" "}
|
|
||||||
{getDisplayNameForModel(
|
|
||||||
assistant.llm_model_version_override || llmName
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<AssistantTools hovered={hovering} assistant={assistant} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@@ -53,11 +53,13 @@ interface HistorySidebarProps {
|
|||||||
toggleSidebar?: () => void;
|
toggleSidebar?: () => void;
|
||||||
toggled?: boolean;
|
toggled?: boolean;
|
||||||
removeToggle?: () => void;
|
removeToggle?: () => void;
|
||||||
|
reset?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
|
reset = () => null,
|
||||||
toggled,
|
toggled,
|
||||||
page,
|
page,
|
||||||
existingChats,
|
existingChats,
|
||||||
@@ -69,7 +71,6 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
|||||||
},
|
},
|
||||||
ref: ForwardedRef<HTMLDivElement>
|
ref: ForwardedRef<HTMLDivElement>
|
||||||
) => {
|
) => {
|
||||||
const commandSymbol = KeyboardSymbol();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
|
|
||||||
@@ -85,9 +86,19 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
|||||||
if (!combinedSettings) {
|
if (!combinedSettings) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const settings = combinedSettings.settings;
|
|
||||||
const enterpriseSettings = combinedSettings.enterpriseSettings;
|
const enterpriseSettings = combinedSettings.enterpriseSettings;
|
||||||
|
|
||||||
|
const handleNewChat = () => {
|
||||||
|
reset();
|
||||||
|
const newChatUrl =
|
||||||
|
`/${page}` +
|
||||||
|
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA && currentChatSession
|
||||||
|
? `?assistantId=${currentChatSession.persona_id}`
|
||||||
|
: "");
|
||||||
|
router.push(newChatUrl);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{popup}
|
{popup}
|
||||||
@@ -144,19 +155,13 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
|||||||
|
|
||||||
{page == "chat" && (
|
{page == "chat" && (
|
||||||
<div className="mx-3 mt-4 gap-y-1 flex-col flex gap-x-1.5 items-center items-center">
|
<div className="mx-3 mt-4 gap-y-1 flex-col flex gap-x-1.5 items-center items-center">
|
||||||
<Link
|
<button
|
||||||
href={
|
onClick={handleNewChat}
|
||||||
"/chat" +
|
|
||||||
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA &&
|
|
||||||
currentChatSession
|
|
||||||
? `?assistantId=${currentChatSession.persona_id}`
|
|
||||||
: "")
|
|
||||||
}
|
|
||||||
className="w-full p-2 bg-white border-border border rounded items-center hover:bg-background-200 cursor-pointer transition-all duration-150 flex gap-x-2"
|
className="w-full p-2 bg-white border-border border rounded items-center hover:bg-background-200 cursor-pointer transition-all duration-150 flex gap-x-2"
|
||||||
>
|
>
|
||||||
<FiEdit className="flex-none " />
|
<FiEdit className="flex-none " />
|
||||||
<p className="my-auto flex items-center text-sm">New Chat</p>
|
<p className="my-auto flex items-center text-sm">New Chat</p>
|
||||||
</Link>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
112
web/src/components/assistants/AssistantCards.tsx
Normal file
112
web/src/components/assistants/AssistantCards.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
|
||||||
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
|
import { AssistantTools } from "@/app/assistants/ToolsDisplay";
|
||||||
|
import { Bubble } from "@/components/Bubble";
|
||||||
|
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||||
|
import { getDisplayNameForModel } from "@/lib/hooks";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { FiBookmark } from "react-icons/fi";
|
||||||
|
import { MdDragIndicator } from "react-icons/md";
|
||||||
|
|
||||||
|
export const AssistantCard = ({
|
||||||
|
assistant,
|
||||||
|
isSelected,
|
||||||
|
onSelect,
|
||||||
|
llmName,
|
||||||
|
}: {
|
||||||
|
assistant: Persona;
|
||||||
|
isSelected: boolean;
|
||||||
|
onSelect: (assistant: Persona) => void;
|
||||||
|
llmName: string;
|
||||||
|
}) => {
|
||||||
|
const [hovering, setHovering] = useState(false);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={() => onSelect(assistant)}
|
||||||
|
className={`
|
||||||
|
p-4
|
||||||
|
cursor-pointer
|
||||||
|
border
|
||||||
|
${isSelected ? "bg-hover" : "hover:bg-hover-light"}
|
||||||
|
shadow-md
|
||||||
|
rounded-lg
|
||||||
|
border-border
|
||||||
|
max-w-full
|
||||||
|
flex items-center
|
||||||
|
overflow-x-hidden
|
||||||
|
`}
|
||||||
|
onMouseEnter={() => setHovering(true)}
|
||||||
|
onMouseLeave={() => setHovering(false)}
|
||||||
|
>
|
||||||
|
<div className="flex-grow">
|
||||||
|
<div className="flex items-center mb-2">
|
||||||
|
<AssistantIcon assistant={assistant} />
|
||||||
|
<div className="ml-2 ellipsis font-bold text-sm text-emphasis">
|
||||||
|
{assistant.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-xs text-wrap text-subtle mb-2 mt-2 line-clamp-3 py-1">
|
||||||
|
{assistant.description}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex flex-col gap-y-1">
|
||||||
|
{assistant.document_sets.length > 0 && (
|
||||||
|
<div className="text-xs text-subtle flex flex-wrap gap-2">
|
||||||
|
<p className="my-auto font-medium">Document Sets:</p>
|
||||||
|
{assistant.document_sets.map((set) => (
|
||||||
|
<Bubble key={set.id} isSelected={false}>
|
||||||
|
<div className="flex flex-row gap-1">
|
||||||
|
<FiBookmark className="mr-1 my-auto" />
|
||||||
|
{set.name}
|
||||||
|
</div>
|
||||||
|
</Bubble>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-xs text-subtle">
|
||||||
|
<span className="font-semibold">Default model:</span>{" "}
|
||||||
|
{getDisplayNameForModel(
|
||||||
|
assistant.llm_model_version_override || llmName
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<AssistantTools hovered={hovering} assistant={assistant} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function DraggableAssistantCard(props: {
|
||||||
|
assistant: Persona;
|
||||||
|
isSelected: boolean;
|
||||||
|
onSelect: (assistant: Persona) => void;
|
||||||
|
llmName: string;
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
isDragging,
|
||||||
|
} = useSortable({ id: props.assistant.id.toString() });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
|
opacity: isDragging ? 0.9 : 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={setNodeRef} style={style} className="flex items-center">
|
||||||
|
<div {...attributes} {...listeners} className="mr-1 cursor-grab">
|
||||||
|
<MdDragIndicator className="h-3 w-3 flex-none" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow">
|
||||||
|
<AssistantCard {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -16,6 +16,7 @@ import KeyboardSymbol from "@/lib/browserUtilities";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { SettingsContext } from "../settings/SettingsProvider";
|
import { SettingsContext } from "../settings/SettingsProvider";
|
||||||
import { pageType } from "@/app/chat/sessionSidebar/types";
|
import { pageType } from "@/app/chat/sessionSidebar/types";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export default function FunctionalHeader({
|
export default function FunctionalHeader({
|
||||||
user,
|
user,
|
||||||
@@ -23,7 +24,9 @@ export default function FunctionalHeader({
|
|||||||
currentChatSession,
|
currentChatSession,
|
||||||
setSharingModalVisible,
|
setSharingModalVisible,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
|
reset = () => null,
|
||||||
}: {
|
}: {
|
||||||
|
reset?: () => void;
|
||||||
page: pageType;
|
page: pageType;
|
||||||
user: User | null;
|
user: User | null;
|
||||||
currentChatSession?: ChatSession | null | undefined;
|
currentChatSession?: ChatSession | null | undefined;
|
||||||
@@ -58,6 +61,17 @@ export default function FunctionalHeader({
|
|||||||
window.removeEventListener("keydown", handleKeyDown);
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleNewChat = () => {
|
||||||
|
reset();
|
||||||
|
const newChatUrl =
|
||||||
|
`/${page}` +
|
||||||
|
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA && currentChatSession
|
||||||
|
? `?assistantId=${currentChatSession.persona_id}`
|
||||||
|
: "");
|
||||||
|
router.push(newChatUrl);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="pb-6 left-0 sticky top-0 z-20 w-full relative flex">
|
<div className="pb-6 left-0 sticky top-0 z-20 w-full relative flex">
|
||||||
<div className="mt-2 mx-4 text-text-700 flex w-full">
|
<div className="mt-2 mx-4 text-text-700 flex w-full">
|
||||||
@@ -86,21 +100,12 @@ export default function FunctionalHeader({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{page == "chat" && (
|
{page == "chat" && (
|
||||||
<Tooltip delayDuration={1000} content={`${commandSymbol}U`}>
|
<Tooltip delayDuration={1000} content="New Chat">
|
||||||
<Link
|
<button className="mobile:hidden my-auto" onClick={handleNewChat}>
|
||||||
className="mobile:hidden my-auto"
|
|
||||||
href={
|
|
||||||
`/${page}` +
|
|
||||||
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA &&
|
|
||||||
currentChatSession
|
|
||||||
? `?assistantId=${currentChatSession.persona_id}`
|
|
||||||
: "")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="cursor-pointer ml-2 flex-none text-text-700 hover:text-text-600 transition-colors duration-300">
|
<div className="cursor-pointer ml-2 flex-none text-text-700 hover:text-text-600 transition-colors duration-300">
|
||||||
<NewChatIcon size={20} />
|
<NewChatIcon size={20} />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -541,6 +541,7 @@ export const SearchSection = ({
|
|||||||
>
|
>
|
||||||
<div className="w-full relative">
|
<div className="w-full relative">
|
||||||
<HistorySidebar
|
<HistorySidebar
|
||||||
|
reset={() => setQuery("")}
|
||||||
page="search"
|
page="search"
|
||||||
ref={innerSidebarElementRef}
|
ref={innerSidebarElementRef}
|
||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
@@ -552,6 +553,7 @@ export const SearchSection = ({
|
|||||||
|
|
||||||
<div className="absolute left-0 w-full top-0">
|
<div className="absolute left-0 w-full top-0">
|
||||||
<FunctionalHeader
|
<FunctionalHeader
|
||||||
|
reset={() => setQuery("")}
|
||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
page="search"
|
page="search"
|
||||||
user={user}
|
user={user}
|
||||||
|
@@ -4,9 +4,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
TableHeaderCell,
|
TableHeaderCell,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
|
||||||
} from "@tremor/react";
|
} from "@tremor/react";
|
||||||
import { DraggableTableBody } from "./DraggableTableBody";
|
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
closestCenter,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
async function updateUserAssistantList(
|
export async function updateUserAssistantList(
|
||||||
chosenAssistants: number[]
|
chosenAssistants: number[]
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const response = await fetch("/api/user/assistant-list", {
|
const response = await fetch("/api/user/assistant-list", {
|
||||||
|
@@ -213,7 +213,6 @@ export async function fetchChatData(searchParams: {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// // TODO check for image capabilities and enable if so
|
|
||||||
|
|
||||||
let folders: Folder[] = [];
|
let folders: Folder[] = [];
|
||||||
if (foldersResponse?.ok) {
|
if (foldersResponse?.ok) {
|
||||||
|
Reference in New Issue
Block a user