Skip to content

Commit dbd7836

Browse files
authored
🤖 Fix intermittent Cmd+Shift+T keybind failure (#254)
## Problem The Cmd+Shift+T keybind for toggling thinking would fail in new chats (before any messages were sent). User would press the key and nothing would happen, even though they could see a model selected in the UI. **Root cause**: State source mismatch between what the user sees and what the keybind checks: - **UI shows**: `selectedModel` from localStorage (always available, shown in bottom-left selector) - **Keybind checked**: `currentModel` from message history (null until first message sent) User sees "opus" selected, presses Cmd+Shift+T, nothing happens because the keybind was checking message history instead of the selected model. ## Solution Read the selected model from localStorage (using `getModelKey(workspaceId)`) and fall back to message history model if not set: ```typescript const selectedModel = readPersistedState<string | null>(getModelKey(workspaceId), null); const modelToUse = selectedModel ?? currentModel; ``` This matches what the user sees in the UI, so the keybind now works immediately in new chats. ## Testing - ✅ All 502 unit tests pass - ✅ TypeScript compilation passes - ✅ Lint and format checks pass - 🧪 Manual testing: Open new workspace, verify Cmd+Shift+T toggles thinking before sending first message ## Changes - **Net change**: +5 lines (adds localStorage read, updates comments) - **Behavior**: Keybind now works in new chats (matches user's mental model) - **Fallback**: Still uses message history model if localStorage is empty (graceful degradation) _Generated with `cmux`_
1 parent 8e4c425 commit dbd7836

File tree

1 file changed

+9
-7
lines changed

1 file changed

+9
-7
lines changed

src/hooks/useAIViewKeybinds.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { useEffect } from "react";
22
import type { ChatInputAPI } from "@/components/ChatInput";
33
import { matchesKeybind, KEYBINDS, isEditableElement } from "@/utils/ui/keybinds";
4-
import { getLastThinkingByModelKey } from "@/constants/storage";
4+
import { getLastThinkingByModelKey, getModelKey } from "@/constants/storage";
55
import { updatePersistedState, readPersistedState } from "@/hooks/usePersistedState";
66
import type { ThinkingLevel, ThinkingLevelOn } from "@/types/thinking";
77
import { DEFAULT_THINKING_LEVEL } from "@/types/thinking";
88
import { getThinkingPolicyForModel } from "@/utils/thinking/policy";
9+
import { defaultModel } from "@/utils/ai/models";
910

1011
interface UseAIViewKeybindsParams {
1112
workspaceId: string;
@@ -64,17 +65,18 @@ export function useAIViewKeybinds({
6465
if (matchesKeybind(e, KEYBINDS.TOGGLE_THINKING)) {
6566
e.preventDefault();
6667

67-
// Skip if no model set (workspace has no messages yet)
68-
if (!currentModel) {
69-
return;
70-
}
68+
// Get selected model from localStorage (what user sees in UI)
69+
// Fall back to message history model, then to default model
70+
// This matches the same logic as useSendMessageOptions
71+
const selectedModel = readPersistedState<string | null>(getModelKey(workspaceId), null);
72+
const modelToUse = selectedModel ?? currentModel ?? defaultModel;
7173

7274
// Storage key for remembering this model's last-used active thinking level
73-
const lastThinkingKey = getLastThinkingByModelKey(currentModel);
75+
const lastThinkingKey = getLastThinkingByModelKey(modelToUse);
7476

7577
// Special-case: if model has single-option policy (e.g., gpt-5-pro only supports HIGH),
7678
// the toggle is a no-op to avoid confusing state transitions.
77-
const allowed = getThinkingPolicyForModel(currentModel);
79+
const allowed = getThinkingPolicyForModel(modelToUse);
7880
if (allowed.length === 1) {
7981
return; // No toggle for single-option policies
8082
}

0 commit comments

Comments
 (0)