feat(omarchy): mark each provider in the bar with its own logo - #3787
jsonMartin wants to merge 4 commits into
Conversation
…ones Claude reports a cap scoped to one model beside its general weekly quota, as an entry in usage.extraRateWindows. The Linux shared model read only primary, secondary and tertiary, so that lane never reached the snapshot, the Omarchy popup or any other adapter: an account could sit at 96% of a scoped cap with nothing on screen saying so, while macOS showed it through standardSemanticWindows, which already treats extras as candidates. Read the extras after the three standard lanes. The ordering matters, because a scoped lane can share the 7-day cadence with the general weekly window and consumers resolve a cadence by first match; a test pins it rather than leaving it to the reading. The same pass fixes what that exposed. A provider also describes windows it cannot measure: Zed reports an overdue invoice and Antigravity a reset-only pool, both carrying a full usedPercent with usageKnown false, and Claude emits a synthetic placeholder when its web API returns no session. Core excludes both. Read as quota they became a full or exhausted lane, so filter them where the percentage is first trusted and every consumer inherits the boundary. Titles are provider-controlled text exported over an IPC contract that excludes account identity, so they are redacted regardless of the identity display preference, which governs the native windows rather than this boundary. The count is bounded like the other provider-controlled lists here, counting lanes that render so a real one trailing unusable entries is not discarded.
…e bar The bar showed one quota per provider and no pace, so the weekly burn rate was only visible by opening a window. Show each provider's session quota, weekly quota and weekly pace instead, joined by the separator the popup already uses. Nothing here recomputes pace. The CLI already emits a signed delta per window, produced by Core, so the shared model carries it through and renders it in the compact form of MenuBarDisplayText.paceText. The pace shown is the weekly window's, never whichever lane is most constrained, and a pace Core cannot compute contributes no segment rather than a placeholder that reads like data. Lanes are resolved from the cadence a provider reports, the way Core's semantic windows do, so no provider name is hard-coded and a provider without a session window emits neither that segment nor its separator. A cadence resolves to the pool that binds hardest, because a provider can report several at one cadence and taking the first would hide an exhausted pool behind an idle one. Windows sharing a duration are not assumed to be the same quota measured twice; a provider lists its own headline quota first. A provider whose only lane at a cadence is a scoped one still gets that cadence rather than showing the scoped lane by name. Percentages run through the existing quota preference, so the bar follows the used or remaining setting without the adapter inverting anything, and the existing Show pace preference now governs the bar as well as the native cards. Caps scoped to a single model are off by default behind a new preference, since most providers that publish them restate a general lane. The adapter also stopped reusing a previous poll's body, which let a backend that had gone away keep reporting the quota it last had.
…reports Three classification faults an independent review reproduced against real provider output. Antigravity emits one window per model family for a cadence, marked with a quota-summary id, and its positional window is only whichever family it chose to represent them. Preferring positional windows reported a 40% session while a 20% one was present. Where a provider marks a summary set that way, the lane resolves across it. Matching on the provider's marker rather than on percentages matters: Claude reports several independently valued scoped caps, one can land on the same rounded percentage as the general weekly window, and reading that coincidence as a summary replaced the general quota with a cap scoped beneath it and dropped its pace. A window promoted to a cadence headline could then render a second time as a scoped cap, because the scoped pass did not know what had already been shown. A Kimi subscription-only account read "30D 20% · Total usage 20%". The tray summary chose the smallest remaining percentage across every pool, which is not the lane the bar leads with. For Cursor, which bills its total, its Auto/Composer usage and its API usage over one cycle, the bar reported the provider's own 75% while the tooltip reported the 10% subquota with no label saying which. Both now name the same lane.
The bar identified providers with a two-letter tag. Draw the provider's own mark instead, recoloured to the bar's foreground so themes still apply. Splitting the label is the part that needed care. barLabel built one joined string, and a frontend cannot safely split that back apart on a separator the lane text also uses. barSegments returns one tag-and-text pair per displayed provider and barLabel is built from it, so the two cannot drift. The cap on how many providers appear lives inside barSegments rather than in barLabel, or a frontend reading the entries would show providers the label said were hidden; a three-provider test pins the providers, their order and the overflow count. The entries travel as barEntries beside barLabel, which the tooltip still uses and which a frontend falls back to when the backend predates this field. Drawing marks hides the button's own label, which is what carried the overflow count, so the count is drawn beside them, after the repeater rather than as a second delegate inside it. The marks are the ones the Mac app already ships. They are copied by glob, so no provider name is hard-coded and a provider without one keeps its text tag rather than leaving a gap. Both the installer and the release archive carry them, since an archive without them would fall back to text with nothing explaining why. The archive's allowlist test checks the fixed files and the mark filenames instead of a member count, so it still fails if a checkout leaks in. The badge sizes itself to whichever child it draws. Taking the larger of the mark and the hidden text tag reserved the width of the provider id, which left a gap beside the mark for any provider without a short tag.
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed September 20, 2026, 4:53 PM ET / 20:53 UTC. ClawSweeper reviewWhat this changesThe branch adds provider logos to the Omarchy bar, packages their SVG assets, and includes preceding stack changes for quota lanes, pace, and optional scoped caps. Merge readiness⛔ Blocked before merge - 5 items remain This is distinct, useful work that neither current main nor v0.62.0 implements. Its open prerequisite does not supersede the logo feature. Priority: P2 Review scores
Verification
How this fits togetherThe Linux desktop backend converts provider CLI output into a private usage snapshot. The Omarchy adapter reads that snapshot and displays provider quotas in the desktop bar. flowchart LR
A[Provider CLI output] --> B[Linux usage model]
C[Display preferences] --> B
B --> D[Private desktop snapshot]
D --> E[Omarchy bar adapter]
F[Packaged provider logos] --> E
E --> G[Logo or text fallback with quotas]
Decision needed
Why: The author explicitly leaves this distribution choice to the maintainer; existing macOS use does not establish approval for the new packaging path. Before merge
Findings
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Use an alpha-based foreground tint with reliable text fallback, within a maintainer-approved packaging approach that preserves existing display preferences. Do we have a high-confidence way to reproduce the issue? Yes, source establishes the introduced rendering defect: select Qoder with a dark bar theme, and its black SVG remains black while the text fallback is hidden. No live QML run was performed. Is this the best way to solve the issue? Partly: structured provider segments and reuse of existing assets are sensible, but luminance-based colourization cannot reliably tint every bundled logo; an alpha-based tint is the narrower correction. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 4b86ea7b3971. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
|
|
Reopened as #3795 with a conventional branch name ( |
Summary
The bar identified providers with a two-letter tag. This draws the provider's own mark instead, recoloured to the bar's foreground so themes still apply.
Splitting the label is the part that needed care.
barLabelbuilt one joined string, and a frontend cannot safely split that back apart on a separator the lane text also uses.barSegmentsreturns one tag-and-text pair per displayed provider andbarLabelis built from it, so the two cannot drift — a test asserts that identity rather than trusting it. The pairs travel asbarEntriesbesidebarLabel, which the tooltip still uses and which a frontend falls back to when the backend predates this field.The marks are the ones the Mac app already ships (
Sources/CodexBar/Resources/ProviderIcon-*.svg). They are copied by glob, so no provider name is hard-coded anywhere and a provider without one keeps its text tag rather than leaving a gap. Both the installer and the release archive carry them; an archive without them would fall back to text with nothing explaining why. The archive's allowlist test now checks the fixed files plus the mark filenames rather than a member count, so it still fails if a checkout leaks in.Depends on
This builds on #3786. GitHub's native stacked pull requests do not support
cross-fork stacks, so this is stacked manually: its base is the previous branch and it
will be retargeted to
mainonce that merges. Reviewing in order is easiest.A question for the maintainer
This puts ~70 brand SVGs (~300KB) into a new distribution path — the Quickshell plugin directory and the release archive. That is your call rather than mine, and I am happy to change the approach: ship a subset, resolve them from the CLI bundle instead, or drop the PR.
Screenshot
◉ 7D 83% · -4% · ✳ 5H 95% · 7D 19% · -11% · ∧ 5H 100% · 7D 94% · Z 5H 97% · 7D 94% · -6%Commands run
Known limitation
CI never loads
Panel.qml, so logo rendering, the missing-mark text fallback and older-backend compatibility are verified by screenshot and by inspection, not by test.