diff --git a/tools/server/public/index.html.gz b/tools/server/public/index.html.gz index f4ff57b4c..75fc856f5 100644 Binary files a/tools/server/public/index.html.gz and b/tools/server/public/index.html.gz differ diff --git a/tools/server/webui/.storybook/main.ts b/tools/server/webui/.storybook/main.ts index bfd16fa22..4f6945f21 100644 --- a/tools/server/webui/.storybook/main.ts +++ b/tools/server/webui/.storybook/main.ts @@ -1,17 +1,24 @@ import type { StorybookConfig } from '@storybook/sveltekit'; +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); const config: StorybookConfig = { stories: ['../tests/stories/**/*.mdx', '../tests/stories/**/*.stories.@(js|ts|svelte)'], addons: [ '@storybook/addon-svelte-csf', '@chromatic-com/storybook', - '@storybook/addon-docs', + '@storybook/addon-vitest', '@storybook/addon-a11y', - '@storybook/addon-vitest' + '@storybook/addon-docs' ], - framework: { - name: '@storybook/sveltekit', - options: {} + framework: '@storybook/sveltekit', + viteFinal: async (config) => { + config.server = config.server || {}; + config.server.fs = config.server.fs || {}; + config.server.fs.allow = [...(config.server.fs.allow || []), resolve(__dirname, '../tests')]; + return config; } }; export default config; diff --git a/tools/server/webui/.storybook/preview.ts b/tools/server/webui/.storybook/preview.ts index 8d530e43e..566dbfd28 100644 --- a/tools/server/webui/.storybook/preview.ts +++ b/tools/server/webui/.storybook/preview.ts @@ -13,7 +13,7 @@ const preview: Preview = { }, backgrounds: { - disable: true + disabled: true }, a11y: { diff --git a/tools/server/webui/docs/flows/settings-flow.md b/tools/server/webui/docs/flows/settings-flow.md index 474aef01b..40ad3bd94 100644 --- a/tools/server/webui/docs/flows/settings-flow.md +++ b/tools/server/webui/docs/flows/settings-flow.md @@ -49,14 +49,20 @@ sequenceDiagram settingsStore->>serverStore: defaultParams serverStore-->>settingsStore: {temperature, top_p, top_k, ...} - settingsStore->>ParamSvc: extractServerDefaults(defaultParams) - ParamSvc-->>settingsStore: Record + loop each SYNCABLE_PARAMETER + alt key NOT in userOverrides + settingsStore->>settingsStore: config[key] = serverDefault[key] + Note right of settingsStore: Non-overridden params adopt server default + else key in userOverrides + Note right of settingsStore: Keep user value, skip server default + end + end - settingsStore->>ParamSvc: mergeWithServerDefaults(config, serverDefaults) - Note right of ParamSvc: For each syncable parameter:
- If NOT in userOverrides → use server default
- If in userOverrides → keep user value - ParamSvc-->>settingsStore: mergedConfig + alt serverStore.props has webuiSettings + settingsStore->>settingsStore: Apply webuiSettings from server + Note right of settingsStore: Server-provided UI settings
(e.g. showRawOutputSwitch) + end - settingsStore->>settingsStore: config = mergedConfig settingsStore->>settingsStore: saveConfig() deactivate settingsStore @@ -67,11 +73,18 @@ sequenceDiagram UI->>settingsStore: updateConfig(key, value) activate settingsStore settingsStore->>settingsStore: config[key] = value - settingsStore->>settingsStore: userOverrides.add(key) - Note right of settingsStore: Mark as user-modified (won't be overwritten by server) + + alt value matches server default for key + settingsStore->>settingsStore: userOverrides.delete(key) + Note right of settingsStore: Matches server default, remove override + else value differs from server default + settingsStore->>settingsStore: userOverrides.add(key) + Note right of settingsStore: Mark as user-modified (won't be overwritten) + end + settingsStore->>settingsStore: saveConfig() - settingsStore->>LS: set("llama-config", config) - settingsStore->>LS: set("llama-userOverrides", [...userOverrides]) + settingsStore->>LS: set(CONFIG_LOCALSTORAGE_KEY, config) + settingsStore->>LS: set(USER_OVERRIDES_LOCALSTORAGE_KEY, [...userOverrides]) deactivate settingsStore UI->>settingsStore: updateMultipleConfig({key1: val1, key2: val2}) @@ -88,10 +101,9 @@ sequenceDiagram UI->>settingsStore: resetConfig() activate settingsStore - settingsStore->>settingsStore: config = SETTING_CONFIG_DEFAULT + settingsStore->>settingsStore: config = {...SETTING_CONFIG_DEFAULT} settingsStore->>settingsStore: userOverrides.clear() - settingsStore->>settingsStore: syncWithServerDefaults() - Note right of settingsStore: Apply server defaults for syncable params + Note right of settingsStore: All params reset to defaults
Next syncWithServerDefaults will adopt server values settingsStore->>settingsStore: saveConfig() deactivate settingsStore diff --git a/tools/server/webui/src/lib/components/app/actions/ActionIconsCodeBlock.svelte b/tools/server/webui/src/lib/components/app/actions/ActionIconsCodeBlock.svelte index 54ff0af1a..b20e79b5e 100644 --- a/tools/server/webui/src/lib/components/app/actions/ActionIconsCodeBlock.svelte +++ b/tools/server/webui/src/lib/components/app/actions/ActionIconsCodeBlock.svelte @@ -1,6 +1,6 @@ + +
+ + + + + + + + +

{triggerTooltipText}

+
+
+
+ + + {#each actions as item (item.id)} + {@const hasDisabledTooltip = !!item.disabled && !!item.disabledReason} + {@const hasEnabledTooltip = !item.disabled && !!item.tooltip} + + {#if hasDisabledTooltip} + + + + {#if item.id === 'images'} + + {:else if item.id === 'audio'} + + {:else if item.id === 'text'} + + {:else if item.id === 'pdf'} + + {:else} + + {/if} + + {item.label} + + + + +

{item.disabledReason}

+
+
+ {:else if hasEnabledTooltip} + + + handleActionClick(item.id)}> + {#if item.id === 'images'} + + {:else if item.id === 'audio'} + + {:else if item.id === 'text'} + + {:else if item.id === 'pdf'} + + {:else} + + {/if} + + {item.label} + + + + +

{item.tooltip}

+
+
+ {:else} + handleActionClick(item.id)}> + {#if item.id === 'images'} + + {:else if item.id === 'audio'} + + {:else if item.id === 'text'} + + {:else if item.id === 'pdf'} + + {:else} + + {/if} + + {item.label} + + {/if} + {/each} +
+
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte index c621a69e0..cf5aca42a 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte @@ -2,7 +2,7 @@ import { Square } from '@lucide/svelte'; import { Button } from '$lib/components/ui/button'; import { - ChatFormActionFileAttachments, + ChatFormActionAttachmentsDropdown, ChatFormActionRecord, ChatFormActionSubmit, ModelsSelector @@ -157,7 +157,7 @@ const { handleModelChange } = useModelChangeValidation({ getRequiredModalities: () => usedModalities(), - onValidationFailure: async (previousModelId) => { + onValidationFailure: async (previousModelId: string | null) => { if (previousModelId) { await modelsStore.selectModelById(previousModelId); } @@ -166,32 +166,39 @@
- +
+ +
- +
+ +
{#if isLoading} {:else if shouldShowRecordButton} diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte index 3470e2f71..25895c83b 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte @@ -62,8 +62,8 @@ assistantMessages: number; messageTypes: string[]; } | null>(null); - let editedContent = $state(message.content); - let editedExtras = $state(message.extra ? [...message.extra] : []); + let editedContent = $derived(message.content); + let editedExtras = $derived(message.extra ? [...message.extra] : []); let editedUploadedFiles = $state([]); let isEditing = $state(false); let showDeleteDialog = $state(false); diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte index 1cb6b274b..867def5fc 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte @@ -105,7 +105,7 @@ const { handleModelChange } = useModelChangeValidation({ getRequiredModalities: () => conversationsStore.getModalitiesUpToMessage(message.id), - onSuccess: (modelName) => onRegenerate(modelName) + onSuccess: (modelName: string) => onRegenerate(modelName) }); function handleCopyModel() { diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte index f812ea2fd..c216ea690 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte @@ -133,7 +133,7 @@ const { handleModelChange } = useModelChangeValidation({ getRequiredModalities, - onValidationFailure: async (previousModelId) => { + onValidationFailure: async (previousModelId: string | null) => { if (previousModelId) { await modelsStore.selectModelById(previousModelId); } diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte index d457e042f..b53e82aaf 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte @@ -28,7 +28,7 @@ initialView = ChatMessageStatsView.GENERATION }: Props = $props(); - let activeView: ChatMessageStatsView = $state(initialView); + let activeView: ChatMessageStatsView = $derived(initialView); let hasAutoSwitchedToGeneration = $state(false); // In live mode: auto-switch to GENERATION tab when prompt processing completes diff --git a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index a5450e6af..3d432e26b 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -35,6 +35,7 @@ import { modelsStore, modelOptions, selectedModelId } from '$lib/stores/models.svelte'; import { isFileTypeSupported, filterFilesByModalities } from '$lib/utils'; import { parseFilesToMessageExtras, processFilesToChatUploaded } from '$lib/utils/browser-only'; + import { ErrorDialogType } from '$lib/enums'; import { onMount } from 'svelte'; import { fade, fly, slide } from 'svelte/transition'; import { Trash2, AlertTriangle, RefreshCw } from '@lucide/svelte'; @@ -616,7 +617,7 @@ contextInfo={activeErrorDialog?.contextInfo} onOpenChange={handleErrorDialogOpenChange} open={Boolean(activeErrorDialog)} - type={activeErrorDialog?.type ?? 'server'} + type={(activeErrorDialog?.type as ErrorDialogType) ?? ErrorDialogType.SERVER} /> diff --git a/tools/server/webui/src/lib/components/app/misc/RemoveButton.svelte b/tools/server/webui/src/lib/components/app/misc/RemoveButton.svelte deleted file mode 100644 index 173685510..000000000 --- a/tools/server/webui/src/lib/components/app/misc/RemoveButton.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/tools/server/webui/src/lib/components/app/misc/SyntaxHighlightedCode.svelte b/tools/server/webui/src/lib/components/app/misc/SyntaxHighlightedCode.svelte deleted file mode 100644 index bc42f9dd1..000000000 --- a/tools/server/webui/src/lib/components/app/misc/SyntaxHighlightedCode.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - -
- -
{@html highlightedHtml}
-
- - diff --git a/tools/server/webui/src/lib/components/app/models/ModelBadge.svelte b/tools/server/webui/src/lib/components/app/models/ModelBadge.svelte index bea1bf6e3..f98ba7d78 100644 --- a/tools/server/webui/src/lib/components/app/models/ModelBadge.svelte +++ b/tools/server/webui/src/lib/components/app/models/ModelBadge.svelte @@ -1,6 +1,6 @@