Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/playback-node-cues.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "user-facing", "releaseNoteId": "release:0.23.0" }
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable Loop Studio releases, newest first. Behavioral changes are pinned
in versioned spec documents (see the [README](README.md#technical-reference));
this file is the narrative history, not the contract.

## v0.23.0 — 2026-10-09

Playback shows what happens inside the nodes (issue #330, second of three): a Pool lights up as its first marker arrives, and a Converter shows a conversion mark while its markers move, as [`docs/simulation-playback.md`](docs/simulation-playback.md) §PB4.7 describes.

- **The Pool arrival pulse.** When the first moving marker of a step reaches a Pool, a soft tint fills the Pool from 7 px inside its outline, under its title and value, for half a second; it carries on across the end of the step. The value still changes when the step ends. Past the 24 marker pairs and when zoomed far out, where no marker is drawn, the pulse plays at the same moment. It replaces the disc that used to play after the marker had gone.
- **The conversion mark (option A).** While a Converter's markers move, a small two-arrow mark shows inside it and fades after the step; its inputs and output still move together. Each Converter has its own spot for the mark, at least 6 px inside the outline and clear of its title and mode text, worked out once per text, language or size change. When zoomed far out it takes the place of the type dot.
- **Focus mode, reduced motion and forced colours.** Both are cues inside a node, so Focus mode never fades them. Under reduced motion they stay still for the step; in forced colours the pulse is a 2 px system-colour line under the text and the mark takes the same colour.

**No migration.** Engine, RNG, files, share links, digests and simulation results are unchanged; node sizes and text placement are unchanged. Three release-note lines in 18 languages, 16 of them without native review. The informational `meta.tool` string is now `loop-studio/0.23.0`.

## v0.22.0 — 2026-10-09

Playback shows what moved where (issue #330, first of three): the path a Gate took, every amount as a `+N` badge beside its round token, and Focus mode winning on connections, as [`docs/simulation-playback.md`](docs/simulation-playback.md) §PB4.6 and [`docs/large-graph-readability.md`](docs/large-graph-readability.md) §LGR2.3 describe.
Expand Down
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,19 @@ Additional feature-specific design documents (localization, mobile, module
system, large-graph readability, simulation playback, edge routing, data
import, …) live under [`docs/`](docs/).

## Latest — v0.22.0
## Latest — v0.23.0

Playback shows what happens inside the nodes: a Pool lights up as a marker arrives, and a
Converter shows that it converts.

- **The Pool lights up on arrival**: when a moving marker reaches a Pool, the inside of the
Pool briefly glows, under its title and value; the number still changes when the step ends
- **A conversion mark inside the Converter**: while a Converter's markers move, a small
two-arrow mark shows inside it, at a spot clear of its text, and fades when the step ends
- **Never faded by Focus mode**: both stay at full strength in nodes outside the focus; with
reduced motion they stay still for the step, and in forced colours the glow is a thin line

## v0.22.0

Playback shows what moved where: the path a Gate took, and every amount beside its moving
marker.
Expand Down Expand Up @@ -180,17 +192,8 @@ A fix release: the edit lock is exact, and its button shows the state.
- **The lock button reads at a glance**: an open padlock when you can edit, a closed,
highlighted one when editing is locked

## v0.21.2

A fix release: values and detail rows sit inside their node.

- **Pool, Parameter and Register rows start with the title**, at least 8 px inside the
drawn outline; before, a Pool's value sat on its slanted side
- **Wider only where needed**: a node grows only when a row would not fit, up to the
usual maximum, and a row too long even then ends in "…"; positions, heights, files and
results are unchanged

See [`CHANGELOG.md`](CHANGELOG.md) for the full notes of these releases, v0.21.1 (Focus
See [`CHANGELOG.md`](CHANGELOG.md) for the full notes of these releases, v0.21.2 (values
and detail rows sit inside their node), v0.21.1 (Focus
mode dims the connections too), v0.21.0 (compact
nodes, so more of a large graph fits in view), v0.20.0 (flow
colours in the minimap and the timeline), v0.19.0 (flow
Expand Down
5 changes: 3 additions & 2 deletions docs/large-graph-readability.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,9 @@ the L0 pulse and the reduced-motion forms are drawn at the same 0.26
colours they are not faded either, the highlight takes the `1 5` dash, the
token turns hollow and the badge's border is dashed
(`docs/simulation-playback.md` §PB4.6). The run-in-progress cue and every cue
INSIDE a node (the fired wave, the evaluated mark) keep full strength, as
above.
INSIDE a node (the fired wave, the evaluated mark, and since issue #330 PR 2,
v0.23.0, the Pool arrival pulse and the conversion mark, §PB4.7) keep full
strength, as above.

**A selected node is never de-emphasised.** Selection outranks focus: whatever
the focus set says, a node the user has selected renders at full strength, with
Expand Down
6 changes: 6 additions & 0 deletions docs/simulation-playback-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ holds (`fill: CanvasText` on `converge`, `stroke-dasharray` + vanish on
1.2×); `absorb` overrides the end with the dissolve.
- No new colour — all three read on `--flow-strength` / `currentColor`.
- The direction marker, trail, count label, and §PB4.5 breakdown are unchanged.
- **The conversion mark (issue #330 PR 2, v0.23.0, option A).** A Converter's
inputs and output keep this order: they share the Converter's one onset and
their tokens move together — there is no consume-then-produce sequence. Only
a 10 px ⇄ mark inside the Converter says "converted here", shown from that
onset while the tokens move and faded after the `settle`
(`docs/simulation-playback.md` §PB4.7). Onsets are unchanged.

## PBO4. Reduced-motion & forced-colors — the role difference must survive

Expand Down
47 changes: 46 additions & 1 deletion docs/simulation-playback.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ wall-clock by the current speed, §PB6):
The exact fractions are a single constants block (`PLAYBACK_BEATS`), tunable in
one place.

The target's inflow cue on a **Pool** is the **arrival pulse** (issue #330 PR 2,
v0.23.0, §PB4.7): it starts the moment that Pool's first token reaches it (the
connection's own arrive beat, on its local τ), plays inside the Pool, and runs a
fixed time across the `settle`; the Pool's value and delta still change only at
`settle`.

**PB2.2 — `committedStep === revealedStep`, always.** There is one clock: the
store's `stepIndex`. It moves only at `settle`, so what the canvas shows and what
the store holds are the same thing at every instant. There is no separate
Expand Down Expand Up @@ -387,6 +393,42 @@ files, digests and every PB-INV / PBO-INV invariant are unchanged.
- **Monte Carlo** draws no tokens, as before. The speed tiers (§PB6.1) are
issue #330 PR 3; until then every speed draws this same form.

**PB4.7 — the cues inside a node: the Pool arrival pulse and the conversion
mark (issue #330 PR 2, v0.23.0).** A presentation layer over §PB2.1 and
§PBO3; the engine, the RNG, files, digests and every PB-INV / PBO-INV invariant
are unchanged. Both are drawn **inside the node only**, never across the
selection, keyboard-focus or invalid rings, and neither moves, resizes or
re-lays out the node.

- **Pool arrival pulse.** A soft tint of the Pool's silhouette from 7 px inside
(clear of the flow-colour band and the focus ring), under the title and value,
which stay on top. It starts when the Pool's first token of the step reaches
it — the earliest arrive beat among its moved incoming connections — and runs
a fixed 0.5 s, carrying on across the `settle` (it is keyed on the step, so the
commit does not restart it); the next arrival at that Pool starts a new one.
Past the 24 pairs and at L0, where no token is drawn, it plays at the same
moment. The value and delta still change at `settle` (§PB2.4). It replaces the
disc that used to play at `settle`, after the token had gone.
- **Conversion mark (option A).** A 10 px ⇄ drawing inside the Converter,
shown from the Converter's own onset (its pull-in and push-out share one
onset, §PBO3) while its tokens move, then faded after the `settle`. Its spot
is the Converter's own: the free square nearest the end of the mode text, at
least 6 px inside the drawn outline and 1 px clear of the title, mode text
and type chip. The spot is computed from the silhouette and the measured text
once per change of the text, language, fonts or node size — never per frame,
never while a step plays — and is the same at L1 (where the mode text is
hidden but keeps its place). At L0 the mark takes the type dot's place and
size. No one fixed spot fits every Converter: the waisted shape and its text
leave a different pocket per node and language (a fixed spot after the mode
text missed 20 of the 450 bundled instances).
- **Focus mode:** both are cues inside a node and keep full strength on a node
outside the focus set (`docs/large-graph-readability.md` §LGR2.3).
- **Reduced motion:** both are held static for the committed step — the tint
and the mark at full strength, no fade — and clear on the next step or Reset
(§PB9).
- **Forced colours:** no tint; the pulse is a 2 px `Highlight` line 7–9 px
inside the outline, drawn under the text, and the mark takes the same colour.

## PB5. Pause / Resume

**PB5.1 — Pause keeps the prepared transition.** Pause stops the `τ` clock at its
Expand Down Expand Up @@ -598,7 +640,10 @@ same RNG result). The choreography then:
(target handle emphasis) cues **briefly** or all at once — the ordering is the
information, so it is not removed, but it is **not padded to a long duration**;
- runs `settle` (value delta chip, state effects) — a very short count-up or an
immediate snap.
immediate snap;
- holds the cues inside the nodes static for the committed step (issue #330
PR 2, §PB4.7): the Pool arrival tint and the conversion mark, at full
strength, cleared on the next step or Reset.

**PB9.2 — timing.** `beatDuration` may collapse toward its floor so the sequence
is quick; it must not stretch playback out. Step still fast-forwards; Play still
Expand Down
6 changes: 6 additions & 0 deletions docs/visual-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,12 @@ byte-identical across L2/L1/L0 (§VL12.5).
the moved path's highlight, the arrival tell, and the `+N` badge at the
target end. A connection's own label never moves; it dims only while the
token or its badge covers it.
- **Playback, issue #330 PR 2 (v0.23.0)** (`docs/simulation-playback.md`
§PB4.7): when a token reaches a Pool, a soft tint pulses inside the Pool
(0.5 s, under the text); a Converter whose tokens move shows a small ⇄ mark
inside it, which fades after the step. Under reduced motion both are held
static for the committed step; in forced colours the pulse is a 2 px
system-colour line instead of a tint.
- Motion never conveys information that isn't also in a static frame.

---
Expand Down
7 changes: 4 additions & 3 deletions e2e/canvas-refresh-edges.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ test.describe('Canvas Refresh PR 2 — reduced motion: the flow bead contract',
// the substitute: a persistent highlight on the edge that carried flow…
await expect(page.locator('.react-flow__edge[data-id="r1"] .flow-edge-pulse')).toHaveCount(1)
// …and the arrival cue on the pool it landed in, HELD (no fade-to-0 keyframe)
const arrival = page.locator('.react-flow__node[data-id="a"] .nodef__arrival')
// (issue #330 PR 2 — the arrival tint inside the Pool)
const arrival = page.locator('.react-flow__node[data-id="a"] .nodef__pulse')
await expect(arrival).toHaveCount(1)
const held = await arrival.evaluate((el) => {
const cs = getComputedStyle(el)
Expand All @@ -168,7 +169,7 @@ test.describe('Canvas Refresh PR 2 — reduced motion: the flow bead contract',
await load(page)
await step(page) // status → paused
const pulse = page.locator('.react-flow__edge[data-id="r1"] .flow-edge-pulse')
const arrival = page.locator('.react-flow__node[data-id="a"] .nodef__arrival')
const arrival = page.locator('.react-flow__node[data-id="a"] .nodef__pulse')
await expect(pulse).toHaveCount(1)
await expect(arrival).toHaveCount(1)

Expand All @@ -179,7 +180,7 @@ test.describe('Canvas Refresh PR 2 — reduced motion: the flow bead contract',
await expect(page.locator(MOTION)).toHaveCount(0) // still nothing moving

await reset(page)
await expect(page.locator('.flow-edge-pulse, .state-edge-pulse, .nodef__wave, .nodef__arrival')).toHaveCount(0)
await expect(page.locator('.flow-edge-pulse, .state-edge-pulse, .nodef__wave, .nodef__pulse, .nodef__conv')).toHaveCount(0)
})

test('a state-edge effect under reduce is also static — no pulse travels', async ({ page }) => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading