fix(widget): layout polish + accent color presets#108
Merged
Conversation
Two unrelated fixes bundled together:
1. KeepAlive: true caused launchd to respawn the panel after a user
clicks Quit. Switch new installs (install.sh + Bootstrap.writePlist)
to the dict form `KeepAlive: { SuccessfulExit: false }` so the
process restarts on crashes but respects clean exits. Existing
installs get a one-shot migration in cleanupPostUpdateBackup that
rewrites the plist on launch and reloads launchd, so the fix takes
effect without a reinstall.
2. CompactView's sideText (resting countdown / hover legend) sat above
the gauge digit's y-center because its intrinsic height differed
from the gauge cluster's. Pin sideText to the gauge cluster's full
height (size + 2 to match the halo Circle) so the ZStack's center
alignment lines up with QuotaGauge's centerReadout in both
compact-content modes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Hammering /api/oauth/usage with the same source IP kept stoking the rate limit — once a 429 streak started it never cleared because every 60s poll kept the window alive. Track retryAfterUntil; while it's in the future, short-circuit fetch() before hitting the network. Parse the Retry-After header (delta-seconds or HTTP-date), fall back to 15 minutes when absent, clamp at 1 hour so a bogus value can't strand the probe. Cleared on the next 200. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User Quit now drops a marker at ~/.stack-nudge/user-quit; notify.sh's ensure_app_running gate honors it and stops opening the bundle on hook events. The marker is cleared on the next manual launch, so reopening restores normal auto-relaunch behavior. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ight Drop the explicit Spacer in the usage pill so sideText fills the slack (maxWidth: .infinity, leading) — gauge center and text center now share the same y, and the expand button sits closer to the cluster instead of being pushed to the far edge. Widen the pill from 150→170 to give the two-line hover legend room without compressing the layout, and bump the resting countdown to .medium/.secondary so it matches the hover legend's visual weight (sizes were already equal at 9pt — the perceived gap was weight + color tier). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…bility, cycling-only headline Mini pill (usage mode): - Reserve hoverLegend's measured width via PreferenceKey so the countdown sits in a fixed-width slot — hovering no longer bumps the expand button. - Vertically center the visible text in sideText (countdown / hover legend / "—" placeholder when no quota). - Gate hover on nav.quota != nil so we don't flash placeholder legend text on hover. - Shrink pill 170→145 and bump horizontal padding 4→10 so both edge gaps resolve to the same value. Full pill (non-usage mode): - Drop the middle Spacer + trailing minLength-4 Spacer in favor of a single trailing Spacer; sessionBadge + expandButton now sit right next to the headline. - Shrink pill 320→290 so the right margin matches the left 12pt padding. - Strip the busy / recent-event / most-recent-active branches in the headline — always show cycling active session names (falls back to "watching" when no sessions are active). Drops the · token-count and · position indicators from the cycling view. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- writePlist: parameterize KeepAlive so the voice daemon keeps its "always" restart policy. Wizard-installed daemons were inheriting the panel's SuccessfulExit:false form, diverging from install.sh. - QuotaProbe: distinguish "rate-limited" from "endpoint may have changed" in the Usage tab so the message is accurate during a 429 backoff. isRateLimited reads off retryAfterUntil; lastProbeFailed now only flags real failures (non-200, non-429). - parseRetryAfter: refactor to take String? so it can be unit-tested without an HTTPURLResponse; add tests covering delta-seconds, HTTP-date, negative values, >1h clamp, empty, and garbage input. - gaugeClusterBody: extract `haloSize = size + 2` so the halo blur frame and gauge frame share one source of truth; drop redundant explicit HStack(alignment: .center). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pill accent (border glow, gauge tracks, mascot tints, spinner dot, stress
sweat-drop) is now themable via five presets surfaced in Settings →
Widget → "Accent color": cyan (default — current color), violet, mint,
rose, and system (follows the macOS accent, matches banners). Routed
through a new themeAccent EnvironmentValue so QuotaGauge and every
mascot pick the color up without per-init plumbing. Persisted via
STACKNUDGE_THEME. Urgency .orange ≥75%, .red ≥90%, and the 50–75%
yellow gauge band stay semantic.
Two layout fixes shipped alongside:
- Gauge side-text slot now sizes to the max of the hover legend's
intrinsic width and a worst-case countdown template ("0h00m"), so
the countdown ("2h24m") no longer wraps onto a second line. Visible
text gets lineLimit(1).fixedSize() so it can't break even at
sub-pixel widths.
- Expand button now anchors to the right edge in both pill modes
(Spacer ahead of it instead of behind), symmetric to how the gauge
anchors left.
Dead fileprivate gaugeColor(pct:) helper deleted — no callers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-gauge-align # Conflicts: # panel/CompactView.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
themeAccentEnvironmentValue, persisted viaSTACKNUDGE_THEME."0h00m"), so"2h24m"stays on one line in both pill sizes. Visible text gets.lineLimit(1).fixedSize().Spacer(minLength: 0)now sits ahead of the expand button (instead of behind) in both pill modes, symmetric to how the gauge anchors left.fileprivate static func gaugeColor(pct:)helper removed (no callers).(Branch name is left over from the merged PR #106; scope here is widget-only.)
Test plan
./build.shclean, swap into~/Applications/..systempreset: change macOS Settings → Appearance → Accent color — pill updates live.🤖 Generated with Claude Code