Skip to content

Fix iOS unread count badge contrast - #6524

Merged
lawrencecchen merged 4 commits into
mainfrom
feat-ios-count-contrast
Jun 21, 2026
Merged

lawrencecchen merged 4 commits into
mainfrom
feat-ios-count-contrast

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Make the iOS workspace back-button unread count badge use a fixed white circle with black text.
  • Avoid adaptive .primary/systemBackground colors for this badge because the toolbar sits over dark terminal chrome.

Verification

  • xcodebuildmcp swift-package build --package-path Packages/iOS/CmuxMobileShellUI --target-name CmuxMobileShellUI --configuration debug failed before compile because this worktree has no local GhosttyKit.xcframework.
  • ./ios/scripts/reload-cloud.sh --tag dog --device-id E4058DA9-F4C7-52DD-951D-0354061B8E89 --wait 1200 succeeded and installed dev.cmux.ios.dog on Lawrence’s iPhone.
  • ./scripts/reload-cloud.sh --tag dog succeeded and installed cmux DEV dog.app.
  • ./ios/scripts/reload.sh --tag dog --simulator "iPhone 17" --no-launch was blocked by the repo guard against local Xcode builds.
  • xcodebuildmcp device launch --device-id E4058DA9-F4C7-52DD-951D-0354061B8E89 --bundle-id dev.cmux.ios.dog launched successfully.

Dogfood

Open a workspace detail on iPhone with unread activity in another workspace. The back-button count should render as black text on a white circular badge over the dark terminal bar.


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


Note

Low Risk
Localized UI styling, CI cache hardening, and accessibility-aligned zoom behavior with unit tests; no auth, data, or security surface.

Overview
Fixes the iOS workspace back-button unread count badge so it stays readable over dark terminal toolbar chrome. The badge no longer uses adaptive .primary / systemBackground; it takes an explicit WorkspaceBackButtonBadgeContrast (.darkBackground → white circle + black text, .lightBackground → black circle + white text). WorkspaceShellView passes .darkBackground on the compact stack detail toolbar.

CI (swift-package-tests): After restoring GhosttyKit.xcframework, the workflow now checks for Info.plist, deletes a corrupt cache entry, and re-downloads when invalid (not only on cache miss). GhosttyKit package test failure handling renames the exit-status variable to test_status (behavior unchanged).

macOS canvas: Discrete keyboard/wheel zoom respects Reduce Motion via an injectable shouldReduceMotionForDiscreteZoom on CanvasRootView (skips animation and applies zoom immediately). CanvasViewportZoomTests stub that hook and add coverage for the reduce-motion path.

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


Summary by cubic

Fixes the iOS workspace back-button unread badge contrast with fixed black/white colors so the count stays readable over dark terminal chrome. Also hardens CI cache validation for GhosttyKit.xcframework and adds a Reduce Motion seam and tests for macOS discrete zoom.

  • New Features

    • Adds WorkspaceBackButtonBadgeContrast with .darkBackground (white badge, black text) and .lightBackground (black badge, white text; default).
    • WorkspaceShellView passes .darkBackground for the compact workspace toolbar.
  • Refactors

    • Moves WorkspaceBackButtonBadgeContrast into its own file.
    • CI: Validates cached GhosttyKit.xcframework by checking Info.plist and re-downloads when invalid.
    • macOS: Injects shouldReduceMotionForDiscreteZoom in CanvasRootView and adds tests to verify immediate zoom when Reduce Motion is on.

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

Review in cubic

Summary by CodeRabbit

  • Style
    • Updated the workspace back button unread-count badge to use an explicit black/white contrast scheme instead of adaptive/primary-based tinting.
    • Added dedicated badge contrast presets for dark vs. light terminal chrome and applied them to badge fill and text for consistent readability.
  • Chores
    • Improved CI cache validation for a prebuilt framework by detecting missing metadata after restore and forcing a re-download when invalid.
    • Tightened the unit test runner’s failure handling for more reliable nonzero-exit behavior.

@vercel

vercel Bot commented Jun 21, 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 21, 2026 6:57am
cmux-staging Building Building Preview, Comment Jun 21, 2026 6:57am

@coderabbitai

coderabbitai Bot commented Jun 21, 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

The unread-count badge on the workspace back button is refactored to use an explicit WorkspaceBackButtonBadgeContrast enum that selects black and white colors instead of adaptive/primary-tinted colors. Computed properties map the contrast parameter to actual fill and text colors, and WorkspaceShellView passes .darkBackground to configure the badge appearance. Separately, the CI workflow adds validation checks for the cached GhosttyKit.xcframework and improves test status variable handling to ensure proper failure propagation.

Changes

Workspace Back Button Badge Styling Refactoring

Layer / File(s) Summary
Badge contrast enum and property
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceBackButtonBadgeContrast.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceBackButton.swift
Introduces WorkspaceBackButtonBadgeContrast enum with darkBackground and lightBackground cases and a badgeContrast property defaulting to .lightBackground.
Color computation and badge view styling
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceBackButton.swift
Adds badgeFillColor and badgeTextColor computed properties that map BadgeContrast cases to explicit black and white color values, then applies these to the badge foreground and circular background instead of hardcoded adaptive/primary-tinted colors.
Parent view configuration
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceShellView.swift
WorkspaceShellView passes badgeContrast: .darkBackground when configuring the workspace back button badge.

CI Workflow Robustness Improvements

Layer / File(s) Summary
GhosttyKit cache integrity check
.github/workflows/ci.yml
CI workflow swift-package-tests job validates the restored GhosttyKit.xcframework by checking for Info.plist and records the result; the subsequent download step now runs when validation fails instead of relying on cache-hit status alone.
Test failure status propagation
.github/workflows/ci.yml
In the swift test loop for CmuxTerminal and CmuxTerminalCore, the nonzero-exit status variable is renamed from status to test_status and the failure path now explicitly exits with the captured status after diagnostic checks.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • manaflow-ai/cmux#6350: Implements the original unread-count badge on the workspace back button — the view and styling properties being parameterized in this PR.

Poem

A badge once fixed, now takes on a dress,
Dark chrome or light — it adapts, I confess!
An enum declares the contrast so clear,
While caches are checked to ensure they're sincere.
🐇✨ Explicit styling, validated with care!

🚥 Pre-merge checks | ✅ 22 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description covers the core changes well (badge contrast fix, CI cache validation) with verification steps and dogfood instructions, but omits the Testing section template and demo video requirements. Add a Testing section explicitly detailing how the changes were tested (e.g., simulator/device verification, unit tests), and clarify if a demo video is applicable for the badge UI change.
✅ Passed checks (22 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Fix iOS unread count badge contrast' accurately summarizes the main change—fixing contrast issues with the unread badge by introducing fixed color pairs instead of adaptive colors.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 Changes are confined to SwiftUI View types and a pure value enum for UI styling. No implicit MainActor issues, Sendable/Codable concerns, or background context access introduced.
Cmux Swift Blocking Runtime ✅ Passed PR introduces no blocking/timing-based synchronization primitives: changes are localized to SwiftUI badge styling and CI cache validation with no Task.sleep, semaphores, locks, or blocking calls.
Cmux Expensive Synchronous Load ✅ Passed PR adds only SwiftUI styling changes (badge color properties) and CI cache validation—no expensive synchronous loads on main actor or interactive paths per the rule.
Cmux Cache Substitution Correctness ✅ Passed All changes are either transient UI hints (badge styling) or proper cache validation with cold-cache fallback (GhosttyKit validation deletes invalid caches and forces fresh downloads).
Cmux No Hacky Sleeps ✅ Passed PR changes are out of scope: iOS Swift files are covered by swift-blocking-runtime.md; GitHub Actions YAML is intentionally out of scope per rule line 5. No TypeScript, JavaScript, or shell runtime...
Cmux Algorithmic Complexity ✅ Passed All changes operate on fixed-size collections or single operations. SwiftUI styling uses O(1) enum switches. CI validates a single file and tests a hardcoded 17-package list without nested scans or...
Cmux Swift Concurrency ✅ Passed PR contains only SwiftUI UI styling updates and CI validation shell scripts. No legacy async patterns (DispatchQueue, Combine, completion handlers, or fire-and-forget Tasks) are introduced.
Cmux Swift @Concurrent ✅ Passed The PR's Swift changes include a @Sendable () async closure for pull-to-refresh that intentionally stays on MainActor to coordinate UI-bound work. This is an allowed pattern per the concurrent an...
Cmux Swift File And Package Boundaries ✅ Passed PR contains a focused UI bug fix with a new 6-line single-responsibility enum and minimal additions to two small files (72 and 339 lines, both well under 800-line threshold). No mixed responsibilit...
Cmux Swiftpm Lockfiles ✅ Passed All SwiftPM lockfile requirements met: .gitignore does not ignore Package.resolved; cmux-owned packages with external dependencies include Package.resolved diffs; root Xcode Package.resolved is com...
Cmux Swift Logging ✅ Passed Swift changes contain no logging violations: no print/debugPrint/dump/NSLog, no ad-hoc logging, no sensitive data exposure. Changes are pure UI styling updates only.
Cmux User-Facing Error Privacy ✅ Passed No user-facing errors, alerts, or sensitive information exposed. Changes are limited to UI badge styling and CI cache validation; no vendor names, credentials, implementation details, or raw upstre...
Cmux Full Internationalization ✅ Passed PR introduces no new user-facing text. Existing localization uses L10n.string() API, new enum contains only developer documentation comments (allowed), and CI changes are operational infrastructure...
Cmux Swiftui State Layout ✅ Passed No SwiftUI state violations found. Changes introduce simple value enum and parameter to pass badge contrast; no new ObservableObject, @Published, GeometryReader, lazy list store refs, or render-tim...
Cmux Architecture Rethink ✅ Passed PR contains only small correctness fixes with clear ownership: badge styling via simple value-type enum parameter and filesystem-based CI cache validation. No timing dependencies, observers, mutabl...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR modifies only badge styling and contrast enum for an existing UI component, not standalone cmux-owned windows (NSWindow, NSPanel, NSWindowController, Window, WindowGroup). No violations of auxil...
Cmux Source Artifacts ✅ Passed All changed files are legitimate hand-written source code and configurations (3x .swift files in Sources/, 1x .github/workflows/ci.yml). No build artifacts, generated code, caches, DerivedData, tem...
Cmux No Test Or Debug Seam In Production Source ✅ Passed All Swift files in production Sources/ paths pass the check. WorkspaceBackButton.swift and WorkspaceBackButtonBadgeContrast.swift contain no test/debug seams. WorkspaceShellView.swift's #if DEBUG b...

✏️ 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 feat-ios-count-contrast

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 Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the iOS workspace back-button unread badge to use fixed black/white colors instead of adaptive .primary/systemBackground, ensuring legibility over dark terminal chrome. Three independently scoped changes: iOS badge contrast, a macOS reduce-motion injectable closure with new test coverage, and a CI step that re-downloads GhosttyKit.xcframework when a cached copy is missing its Info.plist.

  • iOS badge: New WorkspaceBackButtonBadgeContrast enum drives fixed fill/text colors; WorkspaceShellView passes .darkBackground for the compact toolbar.
  • macOS canvas: shouldReduceMotionForDiscreteZoom closure injected into CanvasRootView (default reads system setting unchanged); existing tests pinned to { false } for determinism, new test verifies immediate zoom when reduce motion is on.
  • CI: Validates cached framework integrity before deciding whether to re-download; renames status to test_status to avoid shell-variable ambiguity.

Confidence Score: 5/5

Safe to merge — all three change areas are narrow and independently correct with no shared state risk.

The iOS badge fix replaces adaptive color lookups with two hard-coded fixed-color cases, the macOS closure injection preserves the default system behavior unchanged, and the CI cache validation only adds a guard that forces a clean download when the cached framework is missing its Info.plist. No auth, data, or payment paths are touched, and the new test correctly exercises the reduce-motion branch in isolation.

No files require special attention.

Important Files Changed

Filename Overview
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceBackButtonBadgeContrast.swift New enum defining two fixed-color contrast presets for the back-button badge; clean, minimal file.
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceBackButton.swift Replaces adaptive .primary/.systemBackground with badgeContrast-driven fixed colors; default remains .lightBackground for backward compatibility.
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceShellView.swift One-line change passing .darkBackground to WorkspaceBackButton for the compact toolbar; straightforward and correct.
Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasRootView.swift Adds injectable shouldReduceMotionForDiscreteZoom closure with sensible default; minor comment tightening on pendingViewportRestore.
Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasRootView+Viewport.swift Calls the injected shouldReduceMotionForDiscreteZoom closure instead of NSWorkspace directly; behavior is identical in production.
Packages/macOS/CmuxCanvasUI/Tests/CmuxCanvasUITests/CanvasViewportZoomTests.swift Makes existing tests deterministic by explicitly setting shouldReduceMotionForDiscreteZoom = { false }, and adds a new test verifying immediate zoom with reduce motion on.
.github/workflows/ci.yml Adds a GhosttyKit.xcframework validation step that removes the directory and forces re-download on a cache miss of Info.plist; renames status to test_status for clarity.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    WSV[WorkspaceShellView\nbadgeContrast: .darkBackground] --> WBB[WorkspaceBackButton]
    WBB --> C{badgeContrast}
    C -->|.darkBackground\ndark terminal chrome| D[White fill · Black text]
    C -->|.lightBackground\ndefault| E[Black fill · White text]

    subgraph macOS
        CRV[CanvasRootView\nshouldReduceMotionForDiscreteZoom\ndefault = NSWorkspace] --> VP[CanvasRootView+Viewport\nsetMagnification]
        VP --> RM{reduceMotion?}
        RM -->|true| IM[applyViewport immediately]
        RM -->|false| AN[animateDiscreteZoom]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    WSV[WorkspaceShellView\nbadgeContrast: .darkBackground] --> WBB[WorkspaceBackButton]
    WBB --> C{badgeContrast}
    C -->|.darkBackground\ndark terminal chrome| D[White fill · Black text]
    C -->|.lightBackground\ndefault| E[Black fill · White text]

    subgraph macOS
        CRV[CanvasRootView\nshouldReduceMotionForDiscreteZoom\ndefault = NSWorkspace] --> VP[CanvasRootView+Viewport\nsetMagnification]
        VP --> RM{reduceMotion?}
        RM -->|true| IM[applyViewport immediately]
        RM -->|false| AN[animateDiscreteZoom]
    end
Loading

Reviews (7): Last reviewed commit: "Validate GhosttyKit package test cache" | Re-trigger Greptile

@lawrencecchen
lawrencecchen force-pushed the feat-ios-count-contrast branch from 927332d to 1afa9c8 Compare June 21, 2026 06:12
@lawrencecchen
lawrencecchen merged commit 915b3e5 into main Jun 21, 2026
34 of 36 checks passed
@lawrencecchen
lawrencecchen deleted the feat-ios-count-contrast branch June 21, 2026 06:30

This branch was successfully deployed

1 active deployment
Preview – cmux — 1afa9c86 Deployed Jun 21, 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