feat(LUKE-56): revamp the notch panel into a capsule, a peek, and a panel

Merged
DA
2mo ago
+3559-1468

Summary

LUKE-56, the first UI revamp. The notch surface becomes one black shape at three
sizes — a capsule at rest, a peek under the pointer, a panel on a press — and
every state is drawn to match the camera housing it sits beside.

  • The window is a stage, never the shape. It snaps to the size a state needs
    and the renderer animates the surface inside it, so resizing costs a composite
    rather than a relayout per frame. Measured 9.2ms → 1.6ms of layout per expand.
    A compact window is already wide enough to hold the peek, so hovering never
    touches the main process.
  • One spring, both directions. Every resize runs on one sampled damped
    spring at one duration, because a real spring's motion is a property of the
    spring rather than of how far it is asked to travel. Content rides the same
    spring: the panel's rows arrive as a compressed stack whose gaps spring open,
    and the marks and the count's caption unfold outward from the housing the same
    distance on the same spring, so both wings read as one gesture.
  • Shape and content never cross. Growing, the surface leads; shrinking, the
    content leaves first and the surface closes behind it, so nothing is ever
    drawn on the desktop outside the black.
  • Shaped like the housing. Convex bottom corners and the concave flare where
    the sides meet the top edge are derived from the reported notch inset at the
    ratios measured off the hardware (0.348 and 0.094 of its height). The surface
    is opaque in every state, and the spring may not dip inside the housing's own
    footprint.
  • The count replaces the indicator dot, anchored to the notch: the number
    sits right of the housing, the provider marks and speech meter to its left, in
    the same place in all three states. Growing only reveals the marks the capsule
    had no room for — up to five, which is what the peek holds — and each holds
    its place rather than shifting, so nothing there moves during the morph.
  • Provider marks are each provider's own, inlined as path data: Claude Code
    via Simple Icons, Codex via @lobehub/icons, and Conductor's letter mark
    verbatim from its published brand kit. Each keeps its brand colour; session
    state is carried by the chips, tints, and count instead, so the two colour
    systems never land on the same pixel.
  • A Settings tab: one line per cloud provider — mark, name, connected or
    not, and Connect, or Edit and Delete. The key field appears only while a key
    is being entered, with a link that opens that provider's own API-key page. The
    renderer names a provider, never a URL, so the pages Luke can open are the
    ones in its provider registry. Microphone access and Quit live here too.
  • The menu bar item says Luke in the system font and offers Settings…
    — with Command-, shown against it — and Quit. The shortcut is claimed inside
    Luke's own window rather than registered with the system, since Command-,
    belongs to whichever app is frontmost. Pressing the item no longer opens the
    panel, since the capsule is how the panel opens.
  • Removed: the "notch sidecar" eyebrow, the Live pill, the Electron version
    line, and the hairline edge that drew a visible seam against the menu bar.

Evidence

  • Platform-independent checks: ./scripts/check.sh — pass (biome, tsc,
    node --test across the harness, core, and desktop, and the full build)
  • macOS Electron verification (./scripts/verify.sh): run by CI on
    macos-15 — the macOS app and evidence job packages the app and writes the
    deterministic screenshots, and it passes on this head. It was not run by hand:
    this branch was developed on Linux, so the packaged app has never been driven
    interactively on a Mac by its author.

Behaviour was verified against the built renderer bundle in headless Chrome over
CDP, driving the real pointer and press path rather than setting state directly:

PropertyResult
Hit regions track the drawn shape in all three statesinside capsule/peek/panel hit expanded-panel; the pixels below and beside are click-through
Content never leaves the black shape, either directionno element painted outside the surface during expand or collapse
Spring overshoot and settle1.5% overshoot, settles in both directions; collapse never dips inside the 210×38 housing footprint
Session list scrolls, root never doesclamps at 520px and scrolls at 10 and 25 sessions
Settings: opening the tab does not take the caretfocused: BODY
Settings: pointer away with no entry in progress closespanel → capsule
Settings: pointer away mid-entry holds the panel and the draftpanel, draft half-typed-key kept
An entry begun inside the leave delay is not discardedpanel, draft sk-half-typed kept (was capsule, draft gone)
Settings: connect → save → deleteNot connected/Connect → Connected/Delete+Edit → Not connected/Connect
Settings: the API-key link opens by provider idbridge received conductor, no URL crossed IPC
Escape out of the field with the pointer away closes the panelpanel → capsule
Opened from the tray, pointer never on the panelstays panel / Settings
Keyboard reaches the capsule and can see where it isTab stop 3 draws outline: solid 2px, inset; a press draws nothing and moves no focus
Reduced motion reaches the panel's chromesurface, tab bar, settings sections, quit button all fall to delay 0s / duration 1ms
Both wings unfold as one gesturemark 8 → 3.89 → -0.11 → 0 while the caption mirrors it -8 → -3.89 → 0.11 → 0, same frames, same overshoot
On a display with no housing, the press target follows the shapecapsule top 6/h 20, panel top 0/h 32, both matching the surface
Leaving Settings mid-entry releases the holdpanel → capsule once the pointer leaves, rather than staying open forever
Two presses 80ms apart, second landing mid-flightsetExpanded(true) then (false), shape capsule — window and shape agree
No ink outside the black, sampled every frame0 violations with a notch, without, and with seven providers — peek at rest and through the collapse, comparing clipped painted boxes against the surface
The pill's 6px lift travels rather than snappingheld to 89ms, then 0 → 6.09 → 6.0 on the spring; constant 0 beside a real housing
The shadow fades inside the window rather than meeting its edgebottom-row alpha 0/255 at rest and at the spring's peak (was 6/255 and 16/255)
Closing with Escape stops intercepting without waiting for a movesetPointerInterception [true, false] (was [true])
Pressing the peek where the marks unfold opens and stays openpanel, interception [true] (was capsule, [true, false])
The capsule's ring is the keyboard's alonepress → outline none; programmatic focus → outline none; Tab → outline solid 2px
Quit button hover delaybackground-color 0s (was 0.296s, the arrival stagger leaking onto the hover)

The three sizes

Capsule at rest, then the peek under the pointer — one shape, one spring:

Capsule

Peek

Seven providers, where only five marks fit: the rest are counted rather than
dropped, and nothing is drawn outside the black.

Peek with more providers than fit

The panel on a press. The fixture carries all four states and all three provider
marks, so one screenshot covers them:

Panel

Settings

One line per provider, then the same line connected, then the editor a press on
Connect opens:

Settings

Settings, connected

Settings, entering a key

The cases that used to break it

17 sessions — the list scrolls and the tab bar stays outside it — and a display
with no housing to blend into, where the shape lifts off the edge into a pill:

17 sessions

No notch

Physical-device evidence

  • Screenshot or screen recording: not attached — the images below come from
    the local design harness (headless Chrome, stubbed bridge, fixture sessions),
    not from the macOS evidence pipeline. They were each inspected.
  • Physical-notch check: not performed — the notch geometry here is the fixture
    (210×38); a real housing needs a Mac.
  • Device/display configuration: not recorded
  • Menu bar item: not captured — the tray title is drawn by the system, so no
    in-app capture can reach it. ./scripts/menu-bar-evidence.sh (added in this
    PR) photographs the right end of the menu bar with Luke running; it needs
    macOS and Screen Recording permission.

Notes

  • Blockers or follow-up verification: none that hold the merge. verify.sh runs
    in CI on every push. What remains for a physical Mac is judgement rather than
    correctness — the feel of the springs on real hardware, the fit of the capsule
    against a real camera housing, pnpm evidence:record for the motion, and
    ./scripts/menu-bar-evidence.sh for the menu bar item, which no in-app
    capture can reach.
  • Rebased onto the LUKE-57 multi-provider credential contract and reconciled
    with the Conductor mark that landed separately: main's brand-kit paths were
    kept, our registry and brand-colour tokens were kept, and provider-mark.tsx
    was deleted. CREDENTIAL_PROVIDER_ID now draws from core's PROVIDER_ID, so
    a session row and a key field name the same provider.