storyink 0.3.2 → 0.3.3

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,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.3
4
+
5
+ - **Fix: `snapshot --camera follow` wasn't deterministic on real diagrams** (`follow-deterministic`
6
+ failed on OpenWick's 24- and 18-node architecture/data-flow diagrams). The DOM, camera and
7
+ transform were identical across runs; what varied was Chrome's paint history. Some runs painted a
8
+ frame at the fit transform before the followed camera was placed, which changed the canvas
9
+ raster (anti-aliasing of some node boxes and text). With `#camera=follow&t=` the canvas now stays
10
+ hidden until the followed camera has painted. `follow-deterministic` now re-captures every
11
+ `at` frame, not only the first.
12
+ - **Arrow keys animate step moves.** → plays forward to the next step boundary and pauses (while
13
+ playing: advance one step); ← rewinds backwards at 2× to the previous boundary with a short
14
+ bounce-free ease. Shift goes by chapter, repeated presses extend the target, and the opposite key
15
+ reverses. Space pauses a move, the follow camera follows it (backwards too), and reduced motion
16
+ still jumps.
17
+ - `window.__storyink.step(dir, chapter?)` animates the same way and returns a Promise;
18
+ `stepAnimated()` is the running move. `state()` reads the clock/mode without render lag.
19
+ - Core: `stepBoundary`, `stepMoveTarget`, `stepMoveSpeed`, `STEP_MOVE`.
20
+ - `verify:viewer` drives → / ← / Shift / Space with real key events (CDP `Input.dispatchKeyEvent`).
21
+
3
22
  ## 0.3.2
4
23
 
5
24
  - **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-xmj21twd.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");