Skip to content

Commit

Permalink
feat: unify modals in vector FS, add preview of files + add shortcuts…
Browse files Browse the repository at this point in the history
… support (#603)

* remove unnecessary dependencies

* feat: shortcuts for swtiching ais and selecting tools

* feat: add preview and improve UI

* fixes
  • Loading branch information
paulclindo authored Jan 18, 2025
1 parent 739b708 commit 810586c
Show file tree
Hide file tree
Showing 10 changed files with 426 additions and 341 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useUpdateAgentInJob } from '@shinkai_network/shinkai-node-state/lib/mut
import { useGetAgents } from '@shinkai_network/shinkai-node-state/v2/queries/getAgents/useGetAgents';
import { useGetLLMProviders } from '@shinkai_network/shinkai-node-state/v2/queries/getLLMProviders/useGetLLMProviders';
import {
CommandShortcut,
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
Expand Down Expand Up @@ -64,8 +65,23 @@ export function AIModelSelectorBase({
</DropdownMenuTrigger>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent align="center" side="top">
{t('llmProviders.switch')}
<TooltipContent
align="center"
className="flex flex-col gap-1"
side="top"
>
<span className="text-center text-xs text-white">
{t('llmProviders.switch')}
</span>
<div className="flex items-center gap-4 text-left">
<div className="flex items-center justify-center gap-2 text-xs text-gray-100">
<CommandShortcut>⌘ [</CommandShortcut> or
<CommandShortcut>⌘ ]</CommandShortcut>
</div>
<div className="flex items-center justify-center gap-2">
<span className="text-xs text-gray-100">Prev / Next AI</span>
</div>
</div>
</TooltipContent>
</TooltipPortal>
<DropdownMenuContent
Expand Down
80 changes: 77 additions & 3 deletions apps/shinkai-desktop/src/components/chat/conversation-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ import { DEFAULT_CHAT_CONFIG } from '@shinkai_network/shinkai-node-state/v2/cons
import { useCreateJob } from '@shinkai_network/shinkai-node-state/v2/mutations/createJob/useCreateJob';
import { useSendMessageToJob } from '@shinkai_network/shinkai-node-state/v2/mutations/sendMessageToJob/useSendMessageToJob';
import { useStopGeneratingLLM } from '@shinkai_network/shinkai-node-state/v2/mutations/stopGeneratingLLM/useStopGeneratingLLM';
import { useGetAgents } from '@shinkai_network/shinkai-node-state/v2/queries/getAgents/useGetAgents';
import { useGetChatConfig } from '@shinkai_network/shinkai-node-state/v2/queries/getChatConfig/useGetChatConfig';
import { useGetLLMProviders } from '@shinkai_network/shinkai-node-state/v2/queries/getLLMProviders/useGetLLMProviders';
import { useGetSearchTools } from '@shinkai_network/shinkai-node-state/v2/queries/getToolsSearch/useGetToolsSearch';
import {
Button,
ChatInputArea,
CommandShortcut,
Form,
FormControl,
FormField,
Expand All @@ -48,6 +51,7 @@ import { Loader2, Paperclip, X, XIcon } from 'lucide-react';
import { memo, useCallback, useEffect, useRef } from 'react';
import { useDropzone } from 'react-dropzone';
import { useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { toast } from 'sonner';

Expand Down Expand Up @@ -152,6 +156,8 @@ const useSelectedFilesChat = ({ inboxId }: { inboxId?: string }) => {
};
};

const SUGGESTED_TOOLS_COUNT = 3;

function ConversationChatFooter({
inboxId,
isLoadingMessage,
Expand Down Expand Up @@ -202,6 +208,7 @@ function ConversationChatFooter({
const selectedTool = chatForm.watch('tool');
const currentMessage = chatForm.watch('message');
const currentFiles = chatForm.watch('files');
const currentAI = chatForm.watch('agent');

const { data: chatConfig } = useGetChatConfig(
{
Expand Down Expand Up @@ -271,10 +278,70 @@ function ConversationChatFooter({
},
{
enabled: !!debounceMessage && !!currentMessage && !selectedTool,
select: (data) => data.slice(0, 3),
select: (data) => data.slice(0, SUGGESTED_TOOLS_COUNT),
},
);

// for suggested tools
useHotkeys(
['mod+1', 'ctrl+1', 'mod+2', 'ctrl+2', 'mod+3', 'ctrl+3'],
(event) => {
if (!searchToolList?.length) return;

const toolNumber = parseInt(event.key);
if (toolNumber < 1 || toolNumber > SUGGESTED_TOOLS_COUNT) return;
const selectedTool = searchToolList[toolNumber - 1];
chatForm.setValue('tool', {
key: selectedTool.tool_router_key,
name: selectedTool.name,
description: selectedTool.description,
args: Object.keys(selectedTool.input_args.properties ?? {}),
});
},
{
enabled: !!searchToolList?.length && !!currentMessage && !selectedTool,
enableOnFormTags: true,
},
);

const { data: llmProviders } = useGetLLMProviders({
nodeAddress: auth?.node_address ?? '',
token: auth?.api_v2_key ?? '',
});
const { data: agents } = useGetAgents({
nodeAddress: auth?.node_address ?? '',
token: auth?.api_v2_key ?? '',
});

// For switching AIs
useHotkeys(
['mod+[', 'mod+]', 'ctrl+[', 'ctrl+]'],
(event) => {
if (inboxId) return; // switch for only empty chat for now
if (!llmProviders || !agents) return;
const allAIs = [
...(agents ?? []).map((a) => a.name),
...(llmProviders ?? []).map((l) => l.id),
];

const currentIndex = allAIs.indexOf(currentAI ?? '');
if (currentIndex === -1) return;

let newIndex;
if (event.key === '[') {
newIndex = currentIndex === 0 ? allAIs.length - 1 : currentIndex - 1;
} else {
newIndex = currentIndex === allAIs.length - 1 ? 0 : currentIndex + 1;
}

chatForm.setValue('agent', allAIs[newIndex]);
},
{
enableOnFormTags: true,
enabled: !!llmProviders?.length && !!agents?.length && !!currentAI,
},
);

const onDrop = useCallback(
(acceptedFiles: File[]) => {
const previousFiles = chatForm.getValues('files') ?? [];
Expand Down Expand Up @@ -466,7 +533,7 @@ function ConversationChatFooter({
onValueChange={(value) => {
chatForm.setValue('agent', value);
}}
value={chatForm.watch('agent') ?? ''}
value={currentAI ?? ''}
/>
)}
<FileSelectionActionBar
Expand Down Expand Up @@ -592,7 +659,7 @@ function ConversationChatFooter({
!selectedTool &&
isSearchToolListSuccess &&
searchToolList?.length > 0 &&
searchToolList?.map((tool) => (
searchToolList?.map((tool, idx) => (
<Tooltip key={tool.tool_router_key}>
<TooltipTrigger asChild>
<motion.button
Expand Down Expand Up @@ -627,6 +694,13 @@ function ConversationChatFooter({
side="top"
>
{tool.description}

<br />
<div className="flex items-center justify-end gap-2 text-xs text-gray-100">
<CommandShortcut>
⌘ + {idx + 1}
</CommandShortcut>
</div>
</TooltipContent>
</TooltipPortal>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Sheet, SheetContent } from '@shinkai_network/shinkai-ui';
import { DialogClose } from '@shinkai_network/shinkai-artifacts';
import {
buttonVariants,
Dialog,
DialogContent,
} from '@shinkai_network/shinkai-ui';
import { cn } from '@shinkai_network/shinkai-ui/utils';
import { XIcon } from 'lucide-react';

import { useVectorFsStore } from '../context/vector-fs-context';
import { VectorFolderSelectionProvider } from './folder-selection-list';
Expand Down Expand Up @@ -33,9 +39,9 @@ const VectorFSDrawer = () => {
(state) => state.setSelectedFolder,
);
const setSelectedFile = useVectorFsStore((state) => state.setSelectedFile);
console.log('activeDrawerMenuOption', activeDrawerMenuOption);

return (
<Sheet
<Dialog
onOpenChange={(open) => {
if (!open) {
setActiveDrawerMenuOption(null);
Expand All @@ -45,19 +51,38 @@ const VectorFSDrawer = () => {
}}
open={!!activeDrawerMenuOption}
>
<SheetContent
<DialogContent
className={cn(
activeDrawerMenuOption === VectorFsGlobalAction.CreateTextFile ||
activeDrawerMenuOption === VectorFsItemAction.Edit
? 'max-w-[85%]'
: 'max-w-md',
'flex max-w-lg flex-col bg-gray-300',
activeDrawerMenuOption === VectorFsGlobalAction.NewFolder &&
'max-w-md',
(activeDrawerMenuOption === VectorFsGlobalAction.CreateTextFile ||
activeDrawerMenuOption === VectorFsItemAction.Edit) &&
'h-full max-h-[90vh] max-w-[85%]',
activeDrawerMenuOption === VectorFsGlobalAction.VectorFileDetails &&
'size-full max-h-[99vh] max-w-[99vw] bg-transparent p-1',
(activeDrawerMenuOption === VectorFsItemAction.Move ||
activeDrawerMenuOption === VectorFsItemAction.Copy ||
activeDrawerMenuOption === VectorFsFolderAction.Move ||
activeDrawerMenuOption === VectorFsFolderAction.Copy) &&
'max-w-xl',
)}
>
<DialogClose
className={cn(
buttonVariants({ variant: 'tertiary', size: 'icon' }),
'absolute right-3 top-3 p-1',
activeDrawerMenuOption === VectorFsGlobalAction.VectorFileDetails &&
'right-5 top-5',
)}
>
<XIcon className="size-4" />
</DialogClose>
<VectorFolderSelectionProvider>
<VectorFSDrawerContent selectedOption={activeDrawerMenuOption} />
</VectorFolderSelectionProvider>
</SheetContent>
</Sheet>
</DialogContent>
</Dialog>
);
};
export enum VectorFsGlobalAction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useMoveFolder } from '@shinkai_network/shinkai-node-state/v2/mutations/
import { useRemoveFolder } from '@shinkai_network/shinkai-node-state/v2/mutations/removeFolder/useRemoveFolder';
import {
Button,
SheetFooter,
SheetHeader,
SheetTitle,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@shinkai_network/shinkai-ui';
import React from 'react';
import { toast } from 'sonner';
Expand Down Expand Up @@ -44,20 +44,20 @@ export const VectorFsFolderMoveAction = () => {

return (
<React.Fragment>
<SheetHeader>
<SheetTitle className="font-normal">
<DialogHeader>
<DialogTitle className="font-normal">
{t('vectorFs.actions.move')}
<span className="font-medium">
{' '}
&quot;{selectedFolder?.name}&quot;
</span>{' '}
to ...
</SheetTitle>
</SheetHeader>
</DialogTitle>
</DialogHeader>
<FolderSelectionList />
<SheetFooter>
<DialogFooter className="mt-4">
<Button
className="mt-4"
className="min-w-[100px]"
disabled={destinationFolderPath === selectedFolder?.path}
isLoading={isMovingVrFolder}
onClick={async () => {
Expand All @@ -69,10 +69,11 @@ export const VectorFsFolderMoveAction = () => {
`${destinationFolderPath}/${selectedFolder?.path}` ?? '/',
});
}}
size="sm"
>
{t('vectorFs.actions.move')}
</Button>
</SheetFooter>
</DialogFooter>
</React.Fragment>
);
};
Expand All @@ -94,21 +95,21 @@ export const VectorFsFolderDeleteAction = () => {

return (
<React.Fragment>
<SheetHeader>
<SheetTitle className="font-normal">
<DialogHeader>
<DialogTitle className="font-normal">
{t('vectorFs.actions.delete')}
<span className="font-medium">
{' '}
&quot;{selectedFolder?.name}&quot;
</span>{' '}
</SheetTitle>
</SheetHeader>
</DialogTitle>
</DialogHeader>
<p className="text-gray-80 my-3 text-base">
{t('vectorFs.deleteFolderConfirmation')}
</p>
<SheetFooter>
<DialogFooter className="mt-4">
<Button
className="mt-4"
className="min-w-[100px]"
isLoading={isPending}
onClick={async () => {
await deleteVrFolder({
Expand All @@ -117,11 +118,12 @@ export const VectorFsFolderDeleteAction = () => {
folderPath: selectedFolder?.path ?? '',
});
}}
size="sm"
variant="destructive"
>
{t('vectorFs.actions.delete')}
</Button>
</SheetFooter>
</DialogFooter>
</React.Fragment>
);
};
Expand Down Expand Up @@ -152,20 +154,21 @@ export const VectorFsFolderCopyAction = () => {

return (
<React.Fragment>
<SheetHeader>
<SheetTitle className="font-normal">
<DialogHeader>
<DialogTitle className="font-normal">
{t('vectorFs.actions.copy')}
<span className="font-medium">
{' '}
&quot;{selectedFolder?.name}&quot;
</span>{' '}
to ...
</SheetTitle>
</SheetHeader>
</DialogTitle>
</DialogHeader>
<FolderSelectionList />
<SheetFooter>

<DialogFooter className="mt-4">
<Button
className="mt-4"
className="min-w-[100px]"
disabled={destinationFolderPath === selectedFolder?.path}
isLoading={isPending}
onClick={async () => {
Expand All @@ -177,10 +180,11 @@ export const VectorFsFolderCopyAction = () => {
`${destinationFolderPath}/${selectedFolder?.path}` ?? '/',
});
}}
size="sm"
>
{t('vectorFs.actions.copy')}
</Button>
</SheetFooter>
</DialogFooter>
</React.Fragment>
);
};
Loading

0 comments on commit 810586c

Please sign in to comment.