]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/blob
0a8260a2952b182804fe266cbe5a79aed040bf6b
[pkg/ggml/sources/llama.cpp] /
1 <script lang="ts">
2 import { Plus } 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';
7 import {
8 ATTACHMENT_FILE_ITEMS,
9 ATTACHMENT_EXTRA_ITEMS,
10 ATTACHMENT_MCP_ITEMS,
11 ATTACHMENT_TOOLTIP_TEXT,
12 TOOLTIP_DELAY_DURATION
13 } from '$lib/constants';
14 import { AttachmentMenuItemId } from '$lib/enums';
15 import {
16 ChatFormActionAddToolsSubmenu,
17 ChatFormActionAddMcpServersSubmenu
18 } from '$lib/components/app';
19
20 import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte';
21
22 interface Props {
23 class?: string;
24 disabled?: boolean;
25 hasAudioModality?: boolean;
26 hasVideoModality?: boolean;
27 hasVisionModality?: boolean;
28 hasMcpPromptsSupport?: boolean;
29 hasMcpResourcesSupport?: boolean;
30 onFileUpload?: () => void;
31 onSystemPromptClick?: () => void;
32 onMcpPromptClick?: () => void;
33 onMcpSettingsClick?: () => void;
34 onMcpResourcesClick?: () => void;
35 }
36
37 let {
38 class: className = '',
39 disabled = false,
40 hasAudioModality = false,
41 hasVideoModality = false,
42 hasVisionModality = false,
43 hasMcpPromptsSupport = false,
44 hasMcpResourcesSupport = false,
45 onFileUpload,
46 onSystemPromptClick,
47 onMcpPromptClick,
48 onMcpSettingsClick,
49 onMcpResourcesClick
50 }: Props = $props();
51
52 let dropdownOpen = $state(false);
53
54 function handleMcpSettingsClick() {
55 dropdownOpen = false;
56 onMcpSettingsClick?.();
57 }
58
59 const attachmentMenu = useAttachmentMenu(
60 () => ({
61 hasVisionModality,
62 hasAudioModality,
63 hasVideoModality,
64 hasMcpPromptsSupport,
65 hasMcpResourcesSupport
66 }),
67 () => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }),
68 () => {
69 dropdownOpen = false;
70 }
71 );
72 </script>
73
74 <div class="flex items-center gap-1 {className}">
75 <DropdownMenu.Root bind:open={dropdownOpen}>
76 <Tooltip.Root>
77 <Tooltip.Trigger>
78 {#snippet child({ props })}
79 <DropdownMenu.Trigger
80 {...props}
81 class={cn(
82 buttonVariants({ variant: 'secondary' }),
83 'file-upload-button h-8 w-8 cursor-pointer rounded-full p-0'
84 )}
85 {disabled}
86 >
87 <span class="sr-only">{ATTACHMENT_TOOLTIP_TEXT}</span>
88
89 <Plus class="h-4 w-4" />
90 </DropdownMenu.Trigger>
91 {/snippet}
92 </Tooltip.Trigger>
93
94 <Tooltip.Content>
95 <p>{ATTACHMENT_TOOLTIP_TEXT}</p>
96 </Tooltip.Content>
97 </Tooltip.Root>
98
99 <DropdownMenu.Content align="start" class="w-48">
100 {#each ATTACHMENT_FILE_ITEMS as item (item.id)}
101 {@const enabled = attachmentMenu.isItemEnabled(item.enabledWhen)}
102 {#if enabled}
103 <DropdownMenu.Item
104 class="{item.class ?? ''} flex cursor-pointer items-center gap-2"
105 onclick={() => attachmentMenu.callbacks[item.action]()}
106 >
107 <item.icon class="h-4 w-4" />
108
109 <span>{item.label}</span>
110 </DropdownMenu.Item>
111 {:else if item.disabledTooltip}
112 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
113 <Tooltip.Trigger tabindex={-1}>
114 {#snippet child({ props })}
115 <div {...props} class="cursor-default">
116 <DropdownMenu.Item class="{item.class ?? ''} flex items-center gap-2" disabled>
117 <item.icon class="h-4 w-4" />
118
119 <span>{item.label}</span>
120 </DropdownMenu.Item>
121 </div>
122 {/snippet}
123 </Tooltip.Trigger>
124
125 <Tooltip.Content side="right">
126 <p>{item.disabledTooltip}</p>
127 </Tooltip.Content>
128 </Tooltip.Root>
129 {/if}
130 {/each}
131
132 {#if !attachmentMenu.isItemEnabled('hasVisionModality')}
133 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
134 <Tooltip.Trigger>
135 {#snippet child({ props })}
136 <DropdownMenu.Item
137 {...props}
138 class="flex cursor-pointer items-center gap-2"
139 onclick={attachmentMenu.callbacks.onFileUpload}
140 >
141 {@const pdfItem = ATTACHMENT_FILE_ITEMS.find(
142 (i) => i.id === AttachmentMenuItemId.PDF
143 )}
144 {#if pdfItem}
145 <pdfItem.icon class="h-4 w-4" />
146
147 <span>{pdfItem.label}</span>
148 {/if}
149 </DropdownMenu.Item>
150 {/snippet}
151 </Tooltip.Trigger>
152
153 <Tooltip.Content side="right">
154 <p>PDFs will be converted to text. Image-based PDFs may not work properly.</p>
155 </Tooltip.Content>
156 </Tooltip.Root>
157 {/if}
158
159 <DropdownMenu.Separator />
160
161 {#each ATTACHMENT_EXTRA_ITEMS as item (item.id)}
162 {#if item.id === AttachmentMenuItemId.SYSTEM_MESSAGE}
163 <Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
164 <Tooltip.Trigger>
165 {#snippet child({ props })}
166 <DropdownMenu.Item
167 {...props}
168 class="flex cursor-pointer items-center gap-2"
169 onclick={() => attachmentMenu.callbacks[item.action]()}
170 >
171 <item.icon class="h-4 w-4" />
172
173 <span>{item.label}</span>
174 </DropdownMenu.Item>
175 {/snippet}
176 </Tooltip.Trigger>
177
178 <Tooltip.Content side="right">
179 <p>{attachmentMenu.getSystemMessageTooltip()}</p>
180 </Tooltip.Content>
181 </Tooltip.Root>
182 {/if}
183 {/each}
184
185 <ChatFormActionAddToolsSubmenu />
186
187 <ChatFormActionAddMcpServersSubmenu onMcpSettingsClick={handleMcpSettingsClick} />
188
189 {#each ATTACHMENT_MCP_ITEMS as item (item.id)}
190 {#if attachmentMenu.isItemVisible(item.visibleWhen)}
191 <DropdownMenu.Item
192 class="flex cursor-pointer items-center gap-2"
193 onclick={() => attachmentMenu.callbacks[item.action]()}
194 >
195 <item.icon class="h-4 w-4" />
196
197 <span>{item.label}</span>
198 </DropdownMenu.Item>
199 {/if}
200 {/each}
201 </DropdownMenu.Content>
202 </DropdownMenu.Root>
203 </div>