From e415366eba8c6ae54ac6c7c9d495c57214e69906 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Thu, 27 Aug 2026 09:54:09 +0200 Subject: [PATCH] ui: derive group checkbox state in useToolsPanel Moves the mixed-state derivation out of the submenu and sheet snippets into one getGroupCheckState accessor; the snippets just consume checked and indeterminate. Assisted-by: pi --- .../ChatFormActionAddSheet.svelte | 9 +++------ .../ChatFormActionAddToolsSubmenu.svelte | 12 ++++-------- .../ui/src/lib/hooks/use-tools-panel.svelte.ts | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte index 6b4f12376f..2dbc7fe774 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte @@ -250,11 +250,8 @@ {#snippet sheetGroupRow(group: ToolGroup)} - {@const checked = toolsPanel.isGroupChecked(group)} + {@const checkState = toolsPanel.getGroupCheckState(group)} {@const enabledCount = toolsPanel.getEnabledToolCount(group)} - - {@const indeterminate = - group.tools.length > 0 && (enabledCount === 0 ? checked : enabledCount < group.tools.length)} {@const favicon = toolsPanel.getFavicon(group)} {@const groupDisabled = toolsPanel.isGroupDisabled(group)} @@ -281,9 +278,9 @@ toolsPanel.toggleGroupByKey(group.key)} onclick={(e) => e.stopPropagation()} /> diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte index 6f32ad0920..f495441714 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte @@ -77,11 +77,7 @@ {#snippet groupRow(group: ToolGroup)} {@const isExpanded = toolsPanel.expandedGroups.has(group.key)} - {@const checked = toolsPanel.isGroupChecked(group)} - {@const enabledCount = toolsPanel.getEnabledToolCount(group)} - - {@const indeterminate = - group.tools.length > 0 && (enabledCount === 0 ? checked : enabledCount < group.tools.length)} + {@const checkState = toolsPanel.getGroupCheckState(group)} {@const favicon = toolsPanel.getFavicon(group)} {@const groupDisabled = toolsPanel.isGroupDisabled(group)} @@ -124,9 +120,9 @@ {#snippet child({ props })} toolsPanel.toggleGroupByKey(group.key)} /> {/snippet} @@ -134,7 +130,7 @@

- {checked ? 'Disable' : 'Enable'} + {checkState.checked ? 'Disable' : 'Enable'} {group.tools.length} tool{group.tools.length !== 1 ? 's' : ''}

diff --git a/tools/ui/src/lib/hooks/use-tools-panel.svelte.ts b/tools/ui/src/lib/hooks/use-tools-panel.svelte.ts index dac3ba827c..21deed32d3 100644 --- a/tools/ui/src/lib/hooks/use-tools-panel.svelte.ts +++ b/tools/ui/src/lib/hooks/use-tools-panel.svelte.ts @@ -12,6 +12,7 @@ export interface UseToolsPanelReturn { readonly noToolsInfoMessage: string | null; isGroupChecked(group: ToolGroup): boolean; getEnabledToolCount(group: ToolGroup): number; + getGroupCheckState(group: ToolGroup): { checked: boolean; indeterminate: boolean }; getFavicon(group: ToolGroup): string | null; isGroupDisabled(group: ToolGroup): boolean; isToolEnabled(entry: ToolEntry): boolean; @@ -65,6 +66,22 @@ export function useToolsPanel(): UseToolsPanelReturn { return group.tools.filter((tool) => conversationsStore.preferences.isToolActive(tool)).length; } + /** + * Group checkbox state: checked is the parent flag (category on, or the + * server key on for MCP groups); indeterminate marks the mixed case where + * the parent is on but nothing or only part of the group is enabled. + * isToolActive folds the parent gates into the count, so a disabled parent + * always yields plain unchecked. + */ + function getGroupCheckState(group: ToolGroup): { checked: boolean; indeterminate: boolean } { + const checked = isGroupChecked(group); + const enabledCount = getEnabledToolCount(group); + const indeterminate = + group.tools.length > 0 && (enabledCount === 0 ? checked : enabledCount < group.tools.length); + + return { checked, indeterminate }; + } + function getFavicon(group: ToolGroup): string | null { if (group.source !== ToolSource.MCP || !group.serverId) return null; @@ -123,6 +140,7 @@ export function useToolsPanel(): UseToolsPanelReturn { expandedGroups, getEnabledToolCount, getFavicon, + getGroupCheckState, handleOpen, isGroupChecked, isGroupDisabled,