From 9fece98612c7d599b61c36bf6401517c69b4b75b Mon Sep 17 00:00:00 2001 From: Haileyesus Date: Thu, 12 Feb 2026 23:17:33 +0300 Subject: [PATCH] fix(chat): sync quick settings state and stabilize thinking toggle Synchronize useUiPreferences instances via custom sync events and storage listeners so Quick Settings updates apply across UI consumers immediately. Also hide standalone thinking messages when showThinking is disabled, while preserving hook order to avoid Rendered fewer hooks runtime errors. --- .../view/subcomponents/MessageComponent.tsx | 5 ++ src/hooks/useUiPreferences.ts | 64 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/components/chat/view/subcomponents/MessageComponent.tsx b/src/components/chat/view/subcomponents/MessageComponent.tsx index cfde764..a90332b 100644 --- a/src/components/chat/view/subcomponents/MessageComponent.tsx +++ b/src/components/chat/view/subcomponents/MessageComponent.tsx @@ -86,6 +86,11 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile }, [autoExpandTools, isExpanded, message.isToolUse]); const formattedTime = useMemo(() => new Date(message.timestamp).toLocaleTimeString(), [message.timestamp]); + const shouldHideThinkingMessage = Boolean(message.isThinking && !showThinking); + + if (shouldHideThinkingMessage) { + return null; + } return (
(PREFERENCE_KEYS); // prevents unknown keys from being written +const SYNC_EVENT = 'ui-preferences:sync'; + +type SyncEventDetail = { + storageKey: string; + sourceId: string; + value: Partial>; +}; const parseBoolean = (value: unknown, fallback: boolean): boolean => { if (typeof value === 'boolean') { @@ -140,6 +147,7 @@ function reducer(state: UiPreferences, action: UiPreferencesAction): UiPreferenc } export function useUiPreferences(storageKey = 'uiPreferences') { + const instanceIdRef = useRef(`ui-preferences-${Math.random().toString(36).slice(2)}`); const [state, dispatch] = useReducer( reducer, storageKey, @@ -152,8 +160,62 @@ export function useUiPreferences(storageKey = 'uiPreferences') { } localStorage.setItem(storageKey, JSON.stringify(state)); + + window.dispatchEvent( + new CustomEvent(SYNC_EVENT, { + detail: { + storageKey, + sourceId: instanceIdRef.current, + value: state, + }, + }) + ); }, [state, storageKey]); + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + + const applyExternalUpdate = (value: unknown) => { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + return; + } + dispatch({ type: 'set_many', value: value as Partial> }); + }; + + const handleStorageChange = (event: StorageEvent) => { + if (event.key !== storageKey || event.newValue === null) { + return; + } + + try { + const parsed = JSON.parse(event.newValue); + applyExternalUpdate(parsed); + } catch { + // Ignore malformed storage updates. + } + }; + + const handleSyncEvent = (event: Event) => { + const syncEvent = event as CustomEvent; + const detail = syncEvent.detail; + if (!detail || detail.storageKey !== storageKey || detail.sourceId === instanceIdRef.current) { + return; + } + + applyExternalUpdate(detail.value); + }; + + window.addEventListener('storage', handleStorageChange); + window.addEventListener(SYNC_EVENT, handleSyncEvent as EventListener); + + return () => { + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener(SYNC_EVENT, handleSyncEvent as EventListener); + }; + }, [storageKey]); + const setPreference = (key: UiPreferenceKey, value: unknown) => { dispatch({ type: 'set', key, value }); };