2 import type { Snippet } from 'svelte';
3 import * as Tooltip from '$lib/components/ui/tooltip';
4 import * as Sheet from '$lib/components/ui/sheet';
5 import { TOOLTIP_DELAY_DURATION } from '$lib/constants';
8 ATTACHMENT_EXTRA_ITEMS,
10 } from '$lib/constants/attachment-menu';
11 import { McpLogo } from '$lib/components/app';
12 import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte';
13 import { AttachmentMenuItemId } from '$lib/enums';
14 import { PencilRuler } from '@lucide/svelte';
15 import { ROUTES, SETTINGS_SECTION_SLUGS } from '$lib/constants/routes';
16 import { RouterService } from '$lib/services/router.service';
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 onMcpResourcesClick?: () => void;
30 trigger: Snippet<[{ disabled: boolean; onclick?: () => void }]>;
34 class: className = '',
36 hasAudioModality = false,
37 hasVisionModality = false,
38 hasVideoModality = false,
39 hasMcpPromptsSupport = false,
40 hasMcpResourcesSupport = false,
48 let sheetOpen = $state(false);
50 const attachmentMenu = useAttachmentMenu(
56 hasMcpResourcesSupport
58 () => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }),
64 const sheetItemClass =
65 'flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allowed disabled:opacity-50';
68 <div class="flex items-center gap-1 {className}">
69 <Sheet.Root bind:open={sheetOpen}>
70 {@render trigger({ disabled, onclick: () => (sheetOpen = true) })}
71 <!-- <ChatFormActionAddButton {disabled} onclick={() => (sheetOpen = true)} /> -->
73 <Sheet.Content side="bottom" class="max-h-[85vh] gap-0 overflow-y-auto">
75 <Sheet.Title>Add to chat</Sheet.Title>
77 <Sheet.Description class="sr-only">
78 Add files, system prompt or configure MCP servers
82 <div class="flex flex-col gap-1 px-1.5 pb-2">
83 {#each ATTACHMENT_FILE_ITEMS as item (item.id)}
84 {@const enabled = attachmentMenu.isItemEnabled(item.enabledWhen)}
88 class={sheetItemClass}
89 onclick={() => attachmentMenu.callbacks[item.action]()}
91 <item.icon class="h-4 w-4 shrink-0" />
93 <span>{item.label}</span>
95 {:else if item.disabledTooltip}
96 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
98 <button type="button" class={sheetItemClass} disabled>
99 <item.icon class="h-4 w-4 shrink-0" />
101 <span>{item.label}</span>
105 <Tooltip.Content side="right">
106 <p>{item.disabledTooltip}</p>
112 {#if !attachmentMenu.isItemEnabled('hasVisionModality')}
113 {@const pdfItem = ATTACHMENT_FILE_ITEMS.find((i) => i.id === AttachmentMenuItemId.PDF)}
115 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
119 class={sheetItemClass}
120 onclick={() => attachmentMenu.callbacks[pdfItem.action]()}
122 <pdfItem.icon class="h-4 w-4 shrink-0" />
124 <span>{pdfItem.label}</span>
128 <Tooltip.Content side="right">
129 <p>PDFs will be converted to text. Image-based PDFs may not work properly.</p>
135 {#each ATTACHMENT_EXTRA_ITEMS as item (item.id)}
136 {#if item.id === AttachmentMenuItemId.SYSTEM_MESSAGE}
137 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
141 class={sheetItemClass}
142 onclick={() => attachmentMenu.callbacks[item.action]()}
144 <item.icon class="h-4 w-4 shrink-0" />
146 <span>{item.label}</span>
150 <Tooltip.Content side="right">
151 <p>{attachmentMenu.getSystemMessageTooltip()}</p>
157 <div class="my-2 border-t"></div>
159 <a href={ROUTES.MCP_SERVERS} class="flex items-center gap-3 px-3 py-2">
160 <McpLogo class="inline h-4 w-4" />
162 <span class="text-sm">MCP Servers</span>
166 href={RouterService.settings(SETTINGS_SECTION_SLUGS.TOOLS)}
167 class="flex items-center gap-3 px-3 py-2"
169 <PencilRuler class="inline h-4 w-4" />
171 <span class="text-sm">Tools</span>
174 {#each ATTACHMENT_MCP_ITEMS as item (item.id)}
175 {#if attachmentMenu.isItemVisible(item.visibleWhen)}
178 class={sheetItemClass}
179 onclick={() => attachmentMenu.callbacks[item.action]()}
181 <item.icon class="h-4 w-4 shrink-0" />
183 <span>{item.label}</span>