Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
<Collapsible.Content>
<div class="flex flex-col gap-0.5 pl-4">
{#each toolsPanel.activeGroups as group (group.label)}
{@const { checked, indeterminate } = toolsPanel.getGroupCheckedState(group)}
{@const checked = toolsPanel.isGroupChecked(group)}
{@const enabledCount = toolsPanel.getEnabledToolCount(group)}
{@const favicon = toolsPanel.getFavicon(group)}

Expand Down Expand Up @@ -259,7 +259,6 @@

<Checkbox
{checked}
{indeterminate}
class="h-4 w-4 shrink-0"
onclick={(e) => e.stopPropagation()}
onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { PencilRuler, ChevronDown, ChevronRight, Loader2, Info } from '@lucide/svelte';
import { PencilRuler, ChevronDown, ChevronRight, Loader2, Info, Check } from '@lucide/svelte';
import { Checkbox } from '$lib/components/ui/checkbox';
import * as Collapsible from '$lib/components/ui/collapsible';
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
Expand Down Expand Up @@ -65,7 +65,7 @@
<div class="max-h-80 overflow-y-auto p-2 pr-1">
{#each toolsPanel.activeGroups as group (group.label)}
{@const isExpanded = toolsPanel.expandedGroups.has(group.label)}
{@const { checked, indeterminate } = toolsPanel.getGroupCheckedState(group)}
{@const checked = toolsPanel.isGroupChecked(group)}
{@const favicon = toolsPanel.getFavicon(group)}

<Collapsible.Root
Expand Down Expand Up @@ -104,12 +104,14 @@

<Tooltip.Root>
<Tooltip.Trigger>
<Checkbox
{checked}
{indeterminate}
onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
class="mr-2 h-4 w-4 shrink-0"
/>
{#snippet child({ props })}
<Checkbox
{...props}
{checked}
onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
class="mr-2 h-4 w-4 shrink-0"
/>
{/snippet}
</Tooltip.Trigger>

<Tooltip.Content side="right">
Expand All @@ -123,20 +125,25 @@

<Collapsible.Content>
<div class="ml-4 flex flex-col gap-0.5 border-l border-border/50 pl-2">
{#each group.tools as tool (tool.function.name)}
{#each group.tools as entry (entry.key)}
{@const enabled = toolsStore.isToolEnabled(entry.key)}
<button
type="button"
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm transition-colors hover:bg-muted/50"
onclick={() => toolsStore.toggleTool(tool.function.name)}
onclick={() => toolsStore.toggleTool(entry.key)}
>
<Checkbox
checked={toolsStore.isToolEnabled(tool.function.name)}
onCheckedChange={() => toolsStore.toggleTool(tool.function.name)}
class="h-4 w-4 shrink-0"
/>
<span
data-slot="checkbox"
data-state={enabled ? 'checked' : 'unchecked'}
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"
>
{#if enabled}
<Check class="size-3.5" />
{/if}
</span>

<span class="min-w-0 flex-1 truncate font-mono text-[12px]">
{tool.function.name}
{entry.definition.function.name}
</span>
</button>
{/each}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,19 @@
<span class="w-20 shrink-0 text-center">Always allow</span>
</div>

{#each group.tools as tool (tool.function.name)}
{@const toolName = tool.function.name}
{@const isEnabled = toolsStore.isToolEnabled(toolName)}
{@const permissionKey = toolsStore.getPermissionKey(toolName)}
{@const isAlwaysAllowed = permissionKey
? permissionsStore.hasTool(permissionKey)
: false}
{#each group.tools as entry (entry.key)}
{@const toolName = entry.definition.function.name}
{@const isEnabled = toolsStore.isToolEnabled(entry.key)}
{@const permissionKey = entry.key}
{@const isAlwaysAllowed = permissionsStore.hasTool(permissionKey)}

<div class="flex items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50">
<TruncatedText text={toolName} class="flex-1" showTooltip={true} />

<div class="flex w-16 shrink-0 justify-center">
<Checkbox
checked={isEnabled}
onCheckedChange={() => toolsStore.toggleTool(toolName)}
onCheckedChange={() => toolsStore.toggleTool(entry.key)}
class="h-4 w-4"
/>
</div>
Expand All @@ -86,9 +84,9 @@
checked={isAlwaysAllowed}
onCheckedChange={() => {
if (isAlwaysAllowed) {
permissionsStore.revokeTool(permissionKey!);
permissionsStore.revokeTool(permissionKey);
} else {
permissionsStore.allowTool(permissionKey!);
permissionsStore.allowTool(permissionKey);
}
}}
class="h-4 w-4"
Expand Down
3 changes: 3 additions & 0 deletions tools/ui/src/lib/constants/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const DB_APP_NAME_DEPRECATED = 'LlamacppWebui';
export const ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.alwaysAllowedTools`;
export const CONFIG_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.config`;
export const DISABLED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.disabledTools`;

/** Disabled tools keyed by stable selection identity, no migration from the name based key */
export const DISABLED_TOOL_KEYS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.disabledToolKeys`;
export const FAVORITE_MODELS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.favoriteModels`;
export const MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.mcpDefaultEnabled`;
export const THINKING_ENABLED_DEFAULT_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.thinkingEnabledDefault`;
Expand Down
27 changes: 9 additions & 18 deletions tools/ui/src/lib/hooks/use-tools-panel.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export interface UseToolsPanelReturn {
readonly activeGroups: ToolGroup[];
readonly totalToolCount: number;
readonly noToolsInfoMessage: string | null;
getGroupCheckedState(group: ToolGroup): { checked: boolean; indeterminate: boolean };
isGroupChecked(group: ToolGroup): boolean;
getEnabledToolCount(group: ToolGroup): number;
getFavicon(group: { source: ToolSource; label: string }): string | null;
getFavicon(group: ToolGroup): string | null;
isGroupDisabled(group: ToolGroup): boolean;
toggleGroupExpanded(label: string): void;
/** Toggle all tools in a group by label (avoids stale group object references). */
Expand Down Expand Up @@ -54,27 +54,18 @@ export function useToolsPanel(): UseToolsPanelReturn {
return `To enable Built-In Tools you need to run llama-server with ${CLI_FLAGS.TOOLS} all or ${CLI_FLAGS.TOOLS} <name> flag. To see MCP Tools you need to add / enable MCP Server(s).`;
});

function getGroupCheckedState(group: ToolGroup): { checked: boolean; indeterminate: boolean } {
return {
checked: toolsStore.isGroupFullyEnabled(group),
indeterminate: toolsStore.isGroupPartiallyEnabled(group)
};
function isGroupChecked(group: ToolGroup): boolean {
return toolsStore.isGroupFullyEnabled(group);
}

function getEnabledToolCount(group: ToolGroup): number {
return group.tools.filter((tool) => toolsStore.isToolEnabled(tool.function.name)).length;
return group.tools.filter((tool) => toolsStore.isToolEnabled(tool.key)).length;
}

function getFavicon(group: { source: ToolSource; label: string }): string | null {
if (group.source !== ToolSource.MCP) return null;
function getFavicon(group: ToolGroup): string | null {
if (group.source !== ToolSource.MCP || !group.serverId) return null;

for (const server of mcpStore.getServersSorted()) {
if (mcpStore.getServerLabel(server) === group.label) {
return mcpStore.getServerFavicon(server.id);
}
}

return null;
return mcpStore.getServerFavicon(group.serverId);
}

function isGroupDisabled(group: ToolGroup): boolean {
Expand Down Expand Up @@ -121,7 +112,7 @@ export function useToolsPanel(): UseToolsPanelReturn {
get noToolsInfoMessage() {
return noToolsInfoMessage;
},
getGroupCheckedState,
isGroupChecked,
getEnabledToolCount,
getFavicon,
isGroupDisabled,
Expand Down
Loading