From d8702eb3d222d52ef29c59fbb50d7efaa311337c Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 4 Sep 2026 21:39:19 +0200 Subject: [PATCH] ui : group the model selector components Move the four selector surfaces into models/ModelsSelector with their own barrel, extract the shared reasoning panel and download row, group the option list helpers under navigation/utils, and add the models selector hook wiring (in-flight downloads and reasoning menu included). Assisted-by: pi:GLM-5.3-Flash --- .../ChatFormActionAddReasoningSubmenu.svelte | 75 ------ tools/ui/src/lib/components/app/chat/index.ts | 8 - .../ModelsSelectorDownloadItem.svelte | 125 ++++++++++ .../ModelsSelectorDropdown.svelte | 226 +++++++++--------- .../ModelsSelectorList.svelte | 65 +++-- .../ModelsSelectorOption.svelte | 57 ++++- .../ModelsSelectorReasoningPanel.svelte | 188 +++++++++++++++ .../ModelsSelectorSheet.svelte | 17 +- .../app/models/ModelsSelector/index.ts | 95 ++++++++ .../ui/src/lib/components/app/models/index.ts | 69 +----- .../navigation/DropdownMenuSearchable.svelte | 28 ++- .../app/{models => navigation}/utils.ts | 0 .../dropdown-menu-content.svelte | 2 +- .../lib/constants/css-classes.constants.ts | 6 + .../lib/hooks/use-models-selector.svelte.ts | 2 +- .../lib/hooks/use-reasoning-menu.svelte.ts | 4 +- .../stories/ModelsSelector.stories.svelte | 6 +- 17 files changed, 671 insertions(+), 302 deletions(-) delete mode 100644 tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte create mode 100644 tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDownloadItem.svelte rename tools/ui/src/lib/components/app/models/{ => ModelsSelector}/ModelsSelectorDropdown.svelte (66%) rename tools/ui/src/lib/components/app/models/{ => ModelsSelector}/ModelsSelectorList.svelte (50%) rename tools/ui/src/lib/components/app/models/{ => ModelsSelector}/ModelsSelectorOption.svelte (77%) create mode 100644 tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorReasoningPanel.svelte rename tools/ui/src/lib/components/app/models/{ => ModelsSelector}/ModelsSelectorSheet.svelte (93%) create mode 100644 tools/ui/src/lib/components/app/models/ModelsSelector/index.ts rename tools/ui/src/lib/components/app/{models => navigation}/utils.ts (100%) diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte deleted file mode 100644 index 197d6c2e59..0000000000 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - - - - {#if reasoning.isReasoningActive} - - {:else if reasoning.isOff} - - {:else} - - {/if} - - - Reasoning - - - {reasoning.currentEffort} - - - - - - {#each reasoning.levels as level (level.value)} - {@const tokenLabel = reasoning.tokenLabel(level)} - reasoning.select(level)} - > - {#if reasoning.isSelected(level)} - - {:else} -
- {/if} - - {level.label} - - {#if tokenLabel} - - {tokenLabel} - - {/if} - - {#if level.hasInfo} - - - - - - -

Maximum reasoning effort with extended context usage

-
-
- {/if} -
- {/each} -
-
diff --git a/tools/ui/src/lib/components/app/chat/index.ts b/tools/ui/src/lib/components/app/chat/index.ts index d0cd2de675..59e6cd4921 100644 --- a/tools/ui/src/lib/components/app/chat/index.ts +++ b/tools/ui/src/lib/components/app/chat/index.ts @@ -220,14 +220,6 @@ export { default as ChatFormActionModels } from './ChatForm/ChatFormActions/Chat */ export { default as ChatFormActionAddToolsSubmenu } from './ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte'; -/** Dropdown submenu for selecting reasoning effort level. - * - * Shows a "Reasoning" sub-menu item with a lightbulb icon indicating - * thinking status, and a nested list of effort levels. - * Only visible when the current model supports thinking. - */ -export { default as ChatFormActionAddReasoningSubmenu } from './ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddReasoningSubmenu.svelte'; - /** * Compact context-usage gauge with per-turn and cumulative breakdown in the tooltip. */ diff --git a/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDownloadItem.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDownloadItem.svelte new file mode 100644 index 0000000000..4a88a103ee --- /dev/null +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDownloadItem.svelte @@ -0,0 +1,125 @@ + + + +
+ + + + + {#if entry.progress && entry.progress.totalBytes > 0} + + {/if} +
diff --git a/tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte similarity index 66% rename from tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte index 30dedcbae9..67ce031dee 100644 --- a/tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorDropdown.svelte @@ -1,18 +1,22 @@ -{#snippet defaultOption(item: ModelItem, hideOrgName: boolean)} +{#snippet defaultOption(item: ModelItem, _hideOrgName: boolean)} {@const { option } = item} {@const isSelected = currentModel === option.model || activeId === option.id} {@const isFav = modelsStore.favoriteModelIds.has(option.model)} {}} {onSelect} {option} + showBaseModelAvatar /> {/snippet} -{#if groups.loaded.length > 0} -

Loaded models

- - {#each groups.loaded as item (`loaded-${item.option.id}`)} - {@render render(item, false)} - {/each} -{/if} - {#if groups.favorites.length > 0}

Favorite models

@@ -61,16 +69,35 @@ {/each} {/if} +{#if getDownloadEntries.length > 0} +

Download in progress

+ + {#each getDownloadEntries as entry (entry.repoWithTag)} + + {/each} +{/if} + +{#if groups.loaded.length > 0} +

Loaded models

+ + {#each groups.loaded as item (`loaded-${item.option.id}`)} + {@render render(item, false)} + {/each} +{/if} + {#if groups.available.length > 0} -

Available models

+

Downloaded models

{#each groups.available as group (group.orgName)} - {#if group.orgName} -

{group.orgName}

- {/if} - {#each group.items as item (item.option.id)} {@render render(item, true)} {/each} {/each} {/if} + + (cancelOpen = false)} + open={cancelOpen} + repoWithTag={pendingCancel} +/> diff --git a/tools/ui/src/lib/components/app/models/ModelsSelectorOption.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte similarity index 77% rename from tools/ui/src/lib/components/app/models/ModelsSelectorOption.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte index cee7d0c3b5..1be2a105c9 100644 --- a/tools/ui/src/lib/components/app/models/ModelsSelectorOption.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte @@ -1,5 +1,6 @@ + + +{#snippet triggerContent()} + {#if reasoning.isReasoningActive} + + {:else if reasoning.isOff} + + {:else} + + {/if} + + + Reasoning + + + {reasoning.currentEffort} + + + + {#if expanded} + + {:else} + + {/if} +{/snippet} + +{#if inMenu} + + + {#snippet child({ props })} + + + {/snippet} + +{:else} + +{/if} + + +
+ {#if rowsMounted} + +
+ {#each reasoning.levels as level (level.value)} + {@const tokenLabel = reasoning.tokenLabel(level)} + {#if inMenu} + pickLevel(level)} + > + {@render levelContent(level, tokenLabel)} + + {:else} + + {/if} + {/each} +
+ {/if} +
+ +{#snippet levelContent(level: ReasoningEffortLevel, tokenLabel: string | null)} + {#if reasoning.isSelected(level)} + + {:else} +
+ {/if} + + {level.label} + + {#if tokenLabel} + + {tokenLabel} + + {/if} + + {#if level.hasInfo} + + + + + + +

Maximum reasoning effort with extended context usage

+
+
+ {/if} +{/snippet} diff --git a/tools/ui/src/lib/components/app/models/ModelsSelectorSheet.svelte b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorSheet.svelte similarity index 93% rename from tools/ui/src/lib/components/app/models/ModelsSelectorSheet.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorSheet.svelte index c89ca186e3..e861b330a5 100644 --- a/tools/ui/src/lib/components/app/models/ModelsSelectorSheet.svelte +++ b/tools/ui/src/lib/components/app/models/ModelsSelector/ModelsSelectorSheet.svelte @@ -1,15 +1,17 @@ -
+
-
+
{@render children()} {#if isEmpty} @@ -44,7 +56,13 @@
{#if footer} - +
+ - {@render footer()} + {@render footer()} +
{/if} diff --git a/tools/ui/src/lib/components/app/models/utils.ts b/tools/ui/src/lib/components/app/navigation/utils.ts similarity index 100% rename from tools/ui/src/lib/components/app/models/utils.ts rename to tools/ui/src/lib/components/app/navigation/utils.ts diff --git a/tools/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/tools/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte index 014e85b58b..83f54958f6 100644 --- a/tools/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +++ b/tools/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -17,7 +17,7 @@ import { defineMeta } from '@storybook/addon-svelte-csf'; - import ModelsSelectorList from '$lib/components/app/models/ModelsSelectorList.svelte'; - import ModelsSelectorOption from '$lib/components/app/models/ModelsSelectorOption.svelte'; - import type { GroupedModelOptions, ModelItem } from '$lib/components/app/models/utils'; + import ModelsSelectorList from '$lib/components/app/models/ModelsSelector/ModelsSelectorList.svelte'; + import ModelsSelectorOption from '$lib/components/app/models/ModelsSelector/ModelsSelectorOption.svelte'; + import type { GroupedModelOptions, ModelItem } from '$lib/components/app/navigation/utils'; import { ServerModelStatus } from '$lib/enums'; import { modelsStore } from '$lib/stores/models/index.svelte';