@dev.fast/whiteboard 0.5.1 → 0.5.2-preview.20261104.146

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.
Files changed (117) hide show
  1. package/dist/{account-alias-Bw3PT3Qo.js → account-alias-BlYamXAG.js} +5 -5
  2. package/dist/{agent-cli-kHRrwO4n.js → agent-cli-D7fF5xmm.js} +9 -9
  3. package/dist/agent-cli-DLibAL4u.js +2 -0
  4. package/dist/{agent-client-CMacGbMp.js → agent-client-BCEbSPWU.js} +4 -4
  5. package/dist/{authoring-tools-CiLy46kh.js → authoring-tools-CkfxTVJm.js} +12 -8
  6. package/dist/{background-server-eZWXoVcI.js → background-server-BraP8pJ9.js} +4 -4
  7. package/dist/{background-server-BLNHbksB.js → background-server-CwToVN1g.js} +1 -1
  8. package/dist/build-info.json +1 -1
  9. package/dist/{cli-Bxmiqutp.js → cli-BpadBaGU.js} +1 -1
  10. package/dist/{cli-runner-DQPuu7kJ.js → cli-runner-BebYF50a.js} +22 -22
  11. package/dist/cli.js +2 -2
  12. package/dist/{fuzzy-match-DZjeOaPY.js → fuzzy-match-CxcsLQGx.js} +3 -3
  13. package/dist/{headless-host-Dl2KZ9IT.js → headless-host-FAlbekFD.js} +561 -415
  14. package/dist/{headless-host-B4r0s1ks.js → headless-host-cLipIufH.js} +1 -1
  15. package/dist/{import-YAIU6rB0.js → import-CZ1958L4.js} +234 -62
  16. package/dist/{local-data-C58vzLZR.js → local-data-DdFkxFK_.js} +41 -16
  17. package/dist/{mcp-COYWiGVn.js → mcp-Claiy9xE.js} +9 -6
  18. package/dist/{package-paths-Cg6fnaGp.js → package-paths-Dk7MOXxN.js} +1 -1
  19. package/dist/{profile--BYIjPkc.js → profile-Bsgzi9PA.js} +2 -2
  20. package/dist/profile-BxdzBxHW.js +2 -0
  21. package/dist/{remote-attach-C1prLjmj.js → remote-attach-LGTpwpUv.js} +4 -4
  22. package/dist/{remote-extensions-Bw029iIS.js → remote-extensions-BvV29_gk.js} +1 -1
  23. package/dist/{remote-extensions-MUUgbLJn.js → remote-extensions-L95HNwIH.js} +2 -2
  24. package/dist/{remote-language-server-z3rdHcLL.js → remote-language-server-BAznkpeu.js} +4 -4
  25. package/dist/{remote-uninstall-B3Slwor1.js → remote-uninstall-CLP79Cuk.js} +5 -5
  26. package/dist/{request-origin-B3_XI2VA.js → request-origin-D6B-zrL8.js} +27 -5
  27. package/dist/{review-app-launcher-BZEbDKuK.js → review-app-launcher-tTe8bTwg.js} +2 -2
  28. package/dist/{review-home-paths-LdGS4SpR.js → review-home-paths-BmZX2qiz.js} +1 -1
  29. package/dist/{review-telemetry-x8u-9EZb.js → review-telemetry-DACDMfqq.js} +4 -4
  30. package/dist/server/desktop-host.js +26 -38
  31. package/dist/{server-discovery-WmuFl2S3.js → server-discovery-CV5qf1J8.js} +2 -2
  32. package/dist/sharing/index.d.ts +137 -14
  33. package/dist/sharing/index.js +1 -1
  34. package/dist/{src-B2b8cCoI.js → src-BQDlJXkG.js} +44 -5
  35. package/dist/{src-DLWol7GZ.js → src-CrrW7lcY.js} +16 -1
  36. package/dist/{stored-document-migration-iwIQ4l5G.js → stored-document-migration-D1wK4yID.js} +3 -3
  37. package/dist/{thread-state-B7L1mpN_.js → thread-state-WTqSborE.js} +1 -1
  38. package/dist/{tool-failure-RLR8m9hq.js → tool-failure-YOFGHJCZ.js} +319 -55
  39. package/instructions/animation.md +69 -5
  40. package/instructions/animations/before-after-structure/README.md +27 -0
  41. package/instructions/animations/before-after-structure/animation.html +89 -0
  42. package/instructions/animations/before-after-structure/block.json +10 -0
  43. package/instructions/animations/before-after-structure/frames.json +313 -0
  44. package/instructions/animations/cache-fill-evict/README.md +24 -0
  45. package/instructions/animations/cache-fill-evict/animation.html +81 -0
  46. package/instructions/animations/cache-fill-evict/block.json +13 -0
  47. package/instructions/animations/cache-fill-evict/frames.json +79 -0
  48. package/instructions/animations/cache-fill-evict/scenarios.json +17 -0
  49. package/instructions/animations/log-replay/README.md +25 -0
  50. package/instructions/animations/log-replay/animation.html +86 -0
  51. package/instructions/animations/log-replay/block.json +10 -0
  52. package/instructions/animations/log-replay/frames.json +143 -0
  53. package/instructions/animations/log-replay/scenarios.json +18 -0
  54. package/instructions/animations/queue-workers/README.md +23 -0
  55. package/instructions/animations/queue-workers/animation.html +107 -0
  56. package/instructions/animations/queue-workers/block.json +13 -0
  57. package/instructions/animations/queue-workers/frames.json +119 -0
  58. package/instructions/animations/queue-workers/scenarios.json +18 -0
  59. package/instructions/animations/race-two-lanes/README.md +22 -0
  60. package/instructions/animations/race-two-lanes/animation.html +97 -0
  61. package/instructions/animations/race-two-lanes/block.json +1 -0
  62. package/instructions/animations/race-two-lanes/frames.json +234 -0
  63. package/instructions/animations/race-two-lanes/scenarios.json +19 -0
  64. package/instructions/animations/request-across-layers/README.md +24 -0
  65. package/instructions/animations/request-across-layers/animation.html +85 -0
  66. package/instructions/animations/request-across-layers/block.json +17 -0
  67. package/instructions/animations/request-across-layers/frames.json +58 -0
  68. package/instructions/animations/request-across-layers/scenarios.json +12 -0
  69. package/instructions/animations/request-timing/README.md +22 -0
  70. package/instructions/animations/request-timing/animation.html +120 -0
  71. package/instructions/animations/request-timing/block.json +21 -0
  72. package/instructions/animations/request-timing/markers.json +32 -0
  73. package/instructions/animations/request-timing/scenarios.json +4 -0
  74. package/instructions/animations/retry-backoff/README.md +22 -0
  75. package/instructions/animations/retry-backoff/animation.html +122 -0
  76. package/instructions/animations/retry-backoff/block.json +10 -0
  77. package/instructions/animations/retry-backoff/frames.json +65 -0
  78. package/instructions/animations/retry-backoff/scenarios.json +12 -0
  79. package/instructions/animations/state-machine/README.md +24 -0
  80. package/instructions/animations/state-machine/animation.html +95 -0
  81. package/instructions/animations/state-machine/block.json +9 -0
  82. package/instructions/animations/state-machine/frames.json +79 -0
  83. package/instructions/animations/state-machine/scenarios.json +12 -0
  84. package/instructions/animations/video-platform-architecture/README.md +24 -0
  85. package/instructions/animations/video-platform-architecture/animation.html +76 -0
  86. package/instructions/animations/video-platform-architecture/block.json +17 -0
  87. package/instructions/animations/video-platform-architecture/frames.json +97 -0
  88. package/instructions/animations/video-platform-architecture/scenarios.json +28 -0
  89. package/instructions/whiteboard.d.ts +168 -14
  90. package/package.json +4 -4
  91. package/src/animation-stage-kit.ts +150 -10
  92. package/src/cli-runner.ts +4 -1
  93. package/src/fixtures/blocks/clip.json +19 -0
  94. package/src/review-api/README.md +1 -0
  95. package/src/review-api/agent-cli.ts +3 -2
  96. package/src/review-api/animation-drafts.ts +151 -47
  97. package/src/review-api/animation-references.ts +92 -0
  98. package/src/review-api/animation-render.ts +106 -0
  99. package/src/review-api/authoring-tools.ts +10 -3
  100. package/src/review-api/document-text.ts +27 -12
  101. package/src/review-api/http.ts +52 -6
  102. package/src/review-api/instructions.ts +1 -1
  103. package/src/review-api/mcp.ts +10 -5
  104. package/src/review-api/public-tools.ts +71 -2
  105. package/src/review-api/store.ts +89 -12
  106. package/src/server/desktop-server.ts +17 -5
  107. package/src/server/headless-host.ts +4 -0
  108. package/src/server/review-gateway.ts +1 -0
  109. package/src/server/review-server-core.ts +71 -12
  110. package/src/sharing/client.ts +129 -32
  111. package/src/sharing/export.ts +42 -5
  112. package/src/sharing/file.ts +8 -0
  113. package/src/sharing/host.ts +202 -73
  114. package/src/sharing/import.ts +125 -33
  115. package/src/ui-telemetry-events.ts +25 -1
  116. package/dist/agent-cli-B8KBHuh_.js +0 -2
  117. package/dist/profile-BjcXi-oH.js +0 -2
@@ -0,0 +1,79 @@
1
+ [
2
+ {
3
+ "key": "draft",
4
+ "title": "A new order starts as a draft",
5
+ "label": "DRAFT",
6
+ "caption": "Only a draft can be edited. Every later state is reached through one named transition.",
7
+ "data": { "state": "draft" }
8
+ },
9
+ {
10
+ "key": "submitted",
11
+ "title": "Submit queues it for payment",
12
+ "label": "SUBMIT",
13
+ "caption": "submit moves draft to queued; the line items are now fixed.",
14
+ "data": {
15
+ "state": "queued",
16
+ "transition": { "event": "submit", "from": "draft", "to": "queued" }
17
+ }
18
+ },
19
+ {
20
+ "key": "paid",
21
+ "title": "The payment webhook marks it paid",
22
+ "label": "PAY",
23
+ "caption": "pay is legal only from queued, so a duplicate webhook cannot charge twice.",
24
+ "data": {
25
+ "state": "paid",
26
+ "transition": { "event": "pay", "from": "queued", "to": "paid" }
27
+ }
28
+ },
29
+ {
30
+ "key": "shipped",
31
+ "title": "Fulfilment ships it",
32
+ "label": "SHIP",
33
+ "caption": "shipped is final: no transition leaves it.",
34
+ "data": {
35
+ "state": "shipped",
36
+ "transition": { "event": "ship", "from": "paid", "to": "shipped" }
37
+ }
38
+ },
39
+ {
40
+ "key": "cancel-refused",
41
+ "title": "Cancelling a shipped order is refused",
42
+ "label": "CANCEL REFUSED",
43
+ "caption": "The table has no cancel from shipped, so the API answers 409 and the status stays shipped.",
44
+ "data": {
45
+ "state": "shipped",
46
+ "transition": {
47
+ "event": "cancel",
48
+ "from": "shipped",
49
+ "to": "cancelled",
50
+ "refused": "already shipped"
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "key": "cancelled",
56
+ "title": "The customer cancels before paying",
57
+ "label": "CANCEL",
58
+ "caption": "cancel is legal from queued and from paid; from paid it also starts a refund.",
59
+ "data": {
60
+ "state": "cancelled",
61
+ "transition": { "event": "cancel", "from": "queued", "to": "cancelled" }
62
+ }
63
+ },
64
+ {
65
+ "key": "pay-refused",
66
+ "title": "A late payment is refused",
67
+ "label": "PAY REFUSED",
68
+ "caption": "A webhook that arrives after the cancel tries pay from cancelled. It is refused and the order stays cancelled.",
69
+ "data": {
70
+ "state": "cancelled",
71
+ "transition": {
72
+ "event": "pay",
73
+ "from": "cancelled",
74
+ "to": "paid",
75
+ "refused": "order cancelled"
76
+ }
77
+ }
78
+ }
79
+ ]
@@ -0,0 +1,12 @@
1
+ [
2
+ {
3
+ "key": "happy",
4
+ "label": "Happy path",
5
+ "frames": ["draft", "submitted", "paid", "shipped", "cancel-refused"]
6
+ },
7
+ {
8
+ "key": "cancellation",
9
+ "label": "Cancellation",
10
+ "frames": ["draft", "submitted", "cancelled", "pay-refused"]
11
+ }
12
+ ]
@@ -0,0 +1,24 @@
1
+ # How a video gets from upload to playback
2
+
3
+ An architecture scene: the client, the edge, our services and our storage as four groups, with an upload walking down through them and a playback walking back out through the CDN.
4
+
5
+ ## Use when
6
+
7
+ A change moves work between layers of a system: a new service, a queue between two steps, a cache in front of storage, a third party taking over a step. The reviewer needs to see which layer owns each step and where a request leaves our code.
8
+
9
+ ## Shows
10
+
11
+ - Upload: the gateway routes to the upload service, the original lands in blob storage, a transcode job waits in a queue, a worker renders renditions and the catalog marks the video ready.
12
+ - Playback: the catalog resolves a manifest, the app fetches segments from the CDN, and the first fetch in a region misses to blob storage; later ones never touch our services.
13
+ - Each layer is a `group`; each card wears its kind as a `form`; the storage cards share `teal`; the queue is `violet`; the CDN, which we do not run, is dashed. A `legend` decodes them.
14
+
15
+ ## Technique
16
+
17
+ A scene of six half-step columns, every card two wide so the two edge cards sit centred under the one client, and four rows, one row per layer, placed so that every hop is between neighbouring cells: the app over the gateway, the services under the gateway's row, each store under the service that uses it. `s.group` draws the four layers behind their cells: the client in `accent`, the edge plain, services plain, storage in `teal`. Cards take `form`: `window` for the app, `bars` for our services, `drum` with `outline: "dashed"` for the CDN, `cylinder` for the stores and `stack` for the queue. One `send` per frame carries the request or the data between layers; `on` changes notes and tones as a layer takes over; `highlight` rings the card doing the work. `s.legend` lists the six marks once, under the scene.
18
+
19
+ ## Adapt
20
+
21
+ - Rename the layers and cards to your system; keep three card columns (six half-step ones when a row must centre under another) so the groups stay one row each, and give each layer its own row. Place a card under the one that calls it: an edge between neighbouring cells reads, one that crosses a card bows. Twelve nodes in four columns tangle; nine in three read.
22
+ - Put each real step's range in `code` and name it on the frames that show it.
23
+ - A layer with one card still deserves its group when the boundary is the point.
24
+ - Keep one `send` per frame; a step that fans out is two frames.
@@ -0,0 +1,76 @@
1
+ <script>
2
+ // Six half-step columns, every card two wide, so a row of two can sit
3
+ // centred under a row of one.
4
+ const s = whiteboard.scene({ columns: 6 });
5
+
6
+ // Four layers, one row each, each a group behind its cells. Every hop in
7
+ // the story is between neighbouring cells, so no edge crosses a card.
8
+ s.group({ id: "clients", at: [3, 1, 1, 2], title: "Client", color: "accent" });
9
+ s.group({ id: "edge", at: [2, 2, 1, 4], title: "Edge" });
10
+ s.group({ id: "services", at: [1, 3, 1, 6], title: "Our services" });
11
+ s.group({ id: "storage", at: [1, 4, 1, 6], title: "Storage", color: "teal" });
12
+
13
+ // Each card wears its kind: a window for the app, bars for our services,
14
+ // a drum for the CDN's cache, dashed because we do not run it, cylinders
15
+ // for the stores and a stack for the queue.
16
+ const app = s.actor({ id: "app", at: [3, 1, 1, 2], title: "Video app", form: "window", highlight: ["post", "watch", "play"] });
17
+ const gateway = s.actor({ id: "gateway", at: [2, 2, 1, 2], title: "API gateway", form: "bars", highlight: ["receive", "deliver"] });
18
+ const cdn = s.store({ id: "cdn", at: [4, 2, 1, 2], title: "CDN", note: "edge cache · third party", form: "drum", outline: "dashed", highlight: ["segment", "edge-miss"] });
19
+
20
+ const upload = s.actor({ id: "upload", at: [1, 3, 1, 2], title: "Upload service", form: "bars", code: "upload", highlight: ["store", "enqueue"] });
21
+ const transcoder = s.actor({ id: "transcoder", at: [3, 3, 1, 2], title: "Transcoder", note: "worker pool", form: "bars", code: "transcode", highlight: ["transcode", "renditions"] });
22
+ const catalog = s.actor({ id: "catalog", at: [5, 3, 1, 2], title: "Catalog service", form: "bars", code: "watch", highlight: ["ready", "lookup", "resolve"] });
23
+
24
+ const queue = s.store({ id: "queue", at: [1, 4, 1, 2], title: "Transcode queue", form: "stack", color: "violet" });
25
+ const blob = s.store({ id: "blob", at: [3, 4, 1, 2], title: "Blob store", note: "originals · renditions", form: "cylinder", color: "teal" });
26
+ const db = s.store({ id: "db", at: [5, 4, 1, 2], title: "Catalog DB", note: "videos · renditions", form: "cylinder", color: "teal" });
27
+
28
+ s.legend([
29
+ { form: "window", label: "Client" },
30
+ { form: "bars", label: "Our service" },
31
+ { form: "cylinder", color: "teal", label: "Store" },
32
+ { form: "stack", color: "violet", label: "Queue" },
33
+ { form: "drum", label: "Cache" },
34
+ { outline: "dashed", label: "Third party" },
35
+ ]);
36
+
37
+ // The catalog always reads its database; the other edges arrive with sends.
38
+ s.link(catalog, db);
39
+
40
+ // Upload: down through the layers, then ready.
41
+ s.send(app, gateway, "POST /videos", "post");
42
+ s.send(gateway, upload, "video.mp4", "receive");
43
+ s.on("receive", upload, { text: "receiving 1.2 GB" });
44
+ s.send(upload, blob, "original", "store");
45
+ s.on("store", blob, { text: "v1/original.mp4", tone: "added" });
46
+ s.on("enqueue", upload, { text: "202 Accepted", tone: "added" });
47
+ queue.item("v1", { enter: "enqueue", exit: "transcode", tone: "added" });
48
+ s.send(upload, queue, "transcode v1", "enqueue");
49
+ s.send(queue, transcoder, "job v1", "transcode");
50
+ s.on("transcode", transcoder, { text: "rendering 360p · 720p · 1080p", tone: "accent" });
51
+ s.on("transcode", upload, { tone: "none" });
52
+ s.send(transcoder, blob, "renditions", "renditions");
53
+ s.on("renditions", blob, { text: "v1/{360,720,1080}p" });
54
+ s.send(transcoder, catalog, "v1 ready", "ready");
55
+ s.on("ready", transcoder, { text: "idle", tone: "none" });
56
+ s.on("ready", catalog, { text: "v1: ready", tone: "added" });
57
+ s.on("ready", db, { text: "videos.v1 = ready", tone: "added" });
58
+
59
+ // Playback: resolved by our services once, then served from the edge.
60
+ s.on("watch", catalog, { tone: "none" });
61
+ s.on("watch", db, { tone: "none" });
62
+ s.send(app, gateway, "GET /watch/v1", "watch");
63
+ s.send(gateway, catalog, "watch v1", "lookup");
64
+ s.on("lookup", db, { text: "read videos.v1" });
65
+ s.send(catalog, gateway, "manifest", "resolve");
66
+ s.on("resolve", catalog, { text: "manifest → CDN URLs" });
67
+ s.send(gateway, app, "manifest", "deliver");
68
+ s.on("deliver", app, { text: "playing v1", tone: "accent" });
69
+ s.send(app, cdn, "GET seg-0001", "segment");
70
+ s.on("segment", cdn, { text: "miss", tone: "modified" });
71
+ s.send(cdn, blob, "fetch seg-0001", "edge-miss");
72
+ s.on("edge-miss", blob, { text: "v1/720p/seg-0001" });
73
+ cdn.item("seg-0001", { enter: "edge-miss", tone: "added" });
74
+ s.send(cdn, app, "seg-0001", "play");
75
+ s.on("play", cdn, { text: "cached at the edge", tone: "none" });
76
+ </script>
@@ -0,0 +1,17 @@
1
+ {
2
+ "title": "How a video gets from upload to playback",
3
+ "code": {
4
+ "upload": {
5
+ "source": "head/services/upload/src/handler.ts#L18-L64",
6
+ "label": "handleUpload"
7
+ },
8
+ "transcode": {
9
+ "source": "head/services/transcoder/src/worker.ts#L30-L92",
10
+ "label": "TranscodeWorker.run"
11
+ },
12
+ "watch": {
13
+ "source": "head/services/catalog/src/watch.ts#L12-L58",
14
+ "label": "resolveWatch"
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,97 @@
1
+ [
2
+ {
3
+ "key": "post",
4
+ "title": "A creator uploads a video",
5
+ "label": "POST",
6
+ "caption": "The app posts the file to the API gateway, which fronts every service.",
7
+ "code": ["upload"]
8
+ },
9
+ {
10
+ "key": "receive",
11
+ "title": "The gateway hands it to the upload service",
12
+ "label": "RECEIVE",
13
+ "caption": "Routing by path: /videos belongs to the upload service.",
14
+ "code": ["upload"]
15
+ },
16
+ {
17
+ "key": "store",
18
+ "title": "The original goes to blob storage",
19
+ "label": "STORE",
20
+ "caption": "handleUpload streams the original into the blob store before anything else happens.",
21
+ "code": ["upload"]
22
+ },
23
+ {
24
+ "key": "enqueue",
25
+ "title": "A transcode job is queued",
26
+ "label": "ENQUEUE",
27
+ "caption": "The upload service answers the creator now; the heavy work waits in the queue.",
28
+ "code": ["upload"]
29
+ },
30
+ {
31
+ "key": "transcode",
32
+ "title": "A transcoder takes the job",
33
+ "label": "TRANSCODE",
34
+ "caption": "A worker takes the job and renders the renditions clients can stream.",
35
+ "code": ["transcode"]
36
+ },
37
+ {
38
+ "key": "renditions",
39
+ "title": "Renditions go back to the blob store",
40
+ "label": "RENDITIONS",
41
+ "caption": "Each rendition is written beside the original, keyed by video and quality.",
42
+ "code": ["transcode"]
43
+ },
44
+ {
45
+ "key": "ready",
46
+ "title": "The catalog marks the video ready",
47
+ "label": "READY",
48
+ "caption": "The transcoder tells the catalog service, which updates the catalog database; the video is now playable.",
49
+ "code": ["transcode"]
50
+ },
51
+ {
52
+ "key": "watch",
53
+ "title": "A viewer opens a video",
54
+ "label": "WATCH",
55
+ "caption": "The app asks the gateway for /watch/v1.",
56
+ "code": ["watch"]
57
+ },
58
+ {
59
+ "key": "lookup",
60
+ "title": "The catalog service resolves it",
61
+ "label": "LOOKUP",
62
+ "caption": "resolveWatch reads the video's row: its renditions and where they live.",
63
+ "code": ["watch"]
64
+ },
65
+ {
66
+ "key": "resolve",
67
+ "title": "A manifest comes back",
68
+ "label": "RESOLVE",
69
+ "caption": "The manifest lists segment URLs on the CDN, not the blob store.",
70
+ "code": ["watch"]
71
+ },
72
+ {
73
+ "key": "deliver",
74
+ "title": "The gateway returns the manifest",
75
+ "label": "DELIVER",
76
+ "caption": "The app can now fetch segments without touching our services again.",
77
+ "code": ["watch"]
78
+ },
79
+ {
80
+ "key": "segment",
81
+ "title": "The app fetches a segment from the CDN",
82
+ "label": "SEGMENT",
83
+ "caption": "Segments are plain files: the edge serves them, not the catalog."
84
+ },
85
+ {
86
+ "key": "edge-miss",
87
+ "title": "The edge misses and fetches from blob storage",
88
+ "label": "EDGE MISS",
89
+ "caption": "The first viewer in a region pays the fetch from our blob store; the CDN keeps the segment."
90
+ },
91
+ {
92
+ "key": "play",
93
+ "title": "Segments stream from the edge",
94
+ "label": "PLAY",
95
+ "caption": "Later viewers near this edge are served from the CDN's cache, without touching our services."
96
+ }
97
+ ]
@@ -0,0 +1,28 @@
1
+ [
2
+ {
3
+ "key": "upload",
4
+ "label": "Upload",
5
+ "frames": [
6
+ "post",
7
+ "receive",
8
+ "store",
9
+ "enqueue",
10
+ "transcode",
11
+ "renditions",
12
+ "ready"
13
+ ]
14
+ },
15
+ {
16
+ "key": "playback",
17
+ "label": "Playback",
18
+ "frames": [
19
+ "watch",
20
+ "lookup",
21
+ "resolve",
22
+ "deliver",
23
+ "segment",
24
+ "edge-miss",
25
+ "play"
26
+ ]
27
+ }
28
+ ]
@@ -5,6 +5,9 @@
5
5
  *
6
6
  * Frames are states, not events: whatever a frame shows must also be right
7
7
  * when the reader jumps to it or steps back, not only when it plays.
8
+ *
9
+ * A clip has no frames: its script builds one timeline with clip(), places
10
+ * the block's markers on it, and the reader plays it or jumps to a marker.
8
11
  */
9
12
 
10
13
  /* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-object-parameters -- Documents a script API; anime.js parses its own targets and params. */
@@ -12,6 +15,37 @@
12
15
  /** Colors any scene part, arrow, token, or kit element (as data-tone). */
13
16
  export type Tone = "accent" | "added" | "removed" | "modified" | "muted";
14
17
 
18
+ /** A card's silhouette, so a reader tells kinds apart before reading a
19
+ * title. Pick by what the card stands for: `cylinder` for a database,
20
+ * `drum` for a cache, `stack` for a queue or topic, `window` for a client
21
+ * (a browser, an app), `bars` for a service or worker of this codebase,
22
+ * `pill` for a start or end, `cut` for a branch. Any kit element takes it as
23
+ * data-form. */
24
+ export type Form =
25
+ | "box"
26
+ | "pill"
27
+ | "cut"
28
+ | "cylinder"
29
+ | "drum"
30
+ | "stack"
31
+ | "window"
32
+ | "bars";
33
+
34
+ /** A border's line. `dashed` reads as outside the system: a third-party
35
+ * service or API. As data-outline on a kit element. */
36
+ export type Outline = "solid" | "dashed" | "dotted";
37
+
38
+ /** A border's colour at rest, for grouping cards of a kind: `teal` suits
39
+ * stores, `violet` queues, `accent` the part under discussion. A tone from
40
+ * on() takes over while set. As data-color on a kit element. */
41
+ export type Color = "ink" | "accent" | "teal" | "violet";
42
+
43
+ /** What the change under review did to the part a card stands for. A
44
+ * resting mark in the change colours, its dot and a stripe down its left
45
+ * edge, that a frame's `tone` does not replace; a removed part's title is
46
+ * struck through. As data-change on a kit element. */
47
+ export type Change = "added" | "modified" | "removed";
48
+
15
49
  /** One frame, as your scripts read it. */
16
50
  export interface Frame {
17
51
  key: string;
@@ -35,6 +69,41 @@ export interface FrameChange {
35
69
  instant: boolean;
36
70
  }
37
71
 
72
+ /** One of a clip's markers, as your scripts read it. */
73
+ export interface Marker {
74
+ key: string;
75
+ title: string;
76
+ caption?: string;
77
+ /** The code names the marker lists, for data-wb-open. */
78
+ code?: string[];
79
+ /** Milliseconds into the active timeline, once the scripts have run. */
80
+ at?: number;
81
+ }
82
+
83
+ /** A marker the playhead reached: placed, with its place in time order. */
84
+ export interface ReachedMarker extends Marker {
85
+ at: number;
86
+ index: number;
87
+ }
88
+
89
+ /** What a marker handler learns as a clip's playhead reaches its marker. */
90
+ export interface MarkerChange {
91
+ marker: ReachedMarker;
92
+ /** A jump to the marker, the first draw, or reduced motion: draw the
93
+ * marker's state at once. */
94
+ instant: boolean;
95
+ }
96
+
97
+ /** What a paint handler learns as the block paints its first frame in. */
98
+ export interface PaintChange {
99
+ /** The first frame, already drawn in its end state. A clip has none: it
100
+ * paints in at time 0. */
101
+ frame?: Frame;
102
+ /** Skip motion and leave everything in its end state: the block loaded
103
+ * without painting in, or the reader prefers reduced motion. */
104
+ instant: boolean;
105
+ }
106
+
38
107
  /** Return the animations a handler starts, or a finite promise, so playback
39
108
  * waits for them. */
40
109
  export type FrameResult =
@@ -44,7 +113,8 @@ export type FrameResult =
44
113
  | Promise<void>;
45
114
 
46
115
  export interface Whiteboard {
47
- /** The active path's frames, in order: a scenario's, or all of them. */
116
+ /** The active path's frames, in order: a scenario's, or all of them. A
117
+ * frame outside the active scenario is not listed. */
48
118
  readonly frames: readonly Frame[];
49
119
  /** The current frame object; read `.key` for its name. */
50
120
  readonly frame: Frame;
@@ -57,12 +127,25 @@ export interface Whiteboard {
57
127
  * helpers do not manage when it is true. */
58
128
  readonly reducedMotion: boolean;
59
129
 
130
+ /** Paint the first frame in yourself, in place of the default painter. Runs
131
+ * once, after the first frame is drawn: with motion when an agent writes
132
+ * the block, with `instant: true` on a cold load or under reduced motion,
133
+ * when it must leave everything in its end state. Painting ends when what
134
+ * it returns has finished. */
135
+ on(key: "paint", handler: (paint: PaintChange) => FrameResult): () => void;
60
136
  /** Run when a frame shows; "*" runs on every frame, before frame-specific
61
137
  * handlers, and again when reduced motion changes. Cancel earlier work when
62
138
  * another frame shows. Returns an unsubscribe function. */
63
139
  on(key: string, handler: (change: FrameChange) => FrameResult): () => void;
140
+ /** In a clip: run when the playhead crosses the marker playing forward, or
141
+ * lands on it after a jump (`instant: true`); "*" runs on every marker.
142
+ * What a marker sets must also be right when the reader jumps there.
143
+ * Returns an unsubscribe function. */
144
+ on(key: string, handler: (change: MarkerChange) => FrameResult): () => void;
64
145
  /** element.animate that holds its end state, lands at the end on a jump, and
65
- * skips motion under reduced motion. */
146
+ * skips motion under reduced motion. Give every keyframe its start value, and
147
+ * measure positions before starting one: a started animation already
148
+ * reports its transform. */
66
149
  animate(
67
150
  target: Element | string,
68
151
  keyframes: Keyframe[] | PropertyIndexedKeyframes,
@@ -70,8 +153,22 @@ export interface Whiteboard {
70
153
  ): Animation;
71
154
  /** The frame's part of one anime.js timeline: it plays when the reader steps
72
155
  * into the frame and lands at its end on a jump. A tween starts where the
73
- * frames before it left the element, so write only where it goes. */
156
+ * frames before it left the element, so write only where it goes. Add to it
157
+ * while the script first runs, not from a frame handler. "paint" is a
158
+ * timeline of its own that plays once, after the first frame is drawn, as
159
+ * the block paints in, in place of the default painter; it lands at its end
160
+ * otherwise. Numeric positions count from its start. */
74
161
  timeline(key: string): FrameTimeline;
162
+ /** A clip's one timeline: build it while the script first runs and place
163
+ * every marker on it. With scenarios, call it once per scenario key, each
164
+ * a timeline of its own; one built without a key serves every scenario
165
+ * without its own. Throws in an animation. */
166
+ clip(scenario?: string): ClipTimeline;
167
+ /** A clip's markers as declared, each with `at` once placed. Empty in an
168
+ * animation. */
169
+ readonly markers: readonly Marker[];
170
+ /** The marker a clip's playhead last reached, if any. */
171
+ readonly marker: ReachedMarker | undefined;
75
172
  /** anime.js v4, also the global `anime`: easings, stagger, utils. */
76
173
  readonly anime: unknown;
77
174
 
@@ -104,9 +201,10 @@ export interface Whiteboard {
104
201
  /** A dot, or a pill with a label, that travel() moves along arrows. */
105
202
  token(options?: TokenOptions): HTMLElement;
106
203
  /** Move a token along an arrow during a frame, at the arrow's live geometry.
107
- * It fades out on arrival unless `stay` keeps it at the head. Trips append
204
+ * It waits at the head until the next frame unless `stay` is false. Trips append
108
205
  * to the frame; `position: 0` starts trips together, a number of
109
- * milliseconds sequences one after another. */
206
+ * milliseconds sequences one after another. Declare trips while the script
207
+ * first runs, not from a frame handler. */
110
208
  travel(
111
209
  token: HTMLElement | string,
112
210
  arrow: SVGGElement,
@@ -133,6 +231,25 @@ export interface FrameTimeline {
133
231
  call(callback: () => void, position?: number | string): FrameTimeline;
134
232
  }
135
233
 
234
+ /** A clip's timeline. Numeric positions count milliseconds from its start;
235
+ * "<" starts at the previous call's end and "<<" at its beginning. */
236
+ export interface ClipTimeline {
237
+ add(
238
+ targets: unknown,
239
+ params: object,
240
+ position?: number | string,
241
+ ): ClipTimeline;
242
+ set(
243
+ targets: unknown,
244
+ params: object,
245
+ position?: number | string,
246
+ ): ClipTimeline;
247
+ call(callback: () => void, position?: number | string): ClipTimeline;
248
+ /** Place a declared marker at the timeline's end so far, or at an anime.js
249
+ * position. Every marker must be placed once on each timeline. */
250
+ marker(key: string, position?: number | string): ClipTimeline;
251
+ }
252
+
136
253
  export interface TravelOptions {
137
254
  duration?: number;
138
255
  ease?: string;
@@ -140,7 +257,9 @@ export interface TravelOptions {
140
257
  stay?: boolean;
141
258
  }
142
259
 
143
- /** A scene card, its id, or an element. */
260
+ /** A scene card, its id, or an element. In `whiteboard.arrow` and `travel`
261
+ * a string is a CSS selector (`"#api"`); in a scene's methods it is a card
262
+ * id. */
144
263
  export type CardRef = SceneCard | string | Element;
145
264
 
146
265
  /** 1-based column and row, with optional row and column spans: [1, 2, 1, 4] is
@@ -154,12 +273,11 @@ export type Cell = [
154
273
 
155
274
  export interface SceneOptions {
156
275
  /** Declared columns; default 3. Three cards with short labels fit a row at
157
- * the document's width; past four, use a second row. */
276
+ * the document's width; past four, use a second row. The scene keeps its
277
+ * columns at any width: a narrower block scrolls it sideways. */
158
278
  columns?: number;
159
279
  /** Where the scene goes; defaults to the animation's body. */
160
280
  container?: string | HTMLElement;
161
- /** Fold into fewer columns when narrow, then one. Default true. */
162
- responsive?: boolean;
163
281
  }
164
282
 
165
283
  export interface Scene {
@@ -177,14 +295,25 @@ export interface Scene {
177
295
  ): HTMLElement;
178
296
  /** A pill above the scene, e.g. a version; on() changes its text. */
179
297
  badge(options: { text?: string; tone?: Tone }): HTMLElement;
298
+ /** A washed box behind the cards in its cells, titled in its corner: a
299
+ * boundary such as a service, a process or a layer. Declare it before or
300
+ * after its cards. */
301
+ group(options: SceneGroupOptions): HTMLElement;
302
+ /** A row under the scene saying what the cards' marks mean: one glyph,
303
+ * drawn like a card with the entry's marks, and a label each. Name only
304
+ * what the reader must decode. */
305
+ legend(entries: SceneLegendEntry[]): HTMLElement;
180
306
  /** Change a card's note, or an item's or badge's text, or a tone, from this
181
307
  * frame on, until a later frame changes it. Follows the active path. */
182
308
  on(frame: string, part: CardRef, change: SceneChange): void;
183
309
  /** A straight edge between two cards that follows them. It bows around
184
310
  * cards in the way; keep the cells between linked cards empty. */
185
311
  link(from: CardRef, to: CardRef, options?: SceneLinkOptions): SVGGElement;
186
- /** A chip that rides the edge during a frame at one fixed speed, drawing the
187
- * edge if there is none. Keep labels to about 12 characters. */
312
+ /** A message from one card to another in a frame, drawing the edge if there
313
+ * is none. While the frame shows, the message is the edge's label and the
314
+ * edge takes the marker colour, however the frame was reached; stepping
315
+ * into the frame rides a chip from the sender that settles into the label.
316
+ * Keep labels to about 12 characters. */
188
317
  send(
189
318
  from: CardRef,
190
319
  to: CardRef,
@@ -227,12 +356,39 @@ export interface SceneCardOptions extends Lifecycle {
227
356
  /** The second line; on() changes it per frame. */
228
357
  note?: string;
229
358
  tone?: Tone;
359
+ /** What the card is, by silhouette; see Form. Default box. */
360
+ form?: Form;
361
+ /** The border's line; see Outline. Default solid. */
362
+ outline?: Outline;
363
+ /** The border's colour at rest; see Color. */
364
+ color?: Color;
365
+ /** What the change under review did to this part; see Change. */
366
+ change?: Change;
230
367
  /** Frames during which the card is ringed. */
231
368
  highlight?: string[];
232
369
  /** The code the card opens when clicked: a name in the block's code. */
233
370
  code?: string;
234
371
  }
235
372
 
373
+ export interface SceneGroupOptions extends Lifecycle {
374
+ id: string;
375
+ /** The cells the group covers; its cards sit on its wash. */
376
+ at: Cell;
377
+ /** A caps title in its top-left corner, e.g. SEARCH SERVICE. */
378
+ title?: string;
379
+ /** The wash and title colour; see Color. Default a neutral well. */
380
+ color?: Color;
381
+ }
382
+
383
+ export interface SceneLegendEntry {
384
+ label: string;
385
+ form?: Form;
386
+ outline?: Outline;
387
+ color?: Color;
388
+ tone?: Tone;
389
+ change?: Change;
390
+ }
391
+
236
392
  export interface SceneItemOptions extends Lifecycle {
237
393
  tone?: Tone;
238
394
  }
@@ -254,10 +410,8 @@ export interface SceneLinkOptions extends Lifecycle {
254
410
  }
255
411
 
256
412
  export interface SceneSendOptions extends Lifecycle {
413
+ /** Colours the chip, and the edge and its label while the frame shows. */
257
414
  tone?: Tone;
258
- /** Keep the chip at its target instead of fading it out; scope when it shows
259
- * with enter, exit or only. */
260
- stay?: boolean;
261
415
  }
262
416
 
263
417
  export interface ArrowOptions extends Lifecycle {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.fast/whiteboard",
3
- "version": "0.5.1",
3
+ "version": "0.5.2-preview.20261104.146",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -77,10 +77,10 @@
77
77
  "typescript": "6.0.3",
78
78
  "vitest": "4.1.12",
79
79
  "@dev.fast/json": "0.0.0",
80
- "@dev.fast/trace-core": "0.0.0",
81
- "@dev.fast/review-share-protocol": "0.3.1",
80
+ "@dev.fast/review-share-protocol": "0.6.0",
82
81
  "@dev.fast/review-protocol": "0.0.0",
83
82
  "@dev.fast/trace-protocol": "0.5.0",
83
+ "@dev.fast/trace-core": "0.0.0",
84
84
  "@dev.fast/whiteboard-document": "0.2.1"
85
85
  },
86
86
  "engines": {
@@ -91,7 +91,7 @@
91
91
  "repo": "devdotfast/review",
92
92
  "docs": "packages/review/instructions/authoring.md"
93
93
  },
94
- "gitHead": "8c348e988bf5f91c45a9ad8b0a2748680ac0985f",
94
+ "gitHead": "20f4d6dcfaa9f7a40ff77fd6e6b2467fc89e64fa",
95
95
  "scripts": {
96
96
  "prebuild": "pnpm run build:workspace-deps",
97
97
  "build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",