feat(LUKE-56): revamp the notch panel into a capsule, a peek, and a panel
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 --testacross 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:
| Property | Result |
|---|---|
| Hit regions track the drawn shape in all three states | inside capsule/peek/panel hit expanded-panel; the pixels below and beside are click-through |
| Content never leaves the black shape, either direction | no element painted outside the surface during expand or collapse |
| Spring overshoot and settle | 1.5% overshoot, settles in both directions; collapse never dips inside the 210×38 housing footprint |
| Session list scrolls, root never does | clamps at 520px and scrolls at 10 and 25 sessions |
| Settings: opening the tab does not take the caret | focused: BODY |
| Settings: pointer away with no entry in progress closes | panel → capsule |
| Settings: pointer away mid-entry holds the panel and the draft | panel, draft half-typed-key kept |
| An entry begun inside the leave delay is not discarded | panel, draft sk-half-typed kept (was capsule, draft gone) |
| Settings: connect → save → delete | Not connected/Connect → Connected/Delete+Edit → Not connected/Connect |
| Settings: the API-key link opens by provider id | bridge received conductor, no URL crossed IPC |
| Escape out of the field with the pointer away closes the panel | panel → capsule |
| Opened from the tray, pointer never on the panel | stays panel / Settings |
| Keyboard reaches the capsule and can see where it is | Tab stop 3 draws outline: solid 2px, inset; a press draws nothing and moves no focus |
| Reduced motion reaches the panel's chrome | surface, tab bar, settings sections, quit button all fall to delay 0s / duration 1ms |
| Both wings unfold as one gesture | mark 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 shape | capsule top 6/h 20, panel top 0/h 32, both matching the surface |
| Leaving Settings mid-entry releases the hold | panel → capsule once the pointer leaves, rather than staying open forever |
| Two presses 80ms apart, second landing mid-flight | setExpanded(true) then (false), shape capsule — window and shape agree |
| No ink outside the black, sampled every frame | 0 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 snapping | held 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 edge | bottom-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 move | setPointerInterception [true, false] (was [true]) |
| Pressing the peek where the marks unfold opens and stays open | panel, interception [true] (was capsule, [true, false]) |
| The capsule's ring is the keyboard's alone | press → outline none; programmatic focus → outline none; Tab → outline solid 2px |
| Quit button hover delay | background-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:


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

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

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



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:


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.shruns
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:recordfor the motion, and
./scripts/menu-bar-evidence.shfor 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, andprovider-mark.tsx
was deleted.CREDENTIAL_PROVIDER_IDnow draws from core'sPROVIDER_ID, so
a session row and a key field name the same provider.