2 import { Plus, File, MessageSquare, Zap, FolderOpen } from '@lucide/svelte';
3 import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
4 import * as Tooltip from '$lib/components/ui/tooltip';
5 import { buttonVariants } from '$lib/components/ui/button';
6 import { cn } from '$lib/components/ui/utils';
9 ATTACHMENT_TOOLTIP_TEXT,
10 TOOLTIP_DELAY_DURATION
11 } from '$lib/constants';
13 ChatFormActionAddToolsSubmenu,
14 ChatFormActionAddMcpServersSubmenu
15 } from '$lib/components/app';
16 import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte';
21 hasAudioModality?: boolean;
22 hasVideoModality?: boolean;
23 hasVisionModality?: boolean;
24 hasMcpPromptsSupport?: boolean;
25 hasMcpResourcesSupport?: boolean;
26 onFileUpload?: () => void;
27 onSystemPromptClick?: () => void;
28 onMcpPromptClick?: () => void;
29 onMcpSettingsClick?: () => void;
30 onMcpResourcesClick?: () => void;
34 class: className = '',
36 hasAudioModality = false,
37 hasVideoModality = false,
38 hasVisionModality = false,
39 hasMcpPromptsSupport = false,
40 hasMcpResourcesSupport = false,
48 let dropdownOpen = $state(false);
50 function handleMcpSettingsClick() {
52 onMcpSettingsClick?.();
55 const attachmentMenu = useAttachmentMenu(
61 hasMcpResourcesSupport
63 () => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }),
70 <div class="flex items-center gap-1 {className}">
71 <DropdownMenu.Root bind:open={dropdownOpen}>
74 {#snippet child({ props })}
78 buttonVariants({ variant: 'secondary' }),
79 'file-upload-button h-8 w-8 cursor-pointer rounded-full p-0'
83 <span class="sr-only">{ATTACHMENT_TOOLTIP_TEXT}</span>
85 <Plus class="h-4 w-4" />
86 </DropdownMenu.Trigger>
91 <p>{ATTACHMENT_TOOLTIP_TEXT}</p>
95 <DropdownMenu.Content align="start" class="w-48">
97 <DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
98 <File class="h-4 w-4" />
100 <span>Add files</span>
101 </DropdownMenu.SubTrigger>
103 <DropdownMenu.SubContent class="w-48">
104 {#each ATTACHMENT_FILE_ITEMS as item (item.id)}
105 {@const enabled = attachmentMenu.isItemEnabled(item.enabledWhen)}
108 class="{item.class ?? ''} flex cursor-pointer items-center gap-2"
109 onclick={() => attachmentMenu.callbacks[item.action]()}
111 <item.icon class="h-4 w-4" />
113 <span>{item.label}</span>
115 {:else if item.disabledTooltip}
116 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
117 <Tooltip.Trigger tabindex={-1}>
118 {#snippet child({ props })}
119 <div {...props} class="cursor-default">
121 class="{item.class ?? ''} flex items-center gap-2"
124 <item.icon class="h-4 w-4" />
126 <span>{item.label}</span>
132 <Tooltip.Content side="right">
133 <p>{item.disabledTooltip}</p>
138 </DropdownMenu.SubContent>
142 class="flex cursor-pointer items-center gap-2"
143 onclick={onSystemPromptClick}
145 <MessageSquare class="h-4 w-4" />
147 <span>System Message</span>
150 <ChatFormActionAddToolsSubmenu />
152 <ChatFormActionAddMcpServersSubmenu onMcpSettingsClick={handleMcpSettingsClick} />
154 {#if hasMcpPromptsSupport}
155 <DropdownMenu.Separator />
158 class="flex cursor-pointer items-center gap-2"
159 onclick={onMcpPromptClick}
161 <Zap class="h-4 w-4" />
163 <span>MCP Prompt</span>
167 {#if hasMcpResourcesSupport}
169 class="flex cursor-pointer items-center gap-2"
170 onclick={onMcpResourcesClick}
172 <FolderOpen class="h-4 w-4" />
174 <span>MCP Resources</span>
177 </DropdownMenu.Content>