Skip to content
Binary file modified tools/server/public/index.html.gz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
import { ChatSettingsFooter, ChatSettingsFields } from '$lib/components/app';
import * as Dialog from '$lib/components/ui/dialog';
import { ScrollArea } from '$lib/components/ui/scroll-area';
import { SETTING_CONFIG_DEFAULT } from '$lib/constants/settings-config';
import { config, updateMultipleConfig, resetConfig } from '$lib/stores/settings.svelte';
import { config, updateMultipleConfig } from '$lib/stores/settings.svelte';
import { setMode } from 'mode-watcher';
import type { Component } from 'svelte';
Expand Down Expand Up @@ -267,16 +266,13 @@
}
function handleReset() {
resetConfig();
localConfig = { ...config() };
localConfig = { ...SETTING_CONFIG_DEFAULT };
setMode(SETTING_CONFIG_DEFAULT.theme as 'light' | 'dark' | 'system');
originalTheme = SETTING_CONFIG_DEFAULT.theme as string;
setMode(localConfig.theme as 'light' | 'dark' | 'system');
originalTheme = localConfig.theme as string;
}
function handleSave() {
// Validate custom JSON if provided
if (localConfig.custom && typeof localConfig.custom === 'string' && localConfig.custom.trim()) {
try {
JSON.parse(localConfig.custom);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<script lang="ts">
import { RotateCcw } from '@lucide/svelte';
import { Checkbox } from '$lib/components/ui/checkbox';
import { Input } from '$lib/components/ui/input';
import Label from '$lib/components/ui/label/label.svelte';
import * as Select from '$lib/components/ui/select';
import { Textarea } from '$lib/components/ui/textarea';
import { SETTING_CONFIG_DEFAULT, SETTING_CONFIG_INFO } from '$lib/constants/settings-config';
import { supportsVision } from '$lib/stores/server.svelte';
import { getParameterInfo, resetParameterToServerDefault } from '$lib/stores/settings.svelte';
import { ParameterSyncService } from '$lib/services/parameter-sync';
import ParameterSourceIndicator from './ParameterSourceIndicator.svelte';
import type { Component } from 'svelte';
interface Props {
Expand All @@ -16,22 +20,77 @@
}
let { fields, localConfig, onConfigChange, onThemeChange }: Props = $props();
// Helper function to get parameter source info for syncable parameters
function getParameterSourceInfo(key: string) {
if (!ParameterSyncService.canSyncParameter(key)) {
return null;
}
return getParameterInfo(key);
}
</script>

{#each fields as field (field.key)}
<div class="space-y-2">
{#if field.type === 'input'}
<Label for={field.key} class="block text-sm font-medium">
{field.label}
</Label>
{@const paramInfo = getParameterSourceInfo(field.key)}
{@const currentValue = String(localConfig[field.key] ?? '')}
{@const propsDefault = paramInfo?.serverDefault}
{@const isCustomRealTime = (() => {
if (!paramInfo || propsDefault === undefined) return false;

<Input
id={field.key}
value={String(localConfig[field.key] ?? '')}
onchange={(e) => onConfigChange(field.key, e.currentTarget.value)}
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
class="w-full md:max-w-md"
/>
// Apply same rounding logic for real-time comparison
const inputValue = currentValue;
const numericInput = parseFloat(inputValue);
const normalizedInput = !isNaN(numericInput)
? Math.round(numericInput * 1000000) / 1000000
: inputValue;
const normalizedDefault =
typeof propsDefault === 'number'
? Math.round(propsDefault * 1000000) / 1000000
: propsDefault;

return normalizedInput !== normalizedDefault;
})()}

<div class="flex items-center gap-2">
<Label for={field.key} class="text-sm font-medium">
{field.label}
</Label>
{#if isCustomRealTime}
<ParameterSourceIndicator />
{/if}
</div>

<div class="relative w-full md:max-w-md">
<Input
id={field.key}
value={currentValue}
oninput={(e) => {
// Update local config immediately for real-time badge feedback
onConfigChange(field.key, e.currentTarget.value);
}}
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
class="w-full {isCustomRealTime ? 'pr-8' : ''}"
/>
{#if isCustomRealTime}
<button
type="button"
onclick={() => {
resetParameterToServerDefault(field.key);
// Trigger UI update by calling onConfigChange with the default value
const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key];
onConfigChange(field.key, String(defaultValue));
}}
class="absolute top-1/2 right-2 inline-flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded transition-colors hover:bg-muted"
aria-label="Reset to default"
title="Reset to default"
>
<RotateCcw class="h-3 w-3" />
</button>
{/if}
</div>
{#if field.help || SETTING_CONFIG_INFO[field.key]}
<p class="mt-1 text-xs text-muted-foreground">
{field.help || SETTING_CONFIG_INFO[field.key]}
Expand Down Expand Up @@ -59,14 +118,28 @@
(opt: { value: string; label: string; icon?: Component }) =>
opt.value === localConfig[field.key]
)}
{@const paramInfo = getParameterSourceInfo(field.key)}
{@const currentValue = localConfig[field.key]}
{@const propsDefault = paramInfo?.serverDefault}
{@const isCustomRealTime = (() => {
if (!paramInfo || propsDefault === undefined) return false;

<Label for={field.key} class="block text-sm font-medium">
{field.label}
</Label>
// For select fields, do direct comparison (no rounding needed)
return currentValue !== propsDefault;
})()}

<div class="flex items-center gap-2">
<Label for={field.key} class="text-sm font-medium">
{field.label}
</Label>
{#if isCustomRealTime}
<ParameterSourceIndicator />
{/if}
</div>

<Select.Root
type="single"
value={localConfig[field.key]}
value={currentValue}
onValueChange={(value) => {
if (field.key === 'theme' && value && onThemeChange) {
onThemeChange(value);
Expand All @@ -75,16 +148,34 @@
}
}}
>
<Select.Trigger class="w-full md:w-auto md:max-w-md">
<div class="flex items-center gap-2">
{#if selectedOption?.icon}
{@const IconComponent = selectedOption.icon}
<IconComponent class="h-4 w-4" />
{/if}

{selectedOption?.label || `Select ${field.label.toLowerCase()}`}
</div>
</Select.Trigger>
<div class="relative w-full md:w-auto md:max-w-md">
<Select.Trigger class="w-full">
<div class="flex items-center gap-2">
{#if selectedOption?.icon}
{@const IconComponent = selectedOption.icon}
<IconComponent class="h-4 w-4" />
{/if}

{selectedOption?.label || `Select ${field.label.toLowerCase()}`}
</div>
</Select.Trigger>
{#if isCustomRealTime}
<button
type="button"
onclick={() => {
resetParameterToServerDefault(field.key);
// Trigger UI update by calling onConfigChange with the default value
const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key];
onConfigChange(field.key, String(defaultValue));
}}
class="absolute top-1/2 right-8 inline-flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded transition-colors hover:bg-muted"
aria-label="Reset to default"
title="Reset to default"
>
<RotateCcw class="h-3 w-3" />
</button>
{/if}
</div>
<Select.Content>
{#if field.options}
{#each field.options as option (option.value)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import * as AlertDialog from '$lib/components/ui/alert-dialog';
import { forceSyncWithServerDefaults } from '$lib/stores/settings.svelte';
import { RotateCcw } from '@lucide/svelte';
interface Props {
onReset?: () => void;
Expand All @@ -16,7 +18,9 @@
}
function handleConfirmReset() {
forceSyncWithServerDefaults();
onReset?.();
showResetDialog = false;
}
Expand All @@ -26,7 +30,13 @@
</script>

<div class="flex justify-between border-t border-border/30 p-6">
<Button variant="outline" onclick={handleResetClick}>Reset to default</Button>
<div class="flex gap-2">
<Button variant="outline" onclick={handleResetClick}>
<RotateCcw class="h-3 w-3" />

Reset to default
</Button>
</div>

<Button onclick={handleSave}>Save settings</Button>
</div>
Expand All @@ -36,8 +46,9 @@
<AlertDialog.Header>
<AlertDialog.Title>Reset Settings to Default</AlertDialog.Title>
<AlertDialog.Description>
Are you sure you want to reset all settings to their default values? This action cannot be
undone and will permanently remove all your custom configurations.
Are you sure you want to reset all settings to their default values? This will reset all
parameters to the values provided by the server's /props endpoint and remove all your custom
configurations.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { Wrench } from '@lucide/svelte';
import { Badge } from '$lib/components/ui/badge';
interface Props {
class?: string;
}
let { class: className = '' }: Props = $props();
</script>

<Badge
variant="secondary"
class="h-5 bg-orange-100 px-1.5 py-0.5 text-xs text-orange-800 dark:bg-orange-900 dark:text-orange-200 {className}"
>
<Wrench class="mr-1 h-3 w-3" />
Custom
</Badge>
1 change: 1 addition & 0 deletions tools/server/webui/src/lib/components/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export { default as ChatScreen } from './chat/ChatScreen/ChatScreen.svelte';
export { default as ChatSettingsDialog } from './chat/ChatSettings/ChatSettingsDialog.svelte';
export { default as ChatSettingsFooter } from './chat/ChatSettings/ChatSettingsFooter.svelte';
export { default as ChatSettingsFields } from './chat/ChatSettings/ChatSettingsFields.svelte';
export { default as ParameterSourceIndicator } from './chat/ChatSettings/ParameterSourceIndicator.svelte';

export { default as ChatSidebar } from './chat/ChatSidebar/ChatSidebar.svelte';
export { default as ChatSidebarConversationItem } from './chat/ChatSidebar/ChatSidebarConversationItem.svelte';
Expand Down
2 changes: 2 additions & 0 deletions tools/server/webui/src/lib/constants/precision.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const PRECISION_MULTIPLIER = 1000000;
export const PRECISION_DECIMAL_PLACES = 6;
Loading