From d8131d1091786ade6649633f84f47e938d6a3a83 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 14 Jul 2026 02:37:21 -0400 Subject: [PATCH] refac --- src/lib/components/chat/ModelSelector.svelte | 2 + .../chat/ModelSelector/ModelItem.svelte | 26 +-- .../chat/ModelSelector/Selector.svelte | 221 +++++++----------- src/lib/components/chat/Navbar.svelte | 2 +- .../components/layout/Sidebar/Section.svelte | 2 +- .../workspace/Models/ModelEditor.svelte | 1 - .../workspace/common/TagSelector.svelte | 14 +- 7 files changed, 116 insertions(+), 152 deletions(-) diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte index dbbd9ed2ac..7d59c9eac4 100644 --- a/src/lib/components/chat/ModelSelector.svelte +++ b/src/lib/components/chat/ModelSelector.svelte @@ -13,6 +13,7 @@ export let showSetDefault = true; export let triggerClassName = 'text-lg'; + export let className = undefined; let compareModels = selectedModels.length > 1; @@ -69,6 +70,7 @@ model: model }))} {pinModelHandler} + {className} {triggerClassName} {showSetDefault} onSetDefault={saveDefaultModel} diff --git a/src/lib/components/chat/ModelSelector/ModelItem.svelte b/src/lib/components/chat/ModelSelector/ModelItem.svelte index 40b2a481f6..ce0aadcac5 100644 --- a/src/lib/components/chat/ModelSelector/ModelItem.svelte +++ b/src/lib/components/chat/ModelSelector/ModelItem.svelte @@ -52,17 +52,17 @@ role="option" aria-selected={isSelected} aria-label={$i18n.t('Select {{modelName}} model', { modelName: item.label })} - class="flex group/item w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-[7px] pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl cursor-pointer data-highlighted:bg-muted {index === + class="group/item flex h-8 w-full cursor-pointer select-none items-center rounded-xl px-2 text-left text-[13px] font-normal text-gray-700 outline-hidden transition-colors duration-75 hover:bg-gray-50/40 dark:text-gray-100 dark:hover:bg-gray-800/40 {index === selectedModelIdx && !compareEnabled - ? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent' - : ''} {isSelected ? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent' : ''}" + ? 'bg-gray-50/70 dark:bg-gray-800/60' + : ''} {isSelected ? 'bg-gray-50/70 dark:bg-gray-800/60' : ''}" data-arrow-selected={index === selectedModelIdx} data-value={item.value} on:click={() => { onClick(); }} > -
+
-
+
{$i18n.t('{{modelName}} { e.currentTarget.src = '/favicon.png'; @@ -94,7 +94,7 @@
-
+
{item.label} @@ -102,7 +102,7 @@
-
+
{#if item.model.owned_by === 'ollama'} {#if (item.model.ollama?.details?.parameter_size ?? '') !== ''}
@@ -118,7 +118,7 @@ }`} className="self-end" > - {item.model.ollama?.details?.parameter_size ?? ''} @@ -127,7 +127,7 @@ {/if} {#if item.model.loaded} -
+
new Date() @@ -138,11 +138,11 @@ className="self-end" >
- + - +
@@ -240,7 +240,7 @@
-
+
{#if !selectionOnly && $user?.role === 'admin' && item.model.loaded} Promise | void = () => {}; export let pinModelHandler: (modelId: string) => void = () => {}; - let tagsContainerElement; - let show = false; let triggerElement: HTMLElement | null = null; let contentElement: HTMLElement | null = null; @@ -112,7 +112,8 @@ const target = e.target as Node; if ( (triggerElement && triggerElement.contains(target)) || - (contentElement && contentElement.contains(target)) + (contentElement && contentElement.contains(target)) || + ((target as HTMLElement).closest?.('.model-selector-child-menu') ?? false) ) { return; } @@ -146,6 +147,8 @@ let selectedTag = ''; let selectedConnectionType = ''; + let selectedFilter = ''; + let modelFilterItems = []; let ollamaVersion = null; let selectedModelIdx = 0; @@ -243,6 +246,38 @@ resetView(); } + $: modelFilterItems = [ + ...(items.find((item) => item.model?.connection_type === 'local') + ? [{ value: 'connection:local', label: $i18n.t('Local') }] + : []), + ...(items.find((item) => item.model?.connection_type === 'external') + ? [{ value: 'connection:external', label: $i18n.t('External') }] + : []), + ...(items.find((item) => item.model?.direct) + ? [{ value: 'connection:direct', label: $i18n.t('Direct') }] + : []), + ...tags.map((tag) => ({ value: `tag:${tag}`, label: tag })) + ]; + + $: selectedFilter = selectedConnectionType + ? `connection:${selectedConnectionType}` + : selectedTag + ? `tag:${selectedTag}` + : ''; + + const setModelFilter = (filterValue: string) => { + if (!filterValue) { + selectedConnectionType = ''; + selectedTag = ''; + } else if (filterValue.startsWith('connection:')) { + selectedConnectionType = filterValue.replace('connection:', ''); + selectedTag = ''; + } else if (filterValue.startsWith('tag:')) { + selectedConnectionType = ''; + selectedTag = filterValue.replace('tag:', ''); + } + }; + const resetView = async () => { await tick(); @@ -527,7 +562,7 @@ deleteModelTarget = null; }; - const ITEM_HEIGHT = 39; + const ITEM_HEIGHT = 32; const OVERSCAN = 10; let listScrollTop = 0; @@ -536,7 +571,7 @@ $: visibleStart = Math.max(0, Math.floor(listScrollTop / ITEM_HEIGHT) - OVERSCAN); $: visibleEnd = Math.min( filteredItems.length, - Math.ceil((listScrollTop + 256) / ITEM_HEIGHT) + OVERSCAN + Math.ceil((listScrollTop + 288) / ITEM_HEIGHT) + OVERSCAN ); @@ -603,18 +638,18 @@
{#if searchEnabled} -
- +
+ - {#if multipleEnabled} - + {#if modelFilterItems.length > 0 || multipleEnabled} +
+ {#if multipleEnabled} + + + + {/if} + + {#if modelFilterItems.length > 0} + + {/if} +
{/if}
{/if} -
- {#if tags && items.filter((item) => includeHidden || !(item.model?.info?.meta?.hidden ?? false)).length > 0} -
{ - if (e.deltaY !== 0) { - e.preventDefault(); - e.currentTarget.scrollLeft += e.deltaY; - } - }} - > -
- {#if items.find((item) => item.model?.connection_type === 'local') || items.find((item) => item.model?.connection_type === 'external') || items.find((item) => item.model?.direct) || tags.length > 0} - - {/if} - - {#if items.find((item) => item.model?.connection_type === 'local')} - - {/if} - - {#if items.find((item) => item.model?.connection_type === 'external')} - - {/if} - - {#if items.find((item) => item.model?.direct)} - - {/if} - - {#each tags as tag} - - - - {/each} -
-
- {/if} -
- -
+
{#if filteredItems.length === 0} {#if items.length === 0 && $user?.role === 'admin'}
@@ -781,7 +736,7 @@
{:else}
-
+
{$i18n.t('No results found')}
@@ -789,7 +744,7 @@ {:else}
{:else} -
+
{/if}