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