mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🎨 style: update Assistants builder (#3397)
* style: update Assistant builder * fix(Eng): re-introduce old file_search info message * feat: new OGDialogTemplate; style: imporved tools + actions dialogs * style: fix alignment issue for delete tool dialog * chore: import order --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
344297021f
commit
0bd59c0efe
29 changed files with 1115 additions and 916 deletions
|
|
@ -2,7 +2,7 @@ export default function RemoveFile({ onRemove }: { onRemove: () => void }) {
|
|||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-1 top-1 -translate-y-1/2 translate-x-1/2 rounded-full border border-white bg-gray-500 p-0.5 text-white transition-colors hover:bg-black hover:opacity-100 group-hover:opacity-100 md:opacity-0"
|
||||
className="absolute right-1 top-1 -translate-y-1/2 translate-x-1/2 rounded-full border border-gray-500 bg-gray-500 p-0.5 text-white transition-colors hover:bg-gray-700 hover:opacity-100 group-hover:opacity-100 md:opacity-0"
|
||||
onClick={onRemove}
|
||||
>
|
||||
<span>
|
||||
|
|
@ -15,6 +15,8 @@ export default function RemoveFile({ onRemove }: { onRemove: () => void }) {
|
|||
strokeLinejoin="round"
|
||||
className="icon-sm"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from '~/components/ui';
|
||||
import { useDeleteFilesFromTable } from '~/hooks/Files';
|
||||
import { NewTrashIcon, Spinner } from '~/components/svg';
|
||||
import { TrashIcon, Spinner } from '~/components/svg';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
|
|
@ -102,7 +102,7 @@ export default function DataTable<TData, TValue>({ columns, data }: DataTablePro
|
|||
{isDeleting ? (
|
||||
<Spinner className="h-4 w-4" />
|
||||
) : (
|
||||
<NewTrashIcon className="h-4 w-4 text-red-400" />
|
||||
<TrashIcon className="h-4 w-4 text-red-400" />
|
||||
)}
|
||||
{localize('com_ui_delete')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { CrossIcon, NewTrashIcon } from '~/components/svg';
|
||||
import { CrossIcon, TrashIcon } from '~/components/svg';
|
||||
import { Button } from '~/components/ui';
|
||||
|
||||
type DeleteIconButtonProps = {
|
||||
|
|
@ -10,7 +10,7 @@ export default function DeleteIconButton({ onClick }: DeleteIconButtonProps) {
|
|||
return (
|
||||
<div className="w-fit">
|
||||
<Button className="bg-red-400 p-3" onClick={onClick}>
|
||||
<NewTrashIcon />
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from '~/components/ui';
|
||||
import { useDeleteFilesFromTable } from '~/hooks/Files';
|
||||
import { NewTrashIcon, Spinner } from '~/components/svg';
|
||||
import { TrashIcon, Spinner } from '~/components/svg';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import ActionButton from '../ActionButton';
|
||||
import UploadFileButton from './UploadFileButton';
|
||||
|
|
@ -112,7 +112,7 @@ export default function DataTableFile<TData, TValue>({
|
|||
{isDeleting ? (
|
||||
<Spinner className="h-4 w-4" />
|
||||
) : (
|
||||
<NewTrashIcon className="h-4 w-4 text-red-400" />
|
||||
<TrashIcon className="h-4 w-4 text-red-400" />
|
||||
)}
|
||||
{localize('com_ui_delete')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { TFile } from 'librechat-data-provider';
|
||||
import React from 'react';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
import { Button } from '~/components/ui';
|
||||
|
||||
type FileListItemProps = {
|
||||
|
|
@ -25,7 +25,7 @@ export default function FileListItem({ file, deleteFile, width = '400px' }: File
|
|||
className="my-0 ml-3 bg-transparent p-0 text-[#666666] hover:bg-slate-200"
|
||||
onClick={() => deleteFile(file._id)}
|
||||
>
|
||||
<NewTrashIcon className="m-0 p-0" />
|
||||
<TrashIcon className="m-0 p-0" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { TFile } from 'librechat-data-provider';
|
|||
import { FileIcon, PlusIcon } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { DotsIcon, NewTrashIcon } from '~/components/svg';
|
||||
import { DotsIcon, TrashIcon } from '~/components/svg';
|
||||
import { Button } from '~/components/ui';
|
||||
|
||||
type FileListItemProps = {
|
||||
|
|
@ -68,7 +68,7 @@ export default function FileListItem2({
|
|||
className="w-min bg-transparent text-[#666666] hover:bg-slate-200"
|
||||
onClick={() => deleteFile(file._id)}
|
||||
>
|
||||
<NewTrashIcon className="" />
|
||||
<TrashIcon className="" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { TFile } from 'librechat-data-provider/dist/types';
|
||||
import React, { useState } from 'react';
|
||||
import { TThread, TVectorStore } from '~/common';
|
||||
import { CheckMark, NewTrashIcon } from '~/components/svg';
|
||||
import { CheckMark, TrashIcon } from '~/components/svg';
|
||||
import { Button } from '~/components/ui';
|
||||
import DeleteIconButton from '../DeleteIconButton';
|
||||
import VectorStoreButton from '../VectorStore/VectorStoreButton';
|
||||
|
|
@ -140,7 +140,7 @@ export default function FilePreview() {
|
|||
}}
|
||||
variant={'ghost'}
|
||||
>
|
||||
<NewTrashIcon className="m-0 p-0" />
|
||||
<TrashIcon className="m-0 p-0" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -167,7 +167,7 @@ export default function FilePreview() {
|
|||
console.log('Remove from thread');
|
||||
}}
|
||||
>
|
||||
<NewTrashIcon className="m-0 p-0" />
|
||||
<TrashIcon className="m-0 p-0" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TVectorStore } from '~/common';
|
||||
import { DotsIcon, NewTrashIcon, TrashIcon } from '~/components/svg';
|
||||
import { DotsIcon, TrashIcon, TrashIcon } from '~/components/svg';
|
||||
import { Button } from '~/components/ui';
|
||||
|
||||
type VectorStoreListItemProps = {
|
||||
|
|
@ -39,7 +39,7 @@ export default function VectorStoreListItem({
|
|||
className="m-0 w-full bg-transparent p-0 text-[#666666] hover:bg-slate-200 sm:w-fit"
|
||||
onClick={() => deleteVectorStore(vectorStore._id)}
|
||||
>
|
||||
<NewTrashIcon className="m-0 p-0" />
|
||||
<TrashIcon className="m-0 p-0" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import DeleteIconButton from '../DeleteIconButton';
|
||||
import { Button } from '~/components/ui';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
import { TFile } from 'librechat-data-provider/dist/types';
|
||||
import UploadFileButton from '../FileList/UploadFileButton';
|
||||
import UploadFileModal from '../FileList/UploadFileModal';
|
||||
|
|
@ -204,7 +204,7 @@ export default function VectorStorePreview() {
|
|||
className="my-0 ml-3 h-min bg-transparent p-0 text-[#666666] hover:bg-slate-200"
|
||||
onClick={() => console.log('click')}
|
||||
>
|
||||
<NewTrashIcon className="m-0 p-0" />
|
||||
<TrashIcon className="m-0 p-0" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import CategoryIcon from '~/components/Prompts/Groups/CategoryIcon';
|
|||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { RenameButton } from '~/components/Conversations';
|
||||
import { useLocalize, useAuthContext } from '~/hooks';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
export default function DashGroupItem({
|
||||
|
|
@ -169,7 +169,7 @@ export default function DashGroupItem({
|
|||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<NewTrashIcon className="icon-md text-gray-600 dark:text-gray-300" />
|
||||
<TrashIcon className="icon-md text-gray-600 dark:text-gray-300" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogTemplate
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import {
|
|||
} from 'librechat-data-provider';
|
||||
import type { AssistantPanelProps, ActionAuthForm } from '~/common';
|
||||
import { useAssistantsMapContext, useToastContext } from '~/Providers';
|
||||
import { Dialog, DialogTrigger } from '~/components/ui';
|
||||
import { Dialog, DialogTrigger, OGDialog, OGDialogTrigger, Label } from '~/components/ui';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { useDeleteAction } from '~/data-provider';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import ActionsInput from './ActionsInput';
|
||||
import ActionsAuth from './ActionsAuth';
|
||||
|
|
@ -119,33 +120,52 @@ export default function ActionsPanel({
|
|||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{!!action && (
|
||||
<div className="absolute right-0 top-6">
|
||||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id || !action.action_id}
|
||||
className="btn relative bg-transparent text-red-500 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
onClick={() => {
|
||||
if (!assistant_id) {
|
||||
return prompt('No assistant_id found, is the assistant created?');
|
||||
}
|
||||
const confirmed = confirm('Are you sure you want to delete this action?');
|
||||
if (confirmed) {
|
||||
<OGDialog>
|
||||
<OGDialogTrigger asChild>
|
||||
<div className="absolute right-0 top-6">
|
||||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id || !action.action_id}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
|
||||
>
|
||||
<TrashIcon className="text-red-500" />
|
||||
</button>
|
||||
</div>
|
||||
</OGDialogTrigger>
|
||||
<OGDialogTemplate
|
||||
showCloseButton={false}
|
||||
title={localize('com_ui_delete_action')}
|
||||
className="max-w-[450px]"
|
||||
main={
|
||||
<Label className="text-left text-sm font-medium">
|
||||
{localize('com_ui_delete_action_confirm')}
|
||||
</Label>
|
||||
}
|
||||
selection={{
|
||||
selectHandler: () => {
|
||||
if (!assistant_id) {
|
||||
return showToast({
|
||||
message: 'No assistant_id found, is the assistant created?',
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
deleteAction.mutate({
|
||||
model: assistantMap[endpoint][assistant_id].model,
|
||||
action_id: action.action_id,
|
||||
assistant_id,
|
||||
endpoint,
|
||||
});
|
||||
}
|
||||
},
|
||||
selectClasses:
|
||||
'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 transition-color duration-200 text-white',
|
||||
selectText: localize('com_ui_delete'),
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<NewTrashIcon className="icon-md text-red-500" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
/>
|
||||
</OGDialog>
|
||||
)}
|
||||
|
||||
<div className="text-xl font-medium">{(action ? 'Edit' : 'Add') + ' ' + 'actions'}</div>
|
||||
<div className="text-token-text-tertiary text-sm">
|
||||
{localize('com_assistants_actions_info')}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import type { Action } from 'librechat-data-provider';
|
||||
import GearIcon from '~/components/svg/GearIcon';
|
||||
|
||||
|
|
@ -8,11 +9,15 @@ export default function AssistantAction({
|
|||
action: Action;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
onClick={onClick}
|
||||
className="border-token-border-medium flex w-full rounded-lg border text-sm hover:cursor-pointer"
|
||||
className="flex w-full rounded-lg text-sm hover:cursor-pointer"
|
||||
onMouseEnter={() => setIsHovering(true)}
|
||||
onMouseLeave={() => setIsHovering(false)}
|
||||
>
|
||||
<div
|
||||
className="h-9 grow whitespace-nowrap px-3 py-2"
|
||||
|
|
@ -20,13 +25,14 @@ export default function AssistantAction({
|
|||
>
|
||||
{action.metadata.domain}
|
||||
</div>
|
||||
<div className="w-px bg-gray-300 dark:bg-gray-600" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-9 w-9 min-w-9 items-center justify-center rounded-lg rounded-l-none"
|
||||
>
|
||||
<GearIcon className="icon-sm" />
|
||||
</button>
|
||||
{isHovering && (
|
||||
<button
|
||||
type="button"
|
||||
className="transition-color flex h-9 w-9 min-w-9 items-center justify-center rounded-lg duration-200 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
<GearIcon className="icon-sm" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
import type { FunctionTool, TConfig, TPlugin } from 'librechat-data-provider';
|
||||
import type { AssistantForm, AssistantPanelProps } from '~/common';
|
||||
import { useCreateAssistantMutation, useUpdateAssistantMutation } from '~/data-provider';
|
||||
import { cn, cardStyle, defaultTextProps, removeFocusOutlines } from '~/utils';
|
||||
import { useAssistantsMapContext, useToastContext } from '~/Providers';
|
||||
import { useSelectAssistant, useLocalize } from '~/hooks';
|
||||
import { ToolSelectDialog } from '~/components/Tools';
|
||||
|
|
@ -24,13 +25,15 @@ import AssistantAction from './AssistantAction';
|
|||
import ContextButton from './ContextButton';
|
||||
import AssistantTool from './AssistantTool';
|
||||
import { Spinner } from '~/components/svg';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import Knowledge from './Knowledge';
|
||||
import { Panel } from '~/common';
|
||||
|
||||
const labelClass = 'mb-2 block text-xs font-bold text-gray-700 dark:text-gray-400';
|
||||
const inputClass =
|
||||
'focus:shadow-outline w-full appearance-none rounded-md border px-3 py-2 text-sm leading-tight text-gray-700 dark:text-white shadow focus:border-green-500 focus:outline-none focus:ring-0 dark:bg-gray-800 dark:border-gray-700/80';
|
||||
const labelClass = 'mb-2 text-token-text-primary block font-medium';
|
||||
const inputClass = cn(
|
||||
defaultTextProps,
|
||||
'flex w-full px-3 py-2 dark:border-gray-800 dark:bg-gray-800',
|
||||
removeFocusOutlines,
|
||||
);
|
||||
|
||||
export default function AssistantPanel({
|
||||
// index = 0,
|
||||
|
|
@ -297,7 +300,7 @@ export default function AssistantPanel({
|
|||
{...field}
|
||||
value={field.value ?? ''}
|
||||
{...{ max: 32768 }}
|
||||
className="focus:shadow-outline min-h-[150px] w-full resize-none resize-y appearance-none rounded-md border px-3 py-2 text-sm leading-tight text-gray-700 shadow focus:border-green-500 focus:outline-none focus:ring-0 dark:border-gray-700/80 dark:bg-gray-800 dark:text-white"
|
||||
className={cn(inputClass, 'min-h-[100px] resize-none resize-y')}
|
||||
id="instructions"
|
||||
placeholder={localize('com_assistants_instructions_placeholder')}
|
||||
rows={3}
|
||||
|
|
@ -357,7 +360,7 @@ export default function AssistantPanel({
|
|||
${toolsEnabled && actionsEnabled ? ' + ' : ''}
|
||||
${actionsEnabled ? localize('com_assistants_actions') : ''}`}
|
||||
</label>
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-2">
|
||||
{functions.map((func, i) => (
|
||||
<AssistantTool
|
||||
key={`${func}-${i}-${assistant_id}`}
|
||||
|
|
@ -373,37 +376,39 @@ export default function AssistantPanel({
|
|||
<AssistantAction key={i} action={action} onClick={() => setAction(action)} />
|
||||
);
|
||||
})}
|
||||
{toolsEnabled && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowToolDialog(true)}
|
||||
className="btn border-token-border-light relative mx-1 mt-2 h-8 rounded-lg bg-transparent font-medium hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_assistants_add_tools')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
{actionsEnabled && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id}
|
||||
onClick={() => {
|
||||
if (!assistant_id) {
|
||||
return showToast({
|
||||
message: localize('com_assistants_actions_disabled'),
|
||||
status: 'warning',
|
||||
});
|
||||
}
|
||||
setActivePanel(Panel.actions);
|
||||
}}
|
||||
className="btn border-token-border-light relative mt-2 h-8 rounded-lg bg-transparent font-medium hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_assistants_add_actions')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<div className="flex space-x-2">
|
||||
{toolsEnabled && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowToolDialog(true)}
|
||||
className="btn btn-neutral border-token-border-light relative h-8 w-full rounded-lg font-medium"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_assistants_add_tools')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
{actionsEnabled && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id}
|
||||
onClick={() => {
|
||||
if (!assistant_id) {
|
||||
return showToast({
|
||||
message: localize('com_assistants_actions_disabled'),
|
||||
status: 'warning',
|
||||
});
|
||||
}
|
||||
setActivePanel(Panel.actions);
|
||||
}}
|
||||
className="btn btn-neutral border-token-border-light relative h-8 w-full rounded-lg font-medium"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_assistants_add_actions')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
|
|
@ -415,23 +420,9 @@ export default function AssistantPanel({
|
|||
createMutation={create}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
{/* Secondary Select Button */}
|
||||
{assistant_id && (
|
||||
<button
|
||||
className="btn btn-secondary"
|
||||
type="button"
|
||||
disabled={!assistant_id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onSelectAssistant(assistant_id);
|
||||
}}
|
||||
>
|
||||
{localize('com_ui_select')}
|
||||
</button>
|
||||
)}
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
className="btn btn-primary focus:shadow-outline flex w-[90px] items-center justify-center px-4 py-2 font-semibold text-white hover:bg-green-600 focus:border-green-500"
|
||||
className="btn btn-primary focus:shadow-outline flex w-full items-center justify-center px-4 py-2 font-semibold text-white hover:bg-green-600 focus:border-green-500"
|
||||
type="submit"
|
||||
>
|
||||
{create.isLoading || update.isLoading ? (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import GearIcon from '~/components/svg/GearIcon';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import { OGDialog, OGDialogTrigger, Label } from '~/components/ui';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function AssistantTool({
|
||||
|
|
@ -11,42 +18,90 @@ export default function AssistantTool({
|
|||
allTools: TPlugin[];
|
||||
assistant_id?: string;
|
||||
}) {
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
const updateUserPlugins = useUpdateUserPluginsMutation();
|
||||
const { getValues, setValue } = useFormContext();
|
||||
const currentTool = allTools.find((t) => t.pluginKey === tool);
|
||||
|
||||
const removeTool = (tool: string) => {
|
||||
if (tool) {
|
||||
updateUserPlugins.mutate(
|
||||
{ pluginKey: tool, action: 'uninstall', auth: null, isAssistantTool: true },
|
||||
{
|
||||
onError: (error: unknown) => {
|
||||
showToast({ message: `Error while deleting the tool: ${error}`, status: 'error' });
|
||||
},
|
||||
onSuccess: () => {
|
||||
const fns = getValues('functions').filter((fn) => fn !== tool);
|
||||
setValue('functions', fns);
|
||||
showToast({ message: 'Tool deleted successfully', status: 'success' });
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (!currentTool) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<OGDialog>
|
||||
<div
|
||||
className={cn(
|
||||
'border-token-border-medium flex w-full rounded-lg border text-sm hover:cursor-pointer',
|
||||
'flex w-full items-center rounded-lg text-sm',
|
||||
!assistant_id ? 'opacity-40' : '',
|
||||
)}
|
||||
onMouseEnter={() => setIsHovering(true)}
|
||||
onMouseLeave={() => setIsHovering(false)}
|
||||
>
|
||||
{currentTool.icon && (
|
||||
<div className="flex h-9 w-9 items-center justify-center overflow-hidden rounded-full">
|
||||
<div
|
||||
className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full bg-center bg-no-repeat dark:bg-white/20"
|
||||
style={{ backgroundImage: `url(${currentTool.icon})`, backgroundSize: 'cover' }}
|
||||
/>
|
||||
<div className="flex grow items-center">
|
||||
{currentTool.icon && (
|
||||
<div className="flex h-9 w-9 items-center justify-center overflow-hidden rounded-full">
|
||||
<div
|
||||
className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full bg-center bg-no-repeat dark:bg-white/20"
|
||||
style={{ backgroundImage: `url(${currentTool.icon})`, backgroundSize: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="h-9 grow px-3 py-2"
|
||||
style={{ textOverflow: 'ellipsis', wordBreak: 'break-all', overflow: 'hidden' }}
|
||||
>
|
||||
{currentTool.name}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="h-9 grow px-3 py-2"
|
||||
style={{ textOverflow: 'ellipsis', wordBreak: 'break-all', overflow: 'hidden' }}
|
||||
>
|
||||
{currentTool.name}
|
||||
</div>
|
||||
<div className="w-px bg-gray-300 dark:bg-gray-600" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-9 w-9 min-w-9 items-center justify-center rounded-lg rounded-l-none"
|
||||
>
|
||||
<GearIcon className="icon-sm" />
|
||||
</button>
|
||||
|
||||
{isHovering && (
|
||||
<OGDialogTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="transition-color flex h-9 w-9 min-w-9 items-center justify-center rounded-lg duration-200 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</OGDialogTrigger>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<OGDialogTemplate
|
||||
showCloseButton={false}
|
||||
title={localize('com_ui_delete_tool')}
|
||||
mainClassName="px-0"
|
||||
className="max-w-[450px]"
|
||||
main={
|
||||
<Label className="text-left text-sm font-medium">
|
||||
{localize('com_ui_delete_tool_confirm')}
|
||||
</Label>
|
||||
}
|
||||
selection={{
|
||||
selectHandler: () => removeTool(currentTool.pluginKey),
|
||||
selectClasses:
|
||||
'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 transition-color duration-200 text-white',
|
||||
selectText: localize('com_ui_delete'),
|
||||
}}
|
||||
/>
|
||||
</OGDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import type { TConfig, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import ImageVision from './ImageVision';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Retrieval from './Retrieval';
|
||||
import CodeFiles from './CodeFiles';
|
||||
import Code from './Code';
|
||||
|
||||
export default function CapabilitiesForm({
|
||||
|
|
@ -21,6 +24,17 @@ export default function CapabilitiesForm({
|
|||
}) {
|
||||
const localize = useLocalize();
|
||||
|
||||
const methods = useFormContext<AssistantForm>();
|
||||
const { control } = methods;
|
||||
const assistant = useWatch({ control, name: 'assistant' });
|
||||
const assistant_id = useWatch({ control, name: 'id' });
|
||||
const files = useMemo(() => {
|
||||
if (typeof assistant === 'string') {
|
||||
return [];
|
||||
}
|
||||
return assistant.code_files;
|
||||
}, [assistant]);
|
||||
|
||||
const retrievalModels = useMemo(
|
||||
() => new Set(assistantsConfig?.retrievalModels ?? []),
|
||||
[assistantsConfig],
|
||||
|
|
@ -31,7 +45,7 @@ export default function CapabilitiesForm({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="mb-4">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<span>
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
|
|
@ -40,11 +54,19 @@ export default function CapabilitiesForm({
|
|||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{codeEnabled && <Code endpoint={endpoint} version={version} />}
|
||||
{imageVisionEnabled && version == 1 && <ImageVision />}
|
||||
{codeEnabled && <Code version={version} />}
|
||||
{retrievalEnabled && (
|
||||
<Retrieval endpoint={endpoint} version={version} retrievalModels={retrievalModels} />
|
||||
)}
|
||||
{imageVisionEnabled && version == 1 && <ImageVision />}
|
||||
{codeEnabled && version && (
|
||||
<CodeFiles
|
||||
assistant_id={assistant_id}
|
||||
version={version}
|
||||
endpoint={endpoint}
|
||||
files={files}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,70 +1,66 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { Checkbox, QuestionMark } from '~/components/ui';
|
||||
import {
|
||||
Checkbox,
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardPortal,
|
||||
HoverCardTrigger,
|
||||
} from '~/components/ui';
|
||||
import { CircleHelpIcon } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import CodeFiles from './CodeFiles';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
export default function Code({
|
||||
version,
|
||||
endpoint,
|
||||
}: {
|
||||
version: number | string;
|
||||
endpoint: AssistantsEndpoint;
|
||||
}) {
|
||||
export default function Code({ version }: { version: number | string }) {
|
||||
const localize = useLocalize();
|
||||
const methods = useFormContext<AssistantForm>();
|
||||
const { control, setValue, getValues } = methods;
|
||||
const assistant = useWatch({ control, name: 'assistant' });
|
||||
const assistant_id = useWatch({ control, name: 'id' });
|
||||
const files = useMemo(() => {
|
||||
if (typeof assistant === 'string') {
|
||||
return [];
|
||||
}
|
||||
return assistant.code_files;
|
||||
}, [assistant]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.code_interpreter}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.code_interpreter}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className="flex select-none items-center">
|
||||
{localize('com_assistants_code_interpreter')}
|
||||
<QuestionMark />
|
||||
<HoverCard openDelay={50}>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.code_interpreter}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.code_interpreter}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{localize('com_assistants_code_interpreter')}
|
||||
</label>
|
||||
<HoverCardTrigger>
|
||||
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
|
||||
</HoverCardTrigger>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
{version == 2 && (
|
||||
<CodeFiles
|
||||
assistant_id={assistant_id}
|
||||
version={version}
|
||||
endpoint={endpoint}
|
||||
files={files}
|
||||
/>
|
||||
)}
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={ESide.Top} className="w-80">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
{version == 2 && localize('com_assistants_code_interpreter_info')}
|
||||
</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
</div>
|
||||
</HoverCard>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export default function CodeFiles({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={'mb-2'}>
|
||||
<div className="mb-2 w-full">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-token-text-tertiary rounded-lg text-xs">
|
||||
{localize('com_assistants_code_interpreter_files')}
|
||||
|
|
@ -75,7 +75,7 @@ export default function CodeFiles({
|
|||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id}
|
||||
className="btn btn-neutral border-token-border-light relative h-8 rounded-lg font-medium"
|
||||
className="btn btn-neutral border-token-border-light relative h-8 w-full rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import * as Popover from '@radix-ui/react-popover';
|
||||
import type { Assistant, AssistantCreateParams, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import { Dialog, DialogTrigger, Label } from '~/components/ui';
|
||||
|
|
@ -7,7 +6,7 @@ import { useDeleteAssistantMutation } from '~/data-provider';
|
|||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { useLocalize, useSetIndexOptions } from '~/hooks';
|
||||
import { cn, removeFocusOutlines } from '~/utils/';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { TrashIcon } from '~/components/svg';
|
||||
|
||||
export default function ContextButton({
|
||||
activeModel,
|
||||
|
|
@ -78,88 +77,40 @@ export default function ContextButton({
|
|||
|
||||
return (
|
||||
<Dialog>
|
||||
<Popover.Root>
|
||||
<Popover.Trigger asChild>
|
||||
<button
|
||||
className={cn(
|
||||
'btn border-token-border-light relative h-9 rounded-lg bg-transparent font-medium hover:bg-gray-100 dark:hover:bg-gray-800',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon-md"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3 12C3 10.8954 3.89543 10 5 10C6.10457 10 7 10.8954 7 12C7 13.1046 6.10457 14 5 14C3.89543 14 3 13.1046 3 12ZM10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12ZM17 12C17 10.8954 17.8954 10 19 10C20.1046 10 21 10.8954 21 12C21 13.1046 20.1046 14 19 14C17.8954 14 17 13.1046 17 12Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</Popover.Trigger>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: ' 0px',
|
||||
top: ' 0px',
|
||||
transform: 'translate(1772.8px, 49.6px)',
|
||||
minWidth: 'max-content',
|
||||
zIndex: 'auto',
|
||||
}}
|
||||
dir="ltr"
|
||||
<DialogTrigger asChild>
|
||||
<button
|
||||
className={cn(
|
||||
'btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
<Popover.Content
|
||||
side="top"
|
||||
role="menu"
|
||||
className="bg-token-surface-primary min-w-[180px] max-w-xs rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-900 dark:bg-gray-850"
|
||||
style={{ outline: 'none', pointerEvents: 'auto' }}
|
||||
sideOffset={8}
|
||||
tabIndex={-1}
|
||||
align="end"
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Popover.Close
|
||||
role="menuitem"
|
||||
className="group m-1.5 flex w-full cursor-pointer gap-2 rounded p-2.5 text-sm text-red-500 hover:bg-black/5 focus:ring-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-white/5"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<NewTrashIcon />
|
||||
{localize('com_ui_delete') + ' ' + localize('com_ui_assistant')}
|
||||
</Popover.Close>
|
||||
</DialogTrigger>
|
||||
</Popover.Content>
|
||||
</div>
|
||||
<DialogTemplate
|
||||
title={localize('com_ui_delete') + ' ' + localize('com_ui_assistant')}
|
||||
className="max-w-[450px]"
|
||||
main={
|
||||
<>
|
||||
<div className="flex w-full flex-col items-center gap-2">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label htmlFor="delete-assistant" className="text-left text-sm font-medium">
|
||||
{localize('com_ui_delete_assistant_confirm')}
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-center gap-2 text-red-500">
|
||||
<TrashIcon />
|
||||
</div>
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogTemplate
|
||||
title={localize('com_ui_delete') + ' ' + localize('com_ui_assistant')}
|
||||
className="max-w-[450px]"
|
||||
main={
|
||||
<>
|
||||
<div className="flex w-full flex-col items-center gap-2">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label htmlFor="delete-assistant" className="text-left text-sm font-medium">
|
||||
{localize('com_ui_delete_assistant_confirm')}
|
||||
</Label>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
selection={{
|
||||
selectHandler: () =>
|
||||
deleteAssistant.mutate({ assistant_id, model: activeModel, endpoint }),
|
||||
selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white',
|
||||
selectText: localize('com_ui_delete'),
|
||||
}}
|
||||
/>
|
||||
</Popover.Root>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
selection={{
|
||||
selectHandler: () =>
|
||||
deleteAssistant.mutate({ assistant_id, model: activeModel, endpoint }),
|
||||
selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white',
|
||||
selectText: localize('com_ui_delete'),
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,7 @@ export default function ImageVision() {
|
|||
})
|
||||
}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{localize('com_assistants_image_vision')}
|
||||
<QuestionMark />
|
||||
</div>
|
||||
<div className="flex items-center">{localize('com_assistants_image_vision')}</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||
import {
|
||||
Checkbox,
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardPortal,
|
||||
HoverCardTrigger,
|
||||
} from '~/components/ui';
|
||||
import OptionHover from '~/components/SidePanel/Parameters/OptionHover';
|
||||
import { Checkbox, HoverCard, HoverCardTrigger } from '~/components/ui';
|
||||
import { CircleHelpIcon } from '~/components/svg';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { ESide } from '~/common';
|
||||
import { cn } from '~/utils/';
|
||||
|
|
@ -40,23 +47,23 @@ export default function Retrieval({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.retrieval}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
disabled={isDisabled}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<HoverCard openDelay={50}>
|
||||
<HoverCardTrigger asChild>
|
||||
<HoverCard openDelay={50}>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.retrieval}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
disabled={isDisabled}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<label
|
||||
className={cn(
|
||||
'form-check-label text-token-text-primary w-full select-none',
|
||||
|
|
@ -74,21 +81,29 @@ export default function Retrieval({
|
|||
? localize('com_assistants_retrieval')
|
||||
: localize('com_assistants_file_search')}
|
||||
</label>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardTrigger>
|
||||
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
|
||||
</HoverCardTrigger>
|
||||
</div>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={ESide.Top} disabled={isDisabled} className="ml-16 w-80">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
{version == 2 && localize('com_assistants_file_search_info')}
|
||||
</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
<OptionHover
|
||||
side={ESide.Top}
|
||||
disabled={!isDisabled}
|
||||
description="com_assistants_non_retrieval_model"
|
||||
langCode={true}
|
||||
sideOffset={20}
|
||||
className="ml-16"
|
||||
/>
|
||||
</HoverCard>
|
||||
</div>
|
||||
{version == 2 && (
|
||||
<div className="text-token-text-tertiary rounded-lg text-xs">
|
||||
{localize('com_assistants_file_search_info')}
|
||||
</div>
|
||||
)}
|
||||
</HoverCard>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ type TOptionHoverProps = {
|
|||
sideOffset?: number;
|
||||
disabled?: boolean;
|
||||
side: ESide;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function OptionHover({
|
||||
|
|
@ -17,6 +18,7 @@ function OptionHover({
|
|||
disabled,
|
||||
langCode,
|
||||
sideOffset = 30,
|
||||
className,
|
||||
}: TOptionHoverProps) {
|
||||
const localize = useLocalize();
|
||||
if (disabled) {
|
||||
|
|
@ -25,11 +27,7 @@ function OptionHover({
|
|||
const text = langCode ? localize(description) : description;
|
||||
return (
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent
|
||||
side={side}
|
||||
className="z-[999] w-80 dark:bg-gray-700"
|
||||
sideOffset={sideOffset}
|
||||
>
|
||||
<HoverCardContent side={side} className={`z-[999] w-80 ${className}`} sideOffset={sideOffset}>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{text}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
export default function NewTrashIcon({ className = 'icon-md' }) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.5555 4C10.099 4 9.70052 4.30906 9.58693 4.75114L9.29382 5.8919H14.715L14.4219 4.75114C14.3083 4.30906 13.9098 4 13.4533 4H10.5555ZM16.7799 5.8919L16.3589 4.25342C16.0182 2.92719 14.8226 2 13.4533 2H10.5555C9.18616 2 7.99062 2.92719 7.64985 4.25342L7.22886 5.8919H4C3.44772 5.8919 3 6.33961 3 6.8919C3 7.44418 3.44772 7.8919 4 7.8919H4.10069L5.31544 19.3172C5.47763 20.8427 6.76455 22 8.29863 22H15.7014C17.2354 22 18.5224 20.8427 18.6846 19.3172L19.8993 7.8919H20C20.5523 7.8919 21 7.44418 21 6.8919C21 6.33961 20.5523 5.8919 20 5.8919H16.7799ZM17.888 7.8919H6.11196L7.30423 19.1057C7.3583 19.6142 7.78727 20 8.29863 20H15.7014C16.2127 20 16.6417 19.6142 16.6958 19.1057L17.888 7.8919ZM10 10C10.5523 10 11 10.4477 11 11V16C11 16.5523 10.5523 17 10 17C9.44772 17 9 16.5523 9 16V11C9 10.4477 9.44772 10 10 10ZM14 10C14.5523 10 15 10.4477 15 11V16C15 16.5523 14.5523 17 14 17C13.4477 17 13 16.5523 13 16V11C13 10.4477 13.4477 10 14 10Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
import { cn } from '~/utils';
|
||||
|
||||
export default function TrashIcon({ className = '' }) {
|
||||
type TrashIconProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function TrashIcon({ className = '' }: TrashIconProps) {
|
||||
return (
|
||||
<svg
|
||||
fill="none"
|
||||
|
|
@ -8,7 +12,7 @@ export default function TrashIcon({ className = '' }) {
|
|||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={cn('icon-md', className)}
|
||||
className={cn('icon-md h-4 w-4', className)}
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ export { default as DotsIcon } from './DotsIcon';
|
|||
export { default as GearIcon } from './GearIcon';
|
||||
export { default as PinIcon } from './PinIcon';
|
||||
export { default as TrashIcon } from './TrashIcon';
|
||||
export { default as NewTrashIcon } from './NewTrashIcon';
|
||||
export { default as MinimalPlugin } from './MinimalPlugin';
|
||||
export { default as AzureMinimalIcon } from './AzureMinimalIcon';
|
||||
export { default as OpenAIMinimalIcon } from './OpenAIMinimalIcon';
|
||||
|
|
|
|||
|
|
@ -11,19 +11,25 @@ const HoverCardPortal = HoverCardPrimitive.Portal;
|
|||
|
||||
const HoverCardContent = React.forwardRef<
|
||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||
>(({ className = '', align = 'center', sideOffset = 6, ...props }, ref) => (
|
||||
<HoverCardPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 w-64 rounded-md border border-gray-200 bg-white p-4 shadow-md outline-none animate-in fade-in-0 dark:border-gray-800 dark:bg-gray-800',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> & { disabled?: boolean }
|
||||
>(({ className = '', align = 'center', sideOffset = 6, disabled = false, ...props }, ref) => {
|
||||
if (disabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<HoverCardPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 w-64 rounded-md border border-gray-200 bg-white p-4 shadow-md outline-none animate-in fade-in-0 dark:border-gray-800 dark:bg-gray-800',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
||||
|
||||
export { HoverCard, HoverCardTrigger, HoverCardContent, HoverCardPortal };
|
||||
|
|
|
|||
95
client/src/components/ui/OGDialogTemplate.tsx
Normal file
95
client/src/components/ui/OGDialogTemplate.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { forwardRef, ReactNode, Ref } from 'react';
|
||||
import {
|
||||
OGDialogTitle,
|
||||
OGDialogClose,
|
||||
OGDialogFooter,
|
||||
OGDialogHeader,
|
||||
OGDialogContent,
|
||||
OGDialogDescription,
|
||||
} from './';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
type SelectionProps = {
|
||||
selectHandler?: () => void;
|
||||
selectClasses?: string;
|
||||
selectText?: string;
|
||||
};
|
||||
|
||||
type DialogTemplateProps = {
|
||||
title: string;
|
||||
description?: string;
|
||||
main?: ReactNode;
|
||||
buttons?: ReactNode;
|
||||
leftButtons?: ReactNode;
|
||||
selection?: SelectionProps;
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
mainClassName?: string;
|
||||
footerClassName?: string;
|
||||
showCloseButton?: boolean;
|
||||
showCancelButton?: boolean;
|
||||
};
|
||||
|
||||
const OGDialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivElement>) => {
|
||||
const localize = useLocalize();
|
||||
const {
|
||||
title,
|
||||
main,
|
||||
buttons,
|
||||
selection,
|
||||
className,
|
||||
leftButtons,
|
||||
description,
|
||||
mainClassName,
|
||||
headerClassName,
|
||||
footerClassName,
|
||||
showCloseButton,
|
||||
showCancelButton = true,
|
||||
} = props;
|
||||
const { selectHandler, selectClasses, selectText } = selection || {};
|
||||
const Cancel = localize('com_ui_cancel');
|
||||
|
||||
const defaultSelect =
|
||||
'bg-gray-800 text-white transition-colors hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-200 dark:text-gray-800 dark:hover:bg-gray-200';
|
||||
return (
|
||||
<OGDialogContent
|
||||
showCloseButton={showCloseButton}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-white dark:border-gray-700 dark:bg-gray-850 dark:text-gray-300',
|
||||
className || '',
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OGDialogHeader className={cn(headerClassName ?? '')}>
|
||||
<OGDialogTitle>{title}</OGDialogTitle>
|
||||
{description && <OGDialogDescription className="">{description}</OGDialogDescription>}
|
||||
</OGDialogHeader>
|
||||
<div className={cn('px-6', mainClassName)}>{main ? main : null}</div>
|
||||
<OGDialogFooter className={footerClassName}>
|
||||
<div>{leftButtons ? leftButtons : null}</div>
|
||||
<div className="flex h-auto gap-3">
|
||||
{showCancelButton && (
|
||||
<OGDialogClose className="btn btn-neutral border-token-border-light relative rounded-lg text-sm">
|
||||
{Cancel}
|
||||
</OGDialogClose>
|
||||
)}
|
||||
{buttons ? buttons : null}
|
||||
{selection ? (
|
||||
<OGDialogClose
|
||||
onClick={selectHandler}
|
||||
className={`${
|
||||
selectClasses || defaultSelect
|
||||
} inline-flex h-10 items-center justify-center rounded-lg border-none px-4 py-2 text-sm`}
|
||||
>
|
||||
{selectText}
|
||||
</OGDialogClose>
|
||||
) : null}
|
||||
</div>
|
||||
</OGDialogFooter>
|
||||
</OGDialogContent>
|
||||
);
|
||||
});
|
||||
|
||||
export default OGDialogTemplate;
|
||||
|
|
@ -26,10 +26,15 @@ const DialogOverlay = React.forwardRef<
|
|||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
type DialogContentProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean;
|
||||
disableScroll?: boolean;
|
||||
};
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
DialogContentProps
|
||||
>(({ className, showCloseButton = true, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
|
|
@ -41,10 +46,12 @@ const DialogContent = React.forwardRef<
|
|||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ export default {
|
|||
com_assistants_capabilities: 'Capabilities',
|
||||
com_assistants_file_search: 'File Search',
|
||||
com_assistants_file_search_info:
|
||||
'Attaching vector stores for File Search is not yet supported. You can attach them from the Provider Playground or attach files to messages for file search on a thread basis.',
|
||||
'File search enables the assistant with knowledge from files that you or your users upload. Once a file is uploaded, the assistant automatically decides when to retrieve content based on user requests. Attaching vector stores for File Search is not yet supported. You can attach them from the Provider Playground or attach files to messages for file search on a thread basis.',
|
||||
com_assistants_code_interpreter_info:
|
||||
'Code Interpreter enables the assistant to write and run code. This tool can process files with diverse data and formatting, and generate files such as graphs.',
|
||||
com_assistants_knowledge: 'Knowledge',
|
||||
com_assistants_knowledge_info:
|
||||
'If you upload files under Knowledge, conversations with your Assistant may include file contents.',
|
||||
|
|
@ -30,8 +32,7 @@ export default {
|
|||
'Assistant must be created, and Code Interpreter or Retrieval must be enabled and saved before uploading files as Knowledge.',
|
||||
com_assistants_image_vision: 'Image Vision',
|
||||
com_assistants_code_interpreter: 'Code Interpreter',
|
||||
com_assistants_code_interpreter_files:
|
||||
'The following files are only available for Code Interpreter:',
|
||||
com_assistants_code_interpreter_files: 'Files below are for Code Interpreter only:',
|
||||
com_assistants_retrieval: 'Retrieval',
|
||||
com_assistants_search_name: 'Search assistants by name',
|
||||
com_assistants_tools: 'Tools',
|
||||
|
|
@ -266,6 +267,10 @@ export default {
|
|||
com_ui_shared_link_not_found: 'Shared link not found',
|
||||
com_ui_delete_conversation: 'Delete chat?',
|
||||
com_ui_delete_confirm: 'This will delete',
|
||||
com_ui_delete_tool: 'Delete Tool',
|
||||
com_ui_delete_tool_confirm: 'Are you sure you want to delete this tool?',
|
||||
com_ui_delete_action: 'Delete Action',
|
||||
com_ui_delete_action_confirm: 'Are you sure you want to delete this action?',
|
||||
com_ui_delete_confirm_prompt_version_var:
|
||||
'This will delete the selected version for "{0}." If no other versions exist, the prompt will be deleted.',
|
||||
com_ui_delete_assistant_confirm:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue