From 245526af991c4466a70e10eae44547a3e7ba449c Mon Sep 17 00:00:00 2001 From: vikingowl Date: Wed, 7 Jan 2026 20:03:38 +0100 Subject: [PATCH] feat: consolidate settings into unified Settings Hub with tabs - Create Settings Hub with 6 tabs: General, Models, Prompts, Tools, Knowledge, Memory - Extract page content into reusable tab components: - GeneralTab: appearance, chat defaults, shortcuts, about - ModelsTab: local models, ollama.com browser, pull/create - PromptsTab: my prompts, browse templates - ToolsTab: built-in and custom tools with enhanced UI - KnowledgeTab: RAG document management - MemoryTab: embedding model, auto-compact, model parameters - Add SettingsTabs navigation component with icons - Consolidate sidebar from 5 links to single Settings link - Add 301 redirects for old URLs (/models, /prompts, /tools, /knowledge) - Upgrade Tools tab with stats bar, search, tool icons, and parameter badges --- .../src/lib/components/layout/Sidenav.svelte | 100 +- .../lib/components/settings/GeneralTab.svelte | 155 +++ .../components/settings/KnowledgeTab.svelte | 266 +++++ .../lib/components/settings/MemoryTab.svelte | 357 +++++++ .../lib/components/settings/ModelsTab.svelte | 966 ++++++++++++++++++ .../lib/components/settings/PromptsTab.svelte | 446 ++++++++ .../components/settings/SettingsTabs.svelte | 70 ++ .../lib/components/settings/ToolsTab.svelte | 511 +++++++++ frontend/src/lib/components/settings/index.ts | 13 + frontend/src/routes/knowledge/+page.server.ts | 6 + frontend/src/routes/models/+page.server.ts | 6 + frontend/src/routes/prompts/+page.server.ts | 6 + frontend/src/routes/settings/+page.svelte | 545 +--------- frontend/src/routes/tools/+page.server.ts | 6 + 14 files changed, 2852 insertions(+), 601 deletions(-) create mode 100644 frontend/src/lib/components/settings/GeneralTab.svelte create mode 100644 frontend/src/lib/components/settings/KnowledgeTab.svelte create mode 100644 frontend/src/lib/components/settings/MemoryTab.svelte create mode 100644 frontend/src/lib/components/settings/ModelsTab.svelte create mode 100644 frontend/src/lib/components/settings/PromptsTab.svelte create mode 100644 frontend/src/lib/components/settings/SettingsTabs.svelte create mode 100644 frontend/src/lib/components/settings/ToolsTab.svelte create mode 100644 frontend/src/lib/components/settings/index.ts create mode 100644 frontend/src/routes/knowledge/+page.server.ts create mode 100644 frontend/src/routes/models/+page.server.ts create mode 100644 frontend/src/routes/prompts/+page.server.ts create mode 100644 frontend/src/routes/tools/+page.server.ts diff --git a/frontend/src/lib/components/layout/Sidenav.svelte b/frontend/src/lib/components/layout/Sidenav.svelte index ed74128..f785900 100644 --- a/frontend/src/lib/components/layout/Sidenav.svelte +++ b/frontend/src/lib/components/layout/Sidenav.svelte @@ -4,15 +4,12 @@ * Contains navigation header, search, projects, and conversation list */ import { page } from '$app/stores'; - import { uiState, projectsState } from '$lib/stores'; + import { uiState } from '$lib/stores'; import SidenavHeader from './SidenavHeader.svelte'; import SidenavSearch from './SidenavSearch.svelte'; import ConversationList from './ConversationList.svelte'; import ProjectModal from '$lib/components/projects/ProjectModal.svelte'; - // Check if a path is active - const isActive = (path: string) => $page.url.pathname === path; - // Project modal state let showProjectModal = $state(false); let editingProjectId = $state(null); @@ -88,100 +85,11 @@ - -
- - - - - - Models - - - - - - - - Knowledge Base - - - - - - - - Tools - - - - - - - - Prompts - - - + +
+ /** + * GeneralTab - General settings including appearance, defaults, shortcuts, and about + */ + import { modelsState, uiState } from '$lib/stores'; + import { getPrimaryModifierDisplay } from '$lib/utils'; + + const modifierKey = getPrimaryModifierDisplay(); + + // Local state for default model selection + let defaultModel = $state(modelsState.selectedId); + + // Save default model when it changes + function handleModelChange(): void { + if (defaultModel) { + modelsState.select(defaultModel); + } + } + + +
+ +
+

+ + + + Appearance +

+ +
+ +
+
+

Dark Mode

+

Toggle between light and dark theme

+
+ +
+ + +
+
+

Use System Theme

+

Match your OS light/dark preference

+
+ +
+
+
+ + +
+

+ + + + Chat Defaults +

+ +
+
+ +

Model used for new conversations

+ +
+
+
+ + +
+

+ + + + Keyboard Shortcuts +

+ +
+
+
+ New Chat + {modifierKey}+N +
+
+ Search + {modifierKey}+K +
+
+ Toggle Sidebar + {modifierKey}+B +
+
+ Send Message + Enter +
+
+ New Line + Shift+Enter +
+
+
+
+ + +
+

+ + + + About +

+ +
+
+
+ + + +
+
+

Vessel

+

+ A modern interface for local AI with chat, tools, and memory management. +

+
+
+
+
+
diff --git a/frontend/src/lib/components/settings/KnowledgeTab.svelte b/frontend/src/lib/components/settings/KnowledgeTab.svelte new file mode 100644 index 0000000..7f984de --- /dev/null +++ b/frontend/src/lib/components/settings/KnowledgeTab.svelte @@ -0,0 +1,266 @@ + + +
+ +
+

Knowledge Base

+

+ Upload documents to enhance AI responses with your own knowledge +

+
+ + +
+
+

Documents

+

{stats.documentCount}

+
+
+

Chunks

+

{stats.chunkCount}

+
+
+

Total Tokens

+

{formatTokenCount(stats.totalTokens)}

+
+
+ + +
+
+

Upload Documents

+ +
+ + + + +
+ + +
+

Documents

+ + {#if isLoading} +
+ + + + +
+ {:else if documents.length === 0} +
+ + + +

No documents yet

+

Upload documents to build your knowledge base

+
+ {:else} +
+ {#each documents as doc (doc.id)} +
+
+ + + +
+

{doc.name}

+

{formatSize(doc.size)} · {doc.chunkCount} chunks · Added {formatDate(doc.createdAt)}

+
+
+ + +
+ {/each} +
+ {/if} +
+ + +
+

+ + + + How RAG Works +

+

+ Documents are split into chunks and converted to embeddings. When you ask a question, + relevant chunks are found by similarity search and included in the AI's context. +

+

+ Note: Requires an embedding model to be installed + in Ollama (e.g., ollama pull nomic-embed-text). +

+
+
diff --git a/frontend/src/lib/components/settings/MemoryTab.svelte b/frontend/src/lib/components/settings/MemoryTab.svelte new file mode 100644 index 0000000..87f875e --- /dev/null +++ b/frontend/src/lib/components/settings/MemoryTab.svelte @@ -0,0 +1,357 @@ + + +
+ +
+

+ + + + Memory Management +

+ +
+ +
+ +

Model used for semantic search and conversation indexing

+ +

+ Note: The model must be installed in Ollama. Run ollama pull {settingsState.embeddingModel} if not installed. +

+
+ + +
+
+

Auto-Compact

+

Automatically summarize older messages when context usage is high

+
+ +
+ + {#if settingsState.autoCompactEnabled} + +
+
+ + {settingsState.autoCompactThreshold}% +
+

Trigger compaction when context usage exceeds this percentage

+ settingsState.updateAutoCompactThreshold(parseInt(e.currentTarget.value))} + class="w-full accent-emerald-500" + /> +
+ {AUTO_COMPACT_RANGES.threshold.min}% + {AUTO_COMPACT_RANGES.threshold.max}% +
+
+ + +
+
+ + {settingsState.autoCompactPreserveCount} +
+

Number of recent messages to keep intact (not summarized)

+ settingsState.updateAutoCompactPreserveCount(parseInt(e.currentTarget.value))} + class="w-full accent-emerald-500" + /> +
+ {AUTO_COMPACT_RANGES.preserveCount.min} + {AUTO_COMPACT_RANGES.preserveCount.max} +
+
+ {:else} +

+ Enable auto-compact to automatically manage context usage. When enabled, older messages + will be summarized when context usage exceeds your threshold. +

+ {/if} +
+
+ + +
+

+ + + + Model Parameters +

+ +
+ +
+
+

Use Custom Parameters

+

Override model defaults with custom values

+
+ +
+ + {#if settingsState.useCustomParameters} + +
+
+ + {settingsState.temperature.toFixed(2)} +
+

{PARAMETER_DESCRIPTIONS.temperature}

+ settingsState.updateParameter('temperature', parseFloat(e.currentTarget.value))} + class="w-full accent-orange-500" + /> +
+ + +
+
+ + {settingsState.top_k} +
+

{PARAMETER_DESCRIPTIONS.top_k}

+ settingsState.updateParameter('top_k', parseInt(e.currentTarget.value))} + class="w-full accent-orange-500" + /> +
+ + +
+
+ + {settingsState.top_p.toFixed(2)} +
+

{PARAMETER_DESCRIPTIONS.top_p}

+ settingsState.updateParameter('top_p', parseFloat(e.currentTarget.value))} + class="w-full accent-orange-500" + /> +
+ + +
+
+ + {settingsState.num_ctx.toLocaleString()} +
+

{PARAMETER_DESCRIPTIONS.num_ctx}

+ settingsState.updateParameter('num_ctx', parseInt(e.currentTarget.value))} + class="w-full accent-orange-500" + /> +
+ + +
+ +
+ {:else} +

+ Using model defaults. Enable custom parameters to adjust temperature, sampling, and context length. +

+ {/if} +
+
+ + +
+

+ + + + Model-Prompt Defaults +

+ +
+

+ Set default system prompts for specific models. When no other prompt is selected, the model's default will be used automatically. +

+ + {#if isLoadingModelInfo} +
+
+ Loading model info... +
+ {:else if modelsState.chatModels.length === 0} +

+ No models available. Make sure Ollama is running. +

+ {:else} +
+ {#each modelsState.chatModels as model (model.name)} + {@const modelInfo = modelInfoCache.get(model.name)} + {@const mappedPromptId = getMappedPromptId(model.name)} +
+
+
+
+ {model.name} + {#if modelInfo?.capabilities && modelInfo.capabilities.length > 0} + {#each modelInfo.capabilities as cap (cap)} + + {cap} + + {/each} + {/if} + {#if modelInfo?.systemPrompt} + + embedded + + {/if} +
+
+ + +
+ + {#if modelInfo?.systemPrompt} +

+ Embedded: {modelInfo.systemPrompt} +

+ {/if} +
+ {/each} +
+ {/if} +
+
+
diff --git a/frontend/src/lib/components/settings/ModelsTab.svelte b/frontend/src/lib/components/settings/ModelsTab.svelte new file mode 100644 index 0000000..2ecd378 --- /dev/null +++ b/frontend/src/lib/components/settings/ModelsTab.svelte @@ -0,0 +1,966 @@ + + +
+ +
+ +
+
+

Models

+

+ Manage local models and browse ollama.com +

+
+ + +
+ {#if activeTab === 'browse' && modelRegistry.syncStatus} +
+
{modelRegistry.syncStatus.modelCount} models cached
+
Last sync: {formatDate(modelRegistry.syncStatus.lastSync ?? undefined)}
+
+ {/if} + {#if activeTab === 'browse'} + + {:else} + + + + + {/if} +
+
+ + +
+ + +
+ + + {#if activeTab === 'local'} + {#if deleteError} +
+
+ + + + {deleteError} + +
+
+ {/if} + + +
+
+ + + + +
+ + {#if localModelsState.families.length > 0} + + {/if} + + + + {#if localModelsState.searchQuery || localModelsState.familyFilter || localModelsState.sortBy !== 'name_asc'} + + {/if} +
+ + {#if localModelsState.loading} +
+ {#each Array(3) as _} +
+
+
+
+
+
+ {/each} +
+ {:else if localModelsState.models.length === 0} +
+ + + +

+ {#if localModelsState.searchQuery || localModelsState.familyFilter} + No models match your filters + {:else} + No local models + {/if} +

+

+ {#if localModelsState.searchQuery || localModelsState.familyFilter} + Try adjusting your search or filters + {:else} + Browse ollama.com to pull models + {/if} +

+ {#if !localModelsState.searchQuery && !localModelsState.familyFilter} + + {/if} +
+ {:else} +
+ {#each localModelsState.models as model (model.name)} + {@const caps = modelsState.getCapabilities(model.name) ?? []} +
+
+
+
+

{model.name}

+ {#if model.name === modelsState.selectedId} + Selected + {/if} + {#if localModelsState.hasUpdate(model.name)} + + Update + + {/if} + {#if hasEmbeddedPrompt(model.name)} + + Custom + + {/if} +
+
+ {formatBytes(model.size)} + Family: {model.family} + Parameters: {model.parameterSize} + Quantization: {model.quantizationLevel} +
+ {#if caps.length > 0} +
+ {#if caps.includes('vision')} + + 👁Vision + + {/if} + {#if caps.includes('tools')} + + 🔧Tools + + {/if} + {#if caps.includes('thinking')} + + 🧠Thinking + + {/if} + {#if caps.includes('embedding')} + + 📊Embedding + + {/if} + {#if caps.includes('code')} + + 💻Code + + {/if} +
+ {/if} +
+
+ {#if deleteConfirm === model.name} + Delete? + + + {:else} + {#if hasEmbeddedPrompt(model.name)} + + {/if} + + {/if} +
+
+
+ {/each} +
+ + {#if localModelsState.totalPages > 1} +
+ + + Page {localModelsState.currentPage + 1} of {localModelsState.totalPages} + + +
+ {/if} + {/if} + {:else} + +
+
+ + + + +
+ +
+ + + +
+ +
+ + +
+ +
+ {modelRegistry.total} model{modelRegistry.total !== 1 ? 's' : ''} found +
+
+ + +
+ Capabilities: + + + + + + from ollama.com +
+ + +
+ Size: + + + + +
+ + +
+ {#if modelRegistry.availableFamilies.length > 0} +
+ Family: + +
+ {/if} + + {#if modelRegistry.selectedCapabilities.length > 0 || modelRegistry.selectedSizeRanges.length > 0 || modelRegistry.selectedFamily || modelRegistry.modelType || modelRegistry.searchQuery || modelRegistry.sortBy !== 'pulls_desc'} + + {/if} +
+ + {#if modelRegistry.error} +
+
+ + + + {modelRegistry.error} +
+
+ {/if} + + {#if modelRegistry.loading} +
+ {#each Array(6) as _} +
+
+
+
+
+
+
+
+ {/each} +
+ {:else if modelRegistry.models.length === 0} +
+ + + +

No models found

+

+ {#if modelRegistry.searchQuery || modelRegistry.modelType} + Try adjusting your search or filters + {:else} + Click "Sync Models" to fetch models from ollama.com + {/if} +

+
+ {:else} +
+ {#each modelRegistry.models as model (model.slug)} + + {/each} +
+ + {#if modelRegistry.totalPages > 1} +
+ + Page {modelRegistry.currentPage + 1} of {modelRegistry.totalPages} + +
+ {/if} + {/if} + {/if} +
+ + + {#if selectedModel} +
+ {/if} +
+ + + + +{#if modelOperationsState.activePulls.size > 0} +
+
+

Active Downloads

+ {#each [...modelOperationsState.activePulls.entries()] as [name, pull]} +
+
+ {name} + +
+
+
+
+
+ {pull.progress.percent}% +
+
+ {pull.progress.status} + {#if pull.progress.speed} + {modelOperationsState.formatBytes(pull.progress.speed)}/s + {/if} +
+
+ {/each} +
+
+{/if} diff --git a/frontend/src/lib/components/settings/PromptsTab.svelte b/frontend/src/lib/components/settings/PromptsTab.svelte new file mode 100644 index 0000000..492c624 --- /dev/null +++ b/frontend/src/lib/components/settings/PromptsTab.svelte @@ -0,0 +1,446 @@ + + +
+ +
+
+

System Prompts

+

+ Create and manage system prompt templates for conversations +

+
+ + {#if activeTab === 'my-prompts'} + + {/if} +
+ + +
+ + +
+ + + {#if activeTab === 'my-prompts'} + {#if promptsState.activePrompt} +
+
+ + + + Active system prompt: {promptsState.activePrompt.name} +
+
+ {/if} + + {#if promptsState.isLoading} +
+
+
+ {:else if promptsState.prompts.length === 0} +
+ + + +

No system prompts yet

+

Create a prompt or browse templates to get started

+
+ + +
+
+ {:else} +
+ {#each promptsState.prompts as prompt (prompt.id)} +
+
+
+
+

{prompt.name}

+ {#if prompt.isDefault} + default + {/if} + {#if promptsState.activePromptId === prompt.id} + active + {/if} + {#if prompt.targetCapabilities && prompt.targetCapabilities.length > 0} + {#each prompt.targetCapabilities as cap (cap)} + {cap} + {/each} + {/if} +
+ {#if prompt.description} +

{prompt.description}

+ {/if} +

{prompt.content}

+

Updated {formatDate(prompt.updatedAt)}

+
+ +
+ + + + +
+
+
+ {/each} +
+ {/if} + {/if} + + + {#if activeTab === 'browse-templates'} +
+ + {#each categories as category (category)} + {@const info = categoryInfo[category]} + + {/each} +
+ +
+ {#each filteredTemplates as template (template.id)} + {@const info = categoryInfo[template.category]} +
+
+
+

{template.name}

+ + {info.icon} + {info.label} + +
+ +
+

{template.description}

+ +
+ {/each} +
+ {/if} +
+ + +{#if showEditor} +
{ if (e.target === e.currentTarget) closeEditor(); }} role="dialog" aria-modal="true"> +
+
+

{editingPrompt ? 'Edit Prompt' : 'Create Prompt'}

+ +
+ +
{ e.preventDefault(); handleSave(); }} class="p-6"> +
+
+ + +
+ +
+ + +
+ +
+ + +

{formContent.length} characters

+
+ +
+ + +
+ +
+ +
+ {#each CAPABILITIES as cap (cap.id)} + + {/each} +
+
+
+ +
+ + +
+
+
+
+{/if} + + +{#if previewTemplate} + {@const info = categoryInfo[previewTemplate.category]} +
{ if (e.target === e.currentTarget) previewTemplate = null; }} role="dialog" aria-modal="true"> +
+
+
+

{previewTemplate.name}

+ + {info.icon} + {info.label} + +
+ +
+
+

{previewTemplate.description}

+
{previewTemplate.content}
+
+
+ + +
+
+
+{/if} diff --git a/frontend/src/lib/components/settings/SettingsTabs.svelte b/frontend/src/lib/components/settings/SettingsTabs.svelte new file mode 100644 index 0000000..9a563c4 --- /dev/null +++ b/frontend/src/lib/components/settings/SettingsTabs.svelte @@ -0,0 +1,70 @@ + + + + + diff --git a/frontend/src/lib/components/settings/ToolsTab.svelte b/frontend/src/lib/components/settings/ToolsTab.svelte new file mode 100644 index 0000000..f5dffbb --- /dev/null +++ b/frontend/src/lib/components/settings/ToolsTab.svelte @@ -0,0 +1,511 @@ + + +
+ +
+
+

Tools

+

+ Extend AI capabilities with built-in and custom tools +

+
+ +
+ Tools enabled + +
+
+ + +
+
+

Total Tools

+

{stats.total}

+
+
+

Enabled

+

{stats.enabled}

+
+
+

Built-in

+

{stats.builtin}

+
+
+

Custom

+

{stats.custom}

+
+
+ + +
+
+ + + + + {#if searchQuery} + + {/if} +
+
+ + +
+

+ + + + + Built-in Tools + ({filteredBuiltinTools.length}) +

+ + {#if filteredBuiltinTools.length === 0} +
+

No tools match your search

+
+ {:else} +
+ {#each filteredBuiltinTools as tool (tool.definition.function.name)} + {@const toolIcon = getToolIcon(tool.definition.function.name)} + {@const params = getParameters(tool.definition)} + {@const isLong = isLongDescription(tool.definition.function.description)} + {@const isExpanded = expandedDescriptions.has(tool.definition.function.name)} +
+
+
+ +
+ {#if toolIcon.icon === 'clock'} + + + + {:else if toolIcon.icon === 'calculator'} + + + + {:else if toolIcon.icon === 'globe'} + + + + {:else if toolIcon.icon === 'location'} + + + + + {:else if toolIcon.icon === 'search'} + + + + {:else} + + + + {/if} +
+ + +
+
+

{tool.definition.function.name}

+ built-in +
+ + +
+

+ {tool.definition.function.description} +

+ {#if isLong} + + {/if} +
+
+ + + +
+ + + {#if params.length > 0} +
+ {#each params as param} +
+ {param.name} + {#if param.required} + * + {/if} + : + {param.type} +
+ {/each} +
+ {/if} +
+
+ {/each} +
+ {/if} +
+ + +
+
+

+ + + + Custom Tools + ({filteredCustomTools.length}) +

+ + +
+ + {#if filteredCustomTools.length === 0 && toolsState.customTools.length === 0} +
+ + + +

No custom tools yet

+

Create JavaScript, Python, or HTTP tools to extend AI capabilities

+ +
+ {:else if filteredCustomTools.length === 0} +
+

No custom tools match your search

+
+ {:else} +
+ {#each filteredCustomTools as tool (tool.id)} + {@const implIcon = getImplementationIcon(tool.implementation)} + {@const customParams = Object.entries(tool.parameters.properties ?? {})} + {@const isLong = isLongDescription(tool.description)} + {@const isExpanded = expandedDescriptions.has(tool.id)} +
+
+
+ +
+ {#if tool.implementation === 'javascript'} + JS + {:else if tool.implementation === 'python'} + PY + {:else} + + + + {/if} +
+ + +
+
+

{tool.name}

+ custom + {tool.implementation} +
+ + +
+

+ {tool.description} +

+ {#if isLong} + + {/if} +
+
+ + +
+ + + +
+
+ + + {#if customParams.length > 0} +
+ {#each customParams as [name, prop]} +
+ {name} + {#if tool.parameters.required?.includes(name)} + * + {/if} + : + {prop.type} +
+ {/each} +
+ {/if} +
+
+ {/each} +
+ {/if} +
+ + +
+

+ + + + How Tools Work +

+

+ Tools extend the AI's capabilities by allowing it to perform actions beyond text generation. + When you ask a question that could benefit from a tool, the AI will automatically select and use the appropriate one. +

+
+
+
+ JS + JavaScript +
+

Runs in browser, instant execution

+
+
+
+ PY + Python +
+

Runs on backend server

+
+
+
+ + + + HTTP +
+

Calls external APIs

+
+
+

+ Note: Not all models support tool calling. Models like Llama 3.1+, Mistral 7B+, and Qwen have built-in tool support. +

+
+
+ + { showEditor = false; editingTool = null; }} + onSave={handleSaveTool} +/> diff --git a/frontend/src/lib/components/settings/index.ts b/frontend/src/lib/components/settings/index.ts new file mode 100644 index 0000000..0d2fe54 --- /dev/null +++ b/frontend/src/lib/components/settings/index.ts @@ -0,0 +1,13 @@ +/** + * Settings components barrel export + */ +export { default as SettingsTabs } from './SettingsTabs.svelte'; +export { default as GeneralTab } from './GeneralTab.svelte'; +export { default as ModelsTab } from './ModelsTab.svelte'; +export { default as PromptsTab } from './PromptsTab.svelte'; +export { default as ToolsTab } from './ToolsTab.svelte'; +export { default as KnowledgeTab } from './KnowledgeTab.svelte'; +export { default as MemoryTab } from './MemoryTab.svelte'; +export { default as ModelParametersPanel } from './ModelParametersPanel.svelte'; + +export type { SettingsTab } from './SettingsTabs.svelte'; diff --git a/frontend/src/routes/knowledge/+page.server.ts b/frontend/src/routes/knowledge/+page.server.ts new file mode 100644 index 0000000..a250b97 --- /dev/null +++ b/frontend/src/routes/knowledge/+page.server.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = () => { + redirect(301, '/settings?tab=knowledge'); +}; diff --git a/frontend/src/routes/models/+page.server.ts b/frontend/src/routes/models/+page.server.ts new file mode 100644 index 0000000..6e91931 --- /dev/null +++ b/frontend/src/routes/models/+page.server.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = () => { + redirect(301, '/settings?tab=models'); +}; diff --git a/frontend/src/routes/prompts/+page.server.ts b/frontend/src/routes/prompts/+page.server.ts new file mode 100644 index 0000000..e121aeb --- /dev/null +++ b/frontend/src/routes/prompts/+page.server.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = () => { + redirect(301, '/settings?tab=prompts'); +}; diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 6325ae7..f3f917e 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -1,516 +1,51 @@ -
-
- -
-

Settings

-

- Configure appearance, model defaults, and behavior -

+
+ +
+
+

Settings

+
+
- -
-

- - - - Appearance -

- -
- -
-
-

Dark Mode

-

Toggle between light and dark theme

-
- -
- - -
-
-

Use System Theme

-

Match your OS light/dark preference

-
- -
-
-
- - -
-

- - - - Chat Defaults -

- -
-
- -

Model used for new conversations

- -
-
-
- - -
-

- - - - Model-Prompt Defaults -

- -
-

- Set default system prompts for specific models. When no other prompt is selected, the model's default will be used automatically. -

- - {#if isLoadingModelInfo} -
-
- Loading model info... -
- {:else if modelsState.chatModels.length === 0} -

- No models available. Make sure Ollama is running. -

- {:else} -
- {#each modelsState.chatModels as model (model.name)} - {@const modelInfo = modelInfoCache.get(model.name)} - {@const mappedPromptId = getMappedPromptId(model.name)} -
-
-
-
- {model.name} - {#if modelInfo?.capabilities && modelInfo.capabilities.length > 0} - {#each modelInfo.capabilities as cap (cap)} - - {cap} - - {/each} - {/if} - {#if modelInfo?.systemPrompt} - - embedded - - {/if} -
-
- - -
- - {#if modelInfo?.systemPrompt} -

- Embedded: {modelInfo.systemPrompt} -

- {/if} -
- {/each} -
- {/if} -
-
- - -
-

- - - - Model Parameters -

- -
- -
-
-

Use Custom Parameters

-

Override model defaults with custom values

-
- -
- - {#if settingsState.useCustomParameters} - -
-
- - {settingsState.temperature.toFixed(2)} -
-

{PARAMETER_DESCRIPTIONS.temperature}

- settingsState.updateParameter('temperature', parseFloat(e.currentTarget.value))} - class="w-full accent-orange-500" - /> -
- - -
-
- - {settingsState.top_k} -
-

{PARAMETER_DESCRIPTIONS.top_k}

- settingsState.updateParameter('top_k', parseInt(e.currentTarget.value))} - class="w-full accent-orange-500" - /> -
- - -
-
- - {settingsState.top_p.toFixed(2)} -
-

{PARAMETER_DESCRIPTIONS.top_p}

- settingsState.updateParameter('top_p', parseFloat(e.currentTarget.value))} - class="w-full accent-orange-500" - /> -
- - -
-
- - {settingsState.num_ctx.toLocaleString()} -
-

{PARAMETER_DESCRIPTIONS.num_ctx}

- settingsState.updateParameter('num_ctx', parseInt(e.currentTarget.value))} - class="w-full accent-orange-500" - /> -
- - -
- -
- {:else} -

- Using model defaults. Enable custom parameters to adjust temperature, sampling, and context length. -

- {/if} -
-
- - -
-

- - - - Memory Management -

- -
- -
- -

Model used for semantic search and conversation indexing

- -

- Note: The model must be installed in Ollama. Run ollama pull {settingsState.embeddingModel} if not installed. -

-
- - -
-
-

Auto-Compact

-

Automatically summarize older messages when context usage is high

-
- -
- - {#if settingsState.autoCompactEnabled} - -
-
- - {settingsState.autoCompactThreshold}% -
-

Trigger compaction when context usage exceeds this percentage

- settingsState.updateAutoCompactThreshold(parseInt(e.currentTarget.value))} - class="w-full accent-emerald-500" - /> -
- {AUTO_COMPACT_RANGES.threshold.min}% - {AUTO_COMPACT_RANGES.threshold.max}% -
-
- - -
-
- - {settingsState.autoCompactPreserveCount} -
-

Number of recent messages to keep intact (not summarized)

- settingsState.updateAutoCompactPreserveCount(parseInt(e.currentTarget.value))} - class="w-full accent-emerald-500" - /> -
- {AUTO_COMPACT_RANGES.preserveCount.min} - {AUTO_COMPACT_RANGES.preserveCount.max} -
-
- {:else} -

- Enable auto-compact to automatically manage context usage. When enabled, older messages - will be summarized when context usage exceeds your threshold. -

- {/if} -
-
- - -
-

- - - - Keyboard Shortcuts -

- -
-
-
- New Chat - {modifierKey}+N -
-
- Search - {modifierKey}+K -
-
- Toggle Sidebar - {modifierKey}+B -
-
- Send Message - Enter -
-
- New Line - Shift+Enter -
-
-
-
- - -
-

- - - - About -

- -
-
-
- - - -
-
-

Vessel

-

- A modern interface for local AI with chat, tools, and memory management. -

-
-
-
-
+ +
+
+ {#if activeTab === 'general'} + + {:else if activeTab === 'models'} + + {:else if activeTab === 'prompts'} + + {:else if activeTab === 'tools'} + + {:else if activeTab === 'knowledge'} + + {:else if activeTab === 'memory'} + + {/if} +
diff --git a/frontend/src/routes/tools/+page.server.ts b/frontend/src/routes/tools/+page.server.ts new file mode 100644 index 0000000..38ace48 --- /dev/null +++ b/frontend/src/routes/tools/+page.server.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = () => { + redirect(301, '/settings?tab=tools'); +};