feat: compact pill widget with mascots, ring gauge, drag-snap#65
Merged
Conversation
Adds an opt-in glance widget that lives pinned to a screen corner above all spaces. Toggle via Settings -> Widget -> Compact widget, or press "M" from Events/Sessions/Usage tabs. Expand button on the pill (or double-click) exits compact mode persistently; hotkey toggles expand/collapse while in compact mode. Layout: concentric quota ring on the left (5h inner / 7d outer with gradient stroke + pulsing inner glow + orbital polling spinner), reset countdown, a tiny vector bot mascot whose expression reflects state (idle / watching / busy / alert / happy), dynamic headline showing project name + token count or last event, session count badge, expand button. Polish: border glow shifts color and pulse rate with quota urgency (cyan -> orange -> red). Ripple capsule animation on every new event. Drag-to-reposition via window-background drag with snap-to-nearest- corner on release. Snap uses a NSEvent .leftMouseUp local monitor for an unambiguous end-of-drag signal so pausing mid-drag doesn't false- trigger a snap. Other changes: - Hotkey/escape/hide funnel through hidePanel which collapses to the pill instead of hiding outright while compact mode is on. - Frame-size persistence skips the compact widget size so the saved full-panel size sticks across mode flips. - Settings layout shifts Sound/Voice/Usage/Actions down by 2 rows to fit the new Widget section. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three nested TimelineViews running at 0.05s each (pill background pulse + glow, quota gauge pulse + spinner, mascot blink) competed with AppKit's window-background drag handler on the main thread, producing visible lag while dragging. Track drag state via the existing .leftMouseDown/.leftMouseUp event monitor and render static variants of all three subviews while compactDragging is true. Animations resume on mouse-up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…MinSize fix - Quota gauge 32x32 -> 42x42, ring stroke 2.8 -> 4pt, center number 9 -> 14pt semibold, punchier cyan, brighter inner glow, soft outer halo behind the rings. Pill height 46 -> 56 to fit. - Bot mascot 18x16 -> 26x24, head/eyes/antenna/mouth scaled proportionally so the character is readable at a glance. - Headline adapts: shows pending count when the queue has >1 event, otherwise the live relative timestamp of the latest event. Single events still show 'X sec ago' for recency; backlog shows 'N pending'. - Drop the panel's contentMinSize to the compact widget size while compact, restore the original 560x260 floor on exit. AppKit was re-applying the original minimum after system events (lock/unlock, screen reconfiguration), inflating the panel to ~1100x500 while SwiftUI still rendered the compact pill content inside it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…fix overflow - Add MascotKind enum (robot, cat, eye, ghost) selectable via new Settings -> Widget -> Mascot row. Each mascot ships its own SwiftUI drawing with per-state expressions (idle/watching/busy/alert/happy) and animation logic; the cat uses a single-path CatHeadShape so the ears feel attached to the face. - Compact mode is always-on now. Drop the Compact widget toggle from Settings (its only role was gating the other widget rows). Both the expand button and double-click expand to the full panel temporarily; M / Esc / hotkey collapse back. Settings rows shift down by 1. - M from the pill (when it has key focus) expands to the full panel, mirroring the M-to-collapse footer shortcut shown across the tabs. - Uppercase footer-hint key labels across all tabs (r -> R, p -> P, n -> N, esc -> Esc) for visual consistency with the existing M, S hints. - Fix headline overflow when the queue has >1 event: 'N pending' was wrapping at the space. Switch to 'xN' format with .lineLimit(1) + .fixedSize(). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.
Adds a pinned glance widget that lives at a screen corner above all spaces. Always-on (no toggle), expand to full panel via button / double-click / hotkey / M, collapse back via Esc / M / hotkey. Built up over several iterations of design polish and bug fixes.
What's in the pill
Settings → Widget → Mascot: Robot, Cat (single-path silhouette with attached ears), Sentinel (HAL-style scanning lens), Ghost (floaty bob, wavy bottom). Each has per-state expressions for idle / watching / busy / alert / happy plus a blink cycle.×N.Polish & behavior
isMovableByWindowBackground, on mouse-up it animates to the nearest screen corner. Uses an explicit.leftMouseUpNSEvent monitor so pausing mid-drag doesn't false-trigger a snap.TimelineViews were competing with AppKit's drag handler on the main thread. Track drag state via the mouse-event monitor and render static variants of the pill background / gauge / mascot during a drag.Bug fixes that emerged from the work
contentMinSizeto the widget size while compact; restore the 560×260 floor on exit.didResizewas saving the compact size as the user's panel size. Skip persistence while in the widget state.nav.compactCorner = cornerfires Combine → applyCompactLayout → setFrame → didMoveNotification, which scheduled another snap. TightignoringProgrammaticMoveflag around each programmatic setFrame swallows those move events.UI consistency
r → R,p → P,n → N,esc → Escacross Events / Sessions / Usage / Phrases / Settings / Updater / Bootstrap.Settings additions
Plus a new
Mshortcut from Events/Sessions/Usage tabs that collapses to the pill; andMfrom the pill (when it has key focus via a click) that expands to the full panel.Test plan
×Nand doesn't wrap.🤖 Generated with Claude Code