Skip to content

Commit 07485b3

Browse files
committed
🤖 Remove coupling with usePersistedState internals
Address PR feedback: instead of manually duplicating localStorage write and event dispatch logic, use a new utility function updatePersistedState() that encapsulates this behavior. Changes: - Add updatePersistedState() utility to usePersistedState.ts - Enable listener in ThinkingProvider to pick up external changes - Remove setThinkingLevel() call from ChatInput toast handler - Rename event to cmux:thinkingLevelToast to clarify it's UI-only - Use updatePersistedState() in App.tsx instead of manual duplication This eliminates coupling between App.tsx and usePersistedState internals while maintaining the same functionality. Change-Id: Iee36b05db11d5e39740f31071fffb9a0e2cbb7e5 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 3299c56 commit 07485b3

File tree

4 files changed

+46
-25
lines changed

4 files changed

+46
-25
lines changed

src/App.tsx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import NewWorkspaceModal from "./components/NewWorkspaceModal";
1010
import { AIView } from "./components/AIView";
1111
import { ErrorBoundary } from "./components/ErrorBoundary";
1212
import { TipsCarousel } from "./components/TipsCarousel";
13-
import { usePersistedState } from "./hooks/usePersistedState";
13+
import { usePersistedState, updatePersistedState } from "./hooks/usePersistedState";
1414
import { matchesKeybind, KEYBINDS } from "./utils/ui/keybinds";
1515
import { useProjectManagement } from "./hooks/useProjectManagement";
1616
import { useWorkspaceManagement } from "./hooks/useWorkspaceManagement";
@@ -296,25 +296,17 @@ function AppInner() {
296296
}
297297

298298
const normalized = THINKING_LEVELS.includes(level) ? level : "off";
299-
300-
if (typeof window !== "undefined" && window.localStorage) {
301-
const key = `thinkingLevel:${workspaceId}`;
302-
try {
303-
window.localStorage.setItem(key, JSON.stringify(normalized));
304-
window.dispatchEvent(
305-
new CustomEvent(`storage-change:${key}`, {
306-
detail: { key, newValue: normalized },
307-
})
308-
);
309-
} catch (error) {
310-
console.warn("Failed to persist thinking level", error);
311-
}
312-
}
313-
299+
const key = `thinkingLevel:${workspaceId}`;
300+
301+
// Use the utility function which handles localStorage and event dispatch
302+
// ThinkingProvider will pick this up via its listener
303+
updatePersistedState(key, normalized);
304+
305+
// Dispatch toast notification event for UI feedback
314306
if (typeof window !== "undefined") {
315307
window.dispatchEvent(
316-
new CustomEvent("cmux:setThinkingLevel", {
317-
detail: { workspaceId, level: normalized, source: "command-palette" },
308+
new CustomEvent("cmux:thinkingLevelToast", {
309+
detail: { workspaceId, level: normalized },
318310
})
319311
);
320312
}

src/components/ChatInput.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
444444
return () => window.removeEventListener("cmux:openModelSelector", handler as EventListener);
445445
}, []);
446446

447-
// Allow command palette to configure thinking effort
447+
// Show toast when thinking level is changed via command palette
448448
useEffect(() => {
449449
const handler = (event: Event) => {
450450
const detail = (event as CustomEvent<{ workspaceId: string; level: ThinkingLevel }>).detail;
@@ -453,8 +453,6 @@ export const ChatInput: React.FC<ChatInputProps> = ({
453453
}
454454

455455
const level = detail.level;
456-
setThinkingLevel(level);
457-
458456
const levelDescriptions: Record<ThinkingLevel, string> = {
459457
off: "Off — fastest responses",
460458
low: "Low — adds light reasoning",
@@ -469,9 +467,9 @@ export const ChatInput: React.FC<ChatInputProps> = ({
469467
});
470468
};
471469

472-
window.addEventListener("cmux:setThinkingLevel", handler as EventListener);
473-
return () => window.removeEventListener("cmux:setThinkingLevel", handler as EventListener);
474-
}, [workspaceId, setThinkingLevel, setToast]);
470+
window.addEventListener("cmux:thinkingLevelToast", handler as EventListener);
471+
return () => window.removeEventListener("cmux:thinkingLevelToast", handler as EventListener);
472+
}, [workspaceId, setToast]);
475473

476474
// Handle command selection
477475
const handleCommandSelect = useCallback(

src/contexts/ThinkingContext.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ interface ThinkingProviderProps {
1818
export const ThinkingProvider: React.FC<ThinkingProviderProps> = ({ workspaceId, children }) => {
1919
const [thinkingLevel, setThinkingLevel] = usePersistedState<ThinkingLevel>(
2020
`thinkingLevel:${workspaceId}`,
21-
"off"
21+
"off",
22+
{ listener: true } // Listen for changes from command palette and other sources
2223
);
2324

2425
return (

src/hooks/usePersistedState.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@ import { useState, useCallback, useEffect } from "react";
33

44
type SetValue<T> = T | ((prev: T) => T);
55

6+
/**
7+
* Update a persisted state value from outside the hook.
8+
* This is useful when you need to update state from a different component/context
9+
* that doesn't have access to the setter (e.g., command palette updating workspace state).
10+
*
11+
* @param key - The same localStorage key used in usePersistedState
12+
* @param value - The new value to set
13+
*/
14+
export function updatePersistedState<T>(key: string, value: T): void {
15+
if (typeof window === "undefined" || !window.localStorage) {
16+
return;
17+
}
18+
19+
try {
20+
if (value === undefined || value === null) {
21+
window.localStorage.removeItem(key);
22+
} else {
23+
window.localStorage.setItem(key, JSON.stringify(value));
24+
}
25+
26+
// Dispatch custom event for same-tab synchronization
27+
const customEvent = new CustomEvent(`storage-change:${key}`, {
28+
detail: { key, newValue: value },
29+
});
30+
window.dispatchEvent(customEvent);
31+
} catch (error) {
32+
console.warn(`Error writing to localStorage key "${key}":`, error);
33+
}
34+
}
35+
636
interface UsePersistedStateOptions {
737
/** Enable listening to storage changes from other components/tabs */
838
listener?: boolean;

0 commit comments

Comments
 (0)