Skip to content

fix(widget): layout polish + accent color presets#108

Merged
hiskudin merged 8 commits into
mainfrom
fix/quit-relaunch-and-gauge-align
Jun 23, 2026
Merged

fix(widget): layout polish + accent color presets#108
hiskudin merged 8 commits into
mainfrom
fix/quit-relaunch-and-gauge-align

Conversation

@hiskudin

Copy link
Copy Markdown
Collaborator

Summary

  • Accent color presets — Settings → Widget → "Accent color" cycles cyan / violet / mint / rose / system. The chosen accent retints the pill border, gauge tracks, mascot accents, spinner dot, and the stress sweat-drop in one place; urgency .orange ≥75%, .red ≥90%, and the 50–75% yellow gauge band stay semantic. Routed through a themeAccent EnvironmentValue, persisted via STACKNUDGE_THEME.
  • Countdown no longer wraps — the gauge side-text slot now sizes to the max of the hover legend's intrinsic width and a worst-case countdown template ("0h00m"), so "2h24m" stays on one line in both pill sizes. Visible text gets .lineLimit(1).fixedSize().
  • Expand button anchors rightSpacer(minLength: 0) now sits ahead of the expand button (instead of behind) in both pill modes, symmetric to how the gauge anchors left.
  • Dead 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.sh clean, swap into ~/Applications/.
  • Settings → Widget → "Accent color" — cycle all 5 presets; verify retint of pill border, gauge tracks, gauge fill <50%, mascot accent, spinner dot, sweat-drop (force stress ≥75%).
  • Urgency: force 5h utilization ≥75% and ≥90% — pill border goes orange / red regardless of theme.
  • Quit via menu → relaunch — selected theme persists.
  • .system preset: change macOS Settings → Appearance → Accent color — pill updates live.
  • Mini + full pill: countdown like "2h24m" stays one line; hovering the gauge doesn't shift the expand button.
  • Expand button visually pinned to the right edge with the same padding the gauge has on the left.

🤖 Generated with Claude Code

hiskudin and others added 8 commits June 18, 2026 09:45
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

@StuBehan StuBehan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@hiskudin hiskudin merged commit 840d1d5 into main Jun 23, 2026
6 checks passed
@hiskudin hiskudin deleted the fix/quit-relaunch-and-gauge-align branch June 23, 2026 15:58
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.

2 participants