Skip to content

Normalize browser navigation icon sizing - #6208

Closed
lawrencecchen wants to merge 1 commit into
mainfrom
task-browser-button-icon-sizing
Closed

lawrencecchen wants to merge 1 commit into
mainfrom
task-browser-button-icon-sizing

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Define measured browser navigation icon variants for back, forward, reload, and stop.
  • Derive one shared navigation raster size from the largest measured SF Symbol edge and scale it from the user tab bar font size.
  • Route the toolbar buttons through the shared variant sizing path.

Testing

  • xcodebuild -project cmux.xcodeproj -scheme cmux-unit -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/cmux-btnsz test -only-testing:cmuxTests/BrowserChromeMetricsTests passed, 9 Swift Testing tests.\n- ./scripts/reload-cloud.sh --tag btnsz passed.\n- Launched tagged app, opened browser surface, verified back/forward/reload toolbar.\n- Navigated to a deliberately hanging local URL and verified reload flipped to the stop xmark toolbar variant.

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


Note

Low Risk
Localized browser chrome layout and metrics only; minor default-scale visual tweak (nav raster 12→15) with no auth, data, or networking impact.

Overview
Browser back, forward, reload, and stop controls now share one scaled raster size derived from measured SF Symbol bounds, so the reload/stop toggle and chevrons no longer shift layout.

A new BrowserNavigationToolbarIcon type centralizes symbol names, per-variant reference measurements, and reloadOrStop(isLoading:). BrowserChromeMetrics drops navigationIconFontSize in favor of navigationIconRasterSize (reference base 15, the max intrinsic edge across the four glyphs) plus navigationIconRasterSize(for:). The address bar button bar routes all three nav buttons through that path with cmuxSymbolRasterSize.

Tests rename the legacy fixture to reference sizes, expect 15 for navigation raster at scale 1, and add coverage for measured bounds and a single raster size across variants.

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


Summary by cubic

Normalized browser navigation icon sizing so back, forward, reload, and stop share one consistent raster size. Prevents size jumps when reload switches to stop and keeps the toolbar scale aligned with the tab bar font size.

  • Bug Fixes
    • Added BrowserNavigationToolbarIcon with measured SF Symbol bounds and a single shared raster size (derived from the largest edge, scaled from the tab bar font).
    • Replaced navigationIconFontSize with navigationIconRasterSize and routed toolbar buttons through the shared sizing path.
    • Expanded tests to cover reference sizes, scaling/clamping, measured symbol bounds, and variant consistency.

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

Review in cubic

Summary by CodeRabbit

  • Refactor

    • Improved internal architecture for navigation icon handling (back, forward, reload, stop) to provide better consistency and maintainability in sizing and symbol management across the browser toolbar.
  • Tests

    • Updated test coverage to validate navigation icon sizing and variants against reference metrics.

@vercel

vercel Bot commented Jun 16, 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 16, 2026 1:49am
cmux-staging Building Building Preview, Comment Jun 16, 2026 1:49am

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f2fad5c6-ecaa-4a92-b7cd-1a4fd0c1f331

📥 Commits

Reviewing files that changed from the base of the PR and between fce1524 and ffa5bf0.

📒 Files selected for processing (3)
  • Sources/Panels/BrowserChromeMetrics.swift
  • Sources/Panels/BrowserPanelView.swift
  • cmuxTests/BrowserChromeMetricsTests.swift

📝 Walkthrough

Walkthrough

Introduces a BrowserNavigationToolbarIcon enum in BrowserChromeMetrics.swift with SF Symbol names, measured reference geometry, and a reloadOrStop(isLoading:) helper. Replaces navigationIconFontSize with navigationIconRasterSize derived from the enum's max reference edge. BrowserPanelView omnibar buttons adopt the new enum and sizing API. Tests are updated and extended accordingly.

Changes

Navigation Icon Raster Sizing Refactor

Layer / File(s) Summary
BrowserNavigationToolbarIcon enum and raster size API
Sources/Panels/BrowserChromeMetrics.swift
Adds BrowserNavigationToolbarIcon enum with symbolName, measuredReferenceSize, measuredReferenceMaxEdge, and reloadOrStop(isLoading:). Replaces navigationIconFontSize with navigationIconRasterSize (scaled from max reference edge across all cases) and adds navigationIconRasterSize(for:) method.
BrowserPanelView omnibar button wiring
Sources/Panels/BrowserPanelView.swift
Back, forward, and reload/stop buttons switch from hardcoded SF Symbol strings and font-size-based sizing to BrowserNavigationToolbarIcon.symbolName and chromeMetrics.navigationIconRasterSize(for:).
BrowserChromeMetricsTests updates and new icon tests
cmuxTests/BrowserChromeMetricsTests.swift
Replaces legacy constants tuple with reference tuple; extends scaling/clamp/fallback assertions to cover navigationIconRasterSize; adds new tests for icon measuredReferenceSize values, uniform raster size across variants, and reloadOrStop state mapping.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • manaflow-ai/cmux#5464: Introduced BrowserChromeMetrics scaling with navigationIconFontSize in the same files — this PR directly replaces that font-size field with the new raster-size approach.
  • manaflow-ai/cmux#5999: Changed SF Symbol rasterization sizing in BrowserPanelView via cmuxSymbolRasterSize, touching the same omnibar navigation button sizing path modified here.

Poem

🐇 Hop hop, the icons now know their own name,
No more hardcoded strings — enum's the game!
Raster size blooms from a reference edge wide,
Back, forward, reload, and stop — all unified!
The metrics now measure what truly was there,
A rabbit-approved refactor, done with great care! ✨


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Cmux Source Artifacts ❌ Error PR adds 25 files from .claude/ (commands, skills, scheduled_tasks.lock)—a hidden scratch directory explicitly listed in source-control-artifacts.md as prohibited. Remove all .claude/ files from the PR or add a broad .claude/ pattern to .gitignore (only .claude/worktrees/ is currently excluded).
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main change: normalizing browser navigation icon sizing across back, forward, reload, and stop icons.
Description check ✅ Passed The description covers all required template sections: summary (what changed and why), testing (how tested and what verified), and includes checklist items and bot review triggers.
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 Both new types (BrowserNavigationToolbarIcon enum and BrowserChromeMetrics struct) are immutable pure value types with no mutable state, never accessed from background contexts, and used only in Sw...
Cmux Swift Blocking Runtime ✅ Passed The diff introduces asyncAfter for focus flash UI animation timing in BrowserPanelView.swift, which qualifies as the allowed exception: short user-visible animation delays with visual timing only,...
Cmux Expensive Synchronous Load ✅ Passed PR introduces only pure metrics/sizing computations (enum icon data, scaling calculations) with no expensive synchronous loaders like RestorableAgentSessionIndex.load(), sysctl calls, or disk I/O o...
Cmux Cache Substitution Correctness ✅ Passed PR involves only UI metric computations (navigational icon sizing); no persistence, history, undo, or snapshot operations that would trigger cache substitution rules.
Cmux No Hacky Sleeps ✅ Passed All PR changes are in Swift files. The runtime-no-hacky-sleeps policy applies only to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts; Swift concerns are covered by a separate ch...
Cmux Algorithmic Complexity ✅ Passed The code iterates over BrowserNavigationToolbarIcon.allCases (4 enum cases) in a tiny fixed-size collection with explicit bounds in navigationIconReferenceRasterSize; per rules, this falls under th...
Cmux Swift Concurrency ✅ Passed PR introduces only pure value types and synchronous computations; no Dispatch queues, Combine, completion handlers, or Tasks with problematic lifecycle patterns are added or expanded.
Cmux Swift @Concurrent ✅ Passed PR introduces no async functions, @concurrent annotations, or heavy CPU/I/O operations. All changes are pure computed properties and synchronous helpers with no actor isolation concerns.
Cmux Swift File And Package Boundaries ✅ Passed PR adds 50 lines to new BrowserChromeMetrics.swift (112 total, under 400-line limit) and 240 lines to existing BrowserPanelView.swift (7928 total, already over 800-line baseline). The 240-line addi...
Cmux Swift Logging ✅ Passed PR adds new browser icon sizing logic without introducing print, debugPrint, dump, NSLog, ad hoc logging, or sensitive data exposure in production code.
Cmux User-Facing Error Privacy ✅ Passed PR modifies UI sizing metrics and navigation buttons without adding or changing user-facing error messages, alerts, or sensitive information. All new code is either developer-only tests or referenc...
Cmux Full Internationalization ✅ Passed PR only adds internal metrics and refactors to use existing localized strings (browser.goBack, goForward, reload, stop) that already have full translations in Localizable.xcstrings. No new user-fac...
Cmux Swiftui State Layout ✅ Passed PR introduces icon enum and metrics struct (pure value types, no SwiftUI state) and updates button code to use them without adding @Observable/@published, GeometryReader changes, store references,...
Cmux Architecture Rethink ✅ Passed PR normalizes navigation icon sizing through clean architectural refactoring: a pure value enum with measured reference sizes, computed scaling logic in BrowserChromeMetrics, and consolidated UI bu...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR does not add or materially change standalone cmux-owned windows. Changes are limited to icon sizing metrics and button rendering logic without creating NSWindow, NSPanel, NSWindowController, or...
✨ 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 task-browser-button-icon-sizing

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 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR normalizes browser navigation icon sizing by introducing a BrowserNavigationToolbarIcon enum with measured SF Symbol reference dimensions, and derives a single shared raster size from the largest intrinsic edge. This ensures the reload↔stop swap no longer resizes the button and that back/forward share the same optical box.

  • Adds BrowserNavigationToolbarIcon (back/forward/reload/stop) with hardcoded measured reference bounds and a shared navigationIconRasterSize derivation in BrowserChromeMetrics.
  • Routes the three toolbar buttons in BrowserPanelView through the new enum and navigationIconRasterSize(for:) method, replacing the old font-size-based sizing path.
  • Expands the test suite with two new tests covering symbol names, measured bounds, uniform sizing across all variants, and reloadOrStop factory behavior.

Confidence Score: 4/5

Safe to merge — the change is a well-scoped visual normalization with 9 passing unit tests and no behavioural changes to navigation logic.

The implementation is clean and the test coverage is thorough. The only items worth addressing are a computed static var that would be better as a static let, and a missing doc comment on a method whose parameter is intentionally unused — neither affects correctness or runtime behaviour.

Sources/Panels/BrowserChromeMetrics.swift — the two style suggestions above both apply here.

Important Files Changed

Filename Overview
Sources/Panels/BrowserChromeMetrics.swift Adds BrowserNavigationToolbarIcon enum with measured reference sizes and a shared raster-size derivation; minor: navigationIconReferenceRasterSize is a computed static var where a static let would be more appropriate.
Sources/Panels/BrowserPanelView.swift Toolbar buttons routed through BrowserNavigationToolbarIcon enum and the shared raster-size path; no logic changes, localization untouched.
cmuxTests/BrowserChromeMetricsTests.swift Tests updated to reference navigationIconRasterSize; two new tests cover enum symbol names, measured bounds, shared sizing, and reloadOrStop logic.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[tabBarFontSize] --> B[BrowserChromeMetrics.scale]
    D[BrowserNavigationToolbarIcon.allCases] --> E[measuredReferenceMaxEdge per case]
    E --> F["max() → 15pt from reload height"]
    F --> G[navigationIconReferenceRasterSize]
    G --> C[navigationIconRasterSize = scaled base]
    B --> C
    C --> H["navigationIconRasterSize(for: .back)"]
    C --> I["navigationIconRasterSize(for: .forward)"]
    C --> J["navigationIconRasterSize(for: reloadOrStop)"]
    H --> K[back button Image]
    I --> L[forward button Image]
    J --> M[reload / stop button Image]
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
    A[tabBarFontSize] --> B[BrowserChromeMetrics.scale]
    D[BrowserNavigationToolbarIcon.allCases] --> E[measuredReferenceMaxEdge per case]
    E --> F["max() → 15pt from reload height"]
    F --> G[navigationIconReferenceRasterSize]
    G --> C[navigationIconRasterSize = scaled base]
    B --> C
    C --> H["navigationIconRasterSize(for: .back)"]
    C --> I["navigationIconRasterSize(for: .forward)"]
    C --> J["navigationIconRasterSize(for: reloadOrStop)"]
    H --> K[back button Image]
    I --> L[forward button Image]
    J --> M[reload / stop button Image]
Loading

Reviews (1): Last reviewed commit: "Normalize browser navigation icon sizing" | Re-trigger Greptile

Comment on lines +105 to +109
private static var navigationIconReferenceRasterSize: CGFloat {
BrowserNavigationToolbarIcon.allCases
.map(\.measuredReferenceMaxEdge)
.max() ?? 12
}

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 All four measuredReferenceSize values are hardcoded constants, so navigationIconReferenceRasterSize always returns 15 and never changes. A computed static var re-traverses allCases, allocates an intermediate array, and re-finds the max on every access to navigationIconRasterSize. Using static let computes the value once (lazily on first use) and is clearer about the intent.

Suggested change
private static var navigationIconReferenceRasterSize: CGFloat {
BrowserNavigationToolbarIcon.allCases
.map(\.measuredReferenceMaxEdge)
.max() ?? 12
}
private static let navigationIconReferenceRasterSize: CGFloat =
BrowserNavigationToolbarIcon.allCases
.map(\.measuredReferenceMaxEdge)
.max() ?? 12

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!

Comment on lines +101 to +103
func navigationIconRasterSize(for _: BrowserNavigationToolbarIcon) -> CGFloat {
return navigationIconRasterSize
}

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 The for: parameter is silently discarded — every call site returns navigationIconRasterSize regardless of which icon is passed. A brief doc comment clarifying this is intentional (all icons share one box so the reload↔stop swap doesn't resize) would prevent future readers from assuming per-icon sizing is already in effect and that changes to the enum will automatically produce different sizes.

Suggested change
func navigationIconRasterSize(for _: BrowserNavigationToolbarIcon) -> CGFloat {
return navigationIconRasterSize
}
/// Returns the shared raster size for every navigation toolbar icon.
/// All variants deliberately share one box so the reload↔stop swap does
/// not cause the button to resize. If per-icon sizing is added in the
/// future, differentiate here.
func navigationIconRasterSize(for _: BrowserNavigationToolbarIcon) -> CGFloat {
return navigationIconRasterSize
}

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants