Skip to content

Issue 3081 workspace color left rail - #3082

Merged
austinywang merged 3 commits into
mainfrom
issue-3081-workspace-color-left-rail
Apr 22, 2026
Merged

austinywang merged 3 commits into
mainfrom
issue-3081-workspace-color-left-rail

Conversation

@austinywang

@austinywang austinywang commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • What changed?
  • Why?

Testing

  • How did you test this change?
  • What did you verify manually?

Demo Video

For UI or behavior changes, include a short demo video (GitHub upload, Loom, or other direct link).

  • Video URL or attachment:

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally
  • I added or updated tests for behavior changes
  • I updated docs/changelog if needed
  • I requested bot reviews after my latest commit (copy/paste block above or equivalent)
  • All code review bot comments are resolved
  • All human review comments are resolved

Note

Low Risk
Low risk UI-only change to sidebar workspace row rendering, with updated unit tests covering the new left-rail vs solid-fill behavior.

Overview
Updates sidebar workspace row styling so active rows no longer use the workspace’s custom color as the selection background in either leftRail or solidFill mode; selection now always uses sidebarSelectedWorkspaceBackgroundNSColor.

For leftRail, custom workspace colors are now rendered as an explicit leading rail (new sidebarWorkspaceRowExplicitRailNSColor + a leading Capsule overlay), while inactive custom-colored rows remain transparent. Unit tests are updated/added to lock in the new selection/background vs rail behavior.

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

Summary by CodeRabbit

  • New Features

    • Added a visual color indicator rail on the sidebar for workspaces with custom colors
  • Bug Fixes

    • Refined background styling for custom-colored workspaces in different selection states
    • Fixed background color behavior in left-rail layout for inactive workspaces
  • Tests

    • Enhanced test coverage for workspace color display across different states

Summary by cubic

Updated the sidebar to show workspace colors as a left-rail indicator and made the selected row background consistent across styles. Addresses Linear 3081 by removing custom-color fills for active rows and adding a clear color rail in left-rail mode.

  • New Features

    • Added an explicit leading color rail in leftRail using the workspace’s custom color.
  • Bug Fixes

    • Active rows now always use sidebarSelectedWorkspaceBackgroundNSColor in both leftRail and solidFill.
    • In leftRail, inactive custom-colored rows stay transparent; in solidFill, they use the custom color at 0.7 opacity. Unit tests added to lock this behavior.

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

@vercel

vercel Bot commented Apr 22, 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 Apr 22, 2026 0:43am

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The changes introduce an explicit leading rail visual indicator for custom-colored workspace rows in the sidebar when using the leftRail indicator style. The rail computation and background color styling logic are refactored to standardize on selected background colors, with tests validating the new rail color and background behavior.

Changes

Cohort / File(s) Summary
Explicit Leading Rail & Background Styling
Sources/ContentView.swift
Added sidebarWorkspaceRowExplicitRailNSColor(...) function to compute NSColor for explicit leading rail under .leftRail style with custom colors. Modified sidebarWorkspaceRowBackgroundStyle(...) to remove custom background fallbacks for selected rows and inactive single-selection rows. Updated TabItemView with showsLeadingRail and explicitRailColor computed properties, plus a 3pt capsule overlay rendering the rail on the leading edge.
Background & Rail Color Tests
cmuxTests/WorkspaceUnitTests.swift
Renamed two test functions to reflect updated assertion semantics for active custom-colored rows. Refactored assertions to compare background colors against sidebarSelectedWorkspaceBackgroundNSColor(...) instead of hard-coded hex values. Added three new test cases: inactive left-rail background transparency, explicit left-rail color resolution to custom hex, and solid-fill inactive opacity behavior (0.7).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #3038: Modifies ContentView.swift's sidebar row background and rail styling logic with overlapping color resolution and TabItemView updates.
  • #664: Updates TabItemView and ContentView selection coloring and background logic for workspace rows.
  • #2046: Modifies TabItemView's observation and update mechanisms alongside property and equality changes.

Poem

🐰 A rail of custom hue now shines so bright,
Leading left through workspace rows of might!
When colors dance and active states align,
The capsule glows—a customized design! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete; the template sections for 'What changed?' and 'Why?' are left empty, testing details are missing, no demo video is provided, and all checklist items are unchecked. Complete the 'What changed?' and 'Why?' sections, describe testing performed, provide a demo video URL or note, and update the checklist to reflect which items were completed.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title relates to the main change but is somewhat vague; it mentions 'workspace color left rail' but doesn't clearly convey that custom colors are now rendered as explicit leading rails instead of background fills.
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.

✏️ 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-3081-workspace-color-left-rail

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 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes how workspace custom colors are displayed in the sidebar's .leftRail indicator style: instead of tinting the row background with the custom color, it now renders a 3 px Capsule on the leading edge and keeps the standard selection background for active rows. The solidFill style is unaffected — inactive rows there still use the custom color as a background tint.

Confidence Score: 5/5

Safe to merge — logic is correct, well-tested, and only P2 style notes remain.

All findings are P2 (dead computation and a local binding name that shadows an outer property). Neither affects correctness, and the three new tests clearly exercise the new code paths.

No files require special attention.

Important Files Changed

Filename Overview
Sources/ContentView.swift Adds sidebarWorkspaceRowExplicitRailNSColor and a Capsule overlay to render the custom workspace color as a 3 px leading rail in .leftRail mode; removes the custom-color background tint for that mode. Minor: customBackground is still eagerly computed even for .leftRail where it is now unused, and the local binding railColor shadows the outer property of the same name.
cmuxTests/WorkspaceUnitTests.swift Renames two existing tests to match the updated behaviour, and adds three new tests covering: leftRail transparent background for inactive custom-coloured rows, explicit rail color resolution, and solidFill inactive custom background. Good coverage of the new path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[TabItemView renders row] --> B{activeTabIndicatorStyle}
    B -->|leftRail| C{customColorHex set?}
    B -->|solidFill| G{isActive?}

    C -->|yes| D[sidebarWorkspaceRowExplicitRailNSColor → NSColor]
    C -->|no| E[explicitRailColor = nil / showsLeadingRail = false]

    D --> F[Render 3px Capsule overlay at leading edge]
    E --> NoRail[No rail rendered]

    F --> BG_LR{isActive?}
    BG_LR -->|yes| SelBG[background = selectedBackground, opacity 1]
    BG_LR -->|no| ClearBG[background = clear]

    G -->|yes| SelBG2[background = selectedBackground, opacity 1]
    G -->|no| H{customColorHex set?}
    H -->|yes| CustomBG[background = customColor, opacity 0.35/0.7]
    H -->|no| I{isMultiSelected?}
    I -->|yes| AccentBG[background = accentBackground, opacity 0.25]
    I -->|no| ClearBG2[background = clear]
Loading

Comments Outside Diff (1)

  1. Sources/ContentView.swift, line 235-241 (link)

    P2 Dead computation for .leftRail in sidebarWorkspaceRowBackgroundStyle

    customBackground is always computed at line 235 (including the WorkspaceTabColorSettings.displayNSColor call with forceBright: true), but after this refactor, it's never referenced inside the .leftRail switch arm. The computation only matters for .solidFill. Consider moving the binding inside the .solidFill case — or deferring it — to avoid the unnecessary call.

Reviews (1): Last reviewed commit: "Keep selected workspace fill above custo..." | Re-trigger Greptile

Comment thread Sources/ContentView.swift
Comment on lines +13972 to +13980
guard let railColor = sidebarWorkspaceRowExplicitRailNSColor(
activeTabIndicatorStyle: activeTabIndicatorStyle,
customColorHex: workspaceSnapshot.customColorHex,
colorScheme: colorScheme
) else {
return nil
}
return Color(nsColor: railColor).opacity(0.95)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Local binding shadows the outer railColor property

Inside explicitRailColor, the guard let railColor = ... binding is the same name as the struct-level railColor: Color computed property. Swift resolves this correctly (the guard's binding is NSColor, the outer property is Color), but it silently shadows the outer name within the guard's scope, which can confuse readers. Consider renaming the local binding to nsColor or resolvedNSColor for clarity.

Suggested change
guard let railColor = sidebarWorkspaceRowExplicitRailNSColor(
activeTabIndicatorStyle: activeTabIndicatorStyle,
customColorHex: workspaceSnapshot.customColorHex,
colorScheme: colorScheme
) else {
return nil
}
return Color(nsColor: railColor).opacity(0.95)
}
guard let nsColor = sidebarWorkspaceRowExplicitRailNSColor(
activeTabIndicatorStyle: activeTabIndicatorStyle,
customColorHex: workspaceSnapshot.customColorHex,
colorScheme: colorScheme
) else {
return nil
}
return Color(nsColor: nsColor).opacity(0.95)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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

🧹 Nitpick comments (1)
Sources/ContentView.swift (1)

13515-13524: Make the decorative rail non-interactive.

The rail is visual-only; disabling hit testing/accessibility avoids any chance of stealing clicks, drags, or VoiceOver focus from the row. Please verify selecting/dragging from the rail area still works.

Suggested hardening
                 .overlay(alignment: .leading) {
                     if showsLeadingRail {
                         Capsule(style: .continuous)
                             .fill(railColor)
                             .frame(width: 3)
                             .padding(.leading, 4)
                             .padding(.vertical, 5)
                             .offset(x: -1)
+                            .allowsHitTesting(false)
+                            .accessibilityHidden(true)
                     }
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/ContentView.swift` around lines 13515 - 13524, The decorative leading
rail inside the overlay (the block conditioned on showsLeadingRail) should be
made non-interactive so it doesn't steal pointer or VoiceOver focus; update the
Capsule created in the overlay (the view using railColor) to disable
interactions and accessibility by applying SwiftUI modifiers such as
allowsHitTesting(false) and accessibilityHidden(true) (or accessibility(hidden:
true)) to that Capsule inside the overlay closure so row selection/dragging
still works when started from the rail area.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Sources/ContentView.swift`:
- Around line 13515-13524: The decorative leading rail inside the overlay (the
block conditioned on showsLeadingRail) should be made non-interactive so it
doesn't steal pointer or VoiceOver focus; update the Capsule created in the
overlay (the view using railColor) to disable interactions and accessibility by
applying SwiftUI modifiers such as allowsHitTesting(false) and
accessibilityHidden(true) (or accessibility(hidden: true)) to that Capsule
inside the overlay closure so row selection/dragging still works when started
from the rail area.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d8573d2-7ccb-419b-9a55-99186b3a52e2

📥 Commits

Reviewing files that changed from the base of the PR and between bd02bed and b32086f.

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

@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 2 potential issues.

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 b32086f. Configure here.

)

XCTAssertNotNil(railColor)
XCTAssertEqual(railColor?.hexString(), "#C0392B")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Test expects raw hex but function applies color brightening

Medium Severity

The test testLeftRailResolvesExplicitRailColorForCustomColoredWorkspaceRow asserts that railColor?.hexString() equals "#C0392B", but sidebarWorkspaceRowExplicitRailNSColor calls WorkspaceTabColorSettings.displayNSColor with forceBright: true. This triggers brightenedForDarkAppearance, which boosts brightness and saturation of the input color, producing a hex value different from the raw input "#C0392B". This test will fail.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b32086f. Configure here.

Comment thread Sources/ContentView.swift
if let customBackground {
return SidebarWorkspaceRowBackgroundStyle(
color: customBackground,
opacity: isMultiSelected ? 0.35 : 0.7

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 customBackground computation in leftRail branch

Low Severity

The customBackground variable is computed for all activeTabIndicatorStyle values but is now only consumed in the solidFill branch. In the leftRail branch it's unused dead computation. Additionally, the forceBright: activeTabIndicatorStyle == .leftRail expression is misleading — it evaluates to true only in the path that never reads customBackground, and is always false when the variable is actually used, making it functionally equivalent to forceBright: false.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b32086f. Configure here.

@austinywang
austinywang merged commit c47634f into main Apr 22, 2026
25 checks passed
rodchristiansen pushed a commit to rodchristiansen/cmux that referenced this pull request Sep 2, 2026
* Add regression test for workspace color left rail

* Fix workspace color left rail rendering

* Keep selected workspace fill above custom color

This branch was successfully deployed

1 active deployment
Preview — b32086f3 Deployed Apr 22, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant