fix(desktop): keep global shortcuts alive when focus leaves the composer - #55
Conversation
Global keymap shortcuts are matched by Flutter's Shortcuts widget, which only fires while the primary focus is a descendant of the scope (key events bubble up the focus tree). Once focus drained to a bare focus scope — after clicking an empty region or dismissing a dialog — every global shortcut went dead until the user clicked back into the composer. DesktopKeymapScope now owns a fallback FocusNode and reclaims focus whenever it goes idle (primary focus is null or a FocusScopeNode), so global shortcuts work regardless of where focus sits. Leaf widgets (text fields, buttons) keep focus untouched; only the dead state is reclaimed. Composer-scope actions (send / Shift+Enter newline) remain local to the text field, unchanged. Adds a regression test proving Ctrl+B toggles the sidebar with nothing focused.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthrough
ChangesDesktop keymap focus retention
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant FocusManager
participant DesktopKeymapScope
participant ScopeFocus
participant KeyboardShortcut
FocusManager->>DesktopKeymapScope: notify focus becomes idle or external
DesktopKeymapScope->>ScopeFocus: request focus
KeyboardShortcut->>DesktopKeymapScope: dispatch Ctrl+B
DesktopKeymapScope->>DesktopKeymapScope: toggle sidebar state
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/lib/desktop/chat/keymap_scope.dart`:
- Around line 64-70: Update _reclaimFocusWhenIdle so it only reclaims focus when
primaryFocus is null, the root/empty scope, or a FocusScopeNode belonging to the
current chat subtree; do not treat every FocusScopeNode as idle. Preserve the
existing mounted, canRequestFocus, and hasPrimaryFocus checks, and add a
regression test covering navigation to Settings and opening a dialog/route
without focus returning to the hidden chat shell.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9217b5a4-57f5-400e-9b19-79a27393a4df
📒 Files selected for processing (2)
app/lib/desktop/chat/keymap_scope.dartapp/test/desktop/keymap_scope_test.dart
…utes - Only reclaim focus when primary focus is truly idle: null, root scope, empty FocusScopeNode (no focused children), or a descendant of our scope. - Add _isEmptyFocusScope() to check if a FocusScopeNode has no focused children. - Add _isDescendantOfScopeFocus() to distinguish internal idle scopes from external scopes (dialogs, routes) that should retain focus. - Fixes issue where global shortcuts would steal focus from open dialogs/routes.
Correct keymap scope resolution in desktop chat and update the corresponding test.
Global keymap shortcuts are matched by Flutter's Shortcuts widget, which only fires while the primary focus is a descendant of the scope (key events bubble up the focus tree). Once focus drained to a bare focus scope — after clicking an empty region or dismissing a dialog — every global shortcut went dead until the user clicked back into the composer.
DesktopKeymapScope now owns a fallback FocusNode and reclaims focus whenever it goes idle (primary focus is null or a FocusScopeNode), so global shortcuts work regardless of where focus sits. Leaf widgets (text fields, buttons) keep focus untouched; only the dead state is reclaimed. Composer-scope actions (send / Shift+Enter newline) remain local to the text field, unchanged.
Adds a regression test proving Ctrl+B toggles the sidebar with nothing focused.
What & why
How it was tested
cd server && pnpm test && pnpm typecheck(if server touched)cd app && flutter test && flutter analyze(if app touched)Checklist
CONTRIBUTING.md.Summary by CodeRabbit