diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx index f41d9543..64f19b2d 100644 --- a/webui/src/components/settings/SettingsView.tsx +++ b/webui/src/components/settings/SettingsView.tsx @@ -112,8 +112,8 @@ import { import { notifyCliAppsChanged } from "@/lib/cli-app-events"; import { copyTextToClipboard } from "@/lib/clipboard"; import { - LOCAL_PREFS_STORAGE_KEY, readLocalPreferences, + writeLocalPreferences, type FileEditDisplayMode, type LocalActivityMode, type LocalDensity, @@ -812,11 +812,7 @@ export function SettingsView({ }, [activeSection, token]); useEffect(() => { - try { - window.localStorage.setItem(LOCAL_PREFS_STORAGE_KEY, JSON.stringify(localPrefs)); - } catch { - // Browser-only preferences should never block settings. - } + writeLocalPreferences(localPrefs); }, [localPrefs]); useEffect(() => { diff --git a/webui/src/hooks/useFileEditDisplayMode.ts b/webui/src/hooks/useFileEditDisplayMode.ts index 6c340755..6effbbe2 100644 --- a/webui/src/hooks/useFileEditDisplayMode.ts +++ b/webui/src/hooks/useFileEditDisplayMode.ts @@ -1,6 +1,12 @@ import { useEffect, useState } from "react"; -import { readLocalPreferences, type FileEditDisplayMode } from "@/lib/local-preferences"; +import { + LOCAL_PREFS_CHANGED_EVENT, + normalizeFileEditDisplayMode, + readLocalPreferences, + type FileEditDisplayMode, + type LocalPreferences, +} from "@/lib/local-preferences"; export function useFileEditDisplayMode(): FileEditDisplayMode { const [mode, setMode] = useState(() => @@ -9,11 +15,21 @@ export function useFileEditDisplayMode(): FileEditDisplayMode { useEffect(() => { const refresh = () => setMode(readLocalPreferences().fileEditDisplayMode); + const refreshFromLocalPreferenceEvent = (event: Event) => { + const detail = (event as CustomEvent | undefined>).detail; + setMode( + detail + ? normalizeFileEditDisplayMode(detail.fileEditDisplayMode) + : readLocalPreferences().fileEditDisplayMode, + ); + }; window.addEventListener("storage", refresh); window.addEventListener("focus", refresh); + window.addEventListener(LOCAL_PREFS_CHANGED_EVENT, refreshFromLocalPreferenceEvent); return () => { window.removeEventListener("storage", refresh); window.removeEventListener("focus", refresh); + window.removeEventListener(LOCAL_PREFS_CHANGED_EVENT, refreshFromLocalPreferenceEvent); }; }, []); diff --git a/webui/src/lib/local-preferences.ts b/webui/src/lib/local-preferences.ts index 7027857a..53e3c483 100644 --- a/webui/src/lib/local-preferences.ts +++ b/webui/src/lib/local-preferences.ts @@ -11,6 +11,7 @@ export interface LocalPreferences { } export const LOCAL_PREFS_STORAGE_KEY = "nanobot-webui.settings-preferences"; +export const LOCAL_PREFS_CHANGED_EVENT = "nanobot-webui.local-preferences-changed"; export const DEFAULT_LOCAL_PREFS: LocalPreferences = { density: "comfortable", @@ -40,3 +41,15 @@ export function readLocalPreferences(): LocalPreferences { return DEFAULT_LOCAL_PREFS; } } + +export function writeLocalPreferences(preferences: LocalPreferences): void { + try { + window.localStorage.setItem(LOCAL_PREFS_STORAGE_KEY, JSON.stringify(preferences)); + } catch { + // Browser-only preferences should never block settings. + } + window.dispatchEvent(new CustomEvent( + LOCAL_PREFS_CHANGED_EVENT, + { detail: preferences }, + )); +} diff --git a/webui/src/tests/useFileEditDisplayMode.test.tsx b/webui/src/tests/useFileEditDisplayMode.test.tsx new file mode 100644 index 00000000..74b4d7bb --- /dev/null +++ b/webui/src/tests/useFileEditDisplayMode.test.tsx @@ -0,0 +1,37 @@ +import { act, render, screen, waitFor } from "@testing-library/react"; +import { afterEach, describe, expect, it } from "vitest"; + +import { useFileEditDisplayMode } from "@/hooks/useFileEditDisplayMode"; +import { + DEFAULT_LOCAL_PREFS, + LOCAL_PREFS_STORAGE_KEY, + writeLocalPreferences, +} from "@/lib/local-preferences"; + +function DisplayModeProbe() { + const mode = useFileEditDisplayMode(); + return
{mode}
; +} + +describe("useFileEditDisplayMode", () => { + afterEach(() => { + localStorage.removeItem(LOCAL_PREFS_STORAGE_KEY); + }); + + it("updates when local preferences change in the same document", async () => { + render(); + + expect(screen.getByTestId("mode")).toHaveTextContent("summary"); + + act(() => { + writeLocalPreferences({ + ...DEFAULT_LOCAL_PREFS, + fileEditDisplayMode: "diff", + }); + }); + + await waitFor(() => { + expect(screen.getByTestId("mode")).toHaveTextContent("diff"); + }); + }); +});