]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/blob
9a5b0cbe8621e425c48f329ca63e1503eafaa70c
[pkg/ggml/sources/llama.cpp] /
1 <script lang="ts">
2 import { PencilRuler, ChevronDown, ChevronRight, Loader2, Info, Check } from '@lucide/svelte';
3 import { Checkbox } from '$lib/components/ui/checkbox';
4 import * as Collapsible from '$lib/components/ui/collapsible';
5 import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
6 import * as Tooltip from '$lib/components/ui/tooltip';
7 import { toolsStore } from '$lib/stores/tools.svelte';
8 import { CLI_FLAGS } from '$lib/constants';
9 import { mcpStore } from '$lib/stores/mcp.svelte';
10 import { useToolsPanel } from '$lib/hooks/use-tools-panel.svelte';
11
12 const toolsPanel = useToolsPanel();
13 const hasMcpServersAvailable = $derived(mcpStore.getServersSorted().length > 0);
14 </script>
15
16 <DropdownMenu.Sub onOpenChange={(open) => open && toolsPanel.handleOpen()}>
17 <DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
18 <PencilRuler class="h-4 w-4" />
19
20 <span>Tools</span>
21 </DropdownMenu.SubTrigger>
22
23 <DropdownMenu.SubContent class="w-72 p-0">
24 {#if toolsPanel.totalToolCount === 0}
25 {#if toolsStore.loading}
26 <div class="px-3 py-4 text-center text-sm text-muted-foreground">
27 <Loader2 class="mx-auto mb-1 h-4 w-4 animate-spin" />
28
29 Loading tools...
30 </div>
31 {:else if toolsStore.isToolsEndpointUnreachable}
32 <div class="grid gap-2.5 px-3 py-4 text-sm text-muted-foreground">
33 <span class="flex gap-2">
34 <Info class="mt-0.5 h-4 w-4 shrink-0" />
35
36 <span>
37 Run llama-server with <code>{CLI_FLAGS.TOOLS}</code> flag to enable
38
39 <strong>Built-in Tools</strong>.
40 </span>
41 </span>
42
43 <span class="flex gap-2">
44 <Info class="mt-0.5 h-4 w-4 shrink-0" />
45
46 <span>
47 {hasMcpServersAvailable ? 'Enable' : 'Add'} MCP Server(s) to access
48
49 <strong>MCP Tools</strong>.
50 </span>
51 </span>
52 </div>
53 {:else if toolsStore.error}
54 <div class="px-3 py-4 text-center text-sm text-muted-foreground">Failed to load tools</div>
55 {:else if toolsPanel.noToolsInfoMessage}
56 <div class="flex gap-2 px-3 py-4 text-sm text-muted-foreground">
57 <Info class="mt-0.5 h-4 w-4 shrink-0" />
58
59 <span>{toolsPanel.noToolsInfoMessage}</span>
60 </div>
61 {:else}
62 <div class="px-3 py-4 text-center text-sm text-muted-foreground">No tools available</div>
63 {/if}
64 {:else}
65 <div class="max-h-80 overflow-y-auto p-2 pr-1">
66 {#each toolsPanel.activeGroups as group (group.label)}
67 {@const isExpanded = toolsPanel.expandedGroups.has(group.label)}
68 {@const checked = toolsPanel.isGroupChecked(group)}
69 {@const favicon = toolsPanel.getFavicon(group)}
70
71 <Collapsible.Root
72 open={isExpanded}
73 onOpenChange={() => toolsPanel.toggleGroupExpanded(group.label)}
74 >
75 <div class="flex items-center gap-1">
76 <Collapsible.Trigger
77 class="flex min-w-0 flex-1 items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50"
78 >
79 {#if isExpanded}
80 <ChevronDown class="h-3.5 w-3.5 shrink-0" />
81 {:else}
82 <ChevronRight class="h-3.5 w-3.5 shrink-0" />
83 {/if}
84
85 <span class="inline-flex min-w-0 items-center gap-1.5 font-medium">
86 {#if favicon}
87 <img
88 src={favicon}
89 alt=""
90 class="h-4 w-4 shrink-0 rounded-sm"
91 onerror={(e) => {
92 (e.currentTarget as HTMLImageElement).style.display = 'none';
93 }}
94 />
95 {/if}
96
97 <span class="truncate">{group.label}</span>
98 </span>
99
100 <span class="ml-auto shrink-0 text-xs text-muted-foreground">
101 {toolsPanel.getEnabledToolCount(group)}/{group.tools.length}
102 </span>
103 </Collapsible.Trigger>
104
105 <Tooltip.Root>
106 <Tooltip.Trigger>
107 {#snippet child({ props })}
108 <Checkbox
109 {...props}
110 {checked}
111 onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
112 class="mr-2 h-4 w-4 shrink-0"
113 />
114 {/snippet}
115 </Tooltip.Trigger>
116
117 <Tooltip.Content side="right">
118 <p>
119 {checked ? 'Disable' : 'Enable'}
120 {group.tools.length} tool{group.tools.length !== 1 ? 's' : ''}
121 </p>
122 </Tooltip.Content>
123 </Tooltip.Root>
124 </div>
125
126 <Collapsible.Content>
127 <div class="ml-4 flex flex-col gap-0.5 border-l border-border/50 pl-2">
128 {#each group.tools as entry (entry.key)}
129 {@const enabled = toolsStore.isToolEnabled(entry.key)}
130 <button
131 type="button"
132 class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm transition-colors hover:bg-muted/50"
133 onclick={() => toolsStore.toggleTool(entry.key)}
134 >
135 <span
136 data-slot="checkbox"
137 data-state={enabled ? 'checked' : 'unchecked'}
138 class="flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
139 >
140 {#if enabled}
141 <Check class="size-3.5" />
142 {/if}
143 </span>
144
145 <span class="min-w-0 flex-1 truncate font-mono text-[12px]">
146 {entry.definition.function.name}
147 </span>
148 </button>
149 {/each}
150 </div>
151 </Collapsible.Content>
152 </Collapsible.Root>
153 {/each}
154 </div>
155 {/if}
156 </DropdownMenu.SubContent>
157 </DropdownMenu.Sub>