Skip to content

fix: show 'Open as Pane' for Feed mode and render it as a detached pane - #5105

Closed
austinywang wants to merge 25 commits into
mainfrom
fix-feed-open-as-pane
Closed

austinywang wants to merge 25 commits into
mainfrom
fix-feed-open-as-pane

Conversation

@austinywang

@austinywang austinywang commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem

In the right-sidebar mode-switcher header, the trailing "Open as Pane" icon (rectangle.split.2x1) is shown for Files / Find / Vault but disappears when Feed is selected. Reported as a bug — the icon should be present in Feed mode too.

Root cause

"Can this mode open as a pane" was a hand-maintained whitelist:

static let paneModes: [RightSidebarMode] = [.files, .find, .sessions]
var canOpenAsPane: Bool { Self.paneModes.contains(self) }

.feed was never added, so the header button (if mode.canOpenAsPane) was hidden in Feed mode. This is a drift bug: a whitelist array decoupled from the actual pane-content factory means every new mode is silently excluded by default. Three more per-mode registries (the pane-content switch, and the command-palette ID/title switches) had .feed lumped with .dock in their "do nothing" buckets.

Fix

Make Feed openable-as-pane end to end, and remove the class of drift:

  • Single source of truth + compile-time invariant. canOpenAsPane is now an exhaustive switch instead of a whitelist lookup, so adding a new RightSidebarMode is a compile error until the author explicitly decides whether it opens as a pane. paneModes is derived (allCases.filter(\.canOpenAsPane)) and can no longer drift.
  • Real pane content. RightSidebarToolPanelView now renders FeedPanelView() for the detached .feed pane instead of EmptyView — a visible button that opened a blank pane would be worse than the bug. FeedPanelView is self-contained over FeedCoordinator.shared and safe to instantiate alongside the sidebar copy (its view-model is a read-only projection of the shared store).
  • Consistent entrypoints. Added the matching command-palette "Open Feed as Pane" command (id + localized title, EN + JA), per the shared-behavior policy.
  • Dock stays excluded — beta terminal-controls surface with no pane content; its omission is intentional.

Tests

Two-commit red/green structure:

  1. Failing test asserting RightSidebarMode.feed.canOpenAsPane == true and that the command palette exposes an "Open Feed as Pane" command (and that Dock stays excluded).
  2. The fix, turning it green.

Added to the already-wired cmuxTests/RightSidebarCommandPaletteTests.swift.

🤖 Generated with Claude Code


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


Note

Low Risk
UI and localization changes with focused keyboard-focus behavior; no auth or data-path changes. Dock remains explicitly excluded from panes.

Overview
Feed can now be opened as a workspace pane, matching Files, Find, and Vault: the header “Open as Pane” control, command palette (palette.openFeedPane), and openRightSidebarToolPane all honor Feed beta availability.

Pane eligibility no longer drifts from a static whitelist. canOpenAsPane is an exhaustive switch (Feed in, Dock out); paneModes and availablePaneModes() are derived for palette descriptors. Detached panes render real FeedPanelView(placement: .pane) with pane-specific keyboard focus (no right-sidebar coordinator registration; focus host wired through RightSidebarToolPanel).

Feed row actions drop Task { @MainActor in } in favor of MainActor.assumeIsolated. Localizable.xcstrings adds many-locale strings for open-as-pane commands. Tests move to Swift Testing and cover Feed pane flags, palette visibility, and placement focus behavior.

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


Summary by cubic

Restores “Open as Pane” for Feed and renders it as a real detached pane with scoped keyboard focus. Adds an “Open Feed as Pane” command, enforces beta gating across entry points, and keeps Feed selection inactive until the pane is focused.

  • Bug Fixes

    • Show “Open as Pane” in Feed; detached panes render FeedPanelView(placement: .pane); Dock stays excluded.
    • Consistent gating: header, command palette (via availablePaneModes()), openRightSidebarToolPane, and Workspace create/open all check mode.isAvailable(); Feed commands hide when disabled.
    • Isolated pane focus: pane hosts don’t register with the right‑sidebar coordinator; focus host is attached via RightSidebarToolPanel; ownership‑scoped responders keep inline editors and preview focused; selection stays inactive until pane focus.
  • Refactors

    • Single source of truth: canOpenAsPane is an exhaustive switch; paneModes is derived; added availablePaneModes() for palette descriptors.
    • Extracted feed pane focus helpers (e.g., focusFeedHost, noteFeedKeyboardFocusIntent); MainWindowFocusController relies on host ownership instead of type checks.
    • Localized “Open X as Pane” command titles across locales; tests use Swift Testing with coverage for feed pane flags, availability gating, placement registration, focus ownership, and palette visibility.

Written for commit 1daa6b3. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Feed can now be opened and displayed as a dedicated pane, offering additional layout options.
  • Localization

    • Extended language support for sidebar navigation commands, including translations for the new feed pane feature.
  • Improvements

    • Refined keyboard focus handling within feed interactions for improved navigation and usability.

austinywang and others added 2 commits June 1, 2026 04:33
Add coverage asserting RightSidebarMode.feed.canOpenAsPane is true and that
the command palette exposes an 'Open Feed as Pane' command, mirroring the
header 'Open as Pane' button. This fails before the fix: feed is omitted from
paneModes and from the command-palette pane registries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The right-sidebar header gates its 'Open as Pane' button on
RightSidebarMode.canOpenAsPane, which was a membership check against a
hand-maintained paneModes whitelist that omitted .feed. Selecting Feed hid the
button, inconsistent with Files/Find/Vault.

Make .feed openable-as-pane and render it correctly end to end:

- canOpenAsPane is now an exhaustive switch (single source of truth); paneModes
  is derived from it via allCases.filter(\.canOpenAsPane). A newly added mode
  is a compile error until it explicitly opts in/out, so this class of drift
  can't recur. Dock stays excluded (beta terminal-controls surface, no pane).
- RightSidebarToolPanelView renders FeedPanelView() for the detached .feed pane
  instead of EmptyView, so the button opens real Feed content (FeedPanelView is
  self-contained over FeedCoordinator.shared and safe to instantiate alongside
  the sidebar copy).
- Add the matching command-palette 'Open Feed as Pane' command (id + localized
  title, EN + JA) so every entrypoint stays consistent (shared-behavior policy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 1, 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 Jun 18, 2026 7:52pm
cmux-staging Building Building Preview, Comment Jun 18, 2026 7:52pm

@coderabbitai

coderabbitai Bot commented Jun 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

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

Adds Feed as an openable right-sidebar pane: RightSidebarMode.canOpenAsPane is refactored to an exhaustive switch including .feed, command palette mappings are split between .feed and .dock, FeedPanelView gains a Placement enum that gates keyboard-focus coordinator registration, and availability guards are added throughout the open/focus code paths.

Changes

Feed Pane Opening Feature

Layer / File(s) Summary
RightSidebarMode pane capability refactor
Sources/RightSidebarPanelView.swift
canOpenAsPane becomes an exhaustive switch with .feed → true and .dock → false; paneModes is derived from allCases.filter(\.canOpenAsPane) instead of a hardcoded list.
Command palette wiring, tool panel rendering, and localizations
Sources/ContentView+RightSidebarCommandPalette.swift, Sources/RightSidebarToolPanel.swift, Resources/Localizable.xcstrings
.feed separated from .dock in command-palette ID/title mappings; tool panel renders FeedPanelView for .feed and EmptyView for .dock; command.openFeedPane.title added and existing pane title keys expanded to include all locales.
Availability guards in content and workspace paths
Sources/ContentView.swift, Sources/Workspace.swift
mode.isAvailable() added alongside mode.canOpenAsPane checks in openRightSidebarToolPane, openOrFocusRightSidebarToolSurface, and newRightSidebarToolSurface.
FeedPanel placement and keyboard-focus coordination infrastructure
Sources/Feed/FeedPanelView.swift
FeedPanelView.Placement enum added with registersWithKeyboardFocusCoordinator; FeedFocusHostBox, focusOwnershipId, FeedKeyboardFocusBridge properties, and FeedKeyboardFocusView registration guards threaded through to support non-coordinator focus tracking in pane placement.
RightSidebarToolPanel feed focus host management
Sources/RightSidebarToolPanel.swift
feedFocusHostView weak property added; attachFeedFocusHost method introduced; focus() and ownedFocusIntent(for:in:) extended to handle .feed via host ownership checks; host cleared on close().
FeedListView focus logic branching on placement
Sources/Feed/FeedPanelView.swift
selectRow, focusFirstVisibleItem, preferredFocusItemId, moveSelection, syncFeedFocusSnapshot, focusFeedHost, and noteFeedKeyboardFocusIntent branch between coordinator-driven and local host-ownership paths based on usesRightSidebarFocusCoordinator.
Focus ownership protocol and controller updates
Sources/MainWindowFocusTypes.swift, Sources/MainWindowFocusController.swift
FeedKeyboardFocusResponder gains required feedKeyboardFocusOwnerId: UUID?; ownsRightSidebarFocus and rightSidebarModeOwning drop broad responder-type checks in favor of feedHost?.ownsKeyboardFocus only.
onFocusFeedHost callback threading through row components
Sources/Feed/FeedPanelView.swift, Sources/Feed/FeedPreviewWindowController.swift
onFocusFeedHost and focusOwnershipId threaded from FeedListView through FeedRowSurface, FeedItemRow, QuestionActionArea, StopActionArea, customAnswerField, and FeedInlineTextField; blur handler replaced with moveFocusToFeedHost; FeedRowActions.bound() uses MainActor.assumeIsolated instead of Task.
Tests for pane capability, descriptors, focus ownership, and workspace gating
cmuxTests/RightSidebarCommandPaletteTests.swift, cmuxTests/ShortcutAndCommandPaletteTests.swift
RightSidebarCommandPaletteTests migrated to Swift Testing; tests added for .feed canOpenAsPane, command-palette Feed-gated descriptors, focus-host placement registration, workspace surface availability, and shortcut actions; StubFeedKeyboardFocusResponder added; TestRightSidebarResponder gains feedKeyboardFocusOwnerId with ownership wired in focus toggle tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#4785: Modifies the same RightSidebarMode paneModes/pane-availability plumbing in RightSidebarPanelView.swift by removing .history from pane mode contributions—directly adjacent to the exhaustive canOpenAsPane switch added here.
  • manaflow-ai/cmux#5174: Introduces the rightSidebar.beta.feed.enabled feature flag and RightSidebarMode.isAvailable() that this PR's new mode.isAvailable() guards in ContentView and Workspace depend on.

Poem

🐰 Hoppy news from the warren today,
Feed now pops open in its own display!
A pane of its own, with an ID so grand,
Focus and ownership — perfectly planned.
The sidebar's no longer a one-trick delight,
This rabbit refactored things just right! 🎉

🚥 Pre-merge checks | ✅ 20 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main fix: enabling 'Open as Pane' for Feed mode and rendering it as a detached pane.
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 Swift changes properly follow Swift 6 actor isolation rules. FeedPanelViewModel and RightSidebarToolPanel are explicitly @MainActor-isolated; RightSidebarMode is nonisolated Sendable...
Cmux Swift Blocking Runtime ✅ Passed No blocking/timing-based synchronization patterns introduced or expanded; PR adds focus management, MainActor.assumeIsolated (non-blocking), and availability guards only. Existing DispatchSemaphore...
Cmux Expensive Synchronous Load ✅ Passed PR adds only lightweight UserDefaults availability checks in guard statements on interactive paths (button clicks, command palette). No expensive synchronous loaders like RestorableAgentSessionInde...
Cmux Cache Substitution Correctness ✅ Passed All persistence paths in the PR correctly handle fresh reads of authoritative sources and gracefully degrade when features become unavailable. Session restoration checks both canOpenAsPane and isAv...
Cmux No Hacky Sleeps ✅ Passed Custom check for non-Swift files only. PR modifies only Swift source/test files and localization strings; no TypeScript, JavaScript, shell, or build/runtime scripts present.
Cmux Algorithmic Complexity ✅ Passed RightSidebarMode enum has exactly 5 fixed cases; paneModes is a static let filtering this tiny collection once; availablePaneModes() similarly filters 5 elements, called during non-hot command pale...
Cmux Swift Concurrency ✅ Passed PR replaces Task { @MainActor } with MainActor.assumeIsolated (modern), adds one Task in NSViewRepresentable Coordinator (allowed AppKit boundary), and pre-existing DispatchQueue.main.asyncAfter fo...
Cmux Swift @Concurrent ✅ Passed No violations found: no missing @concurrent on nonisolated async functions, no invalid @concurrent on sync/actor-isolated functions, MainActor.assumeIsolated usage is safe and correct in closures c...
Cmux Swift File And Package Boundaries ✅ Passed All file changes comply with swift-file-package-boundaries.md: no new oversized files; +154 net lines to FeedPanelView (4091 total) is below 250-line threshold for >800-line files; changes are UI-o...
Cmux Swift Logging ✅ Passed No logging violations found. The PR adds no print, debugPrint, dump, or NSLog statements in production code. All changes are architectural: adding Placement enum, focus management, and refactoring...
Cmux User-Facing Error Privacy ✅ Passed No privacy violations found. Localization strings use safe generic product terminology. Error handling relies on silent failures without exposing vendor/internal names, credentials, or sensitive data.
Cmux Full Internationalization ✅ Passed All user-facing Swift text uses String(localized:defaultValue:); new key command.openFeedPane.title and expanded keys have complete translations across all 20 supported locales with state:translated.
Cmux Swiftui State Layout ✅ Passed All SwiftUI state patterns are pre-existing and unchanged. Feed row actions use MainActor.assumeIsolated (approved). List rows isolated with snapshots, no store refs. No new ObservableObject/@Publi...
Cmux Architecture Rethink ✅ Passed PR replaces whitelist-based pane eligibility with exhaustive switch, deriving paneModes to eliminate drift. Clean single-path command wiring, weak ref focus host attachment, isolated pane placement...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR does not create new standalone NSWindow/NSPanel/SwiftUI Window/WindowGroup. Feed panes render as SwiftUI Views within existing workspace bonsplit pane infrastructure, not as new key windows. No...
Cmux Source Artifacts ✅ Passed PR contains only legitimate hand-written Swift source files, test files, and localization catalogs; no generated artifacts, build output, or local tool outputs in feature files.
Description check ✅ Passed PR description provides comprehensive summary, root cause analysis, specific fix breakdown, and test structure with clear objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 fix-feed-open-as-pane

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 5 files

Re-trigger cubic

Comment thread Sources/RightSidebarToolPanel.swift Outdated
@greptile-apps

greptile-apps Bot commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a whitelist-drift bug where the "Open as Pane" button vanished in Feed mode, and wires Feed through every relevant entrypoint end-to-end. The feature-flag bypass reported in the previous review (Feed pane commands appearing even when feedEnabledKey was off) is also addressed here via availablePaneModes() and isAvailable() guards in ContentView, ContentView+RightSidebarCommandPalette, and Workspace.

  • canOpenAsPane is now an exhaustive switch — adding a new RightSidebarMode is a compile error until the author opts in or out, eliminating the drift class. paneModes and availablePaneModes() are both derived, so they can never lag behind.
  • Detached pane gets real content — RightSidebarToolPanelView renders FeedPanelView(placement: .pane) instead of EmptyView; FeedKeyboardFocusResponder gains a feedKeyboardFocusOwnerId property so the main window's focus controller can scope ownership to the sidebar copy and not the pane copy.
  • Localization — command.openFeedPane.title is fully translated across all 20 supported locales, and the three pre-existing pane-command keys have their 18 missing locales backfilled.

Confidence Score: 5/5

Safe to merge — the change is scoped to the Feed pane entrypoint, focus isolation, and localization; all previously reported concerns are addressed.

The fix is thorough: the whitelist drift root cause is eliminated via an exhaustive switch, the feature-flag bypass is closed at every relevant callsite, focus ownership between the sidebar and detached pane is correctly scoped via UUIDs, and localization is complete for all 20 locales. The only finding is a minor UUID() in a preview-window view body that causes redundant NSTextView configure calls but no functional regression.

Sources/Feed/FeedPreviewWindowController.swift — focusOwnershipId: UUID() inline in the view body should be a stable constant or @State.

Important Files Changed

Filename Overview
Sources/RightSidebarPanelView.swift Converts canOpenAsPane from a whitelist array to an exhaustive switch and derives paneModes from it; eliminates the drift class for all future RightSidebarMode additions.
Sources/RightSidebarMode+Availability.swift Adds availablePaneModes() that gates pane commands on both availability (feature flag) and canOpenAsPane; correctly fixes the feature-flag bypass reported in the previous review.
Sources/ContentView+RightSidebarCommandPalette.swift Switches descriptor enumeration to availablePaneModes() and adds Feed command ID + localized title; Dock correctly remains excluded.
Sources/ContentView.swift Adds mode.isAvailable() guard to openRightSidebarToolPane, closing the feature-flag bypass in the imperative open path.
Sources/Feed/FeedPanelView.swift Large focus isolation refactor: Placement enum, FeedFocusHostBox, ownership ID routing, and MainActor.assumeIsolated modernization. focusHostFromCoordinator() correctly calls window.makeFirstResponder(self) so pane focus works without coordinator registration.
Sources/RightSidebarToolPanel.swift Wires pane Feed focus: stores feedFocusHostView, delegates focus() and focusOwner() through it, clears on close().
Sources/MainWindowFocusController.swift Removes the broad FeedKeyboardFocusResponder class-check, replacing it with ownership-ID-scoped checks; prevents pane feed responders from being mistakenly attributed to the sidebar feed.
Sources/Feed/FeedPreviewWindowController.swift Adds required onFocusFeedHost and focusOwnershipId parameters; passes no-op closure and inline UUID() that is re-evaluated on every body render cycle.
Resources/Localizable.xcstrings Adds full 20-locale translations for command.openFeedPane.title and backfills the 18 missing locales for the three existing pane-command keys.
cmuxTests/RightSidebarCommandPaletteTests.swift Migrated to Swift Testing; adds Feed pane flag tests, palette visibility tests with beta gate on/off, focus isolation unit tests, and workspace availability gate tests.

Reviews (17): Last reviewed commit: "refactor: isolate feed pane focus helper..." | Re-trigger Greptile

Comment thread Resources/Localizable.xcstrings
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 1, 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

🤖 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 `@cmuxTests/RightSidebarCommandPaletteTests.swift`:
- Around line 44-65: Convert this XCTest-based file to Swift Testing: replace
the XCTest harness by adding "import Testing" and change the test suite
declaration from "final class RightSidebarCommandPaletteTests : XCTestCase" to
an "`@Suite` struct RightSidebarCommandPaletteTests", convert the two methods
"testFeedModeCanOpenAsPane" and "testCommandPaletteOffersOpenFeedAsPane" to
"`@Test`" functions, and replace all XCTest assertions inside
(XCTAssertTrue/XCTAssertFalse and any XCTUnwrap usages) with the Swift Testing
equivalents (`#expect`(...) assertions and try `#require`(...) for unwraps). Keep
the existing logic and the referenced symbols (RightSidebarMode.feed,
RightSidebarMode.dock, RightSidebarMode.paneModes, and
ContentView.commandPaletteRightSidebarToolPaneCommandDescriptors()) intact while
doing the conversion.
🪄 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: bc875d36-7ce2-46d6-93ac-253bef19beec

📥 Commits

Reviewing files that changed from the base of the PR and between 865b773 and f0159b3.

📒 Files selected for processing (5)
  • Resources/Localizable.xcstrings
  • Sources/ContentView+RightSidebarCommandPalette.swift
  • Sources/RightSidebarPanelView.swift
  • Sources/RightSidebarToolPanel.swift
  • cmuxTests/RightSidebarCommandPaletteTests.swift

Comment thread cmuxTests/RightSidebarCommandPaletteTests.swift Outdated
Comment thread Sources/RightSidebarToolPanel.swift
Comment thread Sources/ContentView+RightSidebarCommandPalette.swift
Comment thread Sources/RightSidebarPanelView.swift

@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 eea3653. Configure here.

Comment thread Sources/Feed/FeedPanelView.swift
@austinywang
austinywang dismissed coderabbitai[bot]’s stale review June 6, 2026 09:28

Stale bot review on f0159b3; addressed by later commits. Current head uses Swift Testing in cmuxTests/RightSidebarCommandPaletteTests.swift and CodeRabbit status is passing.

@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

Caution

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

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

486-509: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't mark detached rows as keyboard-active before the pane host actually owns focus.

In the non-coordinator branch, selectRow(..., focusFeed: false) still writes isKeyboardActive: true. A plain click can therefore paint the pane as keyboard-active while first responder is still some other control, so arrow-key handling stays elsewhere. Keep the selection update separate from the active-focus bit, or explicitly focus the pane host on row selection.

🤖 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/Feed/FeedPanelView.swift` around lines 486 - 509, The selectRow
method unconditionally creates an optimisticSnapshot with isKeyboardActive set
to true, but this incorrectly marks the pane as keyboard-active even when
focusFeed is false (a plain click selection). The issue is that when focusFeed
is false, the pane hasn't actually received focus, so isKeyboardActive should
reflect that. Fix this by either creating the optimisticSnapshot with
isKeyboardActive set to the focusFeed parameter value (so it's true only when
actually focusing the feed), or explicitly call focusFeedHost() in the else
branch before setting focusSnapshot to ensure the pane owns focus before marking
it as keyboard-active.
🤖 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/ContentView.swift`:
- Around line 13294-13296: The three `@State` properties
workspaceFinderDirectoryOpenRequest, metadataRowsExpanded, and
metadataBlocksExpanded are internal view implementation details that should not
be exposed outside the view. Add the private access control modifier to each of
these `@State` properties to restrict their visibility and follow SwiftLint best
practices for encapsulation.

In `@Sources/Feed/FeedPanelView.swift`:
- Around line 605-613: The isKeyboardActive check in syncFeedFocusSnapshot is
using focusHostBox.view?.ownsKeyboardFocus() to determine focus ownership, but
this check is not host-specific and will return true for any
FeedKeyboardFocusResponder in the window, including responders from other Feed
instances (like the sidebar's inline editor). This causes focus state to leak
across different Feed panes in the same window. Fix this by ensuring the
ownership check is specific to this Feed instance's host rather than checking
responder type generically. Modify the logic to verify that the focused
responder actually belongs to this particular Feed instance's focusHostBox, not
just to any Feed pane in the window.

---

Outside diff comments:
In `@Sources/Feed/FeedPanelView.swift`:
- Around line 486-509: The selectRow method unconditionally creates an
optimisticSnapshot with isKeyboardActive set to true, but this incorrectly marks
the pane as keyboard-active even when focusFeed is false (a plain click
selection). The issue is that when focusFeed is false, the pane hasn't actually
received focus, so isKeyboardActive should reflect that. Fix this by either
creating the optimisticSnapshot with isKeyboardActive set to the focusFeed
parameter value (so it's true only when actually focusing the feed), or
explicitly call focusFeedHost() in the else branch before setting focusSnapshot
to ensure the pane owns focus before marking it as keyboard-active.
🪄 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: 9c9d58a9-29b7-4a4f-ae5e-d5a1790c1b33

📥 Commits

Reviewing files that changed from the base of the PR and between 7389ea7 and 6b45919.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (5)
  • Sources/ContentView.swift
  • Sources/Feed/FeedPanelView.swift
  • Sources/Feed/FeedPreviewWindowController.swift
  • Sources/Workspace.swift
  • cmuxTests/RightSidebarCommandPaletteTests.swift

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

Caution

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

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

486-509: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't mark detached rows as keyboard-active before the pane host actually owns focus.

In the non-coordinator branch, selectRow(..., focusFeed: false) still writes isKeyboardActive: true. A plain click can therefore paint the pane as keyboard-active while first responder is still some other control, so arrow-key handling stays elsewhere. Keep the selection update separate from the active-focus bit, or explicitly focus the pane host on row selection.

🤖 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/Feed/FeedPanelView.swift` around lines 486 - 509, The selectRow
method unconditionally creates an optimisticSnapshot with isKeyboardActive set
to true, but this incorrectly marks the pane as keyboard-active even when
focusFeed is false (a plain click selection). The issue is that when focusFeed
is false, the pane hasn't actually received focus, so isKeyboardActive should
reflect that. Fix this by either creating the optimisticSnapshot with
isKeyboardActive set to the focusFeed parameter value (so it's true only when
actually focusing the feed), or explicitly call focusFeedHost() in the else
branch before setting focusSnapshot to ensure the pane owns focus before marking
it as keyboard-active.
🤖 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/ContentView.swift`:
- Around line 13294-13296: The three `@State` properties
workspaceFinderDirectoryOpenRequest, metadataRowsExpanded, and
metadataBlocksExpanded are internal view implementation details that should not
be exposed outside the view. Add the private access control modifier to each of
these `@State` properties to restrict their visibility and follow SwiftLint best
practices for encapsulation.

In `@Sources/Feed/FeedPanelView.swift`:
- Around line 605-613: The isKeyboardActive check in syncFeedFocusSnapshot is
using focusHostBox.view?.ownsKeyboardFocus() to determine focus ownership, but
this check is not host-specific and will return true for any
FeedKeyboardFocusResponder in the window, including responders from other Feed
instances (like the sidebar's inline editor). This causes focus state to leak
across different Feed panes in the same window. Fix this by ensuring the
ownership check is specific to this Feed instance's host rather than checking
responder type generically. Modify the logic to verify that the focused
responder actually belongs to this particular Feed instance's focusHostBox, not
just to any Feed pane in the window.

---

Outside diff comments:
In `@Sources/Feed/FeedPanelView.swift`:
- Around line 486-509: The selectRow method unconditionally creates an
optimisticSnapshot with isKeyboardActive set to true, but this incorrectly marks
the pane as keyboard-active even when focusFeed is false (a plain click
selection). The issue is that when focusFeed is false, the pane hasn't actually
received focus, so isKeyboardActive should reflect that. Fix this by either
creating the optimisticSnapshot with isKeyboardActive set to the focusFeed
parameter value (so it's true only when actually focusing the feed), or
explicitly call focusFeedHost() in the else branch before setting focusSnapshot
to ensure the pane owns focus before marking it as keyboard-active.
🪄 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: 9c9d58a9-29b7-4a4f-ae5e-d5a1790c1b33

📥 Commits

Reviewing files that changed from the base of the PR and between 7389ea7 and 6b45919.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (5)
  • Sources/ContentView.swift
  • Sources/Feed/FeedPanelView.swift
  • Sources/Feed/FeedPreviewWindowController.swift
  • Sources/Workspace.swift
  • cmuxTests/RightSidebarCommandPaletteTests.swift
🛑 Comments failed to post (2)
Sources/ContentView.swift (1)

13294-13296: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Mark state properties as private.

SwiftLint correctly flags these three @State properties as needing private access control. State properties are internal view implementation details and should not be exposed outside the view.

🔒 Proposed fix
-    `@State` var workspaceFinderDirectoryOpenRequest: WorkspaceFinderDirectoryOpenRequest?
-    `@State` var metadataRowsExpanded = false
-    `@State` var metadataBlocksExpanded = false
+    `@State` private var workspaceFinderDirectoryOpenRequest: WorkspaceFinderDirectoryOpenRequest?
+    `@State` private var metadataRowsExpanded = false
+    `@State` private var metadataBlocksExpanded = false
🧰 Tools
🪛 SwiftLint (0.63.3)

[Warning] 13294-13294: SwiftUI state properties should be private

(private_swiftui_state)


[Warning] 13295-13295: SwiftUI state properties should be private

(private_swiftui_state)


[Warning] 13296-13296: SwiftUI state properties should be private

(private_swiftui_state)

🤖 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/ContentView.swift` around lines 13294 - 13296, The three `@State`
properties workspaceFinderDirectoryOpenRequest, metadataRowsExpanded, and
metadataBlocksExpanded are internal view implementation details that should not
be exposed outside the view. Add the private access control modifier to each of
these `@State` properties to restrict their visibility and follow SwiftLint best
practices for encapsulation.

Source: Linters/SAST tools

Sources/Feed/FeedPanelView.swift (1)

605-613: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pane-mode focus ownership still leaks across Feed instances in the same window.

This new path treats any FeedKeyboardFocusResponder as “this pane is active.” Because the right-sidebar Feed host and a detached Feed pane can coexist in one window, focusing the sidebar's inline editor will also make the pane snapshot go active. syncFeedFocusSnapshot needs host-specific ownership for this Feed instance, not a responder-type check.

🤖 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/Feed/FeedPanelView.swift` around lines 605 - 613, The
isKeyboardActive check in syncFeedFocusSnapshot is using
focusHostBox.view?.ownsKeyboardFocus() to determine focus ownership, but this
check is not host-specific and will return true for any
FeedKeyboardFocusResponder in the window, including responders from other Feed
instances (like the sidebar's inline editor). This causes focus state to leak
across different Feed panes in the same window. Fix this by ensuring the
ownership check is specific to this Feed instance's host rather than checking
responder type generically. Modify the logic to verify that the focused
responder actually belongs to this particular Feed instance's focusHostBox, not
just to any Feed pane in the window.

austinywang and others added 5 commits June 14, 2026 14:58
# Conflicts:
#	.github/swift-file-length-budget.tsv
The FeedKeyboardFocusResponder protocol gained a feedKeyboardFocusOwnerId
requirement and the controller dropped its global 'is FeedKeyboardFocusResponder'
classification fallback, but the test target was never recompiled. Fix the two
broken conformers and keep the pre-existing focus-toggle / stale-feed-responder
coverage meaningful under the new ownership model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	.github/swift-file-length-budget.tsv
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

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

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants