refactor: remove the orphaned admin settings components (#28855)
Some checks are pending
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / merge (map[name:cuda suffix:-cuda]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:main suffix:]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:cuda126 suffix:-cuda126]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:ollama suffix:-ollama]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:slim suffix:-slim]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / notify-helm-charts (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (, main) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda, cuda) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda126, cuda126) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-ollama, ollama) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-slim, slim) (push) Blocked by required conditions
Frontend Build / Format & Build (push) Waiting to run
Frontend Build / Unit Tests (push) Waiting to run
Python CI / Ruff Format (3.11) (push) Waiting to run
Python CI / Ruff Format (3.12) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run

Two components under the admin settings area are not imported by any route or component. The admin settings shell went dead when the admin settings route became a redirect into the settings modal, which imports every admin tab directly and carries its own tab list and search. The model selector beside it lost its last importer in a separate models refactor. Every child component the shell used is still imported by the settings modal, so nothing goes with them.

This removes around 590 lines that still turn up in every search across the admin area.
This commit is contained in:
Classic298 2026-08-20 22:37:53 +02:00 committed by GitHub
parent c7f306031d
commit 7229fac0c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 0 additions and 589 deletions

View file

@ -1,497 +0,0 @@
<script>
import { getContext, tick, onMount } from 'svelte';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { toast } from 'svelte-sonner';
import { config } from '$lib/stores';
import { getBackendConfig } from '$lib/apis';
import Database from './Settings/Database.svelte';
import Authentication from './Settings/Authentication.svelte';
import General from './Settings/General.svelte';
import Pipelines from './Settings/Pipelines.svelte';
import Audio from './Settings/Audio.svelte';
import Images from './Settings/Images.svelte';
import Interface from './Settings/Interface.svelte';
import Models from './Settings/Models.svelte';
import Connections from './Settings/Connections.svelte';
import Documents from './Settings/Documents.svelte';
import WebSearch from './Settings/WebSearch.svelte';
import Evaluations from './Settings/Evaluations.svelte';
import Analytics from './Analytics.svelte';
import CodeExecution from './Settings/CodeExecution.svelte';
import Integrations from './Settings/Integrations.svelte';
import Subagents from './Settings/Subagents.svelte';
import Search from '../icons/Search.svelte';
import AdminTabIcon from './Settings/AdminTabIcon.svelte';
const i18n = getContext('i18n');
let selectedTab = 'general';
// Get current tab from URL pathname, default to 'general'
$: {
const pathParts = $page.url.pathname.split('/');
const tabFromPath = pathParts[pathParts.length - 1];
selectedTab = [
'general',
'authentication',
'connections',
'models',
'subagents',
'evaluations',
'analytics',
'integrations',
'documents',
'web',
'code-execution',
'interface',
'audio',
'images',
'pipelines',
'db'
].includes(tabFromPath)
? tabFromPath
: 'general';
}
$: if (selectedTab) {
// scroll to selectedTab
scrollToTab(selectedTab);
}
const scrollToTab = (tabId) => {
const tabElement = document.getElementById(tabId);
if (tabElement) {
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
}
};
let search = '';
let searchDebounceTimeout;
let filteredSettings = [];
const allSettings = [
{
id: 'general',
title: 'General',
route: '/admin/settings/general',
keywords: [
'general',
'admin',
'settings',
'version',
'update',
'language',
'theme',
'data',
'users',
'roles',
'ldap',
'authentication',
'reverse proxy',
'webhook',
'community',
'channels'
]
},
{
id: 'authentication',
title: 'Authentication',
route: '/admin/settings/authentication',
keywords: [
'authentication',
'auth',
'login',
'signup',
'ldap',
'oauth',
'oidc',
'sso',
'roles',
'groups',
'identity'
]
},
{
id: 'connections',
title: 'Connections',
route: '/admin/settings/connections',
keywords: [
'connections',
'ollama',
'openai',
'api',
'base url',
'direct connections',
'proxy',
'key'
]
},
{
id: 'models',
title: 'Models',
route: '/admin/settings/models',
keywords: [
'models',
'pull',
'delete',
'create',
'edit',
'modelfile',
'gguf',
'import',
'export'
]
},
{
id: 'subagents',
title: 'Sub-agents',
route: '/admin/settings/subagents',
keywords: ['sub-agents', 'subagents', 'delegation', 'background', 'agents']
},
{
id: 'evaluations',
title: 'Evaluations',
route: '/admin/settings/evaluations',
keywords: ['evaluations', 'feedback', 'rating', 'arena', 'leaderboard', 'preference']
},
{
id: 'analytics',
title: 'Analytics',
route: '/admin/settings/analytics',
keywords: ['analytics', 'usage', 'stats', 'dashboard', 'models', 'users', 'messages']
},
{
id: 'integrations',
title: 'Integrations',
route: '/admin/settings/integrations',
keywords: [
'tools',
'integrations',
'plugins',
'extensions',
'functions',
'openapi',
'server',
'knowledge',
'vector db',
'qdrant',
'rag',
'retrieval',
'sources'
]
},
{
id: 'documents',
title: 'Documents',
route: '/admin/settings/documents',
keywords: [
'documents',
'files',
'rag',
'knowledge',
'upload',
'embedding',
'vector db',
'chunk',
'overlap',
'splitter',
'pdf',
'ocr',
'tika',
'docling',
'unstructured'
]
},
{
id: 'web',
title: 'Web Search',
route: '/admin/settings/web',
keywords: [
'web search',
'google',
'bing',
'duckduckgo',
'serp',
'searxng',
'moojeh',
'yacy',
'serper',
'serply',
'tavily',
'exa',
'perplexity',
'firecrawl'
]
},
{
id: 'code-execution',
title: 'Code Execution',
route: '/admin/settings/code-execution',
keywords: ['code execution', 'python', 'sandbox', 'compiler', 'jupyter', 'interpreter']
},
{
id: 'interface',
title: 'Interface',
route: '/admin/settings/interface',
keywords: [
'interface',
'ui',
'appearance',
'banners',
'tasks',
'prompt suggestions',
'title generation',
'tags'
]
},
{
id: 'audio',
title: 'Audio',
route: '/admin/settings/audio',
keywords: [
'audio',
'voice',
'speech',
'tts',
'stt',
'whisper',
'deepgram',
'azure',
'openai',
'elevenlabs'
]
},
{
id: 'images',
title: 'Images',
route: '/admin/settings/images',
keywords: [
'images',
'generation',
'dalle',
'stable diffusion',
'comfyui',
'automatic1111',
'gemini'
]
},
{
id: 'pipelines',
title: 'Pipelines',
route: '/admin/settings/pipelines',
keywords: ['pipelines', 'workflows', 'filters', 'valves', 'middleware']
},
{
id: 'db',
title: 'Database',
route: '/admin/settings/db',
keywords: ['database', 'export', 'import', 'backup', 'chats', 'users']
}
];
const setFilteredSettings = () => {
filteredSettings = allSettings.filter((tab) => {
const searchTerm = search.toLowerCase().trim();
if (tab.id === 'analytics' && !($config?.features.enable_admin_analytics ?? true)) {
return false;
}
return (
search === '' ||
tab.title.toLowerCase().includes(searchTerm) ||
tab.keywords.some((keyword) => keyword.includes(searchTerm))
);
});
};
const searchDebounceHandler = () => {
if (searchDebounceTimeout) {
clearTimeout(searchDebounceTimeout);
}
searchDebounceTimeout = setTimeout(() => {
setFilteredSettings();
}, 100);
};
const tabButtonClass = (active) =>
`flex items-center gap-1.5 h-7 px-2 lg:w-full shrink-0 rounded-lg text-xs text-left transition-colors duration-75 select-none ${
active
? 'font-medium text-gray-900 dark:text-white bg-gray-100 dark:bg-white/6'
: 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
}`;
onMount(() => {
const containerElement = document.getElementById('admin-settings-tabs-container');
if (containerElement) {
containerElement.addEventListener('wheel', function (event) {
if (event.deltaY !== 0) {
// Adjust horizontal scroll position based on vertical scroll
containerElement.scrollLeft += event.deltaY;
}
});
}
setFilteredSettings();
// Scroll to the selected tab on mount
scrollToTab(selectedTab);
});
</script>
<div class="flex flex-col lg:flex-row w-full h-full min-h-0 pb-2">
<nav
id="admin-settings-tabs-container"
class="shrink-0 min-w-0 lg:min-h-0 flex lg:block border-b lg:border-b-0 lg:border-r border-gray-100/30 dark:border-white/[0.02] lg:w-[15rem]"
>
<div
class="tabs flex min-w-0 flex-1 overflow-x-auto lg:overflow-x-hidden lg:overflow-y-auto lg:flex-col p-1 lg:pt-4 gap-px"
>
<span class="hidden lg:block text-[0.625rem] text-gray-400 dark:text-gray-600 px-2 mb-1">
{$i18n.t('Admin')}
</span>
<div
class="hidden lg:flex items-center gap-1.5 h-7 px-2 lg:w-full shrink-0 rounded-lg text-xs bg-gray-50/70 dark:bg-white/[0.03] mb-2"
id="settings-search"
>
<div class="self-center rounded-l-xl bg-transparent">
<Search className="size-3.5" strokeWidth="1.5" />
</div>
<label class="sr-only" for="search-input-admin-settings">{$i18n.t('Search')}</label>
<input
class="w-full py-1 text-xs bg-transparent dark:text-gray-300 outline-hidden"
bind:value={search}
id="search-input-admin-settings"
on:input={searchDebounceHandler}
placeholder={$i18n.t('Search')}
/>
</div>
<!-- {$i18n.t('General')} -->
<!-- {$i18n.t('Authentication')} -->
<!-- {$i18n.t('Connections')} -->
<!-- {$i18n.t('Models')} -->
<!-- {$i18n.t('Sub-agents')} -->
<!-- {$i18n.t('Evaluations')} -->
<!-- {$i18n.t('Analytics')} -->
<!-- {$i18n.t('Integrations')} -->
<!-- {$i18n.t('Documents')} -->
<!-- {$i18n.t('Web Search')} -->
<!-- {$i18n.t('Code Execution')} -->
<!-- {$i18n.t('Interface')} -->
<!-- {$i18n.t('Audio')} -->
<!-- {$i18n.t('Images')} -->
<!-- {$i18n.t('Pipelines')} -->
<!-- {$i18n.t('Database')} -->
{#each filteredSettings as tab (tab.id)}
<a
id={tab.id}
href={tab.route}
draggable="false"
class={tabButtonClass(selectedTab === tab.id)}
>
<AdminTabIcon id={tab.id} className="size-3.5" strokeWidth="2" />
<div class="self-center truncate">{$i18n.t(tab.title)}</div>
</a>
{/each}
</div>
</nav>
<div class="flex-1 min-h-0 p-4 lg:px-5 flex flex-col">
<div class="w-full h-full min-h-0 flex flex-col">
<div class="flex-1 min-h-0 overflow-hidden">
{#if selectedTab === 'general'}
<General
saveHandler={async () => {
toast.success($i18n.t('Settings saved successfully!'));
await tick();
await config.set(await getBackendConfig());
}}
/>
{:else if selectedTab === 'authentication'}
<Authentication />
{:else if selectedTab === 'connections'}
<Connections
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'models'}
<Models />
{:else if selectedTab === 'subagents'}
<Subagents />
{:else if selectedTab === 'evaluations'}
<Evaluations />
{:else if selectedTab === 'analytics'}
<Analytics />
{:else if selectedTab === 'integrations'}
<Integrations />
{:else if selectedTab === 'documents'}
<Documents
on:save={async () => {
toast.success($i18n.t('Settings saved successfully!'));
await tick();
await config.set(await getBackendConfig());
}}
/>
{:else if selectedTab === 'web'}
<WebSearch
saveHandler={async () => {
toast.success($i18n.t('Settings saved successfully!'));
await tick();
await config.set(await getBackendConfig());
}}
/>
{:else if selectedTab === 'code-execution'}
<CodeExecution
saveHandler={async () => {
toast.success($i18n.t('Settings saved successfully!'));
await tick();
await config.set(await getBackendConfig());
}}
/>
{:else if selectedTab === 'interface'}
<Interface
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'audio'}
<Audio
saveHandler={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'images'}
<Images
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'db'}
<Database
saveHandler={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'pipelines'}
<Pipelines
saveHandler={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{/if}
</div>
</div>
</div>
</div>

View file

@ -1,92 +0,0 @@
<script lang="ts">
import { getContext } from 'svelte';
const i18n = getContext('i18n');
import Minus from '$lib/components/icons/Minus.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import SettingsSelect from '$lib/components/common/SettingsSelect.svelte';
export let title = '';
export let tooltip = '';
export let models = [];
export let modelIds = [];
let selectedModelId = '';
</script>
<div>
<div class="flex flex-col w-full">
<div class="mb-1 flex justify-between">
<div class="text-xs text-gray-500 flex items-center gap-1">
{title}
{#if tooltip}
<Tooltip content={tooltip} className="cursor-help">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-3"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
/>
</svg>
</Tooltip>
{/if}
</div>
</div>
<div class="flex items-center -mr-1">
<SettingsSelect
className="w-full"
selectClassName="text-sm"
bind:value={selectedModelId}
on:change={() => {
if (selectedModelId && !modelIds.includes(selectedModelId)) {
modelIds = [...modelIds, selectedModelId];
}
selectedModelId = '';
}}
>
<option value="">{$i18n.t('Select a model')}</option>
{#each models as model}
{#if !modelIds.includes(model.id)}
<option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
{/if}
{/each}
</SettingsSelect>
</div>
<!-- <hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" /> -->
{#if modelIds.length > 0}
<div class="flex flex-col">
{#each modelIds as modelId, modelIdx}
<div class=" flex gap-2 w-full justify-between items-center">
<div class=" text-sm flex-1 py-1 rounded-lg">
{models.find((model) => model.id === modelId)?.name}
</div>
<div class="shrink-0">
<button
type="button"
on:click={() => {
modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
}}
>
<Minus strokeWidth="2" className="size-3.5" />
</button>
</div>
</div>
{/each}
</div>
{:else}
<div class="text-gray-500 text-xs text-center py-2">
{$i18n.t('No models selected')}
</div>
{/if}
</div>
</div>