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:
pablodanswer 2024-08-05 11:22:57 -07:00 committed by GitHub
parent cd22cca4e8
commit 5825d01d53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 374 additions and 308 deletions

View File

@ -248,7 +248,6 @@ def create_initial_default_connector(db_session: Session) -> None:
default_connector_id = 0
default_connector = fetch_connector_by_id(default_connector_id, db_session)
if default_connector is not None:
# Check if the existing connector has the correct values
if (
default_connector.source != DocumentSource.INGESTION_API
or default_connector.input_type != InputType.LOAD_STATE

View File

@ -211,7 +211,7 @@ export default function AddConnector({
}
// Without credential
if (isSuccess && response && credentialActivated) {
if (credentialActivated && isSuccess && response) {
const credential =
currentCredential || liveGDriveCredential || liveGmailCredential;
const linkCredentialResponse = await linkCredential(
@ -228,7 +228,19 @@ export default function AddConnector({
setTimeout(() => {
window.open("/admin/indexing/status", "_self");
}, 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 {
setPopup({ message: message, type: "error" });
}

View File

@ -250,10 +250,21 @@ const DynamicConnectionForm: React.FC<DynamicConnectionFormProps> = ({
{field.description}
</CredentialSubText>
)}
<Field
onChange={(
e: React.ChangeEvent<HTMLSelectElement>
) =>
updateValue(setFieldValue)(
field.name,
e.target.value
)
}
as="select"
value={values[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>
{field.options?.map((option) => (

View File

@ -102,6 +102,7 @@ export function AssistantsGallery({
my-auto
ml-2
text-strong
line-clamp-2
"
>
{assistant.name}

View File

@ -4,25 +4,10 @@ import { useState } from "react";
import { MinimalUserSnapshot, User } from "@/lib/types";
import { Persona } from "@/app/admin/assistants/interfaces";
import { Divider, Text } from "@tremor/react";
import {
FiArrowDown,
FiArrowUp,
FiEdit2,
FiMoreHorizontal,
FiPlus,
FiSearch,
FiX,
FiShare2,
FiImage,
} from "react-icons/fi";
import { FiEdit2, FiPlus, FiSearch, FiShare2 } from "react-icons/fi";
import Link from "next/link";
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
import {
addAssistantToList,
moveAssistantDown,
moveAssistantUp,
removeAssistantFromList,
} from "@/lib/assistants/updateAssistantPreferences";
import { updateUserAssistantList } from "@/lib/assistants/updateAssistantPreferences";
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
import { DefaultPopover } from "@/components/popover/DefaultPopover";
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
@ -34,8 +19,56 @@ import { AssistantSharingModal } from "./AssistantSharingModal";
import { AssistantSharedStatusDisplay } from "../AssistantSharedStatus";
import useSWR from "swr";
import { errorHandlingFetcher } from "@/lib/fetcher";
import { AssistantTools, ToolsDisplay } from "../ToolsDisplay";
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
import { AssistantTools } from "../ToolsDisplay";
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({
assistant,
@ -50,7 +83,7 @@ function AssistantListItem({
assistant: Persona;
user: User | null;
allUsers: MinimalUserSnapshot[];
allAssistantIds: number[];
allAssistantIds: string[];
isFirst: boolean;
isLast: boolean;
isVisible: boolean;
@ -59,7 +92,6 @@ function AssistantListItem({
const router = useRouter();
const [showSharingModal, setShowSharingModal] = useState(false);
const currentChosenAssistants = user?.preferences?.chosen_assistants;
const isOwnedByUser = checkUserOwnsAssistant(user, assistant);
return (
@ -83,7 +115,6 @@ function AssistantListItem({
>
<div
className="
flex
justify-between
items-center
@ -92,13 +123,13 @@ function AssistantListItem({
<div className="w-3/4">
<div className="flex items-center">
<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}
</h2>
</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} />
{assistant.tools.length != 0 && (
<AssistantTools list assistant={assistant} />
@ -111,7 +142,10 @@ function AssistantListItem({
{!assistant.is_public && (
<div
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
onClick={() => setShowSharingModal(true)}
onClick={(e) => {
e.stopPropagation();
setShowSharingModal(true);
}}
>
<FiShare2 size={16} />
</div>
@ -124,158 +158,31 @@ function AssistantListItem({
</Link>
</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>
</>
);
}
interface AssistantsListProps {
export function AssistantsList({
user,
assistants,
}: {
user: User | null;
assistants: Persona[];
}
}) {
const [filteredAssistants, setFilteredAssistants] = useState(
orderAssistantsForUser(assistants, user)
);
export function AssistantsList({ user, assistants }: AssistantsListProps) {
const filteredAssistants = orderAssistantsForUser(assistants, user);
const ownedButHiddenAssistants = assistants.filter(
(assistant) =>
checkUserOwnsAssistant(user, assistant) &&
user?.preferences?.chosen_assistants &&
!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();
@ -284,6 +191,32 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
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 (
<>
{popup}
@ -323,24 +256,35 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
<Text>
The order the assistants appear below will be the order they appear in
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>
<div className="w-full p-4 mt-3">
{filteredAssistants.map((assistant, index) => (
<AssistantListItem
key={assistant.id}
assistant={assistant}
user={user}
allAssistantIds={allAssistantIds}
allUsers={users || []}
isFirst={index === 0}
isLast={index === filteredAssistants.length - 1}
isVisible
setPopup={setPopup}
/>
))}
</div>
<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">
{filteredAssistants.map((assistant, index) => (
<DraggableAssistantListItem
key={assistant.id}
assistant={assistant}
user={user}
allAssistantIds={allAssistantIds}
allUsers={users || []}
isFirst={false}
isLast={index === filteredAssistants.length - 1}
isVisible
setPopup={setPopup}
/>
))}
</div>
</SortableContext>
</DndContext>
{ownedButHiddenAssistants.length > 0 && (
<>
@ -362,7 +306,7 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) {
allAssistantIds={allAssistantIds}
allUsers={users || []}
isFirst={index === 0}
isLast={index === filteredAssistants.length - 1}
isLast={index === ownedButHiddenAssistants.length - 1}
isVisible={false}
setPopup={setPopup}
/>

View File

@ -1,16 +1,9 @@
import { HistorySidebar } from "@/app/chat/sessionSidebar/HistorySidebar";
import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
import { UserDropdown } from "@/components/UserDropdown";
import { ChatProvider } from "@/components/context/ChatContext";
import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
import { fetchChatData } from "@/lib/chat/fetchChatData";
import { unstable_noStore as noStore } from "next/cache";
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";
export default async function GalleryPage({

View File

@ -55,7 +55,6 @@ import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
import { FiArrowDown } from "react-icons/fi";
import { ChatIntro } from "./ChatIntro";
import { AIMessage, HumanMessage } from "./message/Messages";
import { ThreeDots } from "react-loader-spinner";
import { StarterMessage } from "./StarterMessage";
import { AnswerPiecePacket, DanswerDocument } from "@/lib/search/interfaces";
import { buildFilters } from "@/lib/search/utils";
@ -1169,6 +1168,7 @@ export function ChatPage({
>
<div className="w-full relative">
<HistorySidebar
reset={() => setMessage("")}
page="chat"
ref={innerSidebarElementRef}
toggleSidebar={toggleSidebar}
@ -1192,6 +1192,7 @@ export function ChatPage({
<div className="flex h-full flex-col w-full">
{liveAssistant && (
<FunctionalHeader
reset={() => setMessage("")}
page="chat"
setSharingModalVisible={
chatSessionIdRef.current !== null

View File

@ -1,108 +1,92 @@
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 { 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 React, { useState } from "react";
import { FiBookmark, FiPlus } from "react-icons/fi";
interface AssistantsTabProps {
selectedAssistant: Persona;
availableAssistants: Persona[];
llmProviders: LLMProviderDescriptor[];
onSelect: (assistant: Persona) => void;
}
import { updateUserAssistantList } from "@/lib/assistants/updateAssistantPreferences";
import { DraggableAssistantCard } from "@/components/assistants/AssistantCards";
export function AssistantsTab({
selectedAssistant,
availableAssistants,
llmProviders,
onSelect,
}: AssistantsTabProps) {
}: {
selectedAssistant: Persona;
availableAssistants: Persona[];
llmProviders: LLMProviderDescriptor[];
onSelect: (assistant: Persona) => void;
}) {
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 (
<div className="py-4">
<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">
{availableAssistants.map((assistant) => (
<AssistantCard
key={assistant.id}
assistant={assistant}
isSelected={selectedAssistant.id === assistant.id}
onSelect={onSelect}
llmName={llmName}
/>
))}
</div>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
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}
isSelected={selectedAssistant.id === assistant.id}
onSelect={onSelect}
llmName={llmName}
/>
))}
</div>
</SortableContext>
</DndContext>
</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>
);
};

View File

@ -53,11 +53,13 @@ interface HistorySidebarProps {
toggleSidebar?: () => void;
toggled?: boolean;
removeToggle?: () => void;
reset?: () => void;
}
export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
(
{
reset = () => null,
toggled,
page,
existingChats,
@ -69,7 +71,6 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
},
ref: ForwardedRef<HTMLDivElement>
) => {
const commandSymbol = KeyboardSymbol();
const router = useRouter();
const { popup, setPopup } = usePopup();
@ -85,9 +86,19 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
if (!combinedSettings) {
return null;
}
const settings = combinedSettings.settings;
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 (
<>
{popup}
@ -144,19 +155,13 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
{page == "chat" && (
<div className="mx-3 mt-4 gap-y-1 flex-col flex gap-x-1.5 items-center items-center">
<Link
href={
"/chat" +
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA &&
currentChatSession
? `?assistantId=${currentChatSession.persona_id}`
: "")
}
<button
onClick={handleNewChat}
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 " />
<p className="my-auto flex items-center text-sm">New Chat</p>
</Link>
</button>
<button
onClick={() =>

View 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>
);
}

View File

@ -16,6 +16,7 @@ import KeyboardSymbol from "@/lib/browserUtilities";
import Link from "next/link";
import { SettingsContext } from "../settings/SettingsProvider";
import { pageType } from "@/app/chat/sessionSidebar/types";
import { useRouter } from "next/navigation";
export default function FunctionalHeader({
user,
@ -23,7 +24,9 @@ export default function FunctionalHeader({
currentChatSession,
setSharingModalVisible,
toggleSidebar,
reset = () => null,
}: {
reset?: () => void;
page: pageType;
user: User | null;
currentChatSession?: ChatSession | null | undefined;
@ -58,6 +61,17 @@ export default function FunctionalHeader({
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 (
<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">
@ -86,21 +100,12 @@ export default function FunctionalHeader({
</div>
{page == "chat" && (
<Tooltip delayDuration={1000} content={`${commandSymbol}U`}>
<Link
className="mobile:hidden my-auto"
href={
`/${page}` +
(NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA &&
currentChatSession
? `?assistantId=${currentChatSession.persona_id}`
: "")
}
>
<Tooltip delayDuration={1000} content="New Chat">
<button className="mobile:hidden my-auto" onClick={handleNewChat}>
<div className="cursor-pointer ml-2 flex-none text-text-700 hover:text-text-600 transition-colors duration-300">
<NewChatIcon size={20} />
</div>
</Link>
</button>
</Tooltip>
)}
</div>

View File

@ -541,6 +541,7 @@ export const SearchSection = ({
>
<div className="w-full relative">
<HistorySidebar
reset={() => setQuery("")}
page="search"
ref={innerSidebarElementRef}
toggleSidebar={toggleSidebar}
@ -552,6 +553,7 @@ export const SearchSection = ({
<div className="absolute left-0 w-full top-0">
<FunctionalHeader
reset={() => setQuery("")}
toggleSidebar={toggleSidebar}
page="search"
user={user}

View File

@ -4,9 +4,7 @@ import {
TableRow,
TableHeaderCell,
TableBody,
TableCell,
} from "@tremor/react";
import { DraggableTableBody } from "./DraggableTableBody";
import React, { useMemo, useState } from "react";
import {
closestCenter,

View File

@ -1,4 +1,4 @@
async function updateUserAssistantList(
export async function updateUserAssistantList(
chosenAssistants: number[]
): Promise<boolean> {
const response = await fetch("/api/user/assistant-list", {

View File

@ -213,7 +213,6 @@ export async function fetchChatData(searchParams: {
)
);
}
// // TODO check for image capabilities and enable if so
let folders: Folder[] = [];
if (foldersResponse?.ok) {