From 43e792a8f4cd1e26f8e292835edb13845065310d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 15 Jul 2026 01:16:06 -0400 Subject: [PATCH] refac --- src/lib/components/admin/Settings.svelte | 8 +- .../components/admin/Settings/Models.svelte | 599 +++++++++--------- .../Settings/Models/AdminViewSelector.svelte | 8 +- src/lib/components/chat/SettingsModal.svelte | 2 +- src/lib/components/layout/Sidebar.svelte | 42 +- 5 files changed, 345 insertions(+), 314 deletions(-) diff --git a/src/lib/components/admin/Settings.svelte b/src/lib/components/admin/Settings.svelte index 6f06a0ef27..de18b564c8 100644 --- a/src/lib/components/admin/Settings.svelte +++ b/src/lib/components/admin/Settings.svelte @@ -394,9 +394,11 @@
-

- {$i18n.t(selectedTabTitle)} -

+ {#if selectedTab !== 'models'} +

+ {$i18n.t(selectedTabTitle)} +

+ {/if}
{#if selectedTab === 'general'} diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte index 121373ecbe..ce0c580c18 100644 --- a/src/lib/components/admin/Settings/Models.svelte +++ b/src/lib/components/admin/Settings/Models.svelte @@ -31,15 +31,13 @@ import { toast } from 'svelte-sonner'; import Badge from '$lib/components/common/Badge.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; - import Cog6 from '$lib/components/icons/Cog6.svelte'; import ModelSettingsModal from './Models/ModelSettingsModal.svelte'; - import Wrench from '$lib/components/icons/Wrench.svelte'; - import Download from '$lib/components/icons/Download.svelte'; import ManageModelsModal from './Models/ManageModelsModal.svelte'; import ModelMenu from '$lib/components/admin/Settings/Models/ModelMenu.svelte'; import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte'; import EyeSlash from '$lib/components/icons/EyeSlash.svelte'; import Eye from '$lib/components/icons/Eye.svelte'; + import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; import CheckCircle from '$lib/components/icons/CheckCircle.svelte'; import Minus from '$lib/components/icons/Minus.svelte'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; @@ -58,6 +56,7 @@ let modelsImportInProgress = false; let importFiles; let modelsImportInputElement: HTMLInputElement; + let tagsContainerElement: HTMLDivElement; let models = null; @@ -380,316 +379,334 @@ {#if models !== null} {#if selectedModelId === null} -
-
-
-
{filteredModels.length}
-
+
+
+

+ {$i18n.t('Models')} + + {filteredModels.length} + +

-
+
{#if $user?.role === 'admin'} - { - if (importFiles.length > 0) { - const reader = new FileReader(); - reader.onload = async (event) => { - modelsImportInProgress = true; - - try { - const models = JSON.parse(String(event.target.result)); - const res = await importModels(localStorage.token, models); - - if (res) { - toast.success($i18n.t('Models imported successfully')); - await init(); - } else { - toast.error($i18n.t('Failed to import models')); - } - } catch (e) { - toast.error(e?.detail ?? $i18n.t('Invalid JSON file')); - console.error(e); - } - - modelsImportInProgress = false; - }; - reader.readAsText(importFiles[0]); - } - }} - /> - - {/if} - -
-
-
-
-
-
- -
- - {#if searchValue} -
- + {#if $user?.role === 'admin'} + { + if (importFiles.length > 0) { + const reader = new FileReader(); + reader.onload = async (event) => { + modelsImportInProgress = true; + + try { + const models = JSON.parse(String(event.target.result)); + const res = await importModels(localStorage.token, models); + + if (res) { + toast.success($i18n.t('Models imported successfully')); + await init(); + } else { + toast.error($i18n.t('Failed to import models')); + } + } catch (e) { + toast.error(e?.detail ?? $i18n.t('Invalid JSON file')); + console.error(e); + } + + modelsImportInProgress = false; + }; + reader.readAsText(importFiles[0]); + } + }} + /> + {/if} + +
+
+
+
+
- {/if} -
-
- -
-
- - {#if (tags ?? []).length > 0} - ({ value: tag, label: tag }))} - onChange={async () => { - currentPage = 1; - await init(); - }} + - {/if} + {#if searchValue} +
+ +
+ {/if} +
+ +
{ + if (e.deltaY !== 0) { + e.preventDefault(); + e.currentTarget.scrollLeft += e.deltaY; + } + }} + > +
+ + + {#if (tags ?? []).length > 0} + { + return { value: tag, label: tag }; + })} + onChange={async () => { + currentPage = 1; + await init(); + }} + /> + {/if} +
+ + + + + + +
+ + + + + +
+ + + + +
+
+
+
-
- - - - - - -
- - - - - -
- - - - -
-
-
-
- -
- {#if filteredModels.length > 0} - {#each filteredModels.slice((currentPage - 1) * perPage, currentPage * perPage) as model, modelIdx (`${model.id}-${modelIdx}`)} -
- -
- {#if shiftKey} - - - - {:else} +
+ {model.name} + + + g.principal_type === 'user' && + g.principal_id === '*' && + g.permission === 'read' + ) + ? 'success' + : 'muted'} + content={(model?.access_grants ?? []).some( + (g) => + g.principal_type === 'user' && + g.principal_id === '*' && + g.permission === 'read' + ) + ? $i18n.t('Public') + : $i18n.t('Private')} + /> +
+ +
+ + {!!model?.meta?.description + ? model?.meta?.description + : model?.ollama?.digest + ? `${model.id} (${model?.ollama?.digest})` + : model.id} + +
+
+ +
+ {#if shiftKey} + + + + {/if} +
- {/if} +
+
+ {/each} + {:else} +
+
+
😕
+
+ {$i18n.t('No models found')} +
+
+ {$i18n.t('Try adjusting your search or filter to find what you are looking for.')} +
- {/each} - {:else} -
-
-
😕
-
- {$i18n.t('No models found')} -
-
- {$i18n.t('Try adjusting your search or filter to find what you are looking for.')} -
-
-
+ {/if} +
+ + {#if filteredModels.length > perPage} + {/if}
- - {#if filteredModels.length > perPage} - - {/if}
{:else} void = () => {}; const items = [ @@ -26,13 +27,14 @@ bind:value {items} {placeholder} - triggerClass="relative w-full flex items-center gap-0.5 px-2.5 py-1.5 bg-gray-50 dark:bg-gray-850 rounded-xl" - labelClass="inline-flex h-input px-0.5 w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden" + {align} + triggerClass="relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100" + labelClass="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden" onChange={() => onChange(value)} > {selectedLabel} diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index 52e966def7..ce63b6bc63 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -1001,7 +1001,7 @@
- {#if selectedTab !== 'account'} + {#if selectedTab !== 'account' && selectedTab !== 'admin:models'}

{$i18n.t(selectedTabTitle)}

diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index db1940c026..3c813e3a4b 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -792,7 +792,7 @@ {#if !$mobile && !$showSidebar}