storyink 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.4
4
+
5
+ - **Fix: → stopped before the next box was visible.** Step moves targeted the next step *start*,
6
+ and stories split a pulse and the reveal it causes into consecutive steps, so → stopped as the
7
+ dot arrived, before its target box appeared. → / ← now land on the next / previous **beat stop**:
8
+ a pulse and the reveal of its target are one beat, and the stop is where they (and the caption)
9
+ have settled. One definition, `beatGroups`, now drives step moves, stepped (reduced-motion)
10
+ playback, scrubber ticks and beat tiles. A reveal step that has its own caption now joins the
11
+ pulse whose target it reveals.
12
+ - Captions finish typing within their step (the last word no longer settles after the step ends).
13
+ - Step end times round up to the millisecond, so a stop is never a fraction of a millisecond
14
+ before its dot lands.
15
+ - **Fix: sharp light-grey corners during animation.** The pulse trail and dot followed the route's
16
+ corner points (straight segments with sharp corners) instead of the rounded wire. Routes now use
17
+ `flattenPath(edge.d)`, the drawn curve sampled in core, in the HTML viewer and the SMIL animated
18
+ SVG. The draw-on dash length is the rounded wire's length, and multi-hop routes no longer cut the
19
+ corner of the next wire.
20
+ - Core: `beatGroups`, `beatStops`, `beatChapters`, `beatTicks`, `flattenPath`.
21
+ - `verify:viewer`: → from OpenWick beat stops ends with the dot's target box visible. Chrome is
22
+ always killed (exit, signals, uncaught errors, plus a watchdog if the runner is SIGKILLed; a
23
+ 20 min cap), CDP calls time out after 30 s, and the run has a 15 min deadline.
24
+
25
+ ## 0.3.3
26
+
27
+ - **Fix: `snapshot --camera follow` wasn't deterministic on real diagrams** (`follow-deterministic`
28
+ failed on OpenWick's 24- and 18-node architecture/data-flow diagrams). The DOM, camera and
29
+ transform were identical across runs; what varied was Chrome's paint history. Some runs painted a
30
+ frame at the fit transform before the followed camera was placed, which changed the canvas
31
+ raster (anti-aliasing of some node boxes and text). With `#camera=follow&t=` the canvas now stays
32
+ hidden until the followed camera has painted. `follow-deterministic` now re-captures every
33
+ `at` frame, not only the first.
34
+ - **Arrow keys animate step moves.** → plays forward to the next step boundary and pauses (while
35
+ playing: advance one step); ← rewinds backwards at 2× to the previous boundary with a short
36
+ bounce-free ease. Shift goes by chapter, repeated presses extend the target, and the opposite key
37
+ reverses. Space pauses a move, the follow camera follows it (backwards too), and reduced motion
38
+ still jumps.
39
+ - `window.__storyink.step(dir, chapter?)` animates the same way and returns a Promise;
40
+ `stepAnimated()` is the running move. `state()` reads the clock/mode without render lag.
41
+ - Core: `stepBoundary`, `stepMoveTarget`, `stepMoveSpeed`, `STEP_MOVE`.
42
+ - `verify:viewer` drives → / ← / Shift / Space with real key events (CDP `Input.dispatchKeyEvent`).
43
+
3
44
  ## 0.3.2
4
45
 
5
46
  - **Fix: clicking the transport's play/pause did nothing** (space worked). The stage's native
package/README.md CHANGED
@@ -123,7 +123,8 @@ Add a `story` to play a diagram as a sequence of beats in the HTML viewer:
123
123
 
124
124
  `"story": "auto"` (or `--story auto`) derives the beats from the graph or message order. Playback
125
125
  has a click-to-play gate, play/pause, a tape-rewind replay and a scrubber with step and chapter
126
- ticks. Space, ←/→ and R control it.
126
+ ticks. Space, ←/→ and R control it: → plays to the next step and pauses, ← rewinds at 2× to the
127
+ previous one (Shift: by chapter; repeated presses extend the move).
127
128
 
128
129
  Motion is **full by default, even when the reader's system asks for reduced motion**. Authors can
129
130
  set `"story": { "motion": "reduced" }` (always step by step) or `"motion": "system"` (follow
@@ -6,7 +6,7 @@ import {
6
6
  renderHtml,
7
7
  renderSvg,
8
8
  validate
9
- } from "./index-n3gf4fyb.js";
9
+ } from "./index-a1ae1dgw.js";
10
10
 
11
11
  // src/node/index.ts
12
12
  import fs3 from "node:fs";
@@ -339,10 +339,15 @@ async function snapshot(htmlPath, opts = {}) {
339
339
  const same = sha256(a1) === sha256(a2);
340
340
  gates.push({ name: "deterministic", pass: same, detail: same ? `${first.theme} at t=${midT} captured twice: identical` : `${first.theme} at t=${midT} differs between runs` });
341
341
  if (follow) {
342
- const f2 = path2.join(tmpRoot, "follow-2.png");
343
- await shoot(`theme=${first.theme}&chrome=0${tq(first.at ?? "end")}${mq}${cq}`, f2, FW, Math.round(FW * 0.5625));
344
- const ok = sha256(f2) === first.sha256;
345
- gates.push({ name: "follow-deterministic", pass: ok, detail: ok ? `camera=follow at t=${first.at} captured twice: identical` : `camera=follow at t=${first.at} differs between runs` });
342
+ const diff = [];
343
+ for (const c of captures.filter((x) => x.theme === first.theme)) {
344
+ const f2 = path2.join(tmpRoot, `follow-${diff.length}-${String(c.at)}.png`);
345
+ await shoot(`theme=${c.theme}&chrome=0${tq(c.at ?? "end")}${mq}${cq}`, f2, FW, Math.round(FW * 0.5625));
346
+ if (sha256(f2) !== c.sha256)
347
+ diff.push(String(c.at));
348
+ }
349
+ const at = captures.filter((x) => x.theme === first.theme).map((x) => x.at).join(",");
350
+ gates.push({ name: "follow-deterministic", pass: !diff.length, detail: !diff.length ? `camera=follow at t=${at} captured twice: identical` : `camera=follow at t=${diff.join(",")} differs between runs` });
346
351
  }
347
352
  if (tl) {
348
353
  const stat = path2.join(tmpRoot, "static.png");