Skip to content

Split sidebar PR visibility from clickability - #3273

Merged
austinywang merged 9 commits into
mainfrom
issue-3265-pr-clickable-toggle
May 4, 2026
Merged

austinywang merged 9 commits into
mainfrom
issue-3265-pr-clickable-toggle

Conversation

@austinywang

@austinywang austinywang commented Apr 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a red UI regression test that disables future sidebar PR clickability and asserts clicks only select the workspace row
  • add a persisted sidebar setting to keep PR metadata visible while turning row clickability off
  • default the make-PR-clickable setting off so new users still see PR metadata, but accidental hits on the small PR fragment do not open the browser
  • render non-clickable sidebar PR rows as plain text with no PR tooltip and keep managed settings/schema/localization in sync

Default behavior update

New installs now show the PR badge/text in the sidebar, but the PR fragment is not clickable by default. Users who want PR links to open from the sidebar can enable Make Sidebar PR Clickable in Settings; this keeps the information visible while avoiding accidental browser navigation when aiming at the workspace row.

Testing

  • not run local XCTest/XCUITest suites per repo policy
  • validated JSON parsing for Resources/Localizable.xcstrings and web/data/cmux-settings.schema.json
  • git diff --check
  • ./scripts/reload.sh --tag issue-3265-pr-clickable-toggle --launch

Closes #3265


Note

Low Risk
Low risk UI/settings change that alters how sidebar PR rows handle clicks; main risk is unintended interaction regressions or mismatched defaults in managed config/localizations.

Overview
Sidebar pull request display is split from interactivity: PR metadata can remain visible while click-to-open is now controlled by a new persisted setting (default off).

When clickability is disabled, sidebar PR rows render as plain text (no implicit link behavior) so clicks select the workspace row; when enabled, rows become Buttons that open the PR URL and show the tooltip/underline.

Settings/config plumbing is updated end-to-end: new toggle in Settings UI, KeyboardShortcutSettingsFileStore + JSON schema support, updated localizations/subtitles (including disabling the “open PR links in cmux browser” toggle unless PRs are shown and clickable), plus new unit/UI tests and Xcode project wiring for the added UI test.

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


Summary by cubic

Split sidebar PR visibility from clickability to reduce accidental navigations (closes #3265). PR metadata stays visible as plain text by default; enabling clickability turns rows into buttons that open links per your browser preference.

  • New Features

    • Added SidebarPullRequestClickabilitySettings persisted as @AppStorage key sidebarMakePullRequestClickable (default: false) with a new Settings toggle; “Open Sidebar PR Links in cmux Browser” is disabled unless PRs are shown and clickable, with clear subtitles when hidden or non-clickable; updated “Show Pull Requests” copy to only promise status/number.
    • Rendering: when clickability is off, PR rows render as plain content (no Button), no tooltip, and title underlines only when clickable; keeps the SidebarPullRequestRow accessibility identifier.
    • Managed config/schema: added sidebar.makePullRequestsClickable to web/data/cmux.schema.json and wired through KeyboardShortcutSettingsFileStore; tests include SidebarPullRequestInteractivityUITests plus unit coverage for default/stored clickability.
  • Refactors

    • Replaced hit-testing suppression with a real Button only when clickable to avoid exposing interactive accessibility in non-clickable mode; fixed Xcode project ID conflicts so the new UI test builds.

Written for commit f5dffb2. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added a setting to toggle sidebar pull request clickability (default: off); when enabled PR rows are interactive, when disabled they render as plain text and clicks select the workspace row
    • “Open Sidebar PR Links” availability now depends on PR visibility and clickability
  • Localization

    • Added and updated localized subtitles for the new options and PR display text across multiple locales
  • Tests

    • Added UI and unit tests covering default/stored values and clickability behavior

Issue #3265 needs a red test that captures the current behavior before the
sidebar row implementation changes. This UI test seeds a pull request over the
control socket, disables the future clickability default, and asserts that a
click should only select the workspace row without opening a browser surface.

Constraint: Local test execution is disabled for this workspace; CI must prove the failure
Rejected: Source-shape assertion against ContentView | does not verify runtime behavior
Confidence: medium
Scope-risk: narrow
Reversibility: clean
Directive: Keep this test behavior-focused; do not replace it with a source-text check
Tested: Not run locally (per repo policy)
Not-tested: Compile/runtime status until CI runs
Issue #3265 needs the sidebar pull-request row to split visibility from
interactivity. This keeps PR metadata visible while allowing users to turn the
link behavior off, preserving row selection and leaving the link-destination
preference intact for when clickability is re-enabled.

Constraint: The existing show/hide setting and managed settings.json surface must keep working
Rejected: Reuse the browser-destination toggle as the clickability toggle | conflates visibility/interactivity and cannot preserve plain-text rows
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep sidebar PR row interactivity separate from link-destination preferences
Tested: JSON parse validation for Resources/Localizable.xcstrings and web/data/cmux-settings.schema.json
Not-tested: Swift compilation/UI behavior until the final tagged reload build
@vercel

vercel Bot commented Apr 29, 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 4, 2026 4:41am
cmux-staging Building Building Preview, Comment May 4, 2026 4:41am

@coderabbitai

coderabbitai Bot commented Apr 29, 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

This change adds a new "Make Sidebar PR Clickable" persisted setting (default: off) and wires it through storage, UI, localization, view snapshot, rendering, tests, and settings templates so sidebar PR review items can be shown as plain non-interactive text or as clickable links.

Changes

Sidebar PR Clickability Feature

Layer / File(s) Summary
Settings Definition
Sources/TabManager.swift
New SidebarPullRequestClickabilitySettings enum: key, defaultClickable = false, and isClickable(defaults:) resolver.
Settings Persistence & Template
Sources/KeyboardShortcutSettingsFileStore.swift
Added sidebar.makePullRequestsClickable to allowed JSON paths, parse logic stores value into managedUserDefaults[SidebarPullRequestClickabilitySettings.key], and default settings template includes "makePullRequestsClickable" with the default.
View State Snapshot
Sources/ContentView.swift (SidebarTabItemSettingsSnapshot)
Snapshot extended with makesPullRequestsClickable initialized from SidebarPullRequestClickabilitySettings.isClickable(defaults:).
View Rendering / Interaction
Sources/ContentView.swift (TabItemView / pull-request row)
PR title underline, tooltip, and hit-testing are conditional on settings.makesPullRequestsClickable. When enabled the PR row is a Button (tooltip + accessibility id); when disabled the row renders without a Button (plain text) and omits the tooltip.
Settings UI Wiring
Sources/cmuxApp.swift
New @AppStorage sidebarMakePullRequestClickable using the settings key and default; settings UI adds a "Make Sidebar PR Clickable" toggle (disabled when PRs or details are hidden) and updates the "Open Sidebar PR Links" subtitle/disabled logic; reset restores default.
Localization
Resources/Localizable.xcstrings
Added settings.app.openSidebarPRLinks.subtitleDisabled / ...subtitleHidden; added settings.app.showPullRequests.subtitle.basic; updated settings.app.makeSidebarPullRequestClickable.subtitle copy (en/ja and other locales).
Unit Tests
cmuxTests/BrowserConfigTests.swift
Added testSidebarPullRequestClickabilityDefaultAndStoredValues asserting isClickable(defaults:) returns default when unset and reflects persisted booleans.
UI Tests
cmuxUITests/SidebarPullRequestInteractivityUITests.swift
New UI test that launches app in UI-test mode with a control socket, clears stored default, runs default/disabled scenarios, locates sidebar PR element via accessibility queries, asserts plain-text rendering when expected, clicks the row, and verifies workspace selection with no new browser surface.
Project Config
GhosttyTabs.xcodeproj/project.pbxproj
Registered the new UI test file in the cmuxUITests target (file reference, build file, group, sources phase).

Sequence Diagram

sequenceDiagram
    participant User
    participant Settings as SettingsView
    participant Store as UserDefaults
    participant View as ContentView/TabItemView
    participant PR as Sidebar PR Row

    User->>Settings: Toggle "Make Sidebar PR Clickable"
    Settings->>Store: Persist sidebarMakePullRequestClickable
    Store-->>Settings: Acknowledgement
    View->>Store: Read SidebarPullRequestClickabilitySettings.isClickable()
    Store-->>View: Return boolean
    View->>View: Update makesPullRequestsClickable
    alt makesPullRequestsClickable = true
        View->>PR: Render as Button (underlined, tooltip)
        User->>PR: Click PR title -> open PR URL (external/browser)
    else makesPullRequestsClickable = false
        View->>PR: Render as plain text (non-interactive)
        User->>PR: Click row -> select workspace (no browser surface)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibbled on toggles beneath the moon's beam,
A switch to keep PRs quiet or let them gleam.
When off they sit gentle, text tidy and still,
When on they wink underlined — a link to thrill.
Hop, click, hop back — the workspace keeps its will.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: splitting sidebar PR visibility from clickability, which is the core objective of this PR.
Linked Issues check ✅ Passed The PR fully addresses issue #3265 requirements: splits PR visibility from clickability [#3265], adds the clickability setting with default-off behavior [#3265], renders non-clickable rows as plain text [#3265], and updates all settings/schema/localization [#3265].
Out of Scope Changes check ✅ Passed All changes are in scope: new clickability setting and UI test, rendering logic updates, settings file store/schema integration, localizations, and project configuration—all directly support splitting PR visibility from clickability.
Description check ✅ Passed The pull request description comprehensively covers all required template sections including summary, testing approach, and a detailed checklist.

✏️ 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 issue-3265-pr-clickable-toggle

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.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a new makeSidebarPullRequestClickable persisted setting that decouples PR metadata visibility from row interactivity — when disabled, PR rows render as plain text with no tooltip and clicks fall through to the workspace row. All wiring is consistent across TabManager, ContentView, cmuxApp, the settings file store, JSON schema, and localization (EN + JA).

Confidence Score: 4/5

Safe to merge; only P2 findings (dead-code ternary in the test helper and an orphaned localization key).

No P0 or P1 findings. The two P2 issues — a dead-code ternary in waitForCurrentWorkspace and the orphaned settings.app.showPullRequests.subtitle localization key — are cosmetic and do not affect runtime behaviour or test correctness.

cmuxUITests/SidebarPullRequestInteractivityUITests.swift (dead-code ternary) and Resources/Localizable.xcstrings (orphaned key).

Important Files Changed

Filename Overview
cmuxUITests/SidebarPullRequestInteractivityUITests.swift New UI test verifying click-through to workspace row when PR clickability is disabled; contains a dead-code ternary in waitForCurrentWorkspace (both branches return same value).
Sources/ContentView.swift Refactors PR row rendering into pullRequestRowView/pullRequestRowContent helpers; correctly gates underline and tooltip on sidebarPullRequestsAreClickable; no issues found.
Sources/TabManager.swift Adds SidebarPullRequestClickabilitySettings enum with key, defaultClickable, and isClickable(defaults:) following existing patterns; clean implementation.
Sources/cmuxApp.swift Wires up new @AppStorage binding, adds openSidebarPullRequestLinksSubtitle computed property, inserts new settings card, updates disabled conditions, and resets to default; well-integrated.
Sources/KeyboardShortcutSettingsFileStore.swift Adds sidebar.makePullRequestsClickable to the persisted key list, JSON parsing, and defaults snapshot; consistent with existing sidebar settings pattern.
Resources/Localizable.xcstrings Adds four new localization entries (EN + JA); old settings.app.showPullRequests.subtitle key is orphaned after the rename to .subtitle.basic and should be removed.
web/data/cmux-settings.schema.json Adds makePullRequestsClickable boolean field with correct default and description; consistent with adjacent schema entries.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SidebarTabItemSettingsSnapshot\ninit] -->|reads UserDefaults| B[SidebarPullRequestClickabilitySettings\n.isClickable]
    B --> C{makesPullRequestsClickable?}
    C -->|true| D[pullRequestRowView\nrenders Button\n+ underline + tooltip]
    C -->|false| E[pullRequestRowView\nrenders plain HStack\nno tooltip, no button]
    D -->|click| F[openPullRequestLink\nopens URL]
    E -->|click| G[falls through to\nworkspace row selection]

    H[SettingsView toggle\nsidebarMakePullRequestClickable] -->|persists| I[UserDefaults\nsidebarMakePullRequestClickable]
    I --> B

    J[settings.json\nsidebar.makePullRequestsClickable] -->|CmuxSettingsFileStore\nparses JSON bool| I

    K[openSidebarPRLinks toggle] -->|disabled when| L{!sidebarMakePullRequestClickable\nOR !sidebarShowPullRequest}
Loading

Comments Outside Diff (1)

  1. Resources/Localizable.xcstrings, line 52888-52905 (link)

    P2 Orphaned localization key after rename

    The PR switches cmuxApp.swift from settings.app.showPullRequests.subtitle to the new settings.app.showPullRequests.subtitle.basic key, but the old settings.app.showPullRequests.subtitle entry in Localizable.xcstrings (with its English and Japanese translations) is never removed. The orphaned string won't cause a runtime error, but it will drift out of sync with any future wording changes and adds noise to the strings catalog.

    Consider removing the old settings.app.showPullRequests.subtitle entry to keep the catalog tidy.

Reviews (1): Last reviewed commit: "Let sidebar PR metadata stay visible wit..." | Re-trigger Greptile

Comment thread cmuxUITests/SidebarPullRequestInteractivityUITests.swift
Comment thread cmuxUITests/SidebarPullRequestInteractivityUITests.swift Outdated

@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

🧹 Nitpick comments (1)
cmuxUITests/SidebarPullRequestInteractivityUITests.swift (1)

137-144: Simplify redundant conditional return in workspace wait helper.

matched ? current : current always returns the same value, which makes the matched variable misleading.

♻️ Suggested cleanup
 private func waitForCurrentWorkspace(_ expectedWorkspaceId: String, timeout: TimeInterval) -> String? {
     var current: String?
-    let matched = pollUntil(timeout: timeout) {
+    _ = pollUntil(timeout: timeout) {
         current = socketCommand("current_workspace")?.trimmingCharacters(in: .whitespacesAndNewlines)
         return current == expectedWorkspaceId
     }
-    return matched ? current : current
+    return current
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/SidebarPullRequestInteractivityUITests.swift` around lines 137 -
144, The helper waitForCurrentWorkspace contains a redundant conditional return
(matched ? current : current); remove the matched variable and simplify the
logic by calling pollUntil to set current (or ignore its Bool result) and then
return current directly. Update the waitForCurrentWorkspace function so it only
declares var current, invokes pollUntil(...) to update current using
socketCommand("current_workspace"), and returns current, eliminating the
misleading matched Bool entirely.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/cmuxApp.swift`:
- Around line 5454-5471: The subtitle logic in
openSidebarPullRequestLinksSubtitle should early-return a message when
sidebarShowPullRequest is false so the disabled state reflects PR visibility
being off; update openSidebarPullRequestLinksSubtitle to first check
!sidebarShowPullRequest and return a localized "PR visibility is off" subtitle,
then fall through to the existing checks for sidebarMakePullRequestClickable and
openSidebarPullRequestLinksInCmuxBrowser to preserve current messages.

---

Nitpick comments:
In `@cmuxUITests/SidebarPullRequestInteractivityUITests.swift`:
- Around line 137-144: The helper waitForCurrentWorkspace contains a redundant
conditional return (matched ? current : current); remove the matched variable
and simplify the logic by calling pollUntil to set current (or ignore its Bool
result) and then return current directly. Update the waitForCurrentWorkspace
function so it only declares var current, invokes pollUntil(...) to update
current using socketCommand("current_workspace"), and returns current,
eliminating the misleading matched Bool entirely.
🪄 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

Run ID: 145a1783-5bb6-4d91-af28-fb4cdee967c7

📥 Commits

Reviewing files that changed from the base of the PR and between 3af6447 and 3e9b878.

📒 Files selected for processing (7)
  • Resources/Localizable.xcstrings
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/TabManager.swift
  • Sources/cmuxApp.swift
  • cmuxUITests/SidebarPullRequestInteractivityUITests.swift
  • web/data/cmux-settings.schema.json

Comment thread Sources/cmuxApp.swift Outdated
The sidebar can keep PR status visible without making the small PR fragment an accidental browser trigger. The existing split setting already provides a single clickability source of truth, so the change flips that default, updates generated schema/copy mirrors, and covers the default plain-text path.

Constraint: Build and launch must use ./scripts/reload.sh with the issue tag; no direct xcodebuild invocation.

Rejected: Add renderer-specific click guards | the PR row already has one Button-versus-plain-text seam driven by SidebarPullRequestClickabilitySettings.

Confidence: high

Scope-risk: narrow

Directive: Keep SidebarPullRequestClickabilitySettings as the single default/resolution owner for sidebar PR clickability.

Tested: jq empty Resources/Localizable.xcstrings; jq empty web/data/cmux-settings.schema.json; git diff --check; ./scripts/reload.sh --tag issue-3265-pr-clickable-toggle --launch

Not-tested: Local XCTest/XCUITest suites per repository instruction to avoid local test runs.
Comment thread Sources/cmuxApp.swift Outdated
Merged origin/main into the clickable pull-request toggle branch and resolved the two conflicts by preserving configurable PR row clickability while adopting the new canonical cmux schema split.

Constraint: git pull origin main produced content conflicts in ContentView.swift and cmux-settings.schema.json
Rejected: Keep the old full cmux-settings schema | origin/main moved settings validation to cmux.schema.json with a compatibility wrapper
Confidence: medium
Scope-risk: moderate
Directive: Keep sidebar.makePullRequestsClickable in cmux.schema.json because cmux-settings.schema.json now only delegates to it
Not-tested: Build verification pending after reapplying pre-pull local changes
The main sync introduced new CLI file references that reused the same PBX object identifiers as SidebarPullRequestInteractivityUITests. Xcode then interpreted a UI test file reference as a build phase entry and refused to open the project. Assigning fresh IDs to the UI test reference keeps both sides of the merge.

Constraint: Xcode project object identifiers must be globally unique inside project.pbxproj
Rejected: Remove SidebarPullRequestInteractivityUITests from the project | the test remains part of the cmuxUITests target and only its conflicting IDs were invalid
Confidence: high
Scope-risk: narrow
Directive: When manually merging project.pbxproj, check duplicate PBX object identifiers before assuming plutil validity is enough
Tested: ./scripts/reload.sh --tag issue-3265-pr-clickable-toggle
Not-tested: Local test suites, per repo policy
The PR merge commit now includes accepted Swift file growth from this branch plus newer main. The CI guard failed because the checked-in budget still reflected the pre-merge line counts, so regenerate the budget from the current tree and keep the reductions for files that shrank.

Constraint: workflow-guard-tests blocks PRs when any tracked Swift file exceeds .github/swift-file-length-budget.tsv
Rejected: Split unrelated large Swift files in this conflict-fix branch | broad refactor would be riskier than accepting the already-merged debt
Confidence: high
Scope-risk: narrow
Directive: Reduce these counts when follow-up cleanup shrinks the large files
Tested: ./tests/test_ci_swift_file_length_budget.sh; python3 scripts/swift_file_length_budget.py --budget .github/swift-file-length-budget.tsv
Not-tested: Full local test suite, per repo policy

@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: 3

♻️ Duplicate comments (1)
Sources/cmuxApp.swift (1)

5356-5373: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Subtitle can still be misleading when PR visibility is off.

At Line 6406 the row is disabled when !sidebarShowPullRequest, but openSidebarPullRequestLinksSubtitle never checks that state first, so users can see a clickability/browser subtitle that doesn’t match the disabled reason.

Suggested fix
 private var openSidebarPullRequestLinksSubtitle: String {
+    if !sidebarShowPullRequest {
+        return String(
+            localized: "settings.app.openSidebarPRLinks.subtitleHidden",
+            defaultValue: "Enable sidebar PR visibility to choose where PR links open."
+        )
+    }
     if !sidebarMakePullRequestClickable {
         return String(
             localized: "settings.app.openSidebarPRLinks.subtitleDisabled",
             defaultValue: "Enable sidebar PR clickability to choose where PR links open."
         )
     }

Also applies to: 6406-6406

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 5356 - 5373,
openSidebarPullRequestLinksSubtitle can show clickability/browser text even when
pull requests are hidden; update the getter to first check
sidebarShowPullRequest and return a subtitle explaining PRs are hidden (or
stating the feature is disabled for visibility) before evaluating
sidebarMakePullRequestClickable and openSidebarPullRequestLinksInCmuxBrowser so
the subtitle matches the disabled row state; locate the
openSidebarPullRequestLinksSubtitle computed property and add an initial branch
that returns the appropriate localized "PRs hidden" message when
sidebarShowPullRequest is false.
🧹 Nitpick comments (2)
Sources/ContentView.swift (2)

14135-14142: ⚡ Quick win

Simplify duplicated Text(...) with .underline(_:).

The Group { if underlinesTitle { … } else { … } } block repeats the identical Text constructor. You can supply a Bool variable to .underline() to control whether the underline is active, collapsing both branches into one line. This also shaves ~5 lines, partially helping the budget.

♻️ Proposed simplification
-            Group {
-                if underlinesTitle {
-                    Text("\(pullRequest.label) #\(pullRequest.number)")
-                        .underline()
-                } else {
-                    Text("\(pullRequest.label) #\(pullRequest.number)")
-                }
-            }
+            Text("\(pullRequest.label) #\(pullRequest.number)")
+                .underline(underlinesTitle)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/ContentView.swift` around lines 14135 - 14142, The Group block
duplicates the same Text initializer; replace the conditional branches by a
single Text("\(pullRequest.label) #\(pullRequest.number)") and call
.underline(underlinesTitle) on it so the underline state is driven by the
underlinesTitle Bool (update the Group containing Text, referencing Group,
underlinesTitle, Text, pullRequest.label, pullRequest.number, and
.underline(_:)).

14135-14142: ⚡ Quick win

Simplify duplicated Text(...) using .underline(_:).

The Group { if underlinesTitle { … .underline() } else { … } } block repeats the same Text constructor. SwiftUI's .underline(_:color:) accepts a Bool, reducing the branch to one line and saving ~5 lines.

♻️ Proposed simplification
-            Group {
-                if underlinesTitle {
-                    Text("\(pullRequest.label) #\(pullRequest.number)")
-                        .underline()
-                } else {
-                    Text("\(pullRequest.label) #\(pullRequest.number)")
-                }
-            }
+            Text("\(pullRequest.label) #\(pullRequest.number)")
+                .underline(underlinesTitle)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/ContentView.swift` around lines 14135 - 14142, Replace the duplicated
Text construction inside the Group by calling .underline(_:) with the existing
boolean; specifically, remove the if/else block that repeats
Text("\(pullRequest.label) #\(pullRequest.number)") and instead use a single
Text(...) and apply .underline(underlinesTitle) so the view uses the
underlinesTitle Bool to toggle the underline (referencing the underlinesTitle
variable and the Text that builds from pullRequest.label and
pullRequest.number).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/cmuxApp.swift`:
- Around line 6383-6396: The file exceeds the Swift file-length budget due to
the new sidebar PR settings block; extract that block into a new small SwiftUI
view (e.g., SidebarPullRequestSettingsView) in a dedicated file and replace the
inline SettingsCardRow block with that view to reduce lines. Move the
SettingsCardRow/Toggle/subtitle logic (the SettingsCardRow initializer using
.json("sidebar.makePullRequestsClickable"), the localized subtitle and title
strings, the Toggle bound to sidebarMakePullRequestClickable, and the
.accessibilityIdentifier) into the new view, accept bindings for
sidebarMakePullRequestClickable, sidebarHideAllDetails, and
sidebarShowPullRequest, and ensure the original call site uses the new view and
preserves the .disabled(sidebarHideAllDetails || !sidebarShowPullRequest)
behavior and the trailing SettingsCardDivider.

In `@Sources/ContentView.swift`:
- Around line 14103-14152: Extract pullRequestRowView and pullRequestRowContent
into a new SwiftUI view type in a new file (e.g.,
SidebarPullRequestRowView.swift): create a value-driven struct (e.g.,
SidebarPullRequestRowView: View) that takes a
SidebarWorkspaceSnapshotBuilder.PullRequestDisplay and any needed
environment/props (pullRequestForegroundColor, a closure for
openPullRequestLink, and boolean for sidebarPullRequestsAreClickable) and
renders the same UI using PullRequestStatusIcon and calling
pullRequestStatusLabel for the status text; preserve the
Button-safeHelp/accessibilityIdentifier behavior when
sidebarPullRequestsAreClickable is true, and keep the same
font/opacity/truncation behavior. Replace the original
pullRequestRowView/pullRequestRowContent definitions in ContentView.swift with a
single line instantiation of the new SidebarPullRequestRowView passing the
necessary symbols (pullRequest, pullRequestForegroundColor, openPullRequestLink,
sidebarPullRequestsAreClickable).
- Around line 14103-14152: ContentView.swift exceeded the file-length budget
because the helper functions pullRequestRowView and pullRequestRowContent were
added inline; extract them into a new file as a standalone, value-driven SwiftUI
View (e.g., SidebarPullRequestRowView) that takes a
SidebarWorkspaceSnapshotBuilder.PullRequestDisplay and the derived values it
needs (pullRequestForegroundColor, pullRequestStatusLabel result, .isStale,
.status, .url, .label, .number, and the sidebarPullRequestsAreClickable flag) so
the view has only value inputs; move usage of PullRequestStatusIcon,
accessibilityIdentifier "SidebarPullRequestRow", safeHelp text, button behavior
(openPullRequestLink) and underline logic into that struct, update ContentView
to call the new SidebarPullRequestRowView with computed values, and delete the
two helper functions from ContentView.swift to bring the file back under budget.

---

Duplicate comments:
In `@Sources/cmuxApp.swift`:
- Around line 5356-5373: openSidebarPullRequestLinksSubtitle can show
clickability/browser text even when pull requests are hidden; update the getter
to first check sidebarShowPullRequest and return a subtitle explaining PRs are
hidden (or stating the feature is disabled for visibility) before evaluating
sidebarMakePullRequestClickable and openSidebarPullRequestLinksInCmuxBrowser so
the subtitle matches the disabled row state; locate the
openSidebarPullRequestLinksSubtitle computed property and add an initial branch
that returns the appropriate localized "PRs hidden" message when
sidebarShowPullRequest is false.

---

Nitpick comments:
In `@Sources/ContentView.swift`:
- Around line 14135-14142: The Group block duplicates the same Text initializer;
replace the conditional branches by a single Text("\(pullRequest.label)
#\(pullRequest.number)") and call .underline(underlinesTitle) on it so the
underline state is driven by the underlinesTitle Bool (update the Group
containing Text, referencing Group, underlinesTitle, Text, pullRequest.label,
pullRequest.number, and .underline(_:)).
- Around line 14135-14142: Replace the duplicated Text construction inside the
Group by calling .underline(_:) with the existing boolean; specifically, remove
the if/else block that repeats Text("\(pullRequest.label)
#\(pullRequest.number)") and instead use a single Text(...) and apply
.underline(underlinesTitle) so the view uses the underlinesTitle Bool to toggle
the underline (referencing the underlinesTitle variable and the Text that builds
from pullRequest.label and pullRequest.number).
🪄 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

Run ID: 2233bc30-7101-499b-866e-75ac904123cc

📥 Commits

Reviewing files that changed from the base of the PR and between 0170746 and 78b90c0.

📒 Files selected for processing (7)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Resources/Localizable.xcstrings
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/TabManager.swift
  • Sources/cmuxApp.swift
  • cmuxTests/BrowserConfigTests.swift
✅ Files skipped from review due to trivial changes (2)
  • cmuxTests/BrowserConfigTests.swift
  • Resources/Localizable.xcstrings
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/KeyboardShortcutSettingsFileStore.swift

Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/ContentView.swift Outdated
The previous budget refresh is intentionally undone. The PR clickability changes now stay within the existing Swift file-length budget by removing helper wrappers, sharing the existing BrowserLinkOpenSettings test fixture, and compacting simple settings parsing.

Constraint: PR CI must pass without changing .github/swift-file-length-budget.tsv

Rejected: Refresh Swift file length budget | user explicitly rejected accepting new line-count debt

Confidence: high

Scope-risk: narrow

Directive: Keep future PR clickability changes within existing budget or split files intentionally

Tested: python3 scripts/swift_file_length_budget.py --budget .github/swift-file-length-budget.tsv; ./tests/test_ci_swift_file_length_budget.sh; ./scripts/reload.sh --tag issue-3265-pr-clickable-toggle

Not-tested: Full local test suite, per repo policy

@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

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

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

13169-13187: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Non-clickable mode still renders PR rows as Button controls.

Line 13169 keeps the row wrapped in Button, and Line 13185 only disables hit-testing. That prevents mouse clicks, but it can still present interactive button semantics (e.g., accessibility/keyboard), which conflicts with the intended “plain non-interactive text” mode.

💡 Suggested fix (render non-clickable rows as plain content)
-                        Button(action: { openPullRequestLink(pullRequest.url) }) {
-                            HStack(spacing: 4) {
+                        let rowContent = HStack(spacing: 4) {
                                 PullRequestStatusIcon(status: pullRequest.status, color: pullRequestForegroundColor)
                                 Text("\(pullRequest.label) #\(pullRequestNumber)")
                                     .underline(settings.makesPullRequestsClickable)
                                 Text(pullRequestStatusLabel(pullRequest.status))
                                     .lineLimit(1)
                                 Spacer(minLength: 0)
                             }
                             .font(.system(size: 10, weight: .semibold))
                             .foregroundColor(pullRequestForegroundColor)
                             .opacity(pullRequest.isStale ? 0.5 : 1)
-                        }
-                        .buttonStyle(.plain)
-                        .allowsHitTesting(settings.makesPullRequestsClickable)
-                        .accessibilityIdentifier("SidebarPullRequestRow")
-                        .safeHelp(settings.makesPullRequestsClickable ? String(localized: "sidebar.pullRequest.openTooltip", defaultValue: "Open \(pullRequest.label) #\(pullRequestNumber)") : "")
+
+                        Group {
+                            if settings.makesPullRequestsClickable {
+                                Button(action: { openPullRequestLink(pullRequest.url) }) {
+                                    rowContent
+                                }
+                                .buttonStyle(.plain)
+                                .safeHelp(String(localized: "sidebar.pullRequest.openTooltip", defaultValue: "Open \(pullRequest.label) #\(pullRequestNumber)"))
+                            } else {
+                                rowContent
+                            }
+                        }
+                        .accessibilityIdentifier("SidebarPullRequestRow")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/ContentView.swift` around lines 13169 - 13187, The PR row is always
created as a Button and only has hit-testing disabled, which still exposes
interactive semantics; change the rendering to branch on
settings.makesPullRequestsClickable so that when clickable is true you create
the existing Button(action: openPullRequestLink...) with the HStack content, and
when clickable is false you render the same HStack as plain non-interactive
content (no Button, no .buttonStyle or .allowsHitTesting), preserving modifiers
like .font, .foregroundColor, .opacity,
.accessibilityIdentifier("SidebarPullRequestRow") and .safeHelp (use the same
tooltip string or empty string), and keep inner elements PullRequestStatusIcon,
Text("\(pullRequest.label) #\(pullRequestNumber)"), and
Text(pullRequestStatusLabel(...)) so the UI looks identical but has no
interactive semantics when settings.makesPullRequestsClickable is false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@Sources/ContentView.swift`:
- Around line 13169-13187: The PR row is always created as a Button and only has
hit-testing disabled, which still exposes interactive semantics; change the
rendering to branch on settings.makesPullRequestsClickable so that when
clickable is true you create the existing Button(action: openPullRequestLink...)
with the HStack content, and when clickable is false you render the same HStack
as plain non-interactive content (no Button, no .buttonStyle or
.allowsHitTesting), preserving modifiers like .font, .foregroundColor, .opacity,
.accessibilityIdentifier("SidebarPullRequestRow") and .safeHelp (use the same
tooltip string or empty string), and keep inner elements PullRequestStatusIcon,
Text("\(pullRequest.label) #\(pullRequestNumber)"), and
Text(pullRequestStatusLabel(...)) so the UI looks identical but has no
interactive semantics when settings.makesPullRequestsClickable is false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0601f752-7d48-4a51-991f-46e503c2544d

📥 Commits

Reviewing files that changed from the base of the PR and between 78b90c0 and 9f102d6.

📒 Files selected for processing (4)
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/cmuxApp.swift
  • cmuxTests/BrowserConfigTests.swift
✅ Files skipped from review due to trivial changes (1)
  • Sources/cmuxApp.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmuxTests/BrowserConfigTests.swift

The row now switches between a real button and plain content instead of disabling hit testing on a button. Settings copy also distinguishes hidden PR visibility from disabled clickability, and the UI test helper reports timeout failure as nil.

Constraint: Address PR review feedback without changing .github/swift-file-length-budget.tsv

Rejected: Keep disabled-hit-test Button semantics | still exposed interactive accessibility behavior in non-clickable mode

Confidence: high

Scope-risk: narrow

Directive: Non-clickable sidebar PR rows must remain plain content, not disabled or hit-test-suppressed controls

Tested: python3 scripts/swift_file_length_budget.py --budget .github/swift-file-length-budget.tsv; ./tests/test_ci_swift_file_length_budget.sh; jq empty Resources/Localizable.xcstrings; ./scripts/reload.sh --tag issue-3265-pr-clickable-toggle

Not-tested: Full local test suite, per repo policy

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

"value": "ステータスと番号付きのレビュー項目(PR/MRなど)を表示します。"
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused localization key duplicates existing subtitle string

Low Severity

The new localization key "settings.app.showPullRequests.subtitle.basic" is added to Localizable.xcstrings but is never referenced anywhere in the Swift source code. Its English and Japanese values are identical to the already-used "settings.app.showPullRequests.subtitle" key. This is dead localization data that will confuse future contributors about whether the .basic variant is consumed elsewhere.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f5dffb2. Configure here.

@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 the current code and only fix it if needed.

Inline comments:
In `@Resources/Localizable.xcstrings`:
- Around line 55986-56001: The two localization keys
settings.app.showPullRequests.subtitle.basic and
settings.app.showPullRequests.subtitle currently contain identical EN and JA
strings; decide which behavior you intend and update accordingly: if they
represent distinct UI states (non-clickable vs clickable), change
settings.app.showPullRequests.subtitle.basic to copy that communicates the
non-interactive state (e.g., “Review items are shown as plain, non-clickable
text.”) in both languages; otherwise remove the unused key so only the active
key (settings.app.showPullRequests.subtitle) remains to avoid translator drift,
and ensure any UI code referencing either key (search for references to
settings.app.showPullRequests.subtitle.basic and
settings.app.showPullRequests.subtitle) is updated to use the chosen key.
🪄 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

Run ID: 7b03b77e-c501-44cd-931c-82b6c25c9ff2

📥 Commits

Reviewing files that changed from the base of the PR and between 9f102d6 and f5dffb2.

📒 Files selected for processing (4)
  • Resources/Localizable.xcstrings
  • Sources/ContentView.swift
  • Sources/cmuxApp.swift
  • cmuxUITests/SidebarPullRequestInteractivityUITests.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • Sources/cmuxApp.swift
  • cmuxUITests/SidebarPullRequestInteractivityUITests.swift

Comment on lines +55986 to +56001
"settings.app.showPullRequests.subtitle.basic": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Display review items (PR/MR/etc.) with status and number."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ステータスと番号付きのレビュー項目(PR/MRなど)を表示します。"
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

settings.app.showPullRequests.subtitle.basic has identical content to the updated settings.app.showPullRequests.subtitle — clarify intent.

Both keys now share the same EN value ("Display review items (PR/MR/etc.) with status and number.") and the same JA value. Two outcomes are possible:

  1. Distinct UI states — if .basic is rendered when clickability is off and the parent key is rendered when clickability is on, the identical text gives users no indication that anything differs between the two states. The .basic subtitle should carry different copy that reflects non-interactive display (e.g., "Review items are shown as plain, non-clickable text.").
  2. Same UI state — if only one of the two keys is actually used in the final render path, the other is dead string content. The used key should be kept and the other removed to avoid translator drift (future updates to one key may not be reflected in the other).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Localizable.xcstrings` around lines 55986 - 56001, The two
localization keys settings.app.showPullRequests.subtitle.basic and
settings.app.showPullRequests.subtitle currently contain identical EN and JA
strings; decide which behavior you intend and update accordingly: if they
represent distinct UI states (non-clickable vs clickable), change
settings.app.showPullRequests.subtitle.basic to copy that communicates the
non-interactive state (e.g., “Review items are shown as plain, non-clickable
text.”) in both languages; otherwise remove the unused key so only the active
key (settings.app.showPullRequests.subtitle) remains to avoid translator drift,
and ensure any UI code referencing either key (search for references to
settings.app.showPullRequests.subtitle.basic and
settings.app.showPullRequests.subtitle) is updated to use the chosen key.

This branch was successfully deployed

1 active deployment
Preview – cmux — f5dffb2d Deployed May 4, 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.

Sidebar PR setting: option to show PR without making it clickable

1 participant