Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/Sidebar.tsx — The new thread-jump hint call passes only context: { terminalFocus: isTerminalFocused() }, omitting terminalOpen and modelPickerOpen that the component's own keydown dispatcher (lines 3261-3268) supplies. Default keybindings bind modelPicker.jump.N to mod+N with when modelPickerOpen, so with the model picker open the hint layer still advertises ⌘1..9 badges while those keys select a model — the same "hint promises a shortcut that does something else" defect this PR fixes for the terminal. LegacySidebar.tsx avoids the divergence by feeding one shared sidebarShortcutContext to both paths. Inline comment posted with a self-contained suggested fix.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Verification performed:
- Traced
shouldShowThreadJumpHintsForModifiers/findEffectiveShortcutForCommand/resolveContextinapps/web/src/keybindings.tsto confirm that omitted context keys default tofalse, sowhen-scoped bindings that shadowmod+1..9are ignored during hint computation. - Confirmed shipped defaults in
packages/shared/src/keybindings.tsregistermodelPicker.jump.Nonmod+${N}withwhen: "modelPickerOpen", and that these entries come after thethread.jump.*defaults, so they win the conflict claim only whenmodelPickerOpenis true. - Compared both call sites:
Sidebar.tsxkeydown handler (full context) vs. new hint call (partial context);LegacySidebar.tsxbuildssidebarShortcutContextonce withterminalFocus/terminalOpen/modelPickerOpenand reuses it. - Checked reactivity of the new render-time
isTerminalFocused()read:useShortcutModifierStateinstalls capture-phase window keydown/keyup listeners, so focus state is re-read on each modifier transition (including while xterm.js has focus). No finding there. - The new test in
apps/web/src/keybindings.test.tscorrectly covers both terminal-focus branches; no styling, Tailwind, CSS-ownership, scroll/mask, or shared-primitive changes are present in this diff.
🔍 Agent activity — 1 tool call
💭 I need to verify that
isTerminalFocusedis properly defined and imported in both sidebar components, and confirm the context option structure is correct. I need to examine the keybindings file around lines 100-330 and how it relates to the sidebar, then check that LegacySidebar line where terminal focus is being set inside what might be a memoized object—if it's being called during render without triggering a re-render when focus changes, that could explain why the hints aren't updating properly. This is a behavioral change affecting keyboard handling, not UI styling, so it falls outside the scope of the UI consistency check even though the modified files are TypeScript/TSX. The real issue is thatisTerminalFocused()gets called during render without reactivity, which means if terminal focus changes between renders, the hint visibility could become stale and out of sync. However, since hints only display when a modifier key is held (which triggers a re-render through theshortcutModifiersstate), this might not be a practical problem. I need to trace through Sidebar.tsx around line 3300 to understand how the focus state is computed and whether focus changes actually trigger re-renders. I should also check whethersidebarShortcutContextis used elsewhere in LegacySidebar beyond just the hints at line 3414—particularly in functions likebuildThreadJumpLabelMapor command resolution withwhenclauses—since the change from hardcodedterminalFocus: falseto the actual focus value could affect behavior in those places too. I'm noticing that the render-time context in Sidebar.tsx now only includesterminalFocus, whileterminalOpenandmodelPickerOpenare missing—even though they're present in the keydown handler context. This matters becausefindEffectiveShortcutForCommanduses context to evaluatewhenclauses, so if thread.jump bindings have conditions like!terminalOpen, the hints might display incorrectly when the terminal is actually open. Though this is technically a pre-existing issue since context wasn't being passed at all before the diff. But LegacySidebar does pass the full context includingterminalOpen, so there's now an inconsistency between the two versions. The PR modified that exact line and could have includedterminalOpen—this feels like a potential regression. I should check whether the default keybindings for thread.jump actually havewhenclauses that referenceterminalOpen. Looking at whether there's an actual regression here: the main issue is thatisTerminalFocused()gets called during render but isn't reactive. TheshortcutModifiersstate fromuseShortcutModifierState()does trigger re-renders when modifier keys change, so hints correctly hide when Cmd is held while chat is focused. But if the user clicks into the terminal while still holding Cmd, there's no re-render to update the hints visibility since the focus change itself doesn't trigger a subscription. There's also a potential mismatch: Sidebar v2 dropsterminalOpenandmodelPickerOpenfrom the hint context, but the keydown handler and LegacySidebar include them. If any jump bindings havewhenclauses referencingterminalOpen, the hints and actual behavior would diverge. I need to check the terminalFocus implementation and the default keybindings to see if this is actually a problem. I'm checking whetherbuildThreadJumpLabelMapin LegacySidebar needs to account forterminalFocusin addition toterminalOpen, since hints visibility is now controlled by that flag. ThethreadJumpLabelByKeyfunction currently usesbuildThreadJumpLabelMapwith onlyterminalOpen, which might be an inconsistency I need to address in the Sidebar component. The check is really about behavioral constraints and primitives rather than UI consistency, so this diff doesn't fit the intended scope. I'm considering whether the actual bug is thatshouldShowThreadJumpHintsForModifiershardcodes the terminalFocus flag in a generic keybinding helper instead of lettingwhenclauses handle it, though the comment does explain the reasoning. The context defaults and wrapper functions seem fine, so I'm looking at the LegacySidebar change around line 3400 as another possible issue. Actually, I think I've found it — in LegacySidebar,terminalFocuswas previously set to false during render-time context, which affects howfindEffectiveShortcutForCommandresolves bindings for labels. If terminalFocus were true, terminal-scoped bindings would become active and could shadow the thread.jump shortcut, resulting in no hint. This change might be the intended bug, but I need to check if the original code had a comment explaining why terminalFocus was false there. ThesidebarShortcutContextin LegacySidebar is only referenced in a couple of places for terminal state checks, so the earlier change toterminalFocusis actually necessary since the helper function now reads that value from context to gate the behavior properly. There's a potential reactivity issue though:isTerminalFocused()readsdocument.activeElementduring render, which is impure and could behave inconsistently with React 18's concurrent rendering. More importantly, if the user holds Cmd while the terminal closes and focus shifts back to the composer, the hints would stay hidden until the modifier state changes. I should check if there's already a reactive hook likeuseTerminalFocusor a focus store that other components use—if so, callingisTerminalFocused()during render would be inconsistent with the codebase pattern. I need to examine how shortcutModifierState.ts handles state updates—specifically whether it's only responding to keyboard events or if it also accounts for blur and focus scenarios. The keyboard listeners are attached to the window with capture enabled, so they fire regardless of where focus is, and each modifier press checks the terminal focus state fresh. But there's a mismatch: the Sidebar's hint context only passesterminalFocus, while the keydown handler also includesterminalOpenandmodelPickerOpen, creating an inconsistency between what determines hint eligibility and what actually gets dispatched. This means hints can advertise shortcuts that are shadowed by custom bindings scoped to conditions likewhen terminalOpen, since the hint layer computes with incomplete context and doesn't see those conditions. The fix is to extract a shared context builder that both the keydown handler and hint computation use, ensuring they evaluate the same set of conditions when determining which shortcuts are available. But there's also a concrete issue with modelPickerOpen: if the default keybindings include model picker jump commands on mod+1..9 withwhen modelPickerOpen, then the sidebar hints would still show thread jump hints even when the model picker is open and those shortcuts are actually shadowed. LegacySidebar already accounts for this by including modelPickerOpen in its context, so I should check whether the defaults actually have these bindings. I need to check the server defaults file to verify the keybinding configuration for the model picker, specifically themodelPicker.jump.Ncommand onmod+Nand its conditional clause. So the issue is that the default keybindings for the model picker are registered after the thread ones in the array, and when iterating from the end to find effective shortcuts,mod+1gets claimed bymodelPicker.jump.1when the picker is open, leavingthread.jump.1without an effective binding and hiding its hints. The fix involved updating the Sidebar component to pass the terminal focus context alongside the platform information. However, the patch still omitsmodelPickerOpenfrom that context object, so even with the model picker open and Cmd held, the sidebar still shows thread jump hints when it shouldn't — this is the exact bug pattern being addressed. Before the diff, the context was empty too, so this was pre-existing, but the patch directly modifies that line and introduces partial context while the sibling keydown handler builds the full context correctly. This is a consistency issue worth flagging: the hint layer and dispatch layer are diverging, whereas LegacySidebar handles it properly. I should also check whetherrouteTerminalOpencreates any conflicting thread.jump bindings when the terminal is open but not focused. During a React render triggered by something else while modifiers are held, the value could flip, but that's actually the desired freshness. I'll add an inline comment on the Sidebar.tsx suggestion to include terminalOpen and modelPickerOpen in the context object, matching what the dispatch context and LegacySidebar use — both imports are already available in the file.
1. modify_pr — One finding: the new jump-hint context in Sidebar.tsx is partial, so hints can still advertise ⌘1..9 while the default modelPicker.jump.* bindings own those keys. See the inline comment.
Agent Credits: 96 credits