ui : move the provider settings into the settings dialog

Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
Aleksander Grygier
2026-09-28 12:10:11 +02:00
parent d7b976d9e0
commit 6e7b57525a
7 changed files with 75 additions and 71 deletions
@@ -1,19 +1,9 @@
<script lang="ts">
import { SettingsBackends } from '$lib/components/app/backends';
import { Logo } from '$lib/components/app/misc';
import { ModelsDiscover } from '$lib/components/app/models/discover';
import * as Dialog from '$lib/components/ui/dialog';
import * as Tabs from '$lib/components/ui/tabs';
import { SETTINGS_KEYS } from '$lib/constants';
import { serverStore, settingsStore } from '$lib/stores';
import type { BackendProtocol } from '$lib/types';
type ManageTab = 'discover' | 'providers';
const PROTOCOL_TABS: Array<{ label: string; value: BackendProtocol }> = [
{ label: 'Llama-compat', value: 'llama.cpp' },
{ label: 'OpenAI-compat', value: 'openai' }
];
interface Props {
open?: boolean;
@@ -26,15 +16,6 @@
let hasDiscover = $derived(
serverStore.localIsRouter && settingsStore.config[SETTINGS_KEYS.ENABLE_DISCOVER_MODELS] === true
);
let tab = $state<ManageTab>('discover');
let providerProtocol = $state<BackendProtocol>('llama.cpp');
// land on the first available tab each time the dialog opens
$effect(() => {
if (!open) return;
tab = hasDiscover ? 'discover' : 'providers';
});
function handleOpenChange(value: boolean) {
open = value;
@@ -50,56 +31,18 @@
<Dialog.Title class="flex items-center gap-2">
<Logo class="h-5 w-5" style="--size: 1.25rem" />
<span>Manage models</span>
<span>Discover models</span>
</Dialog.Title>
<Dialog.Description>
Download models from the catalog and manage the providers this UI can talk to.
</Dialog.Description>
<Dialog.Description
>Download models from the catalog into the local server.</Dialog.Description
>
</Dialog.Header>
<Tabs.Root
class="min-h-0 flex-1 gap-3"
onValueChange={(value) => (tab = value as ManageTab)}
value={tab}
>
<Tabs.List>
{#if hasDiscover}
<Tabs.Trigger value="discover">Discover</Tabs.Trigger>
{/if}
<Tabs.Trigger value="providers">Providers</Tabs.Trigger>
</Tabs.List>
{#if hasDiscover}
<Tabs.Content
class="grid min-h-0 flex-1 overflow-hidden"
style="grid-template-columns: auto 1fr;"
value="discover"
>
<ModelsDiscover />
</Tabs.Content>
{/if}
<Tabs.Content class="min-h-0 flex-1 overflow-y-auto" value="providers">
<Tabs.Root
class="gap-3"
onValueChange={(value) => (providerProtocol = value as BackendProtocol)}
value={providerProtocol}
>
<Tabs.List>
{#each PROTOCOL_TABS as protocolTab (protocolTab.value)}
<Tabs.Trigger value={protocolTab.value}>{protocolTab.label}</Tabs.Trigger>
{/each}
</Tabs.List>
{#each PROTOCOL_TABS as protocolTab (protocolTab.value)}
<Tabs.Content value={protocolTab.value}>
<SettingsBackends protocol={protocolTab.value} />
</Tabs.Content>
{/each}
</Tabs.Root>
</Tabs.Content>
</Tabs.Root>
{#if hasDiscover}
<div class="grid min-h-0 flex-1 overflow-hidden" style="grid-template-columns: auto 1fr;">
<ModelsDiscover />
</div>
{/if}
</Dialog.Content>
</Dialog.Root>
@@ -9,10 +9,11 @@
ICON_STRIP_TRANSITION_DELAY_MULTIPLIER,
ICON_STRIP_TRANSITION_DURATION,
ROUTES,
SETTINGS_KEYS,
SIDEBAR_ACTIONS_ITEMS
} from '$lib/constants';
import { SidebarAction, TooltipSide } from '$lib/enums';
import { conversationsStore, deviceStore } from '$lib/stores';
import { conversationsStore, deviceStore, serverStore, settingsStore } from '$lib/stores';
import type { Component } from 'svelte';
import { onMount } from 'svelte';
import { circIn } from 'svelte/easing';
@@ -48,7 +49,16 @@
const isOnMobile = $derived(deviceStore.isMobile);
const actionsItems = SIDEBAR_ACTIONS_ITEMS;
// the discovery dialog downloads into the local server, so the entry is only
// useful when that server is a router with discovery enabled
const hasDiscover = $derived(
serverStore.localIsRouter && settingsStore.config[SETTINGS_KEYS.ENABLE_DISCOVER_MODELS] === true
);
const actionsItems = $derived(
SIDEBAR_ACTIONS_ITEMS.filter(
(item) => item.action !== SidebarAction.MANAGE_MODELS || hasDiscover
)
);
$effect(() => {
if (isSearchModeActive && searchInputRef) {
@@ -5,6 +5,7 @@
SettingsChatFields,
SettingsChatImportExportTab,
SettingsChatMobileHeader,
SettingsChatProvidersTab,
SettingsChatToolsTab,
SettingsFooter
} from '$lib/components/app/settings';
@@ -160,7 +161,9 @@
<div class="mx-auto max-w-2xl px-4 flex-1 md:mt-4">
<div class="space-y-6 pt-3">
<div class="grid">
{#if currentSection.slug === SETTINGS_SECTION_SLUGS.TOOLS}
{#if currentSection.slug === SETTINGS_SECTION_SLUGS.BACKENDS}
<SettingsChatProvidersTab />
{:else if currentSection.slug === SETTINGS_SECTION_SLUGS.TOOLS}
<SettingsChatToolsTab />
{:else if currentSection.slug === SETTINGS_SECTION_SLUGS.IMPORT_EXPORT}
<SettingsChatImportExportTab />
@@ -0,0 +1,34 @@
<script lang="ts">
import { SettingsBackends } from '$lib/components/app/backends';
import * as Tabs from '$lib/components/ui/tabs';
import type { BackendProtocol } from '$lib/types';
const PROTOCOL_TABS: Array<{ label: string; value: BackendProtocol }> = [
{ label: 'Llama-compat', value: 'llama.cpp' },
{ label: 'OpenAI-compat', value: 'openai' }
];
let protocol = $state<BackendProtocol>('llama.cpp');
</script>
<div class="space-y-3">
<p class="text-sm text-muted-foreground">Endpoints this UI can talk to.</p>
<Tabs.Root
class="gap-3"
onValueChange={(value) => (protocol = value as BackendProtocol)}
value={protocol}
>
<Tabs.List>
{#each PROTOCOL_TABS as protocolTab (protocolTab.value)}
<Tabs.Trigger value={protocolTab.value}>{protocolTab.label}</Tabs.Trigger>
{/each}
</Tabs.List>
{#each PROTOCOL_TABS as protocolTab (protocolTab.value)}
<Tabs.Content value={protocolTab.value}>
<SettingsBackends protocol={protocolTab.value} />
</Tabs.Content>
{/each}
</Tabs.Root>
</div>
@@ -74,3 +74,9 @@ export { default as SettingsChatFields } from './SettingsChat/SettingsChatFields
* server favicons and permission management controls.
*/
export { default as SettingsChatToolsTab } from './SettingsChat/SettingsChatToolsTab.svelte';
/**
* Providers panel for chat settings. Switches between the llama.cpp and
* OpenAI-compatible protocols and lists the endpoints configured for each.
*/
export { default as SettingsChatProvidersTab } from './SettingsChat/SettingsChatProvidersTab.svelte';
@@ -11,6 +11,7 @@ import {
Monitor,
Moon,
PencilRuler,
Server,
SlidersVertical,
Sun
} from '@lucide/svelte';
@@ -28,7 +29,7 @@ import type {
/** Settings sections — slug is the routing identity, title is the display label. */
export const SETTINGS_SECTIONS = {
AGENTIC: { slug: 'agentic', title: 'Agentic' },
BACKENDS: { slug: 'backends', title: 'Backends' },
BACKENDS: { slug: 'backends', title: 'Providers' },
DEVELOPER: { slug: 'developer', title: 'Developer' },
DISPLAY: { slug: 'display', title: 'Display' },
GENERAL: { slug: 'general', title: 'General' },
@@ -196,6 +197,13 @@ export const SETTINGS_REGISTRY: SettingsSectionEntry[] = [
slug: SETTINGS_SECTION_SLUGS.GENERAL,
title: SETTINGS_SECTION_TITLES.GENERAL
},
// Providers
{
icon: Server,
settings: [],
slug: SETTINGS_SECTION_SLUGS.BACKENDS,
title: SETTINGS_SECTION_TITLES.BACKENDS
},
// Display
{
icon: Monitor,
+1 -1
View File
@@ -81,7 +81,7 @@ export const SIDEBAR_ACTIONS_ITEMS: DesktopIconStripItem[] = [
{
action: SidebarAction.MANAGE_MODELS,
icon: Boxes,
tooltip: 'Manage models'
tooltip: 'Discover models'
},
{
action: SidebarAction.SETTINGS,