Skip to content

automation: a playing tape rests in the window's own corner - #637

Merged
foxnne merged 35 commits into
mainfrom
automation/tape-pill
Oct 19, 2026
Merged

foxnne merged 35 commits into
mainfrom
automation/tape-pill

Conversation

@foxnne

@foxnne foxnne commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #636 (app/history-vial-return), on #635 (app/history-vial).

What changes

A playing tape's bar used to hide into a dome on the window's bottom edge. It was hard to notice, the semicircle didn't look good, and it sat where documents' history vials now are. A tape plays over the whole app, not one document.

  • Where it rests. While a tape plays, the bar rests in the window's own bottom-right corner. It uses the corner glass a document's history rests in (dome.Corner), cut by the window's outline instead of a card's and marked with a cassette tape (Corner.icon).
  • The liquid. It's the history vial's living liquid (Liquid), and its level is how far the tape has played. A new chapter splashes it and a seek knocks it.
  • Growing out and back. Holding the pointer on the corner, or pressing it, grows the bar out of it to the middle of the window's bottom. Its bubbles bud out of the corner and the corner's liquid pours into the scrubber's line. Going back, they change back into the corner and the liquid pours back in, as the history bar does into its vial (dome.pullFrom, dome.homeMerge). It takes 450 ms each way at the default speed, against the history bar's 620 ms, on the same curves (bar_grow_us). The tab sits above the bar, chevron down.
  • Icons ride their bubbles. Each icon (prev, play, next, close, the tab's chevron) scales and moves with its bubble, and fades with how far that bubble has come. It's gone before the bubbles run together going back, and shows only once they've parted coming out.
  • Stacking with history. Each history bar that is out, and each history vial at rest, publishes its rect for the frame (dome.publishBar). The tape's bar is drawn after every pane, so it stands clear above any bar it would overlap (dome.barsOut), easing up and back down as that bar comes and goes, so both show at once. Captions keep above it. While no bar is out, nothing is published.
  • The window's corner radius is now published each frame (core.screens.publishMainRadius): 0 when maximized or full screen, the OS's otherwise, and fizzy's own on Linux.
  • Renames. Transport.dome becomes Transport.rest (sdk/replay, not on the plugin boundary), the anchor demo.dome becomes demo.corner, and docs/AUTOMATION.md is updated.

On the way here, a pill under the title bar (top centre, where the command palette opens) was tried and dropped. The user found it easy to see but in the wrong place.

SDK impact

  • None (test-sdk-version passes)

Verified

  • macOS: in a sandbox with the tour playing.
    • The window's corner shows the tape icon over the liquid.
    • The bar sits centred at the bottom with its tab. Going back and growing out, captured frame by frame, the icons travel with their bubbles and fade before they merge.
    • Going back, captured frame by frame, the bubbles fly to the corner and change into its glass, the tape grows back out of the corner, and the liquid pours in, with no snap.
  • The integration test demo: the bar goes into its corner and grows back out of it covers the tab, the dwell and a press on the corner. demo: the bar stacks above a history bar out under it, and comes back down publishes a stand-in bar in the tape bar's slot: the tape bar ends clear above it and back at its place when it goes.
  • Stacking against a real history bar in the sandbox: the agent can't drive fizzy while a demo is loaded (SomethingElseIsDrivingFizzy), so I haven't seen it there. The user will be the first to see it.
  • Gates: check-fmt, zig build, test, test-integration (81/81 steps), check-web, test-sdk-version, check-tests-reached.
  • Windows / Linux / Web: CI builds only. The corner follows the window's radius there (8 on Windows, fizzy's own on Linux, 0 on the web).

🤖 Generated with Claude Code

foxnne and others added 15 commits October 18, 2026 16:03
`LiquidField.edge`: a place's outline (rect and corner radii). Its outside is one
more shape the field's shapes run into, so a shape near the outline joins the edge
by a neck, metaball-like, and the outline cuts the glass's coverage, so none of it
crosses. The rim and bend come from the joined shape, so there is no rim along the
cut. The edge takes the program's last shape slot, flagged 2, so the uniforms keep
their layout. `glassBubbles` takes it as `.edge`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shapes a control of glass is made of (circles, capsules, panels as rounded
boxes), the cut of one by its place's outline as a polygon, and shape
interpolation: the geometry the orb system builds on, std-only and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rom the top right

`LiquidField.Edge.cut` cuts glass by its place's outline with a bevel and lens band of its own along
the cut, rather than joining the place's outside: a piece of glass laid in a corner, bending the
border and ring it lies along. `light_across` turns a drop's rim light a quarter, for glass whose
edges that show face across the usual light (a place's top-right or bottom-right corner). Both reach
`core.dialogs.glassBubbles` as options.
The history's grip moves from the middle of the card's bottom edge, where a split's bar runs, to a
vial in its bottom-right corner: the corner orb, with a history icon. Its liquid (`app/Liquid.zig`)
sloshes as the pane moves, splashes as steps come and eases up, asking for frames only while it
moves. The bar grows out of it as one liquid: its bubbles pulled out small along arcs, joined and
then pinching off, the liquid pouring from the vial into the line. A pane's activity feed stacks
above the vial.
Going back into a card's corner vial, the bar's bubbles all made for one drop in the middle of the
vial's box, larger than the vial and off its centre, and ran together there at the widest merge.
So the glass ended as a blob bigger than the vial, which then snapped to the vial's size, and the
vial's icon and liquid switched on the frame the bar was gone.

The bar now changes into the vial rather than landing beside it. Each bubble arrives as the vial's
own circle on the card's corner (`dome.Corner.home`, `dome.pullFrom`). The glass between them lets
go over the end (`dome.homeMerge`), since any merge left between coincident circles bulges them.
The vial's liquid drains as the stream pours out into the line and refills as it pours back
(`dome.paintCornerLiquid`), and its icon shrinks into the corner with the vial and grows back with
it (`dome.cornerIcon`). Growing out runs the same way forwards.
A playing tape's bar hid into a dome on the window's bottom edge: hard to notice, and it sat where
documents' history vials are now. A tape plays over the whole app, not one document, so its bar
now lives at the top centre under the title bar, where the command palette opens.

While a tape plays, the bar rests in a glass pill there (`dome.paintPill`): the orb glass of the
history vial, and the same living liquid (`Liquid`), its level how far the tape has played. A new
chapter splashes it and a seek knocks it. Holding the pointer on the pill (or pressing it) drops
the bar down out of it. Its bubbles bud out of the pill and the pill's liquid pours into the
scrubber's line. Going back, they change back into the pill and the liquid pours back in, as the
history bar does into its vial. The collapse tab hangs under the bar with a chevron up.

Captions keep below the bar while it is out, and "Catching up…" moves under it. `Transport.dome`
becomes `Transport.rest` and the anchor `demo.dome` becomes `demo.pill`.
…tape

The pill under the title bar was easy to see, but in the wrong place. The tape's bar now rests in
the window's own bottom-right corner, in the corner glass a document's history rests in
(`dome.Corner`), cut by the window's outline instead of a card's and marked with a cassette tape
(`Corner.icon`). It is the same orb, with the same living liquid filling as the tape plays, and
the same growth and return. The bar grows up out of the corner, right-aligned above it, so it
stays clear of the panes' history bars along their bottoms. Its tab sits above it, chevron down.

The window's corner radius is published each frame (`core.screens.publishMainRadius`): 0 when
maximized or full screen, the OS's otherwise, fizzy's own on Linux. The pill's code goes.
@foxnne foxnne changed the title automation: a playing tape rests in a glass pill under the title bar automation: a playing tape rests in the window's own corner Oct 19, 2026
The shapes a control of glass is made of (circles, capsules, panels as rounded
boxes), the cut of one by its place's outline as a polygon, and shape
interpolation: the geometry the orb system builds on, std-only and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the automation/tape-pill branch from 51b3d95 to 8e57071 Compare October 19, 2026 14:58
A pane in the window's corner puts its history bar in the band the tape's bar takes over the
corner. Each history bar out now says where it is, for the frame (`dome.publishBar`, the rect
`HistoryTransport.place` already gives). The tape's bar, drawn after every pane, stands clear
above any it would overlap (`dome.barsOut`), easing up and back down as that bar comes and goes,
so both show at once. Captions keep above it wherever it is. No bar out, nothing is published.
@foxnne
foxnne force-pushed the automation/tape-pill branch from 8e57071 to 9043580 Compare October 19, 2026 15:01
The history's grip moves from the middle of the card's bottom edge, where a split's bar runs, to a
vial in its bottom-right corner: the corner orb, with a history icon. Its liquid (`app/Liquid.zig`)
sloshes as the pane moves, splashes as steps come and eases up, asking for frames only while it
moves. The bar grows out of it as one liquid: its bubbles pulled out small along arcs, joined and
then pinching off, the liquid pouring from the vial into the line. A pane's activity feed stacks
above the vial.
…rom the top right

`LiquidField.Edge.cut` cuts glass by its place's outline with a bevel and lens band of its own along
the cut, rather than joining the place's outside: a piece of glass laid in a corner, bending the
border and ring it lies along. `light_across` turns a drop's rim light a quarter, for glass whose
edges that show face across the usual light (a place's top-right or bottom-right corner). Both reach
`core.dialogs.glassBubbles` as options.
@foxnne
foxnne force-pushed the app/history-vial-return branch 2 times, most recently from baa2e8f to cd733c0 Compare October 19, 2026 15:07
…e their bubbles, briskly

Merges app/history-vial-return, now on the reviewed vial (#635): `Liquid.step(now, pos)` takes
no width or depth now.

From the user, on #637:
- The open bar sits in the middle of the window's bottom, still growing out of the window's
  corner and going back into it. Its close button covered the history button of a pane in the
  corner. Each history vial at rest now publishes where it is too (`dome.publishBar`), so the
  tape's bar stands clear above any vial or history bar under it.
- Each icon (prev, play, next, close, the tab's chevron) rides its bubble, scaling and moving with
  it, and fades with how far that bubble has come. It's gone before the bubbles run together
  going back, and shows only once they have parted coming out.
- The grow and return take 450 ms, down from 620 ms (`bar_grow_us`, 27% shorter), on the same
  curves. The history bar's own time is unchanged.
foxnne added a commit that referenced this pull request Oct 19, 2026
## What changes

While a demo tape plays, an OS window over the main one (a dialog's, as
About Fizzy or the "unsaved changes before quitting?" prompt; a menu's;
a float's) no longer draws the tape's caption inside itself, cut off at
its bottom edge. The captions, key pills, the transport bar and the
"Catching up" pill stay in the main window; the demo's pointer still
goes over whatever window it is on.

Cause: the popout code hands every layer that takes no input to
whichever OS window its middle is over (`Popout.overlaysFrame`). That is
what the demo's pointer wants, so it is drawn on the dialog it clicks
in, but the tape overlay's captions and bar are layers of the same kind,
so one whose middle fell inside a dialog's window was taken from the
main window and drawn in the dialog.

Fix: the overlay's layers other than the pointer (and a callout placed
beside what the pointer acts on, which goes where that is) are marked as
the main window's this frame (`core.screens.markMain`, beside
`markEverywhere`/`markCarried`), and `overlaysFrame` leaves them to the
main window. `windowFrame` (floats) and `menuFrame` already took no such
layer.

The bar, now drawn under a dialog's OS window rather than over it, would
still have taken presses at its rects meant for that window. The app
hands the player its OS windows' rects each frame
(`replay.Player.Transport.covered`, filled from `Popout.windowsShown`
only while a tape is loaded); a press there goes to the window, and the
bar's hover clears.

Not done (review): folding
`markEverywhere`/`markCarried`/`markMenu`/`markMain` into one generic
helper. dvui checks a data entry's type name in debug, and `markMenu` is
called by plugins built against older `core`, so renaming its type would
trip that.

Touches the same `app/automation/overlay.zig` as #637, in a different
place (the `layer` definition); either rebases trivially over the other.

## SDK impact

- [x] Core-only or additive: reaches plugins at the next SDK release
(`core.screens.markMain`/`isMain`, new; no fingerprint move)

## Verified

- [x] macOS: sandbox profile, main window 900×480, tour started then
About Fizzy opened while the title card shows (its middle inside the
dialog's window). With this change's check disabled the card was drawn
inside the About window, cut at its edges, and gone from the main window
(the bug); with it, the card is drawn in the main window behind the
dialog and the About window shows only itself. Captured per OS window
with screencapture -l. After the review fixes: same check, and paused so
the bar shows; it sits under the About window in the main window. After
the review fixes: same check, and paused so the bar shows; it sits under
the About window in the main window. Gates: check-fmt, zig build, test,
test-integration (81/81 steps), check-web.
- [ ] Windows:
- [ ] Linux:
- [ ] Web: no OS windows there; unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added 10 commits October 19, 2026 10:45
- The vial's liquid stays the one simulated liquid through the return. The bar drains it by
  lowering its level while out and refills it going back, so at the hand-off the surface no longer
  switches from a still, tipped level to the living one.
- The vial's icon is lit by the step's glow and the pointer while it grows back, as at rest, so
  its colour no longer snaps when the vial takes over.
- `pull` and `budded` go: both the dome and the corner call `pullFrom` with their home at the site.
- A unit test holds the no-pop properties: a bubble arrives as the home, the home is the vial
  whole, and no glass merges between them at the end.
- The corner's liquid stays the one simulated liquid through the return. The bar drains it by
  lowering its level while out and refills it going back, so the hand-off no longer switches
  surface. The cassette is lit as at rest while it grows back.
- A zoomed macOS window keeps its rounded corner for the tape's glass; only full screen (and
  maximized elsewhere) is square.
- The bars registry keeps nothing unless something read it the frame before (a loaded tape), so
  panes with history do no work for it while no tape plays. Its cap rises to 32.
- AUTOMATION.md describes the centred bar; `slot_h`, unused, goes.
@foxnne
foxnne marked this pull request as ready for review October 19, 2026 20:06
Base automatically changed from app/history-vial-return to main October 19, 2026 20:24
@foxnne
foxnne enabled auto-merge October 19, 2026 20:25
@foxnne
foxnne added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 9196630 Oct 19, 2026
11 checks passed
@foxnne
foxnne deleted the automation/tape-pill branch October 19, 2026 21:01
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.

1 participant