Repository navigation
fix: show 'Open as Pane' for Feed mode and render it as a detached pane - #5105
austinywang wants to merge 25 commits into
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Feed as an openable right-sidebar pane: ChangesFeed Pane Opening Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 20 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (20 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis 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
Confidence Score: 5/5Safe 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 — Important Files Changed
Reviews (17): Last reviewed commit: "refactor: isolate feed pane focus helper..." | Re-trigger Greptile |
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 `@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
📒 Files selected for processing (5)
Resources/Localizable.xcstringsSources/ContentView+RightSidebarCommandPalette.swiftSources/RightSidebarPanelView.swiftSources/RightSidebarToolPanel.swiftcmuxTests/RightSidebarCommandPaletteTests.swift
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
Stale bot review on f0159b3; addressed by later commits. Current head uses Swift Testing in cmuxTests/RightSidebarCommandPaletteTests.swift and CodeRabbit status is passing.
There was a problem hiding this comment.
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 winDon't mark detached rows as keyboard-active before the pane host actually owns focus.
In the non-coordinator branch,
selectRow(..., focusFeed: false)still writesisKeyboardActive: 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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (5)
Sources/ContentView.swiftSources/Feed/FeedPanelView.swiftSources/Feed/FeedPreviewWindowController.swiftSources/Workspace.swiftcmuxTests/RightSidebarCommandPaletteTests.swift
There was a problem hiding this comment.
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 winDon't mark detached rows as keyboard-active before the pane host actually owns focus.
In the non-coordinator branch,
selectRow(..., focusFeed: false)still writesisKeyboardActive: 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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (5)
Sources/ContentView.swiftSources/Feed/FeedPanelView.swiftSources/Feed/FeedPreviewWindowController.swiftSources/Workspace.swiftcmuxTests/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
@Stateproperties as needingprivateaccess 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 liftPane-mode focus ownership still leaks across Feed instances in the same window.
This new path treats any
FeedKeyboardFocusResponderas “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.syncFeedFocusSnapshotneeds 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.
# 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

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:
.feedwas 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-contentswitch, and the command-palette ID/title switches) had.feedlumped with.dockin their "do nothing" buckets.Fix
Make Feed openable-as-pane end to end, and remove the class of drift:
canOpenAsPaneis now an exhaustiveswitchinstead of a whitelist lookup, so adding a newRightSidebarModeis a compile error until the author explicitly decides whether it opens as a pane.paneModesis derived (allCases.filter(\.canOpenAsPane)) and can no longer drift.RightSidebarToolPanelViewnow rendersFeedPanelView()for the detached.feedpane instead ofEmptyView— a visible button that opened a blank pane would be worse than the bug.FeedPanelViewis self-contained overFeedCoordinator.sharedand safe to instantiate alongside the sidebar copy (its view-model is a read-only projection of the shared store).Tests
Two-commit red/green structure:
RightSidebarMode.feed.canOpenAsPane == trueand that the command palette exposes an "Open Feed as Pane" command (and that Dock stays excluded).Added to the already-wired
cmuxTests/RightSidebarCommandPaletteTests.swift.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmithwith 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), andopenRightSidebarToolPaneall honor Feed beta availability.Pane eligibility no longer drifts from a static whitelist.
canOpenAsPaneis an exhaustive switch (Feed in, Dock out);paneModesandavailablePaneModes()are derived for palette descriptors. Detached panes render realFeedPanelView(placement: .pane)with pane-specific keyboard focus (no right-sidebar coordinator registration; focus host wired throughRightSidebarToolPanel).Feed row actions drop
Task { @MainActor in }in favor ofMainActor.assumeIsolated. Localizable.xcstrings adds many-locale strings for open-as-pane commands. Tests move to SwiftTestingand 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
Refactors
Written for commit 1daa6b3. Summary will update on new commits.
Summary by CodeRabbit
New Features
Localization
Improvements