Skip to content

Flash manual unread workspace jumps - #4436

Merged
lawrencecchen merged 29 commits into
mainfrom
issue-unread-flash-workspace-switch
May 26, 2026
Merged

lawrencecchen merged 29 commits into
mainfrom
issue-unread-flash-workspace-switch

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a regression test for Cmd+Shift+U after marking a workspace unread and switching away.
  • Emit the unread-dismiss flash before clearing workspace-level unread state on jump.

Verification

  • Red: focused regression failed on the test-only commit with missing flash token, panel id, and unread-dismiss reason.
  • Green: xcodebuild -quiet -project cmux.xcodeproj -scheme cmux-unit -configuration Debug -destination "platform=macOS,arch=arm64" -derivedDataPath /tmp/cmux-unread-green "-only-testing:cmuxTests/WorkspaceManualUnreadTests/testJumpToLatestManualWorkspaceUnreadFlashesAfterSwitchingAway" test
  • Tagged reload: ./scripts/reload.sh --tag unreadflash

Notes

  • Running all WorkspaceManualUnreadTests still fails on existing session-restore tests, including testSessionRestorePreservesNotificationUnreadIndicator. That reproduces when the restore test is run by itself, outside this shortcut regression.

View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Note

Medium Risk
Changes span AppDelegate navigation, notification store state, workspace unread badges, overlay animation, and global shortcut matching—user-visible behavior with broad test coverage but non-trivial interaction edge cases.

Overview
This PR tightens unread navigation and visual feedback across workspaces and panels, and fixes keyboard shortcut matching for overlapping ⌘ bindings.

Unread jumps and flashes: jumpToLatestUnread can exclude a workspace (and its notifications) so “mark oldest and jump next” does not immediately reopen the tab you just marked. When opening unread via jump, the app can fire an unread-dismiss flash before clearing manual/restored unread state. The tmux pane overlay tracks flash tokens per workspace, animates on first-seen tokens, replays after switching back, and waits for a flashRect before consuming a token.

Panel vs workspace unread: Toggle/mark flows operate on the focused panel when applicable (notifications, focused-read indicators, restored unread, workspace-only restored state). The notification store clears focused-read indicators on mark-read, treats visible indicators with optional surfaceId correctly, and avoids creating workspace manual unread for missing panel notifications. Workspace/panel restored-unread ownership is refined when clearing panel read state.

Shortcuts: StoredShortcut matching prefers printable event characters for ⌘ shortcuts over physical key fallbacks, while preserving sensible ⌘⌃ letter fallbacks and preventing ⌘⌃ punctuation from stealing letter shortcuts.

Extensive unit tests cover overlay flash timing, unread jump exclusions, panel toggles, and shortcut collisions.

Reviewed by Cursor Bugbot for commit 40b4861. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes missing unread-dismiss flashes and makes unread navigation more predictable. Flashes now animate on first-seen tokens and after workspace switches, and unread toggles act on the focused panel.

  • Bug Fixes
    • Emit unreadIndicatorDismiss before clearing manual/restored unread; add a store-backed fallback and prevent duplicate restored-unread flashes.
    • Overlay: track flash tokens per workspace, animate on the first observed token and when switching back, and wait for a flashRect before consuming a token.
    • Navigation: toggle unread on the focused panel, keep that marker after focus moves, clear focused-read indicators, detect visible notification indicators correctly, avoid duplicating workspace/panel unread when marking oldest, scope duplicate checks to the focused target, exclude the current workspace (and its notifications) when jumping next, and do not create workspace unread for missing panel notifications.
    • Restored unread: clear workspace restored-unread only when the cleared panel contributed; preserve visual-only indicators and keep panel-derived workspace unread in sync.
    • Shortcuts: prefer printable characters for ⌘ shortcuts over physical fallbacks; keep ⌘⌃ fallbacks for letters; prevent ⌘⌃ punctuation from stealing printable letter bindings.

Written for commit 40b4861. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Jump-to-latest-unread can exclude a workspace, avoids reopening the excluded workspace, and triggers a brief unread-dismiss flash when appropriate.
    • Per-workspace flash tracking so flashes replay correctly when returning to a workspace.
    • Mark-and-jump flow skips newly marked manual-unread workspaces when selecting the next target.
    • Shortcut matching now prefers printable characters and refines command/command-control behavior.
  • Tests

    • Added tests for unread-jump exclusions, manual/restored unread clearing and flash behavior, workspace flash replay, and shortcut matching scenarios.

Review Change Stack

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented May 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 26, 2026 3:44pm
cmux-staging Building Building Preview, Comment May 26, 2026 3:44pm

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 20, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Jump-to-latest-unread accepts an excluding workspace ID, may emit a manual-unread dismiss flash before clearing unread state, and avoids re-opening the same workspace; the tmux overlay now tracks last flash tokens per workspace. Tests validate jump exclusion, manual/restored unread flashes, overlay flash timing, and shortcut matching edge cases.

Changes

Jump-to-unread indicator flash + overlay token tracking

Layer / File(s) Summary
Unread jump selection and dismiss-flash
Sources/AppDelegate.swift
jumpToLatestUnread gains excludingWorkspaceId; candidate selection and openLatestWorkspaceUnread exclude that workspace; FocusedNotificationMarkResult.markedWorkspaceWithoutNotification now carries a UUID tabId and callers use it to call jumpToLatestUnread(excludingWorkspaceId:); clearWorkspaceUnreadAfterJump may call workspace.triggerUnreadIndicatorDismissFlash(panelId:) when shouldTriggerManualUnreadJumpFlash is true, then clears unread state.
Per-workspace flash token tracking
Sources/WorkspaceContentView.swift
TmuxWorkspacePaneOverlayModel adds currentWorkspaceId and lastFlashTokenByWorkspaceId: [UUID: UInt64]; apply detects workspace-scoped token changes to set/clear flashStartedAt and records per-workspace tokens; clear resets the new state.
Test: overlay flash animation across workspace switch
cmuxTests/WindowAndDragTests.swift
New test testTmuxWorkspacePaneOverlayModelAnimatesFlashAfterWorkspaceSwitchBackWhenTokenChanges drives apply across two workspace IDs and asserts flashStartedAt and flashReason update only when returning with a new token.
Test: mark-oldest unread excludes newly-created manual unread
cmuxTests/WorkspaceManualUnreadTests.swift
New testMarkOldestUnreadAndJumpNextExcludesNewManualWorkspaceUnread verifies marking oldest-unread and jumping next excludes newly-created manual-unread workspaces from being selected as the oldest unread.
Test: jump-to-latest unread excluding current workspace
cmuxTests/WorkspaceManualUnreadTests.swift
New testJumpToLatestUnreadExcludesNotificationsFromExcludedWorkspace asserts jumpToLatestUnread(excludingWorkspaceId:) ignores notifications from the excluded workspace and opens a notification from another workspace.
Test: manual unread dismissal flash after switching away
cmuxTests/WorkspaceManualUnreadTests.swift
New testJumpToLatestManualWorkspaceUnreadFlashesAfterSwitchingAway enables experiment, marks workspace manual unread, switches away, calls jumpToLatestUnread(), and asserts reselection, manual-unread cleared, and tmux flash metadata (token increment, panel id, reason .unreadIndicatorDismiss).
Test: restored unread flashes once on jump
cmuxTests/WorkspaceManualUnreadTests.swift
New testJumpToLatestRestoredWorkspaceUnreadFlashesOnceAfterSwitchingAway restores an unread indicator, switches away, jumps back, and asserts restored unread cleared and tmux flash metadata set exactly once.

Shortcut matching refinements

Layer / File(s) Summary
Printable-character gating and fallback logic
Sources/KeyboardShortcutSettings.swift
ShortcutStroke.matches now checks eventCharactersArePrintableASCII and uses a commandPrintableCharacterShouldBlockFallback predicate to decide when command shortcuts should block layout fallback; number key rejection moved earlier.
Shortcut matching tests
cmuxTests/WorkspaceUnitTests.swift
Three tests added to cover printable eventCharacter preference, command-control layout fallback, and punctuation vs printable-letter matching edge cases.

Sequence Diagram

sequenceDiagram
  participant AppDelegate
  participant NotificationStore
  participant Workspace
  AppDelegate->>NotificationStore: select unread candidates (filter excludingNotificationId, excludingWorkspaceId)
  AppDelegate->>Workspace: determine shouldTriggerManualUnreadJumpFlash(panelId)
  AppDelegate->>NotificationStore: check manual/restored unread for tabId
  AppDelegate->>Workspace: triggerUnreadIndicatorDismissFlash(panelId)
  AppDelegate->>Workspace: clearUnreadAfterJump(panelId)
  AppDelegate->>Workspace: openLatestWorkspaceUnread(selectedPanelId)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#4371: Related unread-indicator dismissal/refactor touching unread dismiss flow and flash reasons.

Poem

🐰 I hopped to the unread with a curious dash,
Panels blinked once in a neat little flash,
Tokens now whisper per workspace and name,
I cleared and I bounded and brought focus to frame. ✨


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning PR description lacks key sections from template: 'Demo Video' (required for behavior changes), 'Review Trigger' block, and incomplete 'Checklist' (all items unchecked). 'Testing' section is minimal and uses 'Verification' instead of explicit testing steps. Add missing sections: include demo video/link for UI/behavior changes, copy the Review Trigger block, complete checklist items, and expand 'Testing' with explicit manual verification steps.
Cmux Architecture Rethink ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Flash manual unread workspace jumps' accurately describes the main feature—flashing unread indicators when jumping to manual unread workspaces, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed All production code changes maintain Swift 6 actor isolation: @MainActor-isolated classes, Sendable value structs, and properly marked Codable types with no background context access.
Cmux Swift Blocking Runtime ✅ Passed No blocking/timing synchronization primitives found. Flash timing uses Date callbacks, not Task.sleep. All changes are state management and filtering logic.
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift and test files; check applies exclusively to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts, which are out of scope.
Cmux Swift Concurrency ✅ Passed PR adds synchronous navigation and flash-token tracking code. No new background Dispatch queues, Combine app state, completion handlers, or fire-and-forget Tasks detected in production code.
Cmux Swift @Concurrent ✅ Passed All modified Swift functions are synchronous with correct @MainActor isolation. No invalid @concurrent usage. No missing @concurrent on async work. All work is UI-bound and lightweight.
Cmux Swift File And Package Boundaries ✅ Passed PR touches existing oversized files (AppDelegate +26, WorkspaceContentView +1 net lines) only incidentally with focused bug fixes. No mixed responsibilities or extractable logic detected.
Cmux Swift Logging ✅ Passed No logging violations found. Modified production functions contain no print/NSLog calls; only DEBUG-guarded test helper writeJumpUnreadTestData present.
Cmux User-Facing Error Privacy ✅ Passed PR contains only internal logic changes (unread navigation, overlay animation, shortcut matching) with no user-facing error messages, alerts, or recovery text that could expose sensitive information.
Cmux Full Internationalization ✅ Passed No new user-facing text was added; all changes are internal logic and state management. No localization APIs needed; no xcstrings modifications required.
Cmux Swiftui State Layout ✅ Passed Existing legacy ObservableObject state only modified. No new ObservableObject, GeometryReader, lazy-list store references, or render-time mutations from body.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR modifies unread navigation logic and flash tracking; no new user-visible windows created. Single NSWindow in debugInjectWindowContextKeyMismatch is #if DEBUG test-only fixture.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-unread-flash-workspace-switch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

@greptile-apps

greptile-apps Bot commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes missing unread-dismiss flashes on workspace jumps and tightens panel-level unread toggling, navigation exclusion, and shortcut matching.

  • Flash emission: clearWorkspaceUnreadAfterJump now emits triggerUnreadIndicatorDismissFlash before clearing state when manual or restored unread is present; TmuxWorkspacePaneOverlayModel tracks flash tokens per workspace so flashes replay correctly on workspace switch-back and defers consuming a token until a flashRect is available.
  • Navigation exclusion: jumpToLatestUnread and openLatestWorkspaceUnread accept an excludingWorkspaceId so markFocusedNotificationAsOldestUnreadAndJumpToNextLatestUnread skips the just-marked workspace (and its notifications); markLatestNotificationAsOldestUnread now guards the workspace-level fallback behind surfaceId == nil to prevent phantom workspace unread when a panel-specific call finds no matching notification.
  • Toggle & store fixes: toggleFocusedNotificationUnread now targets the focused panel first (manual, restored, focused-read, and workspace-representative indicators), hasVisibleNotificationIndicator is corrected so a missing dict entry does not spuriously match a nil surfaceId, and markRead clears focusedReadIndicator state it previously left behind.

Confidence Score: 5/5

Safe to merge — all three bug fixes (nil-surfaceId false-positive, phantom workspace unread on panel mark, missing unread-dismiss flash) are straightforward and each is pinned by a targeted regression test.

The production changes are incremental corrections to well-understood state machines, not rewrites. The flash-token-per-workspace model is the most structurally new piece, but its four unit tests cover the exact deferred-rect and switch-back scenarios. The shortcut guard is narrowly scoped and covered by four cases. No confirmed behavioural regressions were found across the diff.

AppDelegate.swift carries the most complexity — the multi-condition toggleFocusedNotificationUnread panel-target routing is dense and the combined case (workspace-level notification coexisting with panel manual unread) has no dedicated test, though each individual path is tested.

Important Files Changed

Filename Overview
Sources/AppDelegate.swift Core unread-jump and toggle logic; adds workspace exclusion, panel-target routing, and the flash-before-clear pattern — complex but well-tested
Sources/TerminalNotificationStore.swift Fixes nil-surfaceId false-positive in hasVisibleNotificationIndicator, clears focusedReadIndicator on markRead, and guards workspace-level manual-unread fallback behind surfaceId==nil
Sources/WorkspaceContentView.swift Replaces single-workspace flash token with per-workspace dictionary; defers consuming a token until flashRect is present; well covered by four new model tests
Sources/Workspace.swift Extracts hasWorkspaceContributingRestoredUnreadIndicator computed var; markPanelRead now conditionally clears the workspace-level restored-unread indicator only when the last contributing panel is cleared
Sources/KeyboardShortcutSettings.swift commandPrintableCharacterShouldBlockFallback now blocks ⌘⌃ punctuation from stealing a printable-letter event while preserving ⌘⌃ letter layout-fallback; covered by four new shortcut tests
cmuxTests/WorkspaceManualUnreadTests.swift Large test expansion covering panel toggle, focused-read-indicator clearing, restored-unread clearing, workspace-exclusion on jump, and the new manual-unread flash regression
cmuxTests/WindowAndDragTests.swift Four new overlay model tests covering per-workspace flash replay, first-observed-token animation, and deferred-rect token consumption
cmuxTests/WorkspaceUnitTests.swift Four new shortcut matching tests covering printable-letter priority, ⌘⌃ letter layout fallback, and ⌘⌃ punctuation non-steal scenarios

Sequence Diagram

sequenceDiagram
    participant U as User
    participant AD as AppDelegate
    participant NS as NotificationStore
    participant WS as Workspace
    participant OV as OverlayModel

    U->>AD: markFocusedNotificationAsOldestUnreadAndJumpToNextLatestUnread
    AD->>AD: markFocusedNotificationAsOldestUnread
    alt panel target exists
        AD->>WS: markPanelUnread(panelId)
        AD-->>AD: .markedWorkspaceWithoutNotification(tabId)
    else no panel target
        AD->>NS: markUnread(forTabId:)
        AD-->>AD: .markedWorkspaceWithoutNotification(tabId)
    end
    AD->>AD: jumpToLatestUnread(excludingWorkspaceId: tabId)
    AD->>NS: notifications filtered by excludingWorkspaceId
    AD->>AD: openTerminalNotification / openLatestWorkspaceUnread
    AD->>AD: clearWorkspaceUnreadAfterJump(workspace, panelId)
    alt shouldTriggerManualUnreadJumpFlash
        AD->>WS: triggerUnreadIndicatorDismissFlash(panelId)
        WS-->>NS: flash token incremented
    end
    AD->>WS: clearUnreadAfterJump(panelId)
    Note over OV: apply() sees new flashToken per workspaceId
    OV->>OV: "didChangeFlashToken and flashRect present: flashStartedAt = now()"
Loading

Reviews (20): Last reviewed commit: "fix: scope unread duplicate checks to fo..." | Re-trigger Greptile

Comment thread Sources/AppDelegate.swift
Comment thread Sources/AppDelegate.swift

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/AppDelegate.swift Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes May 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/AppDelegate.swift (1)

10909-10932: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor excludingWorkspaceId during notification candidate selection too

jumpToLatestUnread excludes excludedWorkspaceId only in the workspace-level fallback (openLatestWorkspaceUnread). The notification-first path still allows unread notifications from that same workspace, so .markedWorkspaceWithoutNotification(tabId) can still jump back into the excluded workspace instead of advancing to the next one.

🤖 Prompt for 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.

In `@Sources/AppDelegate.swift` around lines 10909 - 10932, jumpToLatestUnread
currently only applies excludedWorkspaceId in the workspace fallback, so the
notification-first loop can return a notification from the excluded workspace;
update the notification selection to skip notifications belonging to the
excluded workspace. In the for-loop inside jumpToLatestUnread (iterating
notificationStore.notifications and using Self.shouldOpenFromJumpToLatestUnread
and openTerminalNotification), add a check that notification.workspaceId !=
excludedWorkspaceId (or pass excludedWorkspaceId into the predicate if you
prefer) so notifications from the excluded workspace are not considered before
falling back to openLatestWorkspaceUnread.
🤖 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 `@Sources/WorkspaceContentView.swift`:
- Around line 122-133: The code records state.flashToken into
lastFlashTokenByWorkspaceId[state.workspaceId] even when state.flashRect is nil,
which consumes the token before a rect exists and prevents a later non-nil rect
with the same token from animating; change the logic in the block that updates
lastFlashTokenByWorkspaceId (around currentWorkspaceId,
lastFlashTokenByWorkspaceId, state.flashToken, state.flashRect, and
flashStartedAt) so you only set lastFlashTokenByWorkspaceId[state.workspaceId] =
state.flashToken after confirming state.flashRect is non-nil (i.e., preserve the
previous token when flashRect is nil), and keep the existing flashStartedAt and
didChangeWorkspace checks intact.

---

Outside diff comments:
In `@Sources/AppDelegate.swift`:
- Around line 10909-10932: jumpToLatestUnread currently only applies
excludedWorkspaceId in the workspace fallback, so the notification-first loop
can return a notification from the excluded workspace; update the notification
selection to skip notifications belonging to the excluded workspace. In the
for-loop inside jumpToLatestUnread (iterating notificationStore.notifications
and using Self.shouldOpenFromJumpToLatestUnread and openTerminalNotification),
add a check that notification.workspaceId != excludedWorkspaceId (or pass
excludedWorkspaceId into the predicate if you prefer) so notifications from the
excluded workspace are not considered before falling back to
openLatestWorkspaceUnread.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a7646cbc-d059-4e42-ae08-fc9691f36bb2

📥 Commits

Reviewing files that changed from the base of the PR and between 6bef7b4 and 5b71491.

📒 Files selected for processing (4)
  • Sources/AppDelegate.swift
  • Sources/WorkspaceContentView.swift
  • cmuxTests/WindowAndDragTests.swift
  • cmuxTests/WorkspaceManualUnreadTests.swift

Comment thread Sources/WorkspaceContentView.swift
Comment thread Sources/AppDelegate.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@Sources/KeyboardShortcutSettings.swift`:
- Around line 1516-1518: The nil-coalescing default for
eventCharsArePrintableASCII is misleading; update the expression or clarify
intent: either change the fallback from "?? true" to "?? false" to be
conservative, or add a short comment near the declaration of
eventCharsArePrintableASCII (and referencing hasEventChars) stating the fallback
is never reached because hasEventChars guards its usage. Locate the
eventCharsArePrintableASCII variable and the hasEventChars usage to apply the
change or add the explanatory comment.
- Around line 1523-1526: The predicate
commandPrintableCharacterShouldBlockFallback needs to skip blocking when Control
is held; update its condition to include a Control-key check (e.g., require
!flags.contains(.control)) so that when both Command and Control are pressed the
logic does not short-circuit and allows the ANSI keyCode fallback path (which
already checks flags.contains(.control)). Modify the boolean expression around
commandPrintableCharacterShouldBlockFallback (which uses
flags.contains(.command), hasEventChars, eventCharsArePrintableASCII,
shortcutKeyIsLetter or eventCharacterIsLetterOrNumber) to also ensure Control is
not present.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5587bc46-0bbe-4ae6-937c-f116a25552ba

📥 Commits

Reviewing files that changed from the base of the PR and between b71645f and ded6549.

📒 Files selected for processing (2)
  • Sources/KeyboardShortcutSettings.swift
  • cmuxTests/WorkspaceUnitTests.swift

Comment thread Sources/KeyboardShortcutSettings.swift
Comment thread Sources/KeyboardShortcutSettings.swift
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lawrencecchen
lawrencecchen dismissed coderabbitai[bot]’s stale review May 26, 2026 08:31

False positive after verification. A blanket !control carve-out would fail testCommandControlPunctuationDoesNotStealPrintableLetterShortcut and reintroduce the reported printable-u fallback steal. The current matcher still permits Command-Control fallback for control-character events, covered by testCommandControlLetterCanUseLayoutFallbackForControlCharacter. Replied on the review thread.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e655991. Configure here.

Comment thread Sources/KeyboardShortcutSettings.swift
Comment thread Sources/KeyboardShortcutSettings.swift
@lawrencecchen
lawrencecchen merged commit 26cf12e into main May 26, 2026
19 checks passed
@lawrencecchen
lawrencecchen deleted the issue-unread-flash-workspace-switch branch May 26, 2026 16:14

This branch was successfully deployed

1 active deployment
Preview – cmux — 40b4861d Deployed May 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant