2 import type { Snippet } from 'svelte';
3 import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
4 import * as Tooltip from '$lib/components/ui/tooltip';
7 ATTACHMENT_EXTRA_ITEMS,
10 } from '$lib/constants';
11 import { AttachmentMenuItemId } from '$lib/enums';
13 ChatFormActionAddToolsSubmenu,
14 ChatFormActionAddMcpServersSubmenu
15 } from '$lib/components/app';
17 import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte';
22 hasAudioModality?: 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;
31 trigger: Snippet<[{ disabled: boolean }]>;
35 class: className = '',
37 hasAudioModality = false,
38 hasVisionModality = false,
39 hasMcpPromptsSupport = false,
40 hasMcpResourcesSupport = false,
49 let dropdownOpen = $state(false);
51 function handleMcpSettingsClick() {
53 onMcpSettingsClick?.();
56 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}>
72 <DropdownMenu.Trigger name="Attach files" {disabled}>
73 {@render trigger({ disabled })}
74 </DropdownMenu.Trigger>
76 <DropdownMenu.Content align="start" class="w-48">
77 {#each ATTACHMENT_FILE_ITEMS as item (item.id)}
78 {@const enabled = attachmentMenu.isItemEnabled(item.enabledWhen)}
81 class="{item.class ?? ''} flex cursor-pointer items-center gap-2"
82 onclick={() => attachmentMenu.callbacks[item.action]()}
84 <item.icon class="h-4 w-4" />
86 <span>{item.label}</span>
88 {:else if item.disabledTooltip}
89 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
90 <Tooltip.Trigger class="w-full">
92 class="{item.class ?? ''} flex cursor-pointer items-center gap-2"
95 <item.icon class="h-4 w-4" />
97 <span>{item.label}</span>
101 <Tooltip.Content side="right">
102 <p>{item.disabledTooltip}</p>
108 {#if !attachmentMenu.isItemEnabled('hasVisionModality')}
109 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
110 <Tooltip.Trigger class="w-full">
112 class="flex cursor-pointer items-center gap-2"
113 onclick={attachmentMenu.callbacks.onFileUpload}
115 {@const pdfItem = ATTACHMENT_FILE_ITEMS.find(
116 (i) => i.id === AttachmentMenuItemId.PDF
119 <pdfItem.icon class="h-4 w-4" />
121 <span>{pdfItem.label}</span>
126 <Tooltip.Content side="right">
127 <p>PDFs will be converted to text. Image-based PDFs may not work properly.</p>
132 <DropdownMenu.Separator />
134 {#each ATTACHMENT_EXTRA_ITEMS as item (item.id)}
135 {#if item.id === AttachmentMenuItemId.SYSTEM_MESSAGE}
136 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
137 <Tooltip.Trigger class="w-full">
139 class="flex cursor-pointer items-center gap-2"
140 onclick={() => attachmentMenu.callbacks[item.action]()}
142 <item.icon class="h-4 w-4" />
144 <span>{item.label}</span>
148 <Tooltip.Content side="right">
149 <p>{attachmentMenu.getSystemMessageTooltip()}</p>
155 <ChatFormActionAddToolsSubmenu />
157 <ChatFormActionAddMcpServersSubmenu onMcpSettingsClick={handleMcpSettingsClick} />
159 {#each ATTACHMENT_MCP_ITEMS as item (item.id)}
160 {#if attachmentMenu.isItemVisible(item.visibleWhen)}
162 class="flex cursor-pointer items-center gap-2"
163 onclick={() => attachmentMenu.callbacks[item.action]()}
165 <item.icon class="h-4 w-4" />
167 <span>{item.label}</span>
171 </DropdownMenu.Content>