Skip to content

fix: follow system dimming for menu bar icon, standard size and stepper-tuned vertical offset - #2882

Open
luantu wants to merge 4 commits into
steipete:mainfrom
luantu:feature/menubar-icon-dimming
Open

fix: follow system dimming for menu bar icon, standard size and stepper-tuned vertical offset#2882
luantu wants to merge 4 commits into
steipete:mainfrom
luantu:feature/menubar-icon-dimming

Conversation

@luantu

@luantu luantu commented Aug 12, 2026

Copy link
Copy Markdown

Summary

Two related menu bar refinements for the custom layout title, plus a follow-up that makes the vertical nudge editable with a stepper.

1. Icon follows the system's active/inactive display tinting (no more wrong gray-on-secondary-display)

Before, a leading .icon token was baked into the attributed title as an NSTextAttachment (a pre-rendered bitmap). AppKit dims menu bar items on inactive displays automatically, but it only does that for template images assigned to NSStatusBarButton.image — pre-rendered attachments stay bright/gray regardless. On dual-screen setups this made CodexBar's icon look inconsistent with every other menu bar extra.

Now the leading icon token is surfaced as button.image (a template image) so AppKit applies the same active/inactive display tinting as native menu bar icons, while text tokens keep rendering through the attributed title.

2. Standard size + consistent optical centering

  • Provider brand icon rendered at 18×18 (was 16×16), and single-line titles use the 18 pt face instead of 16 — closer to standard menu bar icon/text sizing.
  • Single-line titles get a small optical baseline offset (-1) so they sit vertically balanced like the stacked (-3) layout already did.
  • Status item length now accounts for the surfaced icon width.

3. Stepper-tuned vertical offset

The existing "vertical adjustment" preference (already added as a numeric input in the layout editor) is now a TextField + Stepper pair (range -20…20, default 0, ±1 per click), matching the pattern already used elsewhere in Preferences (e.g. cost history days). The label was shortened from "Vertical Adjustment" to "Vertical" and the whole row aligns with the adjacent Size/Gap pickers.

Why this matters on dual-screen and custom-resolution displays

  • Dual screens: the menu bar on the non-active display is dimmed by macOS. With a bitmap attachment the icon stayed at full brightness (or looked wrong); as a template button.image it now dims exactly like native icons when focus moves to the other display.
  • Custom resolutions / scaled displays: the baseline offsets and the 18 pt face keep the title optically centered in the status bar button across different backing-scale factors, and the stepper lets users fine-tune ±1 px per display if the optical default is still off.

Tests

  • MenuBarLayoutRendererTests extended: leading-icon-as-image behavior, single-line baseline offset, vertical-adjustment baseline shift, stale (refresh-failed) title dimming, and leadingIcon size/template checks.
  • Ran swift test --filter MenuBarLayoutRendererTests (18 tests, all pass) plus the MenuBarLayout/StatusItem/SettingsStore suites (369 tests pass). Lint-clean (swiftlint/swiftformat); the 42 repo-wide lint violations are pre-existing on upstream.

Commits

  • feat: make menu bar vertical adjustment a numeric input
  • fix: follow system dimming for menu bar icon and use standard size
  • refactor: vertical adjustment uses stepper with visible value

@clawsweeper

clawsweeper Bot commented Aug 12, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b878015e5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +239 to +240
let leadingIcon: NSImage? = if layout.lines.first?.first == .icon, icon != nil {
icon

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the high-contrast icon rendering path

When “Improve visibility on inactive displays” is enabled and the layout starts with an icon, this branch still extracts the icon into button.image. AppKit consequently dims the icon on inactive displays—the behavior this preference is meant to avoid—while only the text receives the high-contrast labelColor. Keep the icon as an attributed attachment, or otherwise bypass native dimming, when options.highContrast is true.

Useful? React with 👍 / 👎.

Comment on lines +253 to +254
if leadingIcon != nil, lineIndex == 0, tokenIndex == 0, token == .icon {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Render the surfaced icon in layout previews

When the editor previews any layout beginning with an icon, including the icon presets, this skips the icon in attributedTitle and exposes it only through leadingIcon; however, MenuBarLayoutPreviewText renders only attributedTitle. The preview therefore omits the brand icon, and an icon-only layout appears blank. Update the preview to display leadingIcon as well, or retain the attachment for preview rendering.

Useful? React with 👍 / 👎.

Comment on lines +253 to +254
if leadingIcon != nil, lineIndex == 0, tokenIndex == 0, token == .icon {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retain accessibility text for a surfaced icon

When a valid leading icon is used, this continue runs before renderItem can append its provider-icon accessibility text. For an icon-only layout, accessibilityLabel consequently becomes empty and StatusItemController installs that empty value as the button's accessibility title, leaving VoiceOver users without a label. Preserve the icon's accessibility text even though its visual content is moved to button.image.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 12, 2026
@clawsweeper

clawsweeper Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 12, 2026, 4:54 AM ET / 08:54 UTC.

ClawSweeper review

What this changes

The PR moves a leading menu-bar provider icon into AppKit’s template-image slot, adds a vertical adjustment setting, and updates rendering and layout tests.

Regression provenance

Possible regression — suspected (reviewed change). No predecessor PR is attributed.

Merge readiness

Blocked until real behavior proof is added - 9 items remain

This PR is still needed, but its surfaced-icon path regresses the existing high-contrast option, editor preview, and icon-only accessibility. It also needs real after-fix menu-bar evidence before merge.

Priority: P2
Reviewed head: 800735a0a5e78a0c0d04768cb562cf74146ab845

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The intended fix is focused, but three visible regressions and missing real behavior proof leave the PR unready to merge.
Proof confidence 🧂 unranked krab (1/6) Needs real behavior proof before merge: The PR body reports automated tests but contains no after-fix proof from a freshly built CodexBar bundle; add redacted screenshots or a recording that shows the icon and contrast behavior on the affected display state. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦪 silver shellfish (2/6) 3 actionable review findings remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The PR body reports automated tests but contains no after-fix proof from a freshly built CodexBar bundle; add redacted screenshots or a recording that shows the icon and contrast behavior on the affected display state. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 5 items Current rendering path: Current main embeds the provider icon as an attributed attachment and clears the native status-button image, so the PR's template-image approach is not already implemented.
High-contrast contract: Current main deliberately routes high-contrast status content through an attributed-title path with no native button image; extracting a leading icon into button.image makes AppKit dim it again on an inactive display.
Preview contract: The layout preview displays only rendered.attributedTitle. Since the PR removes a leading icon from that title, icon-first previews need an explicit leading-image rendering path.
Findings 3 actionable findings [P2] Preserve the high-contrast icon rendering path
[P2] Render surfaced icons in the layout preview
[P2] Retain icon accessibility text after extraction
Security None None.

How this fits together

CodexBar renders provider usage into an AppKit status-bar button through a configurable layout. Layout tokens become attributed text and/or a provider image, then the status item exposes that result to macOS and accessibility services.

flowchart LR
A[Provider usage data] --> B[Configurable menu layout]
B --> C[Layout renderer]
C --> D[Attributed title and icon]
D --> E[AppKit status-bar button]
E --> F[macOS display tinting]
E --> G[Accessibility label]
Loading

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The PR body reports automated tests but contains no after-fix proof from a freshly built CodexBar bundle; add redacted screenshots or a recording that shows the icon and contrast behavior on the affected display state. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Preserve the high-contrast icon rendering path (P2) - When inactive-display contrast is enabled, this installs the extracted leading icon as button.image, which AppKit dims on inactive displays. That reverses the existing high-contrast contract; retain an attributed/tinted icon path for this option and cover it with a regression test.
  • Render surfaced icons in the layout preview (P2) - A leading icon is no longer part of attributedTitle, but the preview continues to display only that title. Icon-first presets therefore omit their provider icon and an icon-only layout is blank; render leadingIcon in the preview too.
  • Retain icon accessibility text after extraction (P2) - The leading-icon extraction skips the existing .icon token rendering that appends provider-icon accessibility text. An icon-only layout then installs an empty status-button accessibility title; preserve the icon label independently of its visual representation.
  • Resolve merge risk (P1) - Existing users who enable inactive-display contrast would again receive a dimmed leading provider icon; icon-first previews would omit the icon, and VoiceOver would have no label for icon-only layouts.
  • Resolve merge risk (P1) - The PR adds a persisted preference and changes native AppKit rendering without after-fix runtime proof from a freshly built bundle.
  • Complete next step (P2) - The branch has three concrete repairs, but after-fix real behavior proof must come from the contributor’s native macOS setup before merge.
  • Improve patch quality - Fix and test the high-contrast, preview, and icon-only accessibility paths.
  • Improve patch quality - Add redacted after-fix native menu-bar proof; updating the PR body should trigger re-review, or a maintainer can request @clawsweeper re-review if needed.

Findings

  • [P2] Preserve the high-contrast icon rendering path — Sources/CodexBar/StatusItemController+MenuBarLayout.swift:166-176
  • [P2] Render surfaced icons in the layout preview — Sources/CodexBar/MenuBarLayoutRenderer.swift:202-205
  • [P2] Retain icon accessibility text after extraction — Sources/CodexBar/MenuBarLayoutRenderer.swift:202-205
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Changed surface 32 files affected; 224 added, 26 removed The behavior spans renderer, native status-button, settings, preview, tests, and localization resources.
Production versus test delta production +151, tests +73 The persistent setting and rendering-path change need runtime proof as well as focused regression coverage.

Merge-risk options

Maintainer options:

  1. Repair the alternate rendering paths (recommended)
    Keep high-contrast, preview, and accessibility behavior intact while using the native template image only where it is compatible.
  2. Limit the native-image change
    Remove the extracted-icon path if it cannot coexist with the established high-contrast contract.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Preserve high-contrast icon visibility, preview rendering, and icon-only accessibility; add focused regression coverage and request redacted live menu-bar proof.

Technical review

Best possible solution:

Preserve the attributed-icon path when high contrast is enabled, render the surfaced image in preview, retain icon accessibility text, and provide redacted real macOS menu-bar proof from the freshly built bundle.

Do we have a high-confidence way to reproduce the issue?

Yes from source: a leading icon enters the proposed surfaced-image path, while the current preview and high-contrast behavior rely on the attributed title; icon-only output also loses the current icon accessibility text.

Is this the best way to solve the issue?

No: moving the icon to the native image slot is appropriate only if the high-contrast, preview, and accessibility branches preserve their current contracts.

Full review comments:

  • [P2] Preserve the high-contrast icon rendering path — Sources/CodexBar/StatusItemController+MenuBarLayout.swift:166-176
    When inactive-display contrast is enabled, this installs the extracted leading icon as button.image, which AppKit dims on inactive displays. That reverses the existing high-contrast contract; retain an attributed/tinted icon path for this option and cover it with a regression test.
    Confidence: 0.98
  • [P2] Render surfaced icons in the layout preview — Sources/CodexBar/MenuBarLayoutRenderer.swift:202-205
    A leading icon is no longer part of attributedTitle, but the preview continues to display only that title. Icon-first presets therefore omit their provider icon and an icon-only layout is blank; render leadingIcon in the preview too.
    Confidence: 0.99
  • [P2] Retain icon accessibility text after extraction — Sources/CodexBar/MenuBarLayoutRenderer.swift:202-205
    The leading-icon extraction skips the existing .icon token rendering that appends provider-icon accessibility text. An icon-only layout then installs an empty status-button accessibility title; preserve the icon label independently of its visual representation.
    Confidence: 0.98

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against a90dfed5c264.

Labels

Label changes:

  • add P2: The PR affects a visible menu-bar and accessibility workflow with limited blast radius.
  • add merge-risk: 🚨 compatibility: It changes established high-contrast, preview, and accessibility behavior for stored menu layouts.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body reports automated tests but contains no after-fix proof from a freshly built CodexBar bundle; add redacted screenshots or a recording that shows the icon and contrast behavior on the affected display state. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P2: The PR affects a visible menu-bar and accessibility workflow with limited blast radius.
  • merge-risk: 🚨 compatibility: It changes established high-contrast, preview, and accessibility behavior for stored menu layouts.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body reports automated tests but contains no after-fix proof from a freshly built CodexBar bundle; add redacted screenshots or a recording that shows the icon and contrast behavior on the affected display state. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

Likely related people:

  • Houge: Authored the most recent locally available current-main commit touching the menu-bar layout renderer. (role: recent area contributor; confidence: medium; commits: 2ac8f1a47e0c; files: Sources/CodexBar/MenuBarLayoutRenderer.swift)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant