From 6712c83cf644ac78ae25251ae0dcb6fec868268d Mon Sep 17 00:00:00 2001 From: DrMelone <27028174+Classic298@users.noreply.github.com> Date: Mon, 13 Apr 2026 00:20:48 +0200 Subject: [PATCH] fix: restore JSDoc marker, wire modal bindings, align chatDirection, drop nested form, parallelize load - Restore the /** opener above displayFileHandler in utils/index.ts that was dropped during the earlier merge; the orphaned comment body made the file fail TypeScript parsing and broke the frontend build. - admin Settings/Interface.svelte now imports InterfaceDefaultsModal and ConfirmDialog and declares the showInterfaceDefaultsModal / showResetConfirmDialog flags that were already being bound at the bottom of the template, resolving the unresolved-identifier compile errors. - InterfaceDefaultsModal no longer lowercases chatDirection before saving. The user-side store is typed 'LTR' | 'RTL' | 'auto' and the toggle/render logic matches those uppercase values; lowercasing on the admin side made every saved RTL/LTR default silently render as "Auto" for end users. - InterfaceDefaultsModal drops the outer
wrapper. InterfaceSettings already renders its own and nesting forms is invalid HTML; the Save button is now a plain type="button" with on:click=submitHandler, which keeps the click-to-save behaviour without the nested-form event routing surprises. - +layout.svelte fetches user settings and admin defaults in parallel via Promise.all, with an inline .catch fallback on the defaults call so the endpoint being unavailable still lets user settings apply. This removes an avoidable serial round-trip on every authenticated load. --- .../admin/Settings/Interface.svelte | 7 ++++ .../Interface/InterfaceDefaultsModal.svelte | 35 ++++++++++--------- src/lib/utils/index.ts | 1 + src/routes/+layout.svelte | 26 +++++++------- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/lib/components/admin/Settings/Interface.svelte b/src/lib/components/admin/Settings/Interface.svelte index 0a4d0a6891..204415910c 100644 --- a/src/lib/components/admin/Settings/Interface.svelte +++ b/src/lib/components/admin/Settings/Interface.svelte @@ -10,9 +10,16 @@ import Switch from '$lib/components/common/Switch.svelte'; import Textarea from '$lib/components/common/Textarea.svelte'; import Spinner from '$lib/components/common/Spinner.svelte'; + import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; + import InterfaceDefaultsModal from './Interface/InterfaceDefaultsModal.svelte'; import { resetAllUsersInterfaceSettings } from '$lib/apis/users'; + // Toggles for the admin-defaults modal and the "reset all users" confirm + // dialog rendered at the bottom of this panel. + let showInterfaceDefaultsModal = false; + let showResetConfirmDialog = false; + const dispatch = createEventDispatcher(); const i18n = getContext('i18n'); diff --git a/src/lib/components/admin/Settings/Interface/InterfaceDefaultsModal.svelte b/src/lib/components/admin/Settings/Interface/InterfaceDefaultsModal.svelte index cdefeac1bb..8c9f1dbf81 100644 --- a/src/lib/components/admin/Settings/Interface/InterfaceDefaultsModal.svelte +++ b/src/lib/components/admin/Settings/Interface/InterfaceDefaultsModal.svelte @@ -33,16 +33,15 @@ } }; - // Prepare settings for backend - normalize chatDirection and filter nulls + // Prepare settings for backend — strip nulls so absent keys fall through + // to the user's own setting via deep-merge on load. Do NOT lowercase + // chatDirection: the user-side store is typed 'LTR' | 'RTL' | 'auto' + // and the toggle/render logic in chat/Settings/Interface.svelte matches + // against the uppercase values. Lowercasing here made admin defaults + // silently render as "Auto" because neither 'ltr' nor 'rtl' matched. const prepareForBackend = (settings: any): object => { - const result = { ...settings }; - - if (result.chatDirection) { - result.chatDirection = result.chatDirection.toLowerCase(); - } - return Object.fromEntries( - Object.entries(result).filter(([_, v]) => v !== undefined && v !== null) + Object.entries({ ...settings }).filter(([_, v]) => v !== undefined && v !== null) ); }; @@ -89,12 +88,15 @@ {#if !loading} - { - submitHandler(); - }} - > + +
@@ -104,7 +106,8 @@ class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {saving ? ' cursor-not-allowed' : ''}" - type="submit" + type="button" + on:click={submitHandler} disabled={saving} > {$i18n.t('Save')} @@ -116,7 +119,7 @@ {/if}
-
+ {:else}
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 9617f4e6d4..4d26d1ff4d 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1840,6 +1840,7 @@ export const formatSkillName = (name) => { return name.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); }; +/** * Open the file browser panel to display a specific file. * Used by both the direct tool execution path (client-side) and the * backend event path (server-side) so behaviour is consistent. diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index edebe8b14f..480f01f2c4 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -916,19 +916,21 @@ $socket?.on('events', chatEventHandler); $socket?.on('events:channel', channelEventHandler); - const userSettings = await getUserSettings(localStorage.token); + // Fetch user settings and admin-configured interface defaults in + // parallel — both are independent network calls on the hot + // authenticated-load path, and serializing them added + // avoidable round-trip latency. Defaults fall back to {} if + // the endpoint is unavailable so user settings still apply. + const [userSettings, adminDefaults] = await Promise.all([ + getUserSettings(localStorage.token), + getInterfaceDefaults(localStorage.token).catch((e) => { + console.warn('Failed to load admin interface defaults:', e); + return {}; + }) + ]); - // Fetch admin-configured interface defaults and merge with user settings - let effectiveSettings = {}; - try { - const adminDefaults = await getInterfaceDefaults(localStorage.token); - const userUI = userSettings?.ui ?? {}; - // Admin defaults are base, user settings override - effectiveSettings = deepMerge(adminDefaults, userUI); - } catch (e) { - // Fall back to user settings if admin defaults unavailable - effectiveSettings = userSettings?.ui ?? {}; - } + // Admin defaults are base, user settings override. + const effectiveSettings = deepMerge(adminDefaults ?? {}, userSettings?.ui ?? {}); if (userSettings) { settings.set(effectiveSettings);