@neta-art/cohub 9.0.1 → 11.0.0

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 (240) hide show
  1. package/README.md +108 -102
  2. package/dist/board/editor/align.d.ts +11 -0
  3. package/dist/board/editor/align.js +72 -0
  4. package/dist/board/editor/animation.d.ts +17 -0
  5. package/dist/board/editor/animation.js +187 -0
  6. package/dist/board/editor/camera.d.ts +32 -0
  7. package/dist/board/editor/camera.js +137 -0
  8. package/dist/board/editor/clipboard.d.ts +41 -0
  9. package/dist/board/editor/clipboard.js +129 -0
  10. package/dist/board/editor/connection-ports.d.ts +19 -0
  11. package/dist/board/editor/connection-ports.js +85 -0
  12. package/dist/board/editor/context.d.ts +111 -0
  13. package/dist/board/editor/context.js +30 -0
  14. package/dist/board/editor/copy.d.ts +17 -0
  15. package/dist/board/editor/copy.js +121 -0
  16. package/dist/board/editor/creation.d.ts +51 -0
  17. package/dist/board/editor/creation.js +225 -0
  18. package/dist/board/editor/deletion.d.ts +9 -0
  19. package/dist/board/editor/deletion.js +90 -0
  20. package/dist/board/editor/document-edits.d.ts +22 -0
  21. package/dist/board/editor/document-edits.js +193 -0
  22. package/dist/board/editor/document.d.ts +41 -0
  23. package/dist/board/editor/document.js +302 -0
  24. package/dist/board/editor/editor.d.ts +225 -0
  25. package/dist/board/editor/editor.js +439 -0
  26. package/dist/board/editor/index.d.ts +18 -0
  27. package/dist/board/editor/index.js +12 -0
  28. package/dist/board/editor/interaction.d.ts +115 -0
  29. package/dist/board/editor/interaction.js +584 -0
  30. package/dist/board/editor/query.d.ts +27 -0
  31. package/dist/board/editor/query.js +203 -0
  32. package/dist/board/editor/remote.d.ts +11 -0
  33. package/dist/board/editor/remote.js +55 -0
  34. package/dist/board/editor/selection-transform.d.ts +24 -0
  35. package/dist/board/editor/selection-transform.js +81 -0
  36. package/dist/board/editor/selection.d.ts +33 -0
  37. package/dist/board/editor/selection.js +197 -0
  38. package/dist/board/editor/snapping.d.ts +33 -0
  39. package/dist/board/editor/snapping.js +75 -0
  40. package/dist/board/editor/snapshots.d.ts +27 -0
  41. package/dist/board/editor/snapshots.js +132 -0
  42. package/dist/board/editor/spatial.d.ts +41 -0
  43. package/dist/board/editor/spatial.js +191 -0
  44. package/dist/board/editor/state.d.ts +12 -0
  45. package/dist/board/editor/state.js +49 -0
  46. package/dist/board/editor/tool.d.ts +10 -0
  47. package/dist/board/editor/tool.js +19 -0
  48. package/dist/board/editor/wheel.d.ts +5 -0
  49. package/dist/board/editor/wheel.js +17 -0
  50. package/dist/board/export/index.d.ts +28 -6
  51. package/dist/board/export/index.js +47 -13
  52. package/dist/board/export/node/index.d.ts +56 -0
  53. package/dist/board/{headless → export/node}/index.js +26 -21
  54. package/dist/board/export/node/scripts.d.ts +16 -0
  55. package/dist/board/export/node/scripts.js +258 -0
  56. package/dist/board/export/scene.d.ts +8 -5
  57. package/dist/board/export/scene.js +24 -9
  58. package/dist/board/{animation.d.ts → model/animation.d.ts} +8 -5
  59. package/dist/board/{animation.js → model/animation.js} +37 -10
  60. package/dist/board/{core → model}/arrow-geometry.d.ts +2 -2
  61. package/dist/board/{core → model}/arrow-geometry.js +2 -2
  62. package/dist/board/model/authoring.d.ts +20 -0
  63. package/dist/board/model/authoring.js +185 -0
  64. package/dist/board/{core → model}/draw-geometry.d.ts +2 -2
  65. package/dist/board/{core → model}/draw-geometry.js +1 -1
  66. package/dist/board/model/draw-input.d.ts +14 -0
  67. package/dist/board/model/draw-input.js +28 -0
  68. package/dist/board/{core → model}/export-assets.d.ts +1 -1
  69. package/dist/board/{core → model}/export-assets.js +1 -1
  70. package/dist/board/{core → model}/export-plan.d.ts +2 -2
  71. package/dist/board/{core → model}/export-plan.js +3 -2
  72. package/dist/board/{core → model}/file-preview.d.ts +1 -1
  73. package/dist/board/{core → model}/file-preview.js +2 -2
  74. package/dist/board/{core → model}/file-snapshot.d.ts +1 -1
  75. package/dist/board/{core → model}/file-snapshot.js +4 -3
  76. package/dist/board/{geometry.d.ts → model/geometry.d.ts} +3 -3
  77. package/dist/board/{geometry.js → model/geometry.js} +1 -1
  78. package/dist/board/{image-key.d.ts → model/image-key.d.ts} +2 -2
  79. package/dist/board/{image-key.js → model/image-key.js} +7 -3
  80. package/dist/board/model/index.d.ts +42 -0
  81. package/dist/board/model/index.js +39 -0
  82. package/dist/board/model/items/builtin.d.ts +5 -0
  83. package/dist/board/model/items/builtin.js +119 -0
  84. package/dist/board/model/items/create.d.ts +61 -0
  85. package/dist/board/model/items/create.js +272 -0
  86. package/dist/board/model/items/definition.d.ts +35 -0
  87. package/dist/board/model/items/definition.js +18 -0
  88. package/dist/board/model/items/id.d.ts +5 -0
  89. package/dist/board/model/items/id.js +11 -0
  90. package/dist/board/model/items/index.d.ts +6 -0
  91. package/dist/board/model/items/index.js +6 -0
  92. package/dist/board/model/items/registry.d.ts +35 -0
  93. package/dist/board/model/items/registry.js +84 -0
  94. package/dist/board/{media-playback.d.ts → model/media-playback.d.ts} +4 -3
  95. package/dist/board/{media-playback.js → model/media-playback.js} +15 -5
  96. package/dist/board/{media.d.ts → model/media.d.ts} +1 -1
  97. package/dist/board/{media.js → model/media.js} +8 -2
  98. package/dist/board/{core → model}/palette.d.ts +1 -1
  99. package/dist/board/{core → model}/palette.js +1 -1
  100. package/dist/board/{presets.d.ts → model/presets.d.ts} +3 -3
  101. package/dist/board/{presets.js → model/presets.js} +1 -1
  102. package/dist/board/{replay.d.ts → model/replay.d.ts} +5 -5
  103. package/dist/board/{replay.js → model/replay.js} +2 -2
  104. package/dist/board/model/sampling.d.ts +19 -0
  105. package/dist/board/model/sampling.js +96 -0
  106. package/dist/board/{core → model}/scene.d.ts +1 -1
  107. package/dist/board/{core → model}/scene.js +1 -1
  108. package/dist/board/model/script-example.d.ts +4 -0
  109. package/dist/board/model/script-example.js +21 -0
  110. package/dist/board/model/script-hit.d.ts +10 -0
  111. package/dist/board/model/script-hit.js +52 -0
  112. package/dist/board/model/script-index.d.ts +7 -0
  113. package/dist/board/model/script-index.js +15 -0
  114. package/dist/board/model/script-scene.d.ts +24 -0
  115. package/dist/board/model/script-scene.js +267 -0
  116. package/dist/board/model/script.d.ts +105 -0
  117. package/dist/board/model/script.js +1 -0
  118. package/dist/board/model/shape-types.d.ts +8 -0
  119. package/dist/board/model/shape-types.js +8 -0
  120. package/dist/board/{task.d.ts → model/task.d.ts} +4 -4
  121. package/dist/board/{task.js → model/task.js} +3 -3
  122. package/dist/board/{core → model}/text-metrics.d.ts +1 -1
  123. package/dist/board/{core → model}/text-metrics.js +1 -1
  124. package/dist/board/{core → model}/tool-styles.d.ts +1 -1
  125. package/dist/board/{core → model}/tool-styles.js +1 -1
  126. package/dist/board/{runtime/board-player.d.ts → player/index.d.ts} +2 -2
  127. package/dist/board/{runtime/board-player.js → player/index.js} +5 -4
  128. package/dist/board/render/board-background.d.ts +1 -1
  129. package/dist/board/render/clip.d.ts +1 -1
  130. package/dist/board/render/css-color.d.ts +6 -1
  131. package/dist/board/render/css-color.js +34 -20
  132. package/dist/board/render/diagnostics.d.ts +20 -0
  133. package/dist/board/render/diagnostics.js +117 -0
  134. package/dist/board/render/index.d.ts +9 -6
  135. package/dist/board/render/index.js +10 -7
  136. package/dist/board/render/media-interaction.d.ts +2 -2
  137. package/dist/board/render/media-interaction.js +1 -1
  138. package/dist/board/render/palette.d.ts +3 -2
  139. package/dist/board/render/palette.js +7 -3
  140. package/dist/board/render/renderers/arrow-card-renderer.js +4 -3
  141. package/dist/board/render/renderers/audio-card-renderer.js +2 -1
  142. package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -10
  143. package/dist/board/render/renderers/board-renderer-registry.js +17 -17
  144. package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
  145. package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +16 -15
  146. package/dist/board/render/renderers/draw-card-renderer.js +4 -3
  147. package/dist/board/render/renderers/effect-card-renderer.js +22 -5
  148. package/dist/board/render/renderers/far-plate.js +1 -1
  149. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -1
  150. package/dist/board/render/renderers/file-card-renderer.js +2 -2
  151. package/dist/board/render/renderers/frame-card-renderer.js +30 -14
  152. package/dist/board/render/renderers/image-card-renderer.d.ts +1 -1
  153. package/dist/board/render/renderers/image-card-renderer.js +43 -4
  154. package/dist/board/render/renderers/shape-card-renderer.js +11 -28
  155. package/dist/board/render/renderers/task-card-renderer.js +1 -1
  156. package/dist/board/render/renderers/text-card-renderer.js +6 -4
  157. package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
  158. package/dist/board/render/renderers/video-card-renderer.d.ts +1 -1
  159. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  160. package/dist/board/render/sampling.d.ts +22 -0
  161. package/dist/board/render/sampling.js +21 -0
  162. package/dist/board/render/script-export.d.ts +11 -0
  163. package/dist/board/render/script-export.js +86 -0
  164. package/dist/board/render/script-host.d.ts +31 -0
  165. package/dist/board/render/script-host.js +421 -0
  166. package/dist/board/render/script-modules.d.ts +2 -0
  167. package/dist/board/render/script-modules.js +2 -0
  168. package/dist/board/render/script-runtime.d.ts +29 -0
  169. package/dist/board/render/script-runtime.js +213 -0
  170. package/dist/board/render/script-sandbox.d.ts +4 -0
  171. package/dist/board/render/script-sandbox.js +173 -0
  172. package/dist/board/render/stroke.d.ts +3 -2
  173. package/dist/board/render/stroke.js +23 -2
  174. package/dist/board/render/text-measurement.js +2 -2
  175. package/dist/board/replica/index.d.ts +70 -0
  176. package/dist/board/replica/index.js +276 -0
  177. package/dist/board/stage/assets.d.ts +41 -0
  178. package/dist/board/stage/assets.js +355 -0
  179. package/dist/board/stage/index.d.ts +8 -0
  180. package/dist/board/stage/index.js +8 -0
  181. package/dist/board/stage/lru.d.ts +32 -0
  182. package/dist/board/stage/lru.js +29 -0
  183. package/dist/board/stage/maintenance.d.ts +5 -0
  184. package/dist/board/stage/maintenance.js +11 -0
  185. package/dist/board/stage/pointer.d.ts +8 -0
  186. package/dist/board/stage/pointer.js +33 -0
  187. package/dist/board/stage/scene.d.ts +85 -0
  188. package/dist/board/stage/scene.js +515 -0
  189. package/dist/board/stage/script-pointer.d.ts +19 -0
  190. package/dist/board/stage/script-pointer.js +35 -0
  191. package/dist/board/stage/stage.d.ts +76 -0
  192. package/dist/board/stage/stage.js +774 -0
  193. package/dist/board/stage/texture-pool.d.ts +24 -0
  194. package/dist/board/stage/texture-pool.js +153 -0
  195. package/dist/board/stage/views.d.ts +38 -0
  196. package/dist/board/stage/views.js +93 -0
  197. package/dist/chunks/environment.d.ts +289 -23
  198. package/dist/chunks/host-bridge.d.ts +2 -2
  199. package/dist/chunks/http.d.ts +185 -71
  200. package/dist/chunks/http.js +368 -269
  201. package/dist/chunks/transport.js +3 -1
  202. package/dist/chunks/websocket.d.ts +9 -1
  203. package/dist/chunks/websocket.js +16 -2
  204. package/dist/host-bridge.js +2 -1
  205. package/dist/http.d.ts +2 -2
  206. package/dist/index.d.ts +182 -4
  207. package/dist/index.js +1393 -129
  208. package/dist/media.d.ts +1 -10
  209. package/dist/media.js +16 -27
  210. package/dist/protocol/dist/board-assets.d.ts +1 -0
  211. package/dist/protocol/dist/board-constants.js +2 -2
  212. package/dist/protocol/dist/board-model.d.ts +201 -11
  213. package/dist/protocol/dist/board-model.js +33 -14
  214. package/dist/protocol/dist/board-patch.js +3 -2
  215. package/dist/protocol/dist/board-schema.d.ts +4 -2
  216. package/dist/protocol/dist/board-schema.js +18 -4
  217. package/dist/protocol/dist/board-script-modules.d.ts +25 -0
  218. package/dist/protocol/dist/board-script-modules.js +127 -0
  219. package/dist/protocol/dist/board.d.ts +28 -2
  220. package/dist/protocol/dist/board.js +3 -1
  221. package/dist/protocol/dist/core/media.d.ts +5 -0
  222. package/dist/protocol/dist/core/media.js +39 -0
  223. package/dist/protocol/dist/display/index.d.ts +1 -0
  224. package/dist/protocol/dist/index.d.ts +7 -5
  225. package/dist/protocol/dist/realtime/types.d.ts +1 -0
  226. package/dist/protocol/dist/rtc/index.d.ts +1 -0
  227. package/dist/types.d.ts +1 -1
  228. package/package.json +21 -8
  229. package/dist/board/core/shape-definition.d.ts +0 -23
  230. package/dist/board/core/shape-definition.js +0 -45
  231. package/dist/board/core/shape-types.d.ts +0 -38
  232. package/dist/board/core/shape-types.js +0 -22
  233. package/dist/board/headless/index.d.ts +0 -55
  234. package/dist/board/headless/sketch.d.ts +0 -19
  235. package/dist/board/headless/sketch.js +0 -146
  236. package/dist/board/index.d.ts +0 -30
  237. package/dist/board/index.js +0 -29
  238. package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
  239. package/dist/board/render/sketch-host.d.ts +0 -14
  240. package/dist/board/render/sketch-host.js +0 -216
@@ -0,0 +1,515 @@
1
+ import { parseBoardItem } from "../../protocol/dist/board-model.js";
2
+ import { drawPointsBounds } from "../../protocol/dist/board-layout.js";
3
+ import { CORNER_RESIZE_HANDLES, frameCorners, frameHandlePosition, frameRayIntersection, rotationHandleAnchor, rotationHandlePosition } from "../model/geometry.js";
4
+ import { clippingAncestor, isClippingFrame, syncClipGroup } from "../render/clip.js";
5
+ import { Graphics } from "pixi.js";
6
+ //#region src/board/stage/scene.ts
7
+ const FAR_LAYER_ENTER = 450;
8
+ const FAR_LAYER_EXIT = 350;
9
+ const FAR_PASS_BATCH = 240;
10
+ const FAR_LIVE_LIMIT = 64;
11
+ const Z_FAR_LAYER = -1;
12
+ const Z_LIVE_STROKE = Number.MAX_SAFE_INTEGER - 1;
13
+ const Z_OVERLAY = Number.MAX_SAFE_INTEGER;
14
+ const POOL_LIMIT_PER_RENDERER = 48;
15
+ function idSetSignature(ids) {
16
+ let hash = ids.size;
17
+ for (const id of ids) {
18
+ let local = 0;
19
+ for (let i = 0; i < id.length; i += 1) local = local * 31 + id.charCodeAt(i) | 0;
20
+ hash ^= local;
21
+ }
22
+ return hash;
23
+ }
24
+ function traceFrame(graphics, frame) {
25
+ const corners = frameCorners(frame);
26
+ const first = corners[0];
27
+ if (!first) return graphics;
28
+ graphics.moveTo(first.x, first.y);
29
+ for (let index = 1; index < corners.length; index += 1) {
30
+ const point = corners[index];
31
+ if (point) graphics.lineTo(point.x, point.y);
32
+ }
33
+ return graphics.closePath();
34
+ }
35
+ function frameEdgePoints(frame, handle) {
36
+ switch (handle) {
37
+ case "n": return [frameHandlePosition(frame, "nw"), frameHandlePosition(frame, "ne")];
38
+ case "e": return [frameHandlePosition(frame, "ne"), frameHandlePosition(frame, "se")];
39
+ case "s": return [frameHandlePosition(frame, "sw"), frameHandlePosition(frame, "se")];
40
+ case "w": return [frameHandlePosition(frame, "nw"), frameHandlePosition(frame, "sw")];
41
+ default: return null;
42
+ }
43
+ }
44
+ function createBoardScene(options) {
45
+ const { world, overlay, getRenderer } = options;
46
+ world.sortableChildren = true;
47
+ overlay.zIndex = Z_OVERLAY;
48
+ const cards = /* @__PURE__ */ new Map();
49
+ const clips = /* @__PURE__ */ new Map();
50
+ const animatedIds = /* @__PURE__ */ new Set();
51
+ const pools = /* @__PURE__ */ new Map();
52
+ const heldKeys = /* @__PURE__ */ new Map();
53
+ let orderById = /* @__PURE__ */ new Map();
54
+ let farSig = null;
55
+ let farActive = false;
56
+ let lastStructureVersion = -1;
57
+ const liveStrokes = /* @__PURE__ */ new Map();
58
+ function setHeldKey(context, cardId, desiredKey) {
59
+ const heldKey = heldKeys.get(cardId) ?? null;
60
+ if (desiredKey === heldKey) return;
61
+ if (heldKey) context.releaseTexture(heldKey);
62
+ if (desiredKey) context.acquireTexture(desiredKey);
63
+ if (desiredKey) heldKeys.set(cardId, desiredKey);
64
+ else heldKeys.delete(cardId);
65
+ }
66
+ function takeFromPool(rendererId) {
67
+ const pool = pools.get(rendererId);
68
+ if (!pool || pool.length === 0) return null;
69
+ return pool.pop() ?? null;
70
+ }
71
+ function recycle(id, entry, context, destroyed) {
72
+ setHeldKey(context, id, null);
73
+ cards.delete(id);
74
+ animatedIds.delete(id);
75
+ if (destroyed) return;
76
+ entry.container.parent?.removeChild(entry.container);
77
+ entry.container.visible = false;
78
+ const pool = pools.get(entry.renderer.id) ?? [];
79
+ if (pool.length >= POOL_LIMIT_PER_RENDERER) {
80
+ entry.renderer.destroy?.(entry.container, context);
81
+ return;
82
+ }
83
+ pool.push(entry.container);
84
+ pools.set(entry.renderer.id, pool);
85
+ }
86
+ function hostFor(item, scene) {
87
+ const clipId = clippingAncestor(scene, item);
88
+ const frame = clipId ? scene.get(clipId) : void 0;
89
+ if (!clipId || !frame) return world;
90
+ let entry = clips.get(clipId);
91
+ if (!entry) {
92
+ entry = syncClipGroup(void 0, frame);
93
+ entry.group.sortableChildren = true;
94
+ clips.set(clipId, entry);
95
+ }
96
+ const parent = hostFor(frame, scene);
97
+ if (entry.group.parent !== parent) parent.addChild(entry.group);
98
+ entry.group.zIndex = (orderById.get(clipId) ?? 0) + .5;
99
+ return entry.group;
100
+ }
101
+ function syncClips(scene) {
102
+ for (const entry of cards.values()) {
103
+ const host = hostFor(entry.item, scene);
104
+ if (entry.container.parent !== host) host.addChild(entry.container);
105
+ }
106
+ for (const [id, entry] of clips) {
107
+ const frame = scene.get(id);
108
+ if (!frame || !isClippingFrame(frame) || entry.group.children.length <= 1) {
109
+ for (const child of [...entry.group.children]) if (child !== entry.mask) world.addChild(child);
110
+ entry.group.destroy({ children: true });
111
+ clips.delete(id);
112
+ continue;
113
+ }
114
+ syncClipGroup(entry, frame);
115
+ }
116
+ }
117
+ function materialize(item, context, globalSig, scene) {
118
+ const renderer = getRenderer(item, context);
119
+ const pooled = takeFromPool(renderer.id);
120
+ const container = pooled ?? renderer.create(item, context);
121
+ container.visible = true;
122
+ if (pooled) renderer.update(container, item, context);
123
+ container.alpha = scene.opacity(item.id);
124
+ hostFor(item, scene).addChild(container);
125
+ if (renderer.animated) animatedIds.add(item.id);
126
+ const entry = {
127
+ item,
128
+ container,
129
+ renderer,
130
+ selected: context.selectedIds.has(item.id),
131
+ hovered: context.hoveredId === item.id,
132
+ resizing: context.resizingIds.has(item.id),
133
+ globalSig,
134
+ scriptRevision: context.scripts?.revision?.(item.id) ?? 0
135
+ };
136
+ cards.set(item.id, entry);
137
+ setHeldKey(context, item.id, context.assetKey(item));
138
+ return entry;
139
+ }
140
+ let farFront = createFarLayer();
141
+ let farBack = createFarLayer();
142
+ let farPainted = false;
143
+ let farCovered = /* @__PURE__ */ new Set();
144
+ let farInput = null;
145
+ let farRunning = false;
146
+ let farQueued = false;
147
+ let farFrame = 0;
148
+ function createFarLayer() {
149
+ const layer = new Graphics({ label: "board-far-layer" });
150
+ layer.zIndex = Z_FAR_LAYER;
151
+ layer.visible = false;
152
+ world.addChild(layer);
153
+ return layer;
154
+ }
155
+ function requestFarPass(signature) {
156
+ farSig = signature;
157
+ if (farRunning) farQueued = true;
158
+ else runFarPass();
159
+ }
160
+ function runFarPass() {
161
+ if (!farInput) return;
162
+ farRunning = true;
163
+ farQueued = false;
164
+ const { context, getItem, pinnedIds } = farInput;
165
+ const ids = visibleFacts(farInput).orderedIds;
166
+ const target = farPainted ? farBack : farFront;
167
+ const painted = /* @__PURE__ */ new Set();
168
+ target.clear();
169
+ if (target === farFront) farCovered = painted;
170
+ let index = 0;
171
+ const step = () => {
172
+ farFrame = 0;
173
+ const end = Math.min(ids.length, index + FAR_PASS_BATCH);
174
+ for (; index < end; index += 1) {
175
+ const id = ids[index];
176
+ if (pinnedIds.has(id)) continue;
177
+ const item = getItem(id);
178
+ if (!item) continue;
179
+ getRenderer(item, context).renderFar?.(target, item, context);
180
+ painted.add(id);
181
+ }
182
+ if (index < ids.length) {
183
+ farFrame = requestAnimationFrame(step);
184
+ if (target === farFront) options.onFarLayerFrame?.();
185
+ return;
186
+ }
187
+ if (target === farBack) {
188
+ farBack = farFront;
189
+ farFront = target;
190
+ farBack.visible = false;
191
+ farBack.clear();
192
+ farCovered = painted;
193
+ }
194
+ farFront.visible = farActive;
195
+ farPainted = true;
196
+ farRunning = false;
197
+ options.onFarLayerFrame?.();
198
+ if (farQueued) runFarPass();
199
+ };
200
+ step();
201
+ }
202
+ function resetFarLayer() {
203
+ cancelAnimationFrame(farFrame);
204
+ farFrame = 0;
205
+ farRunning = false;
206
+ farQueued = false;
207
+ farInput = null;
208
+ farPainted = false;
209
+ farCovered = /* @__PURE__ */ new Set();
210
+ farSig = null;
211
+ farFront.clear();
212
+ farBack.clear();
213
+ }
214
+ function syncLiveStrokes(input) {
215
+ const { strokes, context, getItem } = input;
216
+ const live = /* @__PURE__ */ new Set();
217
+ for (const stroke of strokes) {
218
+ if (stroke.points.length === 0 || getItem(stroke.id)) continue;
219
+ let entry = liveStrokes.get(stroke.id);
220
+ if (entry && (entry.template.style.stroke !== stroke.color || entry.template.style.strokeWidth !== stroke.size)) {
221
+ removeLiveStroke(stroke.id, context);
222
+ entry = void 0;
223
+ }
224
+ const item = entry ? liveStrokeItem(stroke, entry.template) : createLiveStroke(stroke, input);
225
+ if (!item) continue;
226
+ live.add(stroke.id);
227
+ entry = liveStrokes.get(stroke.id);
228
+ entry?.renderer.update(entry.container, item, context);
229
+ }
230
+ for (const id of liveStrokes.keys()) if (!live.has(id)) removeLiveStroke(id, context);
231
+ }
232
+ function liveStrokeItem(stroke, template) {
233
+ return {
234
+ ...template,
235
+ id: stroke.id,
236
+ props: { points: stroke.points },
237
+ frame: {
238
+ ...drawPointsBounds(stroke.points, stroke.size),
239
+ rotation: 0
240
+ }
241
+ };
242
+ }
243
+ function createLiveStroke(stroke, input) {
244
+ const parsed = parseBoardItem({
245
+ type: "draw",
246
+ style: {
247
+ stroke: stroke.color,
248
+ strokeWidth: stroke.size
249
+ },
250
+ props: { points: stroke.points.slice(0, 1) }
251
+ });
252
+ if (!parsed.ok || parsed.item.type !== "draw") return null;
253
+ const template = parsed.item;
254
+ const item = liveStrokeItem(stroke, template);
255
+ const renderer = getRenderer(item, input.context);
256
+ const container = renderer.create(item, input.context);
257
+ container.zIndex = Z_LIVE_STROKE;
258
+ world.addChild(container);
259
+ liveStrokes.set(stroke.id, {
260
+ container,
261
+ renderer,
262
+ template
263
+ });
264
+ return item;
265
+ }
266
+ function removeLiveStroke(id, context) {
267
+ const entry = liveStrokes.get(id);
268
+ if (!entry) return;
269
+ liveStrokes.delete(id);
270
+ world.removeChild(entry.container);
271
+ entry.renderer.destroy?.(entry.container, context);
272
+ }
273
+ let visibleMemo = null;
274
+ function visibleFacts(input) {
275
+ const { items, visibleIds, structureVersion, context, getItem } = input;
276
+ if (visibleMemo?.ids === visibleIds && visibleMemo.structureVersion === structureVersion) return visibleMemo;
277
+ const orderedIds = visibleIds === null ? items.map((item) => item.id) : [...visibleIds].sort((a, b) => (orderById.get(a) ?? 0) - (orderById.get(b) ?? 0));
278
+ const unbatched = /* @__PURE__ */ new Set();
279
+ for (const id of orderedIds) {
280
+ const item = getItem(id);
281
+ if (item && !getRenderer(item, context).renderFar) unbatched.add(id);
282
+ }
283
+ visibleMemo = {
284
+ ids: visibleIds,
285
+ structureVersion,
286
+ signature: visibleIds === null ? 0 : idSetSignature(visibleIds),
287
+ orderedIds,
288
+ unbatched
289
+ };
290
+ return visibleMemo;
291
+ }
292
+ function sync(input) {
293
+ const { items, scene, context, getItem, visibleIds, pinnedIds, globalSig, structureVersion, geometryVersion, gestureActive } = input;
294
+ const structureChanged = structureVersion !== lastStructureVersion;
295
+ if (structureChanged) {
296
+ lastStructureVersion = structureVersion;
297
+ const liveIds = new Set(items.map((item) => item.id));
298
+ for (const [id, entry] of [...cards]) if (!liveIds.has(id)) recycle(id, entry, context, false);
299
+ orderById = new Map(items.map((item, index) => [item.id, index]));
300
+ }
301
+ const visibleCount = visibleIds === null ? items.length : visibleIds.size;
302
+ const nextFarActive = farActive ? visibleCount > FAR_LAYER_EXIT : visibleCount > FAR_LAYER_ENTER;
303
+ const farModeChanged = nextFarActive !== farActive;
304
+ farActive = nextFarActive;
305
+ farFront.visible = farActive;
306
+ if (farActive) {
307
+ farInput = input;
308
+ const nextFarSig = [
309
+ structureVersion,
310
+ geometryVersion,
311
+ globalSig,
312
+ idSetSignature(pinnedIds),
313
+ visibleIds === null ? "all" : visibleFacts(input).signature
314
+ ].join("|");
315
+ if (farSig === null || nextFarSig !== farSig && !gestureActive) requestFarPass(nextFarSig);
316
+ } else if (farModeChanged) resetFarLayer();
317
+ const wanted = new Set(pinnedIds);
318
+ if (farActive) {
319
+ const { orderedIds, unbatched } = visibleFacts(input);
320
+ for (const id of unbatched) wanted.add(id);
321
+ let budget = FAR_LIVE_LIMIT;
322
+ for (const id of orderedIds) {
323
+ if (budget === 0) break;
324
+ if (farCovered.has(id) || wanted.has(id)) continue;
325
+ wanted.add(id);
326
+ budget -= 1;
327
+ }
328
+ } else if (visibleIds === null) for (const item of items) wanted.add(item.id);
329
+ else for (const id of visibleIds) wanted.add(id);
330
+ let liveSetChanged = false;
331
+ for (const [id, entry] of [...cards]) if (!wanted.has(id)) {
332
+ recycle(id, entry, context, false);
333
+ liveSetChanged = true;
334
+ }
335
+ for (const id of wanted) {
336
+ const item = getItem(id);
337
+ if (!item) continue;
338
+ let entry = cards.get(id);
339
+ const renderer = getRenderer(item, context);
340
+ if (entry && entry.renderer.id !== renderer.id) {
341
+ world.removeChild(entry.container);
342
+ entry.renderer.destroy?.(entry.container, context);
343
+ recycle(id, entry, context, true);
344
+ entry = void 0;
345
+ }
346
+ if (!entry) {
347
+ materialize(item, context, globalSig, scene);
348
+ liveSetChanged = true;
349
+ continue;
350
+ }
351
+ const selected = context.selectedIds.has(id);
352
+ const hovered = context.hoveredId === id;
353
+ const resizing = context.resizingIds.has(id);
354
+ const scriptRevision = context.scripts?.revision?.(id) ?? 0;
355
+ const changed = scriptRevision !== entry.scriptRevision || item !== entry.item || entry.renderer.animated || selected !== entry.selected || hovered !== entry.hovered || resizing !== entry.resizing || globalSig !== entry.globalSig;
356
+ entry.scriptRevision = scriptRevision;
357
+ entry.item = item;
358
+ entry.selected = selected;
359
+ entry.hovered = hovered;
360
+ entry.resizing = resizing;
361
+ entry.globalSig = globalSig;
362
+ setHeldKey(context, id, context.assetKey(item));
363
+ if (changed) {
364
+ entry.renderer.update(entry.container, item, context);
365
+ entry.container.alpha = scene.opacity(id);
366
+ }
367
+ }
368
+ if (structureChanged || liveSetChanged) syncClips(scene);
369
+ if (structureChanged || farModeChanged || liveSetChanged) applyChildOrder();
370
+ syncLiveStrokes(input);
371
+ }
372
+ function applyChildOrder() {
373
+ for (const [id, entry] of cards) entry.container.zIndex = orderById.get(id) ?? 0;
374
+ world.sortableChildren = true;
375
+ world.sortChildren();
376
+ }
377
+ function drawOverlay(input, palette) {
378
+ overlay.clear();
379
+ const { zoom, pointerType, marquee, selection, transform, controls, hoveredControl, rotationPointer, arrowEndpoints, ports, hoveredPort, arrowDraft, bindTarget } = input;
380
+ const inv = 1 / zoom;
381
+ const brand = palette.brand;
382
+ if (marquee) {
383
+ overlay.rect(marquee.x, marquee.y, marquee.width, marquee.height).fill({
384
+ color: brand,
385
+ alpha: .08
386
+ });
387
+ overlay.rect(marquee.x, marquee.y, marquee.width, marquee.height).stroke({
388
+ color: brand,
389
+ width: inv,
390
+ alpha: .7
391
+ });
392
+ }
393
+ if (bindTarget) traceFrame(overlay, bindTarget).stroke({
394
+ color: brand,
395
+ width: 2 * inv,
396
+ alpha: .9
397
+ });
398
+ if (arrowDraft) {
399
+ const { from, to, size, targetFrame } = arrowDraft;
400
+ if (targetFrame) traceFrame(overlay, targetFrame).stroke({
401
+ color: brand,
402
+ width: 2 * inv,
403
+ alpha: .9
404
+ });
405
+ overlay.moveTo(from.x, from.y).lineTo(to.x, to.y).stroke({
406
+ color: brand,
407
+ width: Math.max(size, 1.5) * inv,
408
+ alpha: .85,
409
+ cap: "round"
410
+ });
411
+ overlay.circle(to.x, to.y, 3.5 * inv).fill({
412
+ color: brand,
413
+ alpha: .95
414
+ });
415
+ }
416
+ if (ports && ports.length > 0) for (const port of ports) {
417
+ const hovered = hoveredPort && Math.abs(hoveredPort.x - port.x) < .001 && Math.abs(hoveredPort.y - port.y) < .001;
418
+ const r = (hovered ? port.radius * 1.5 : port.radius) * inv;
419
+ overlay.circle(port.x, port.y, r).fill({
420
+ color: palette.surface,
421
+ alpha: 1
422
+ }).stroke({
423
+ color: brand,
424
+ width: 1.5 * inv,
425
+ alpha: hovered ? 1 : .8
426
+ });
427
+ }
428
+ if (!transform || selection.length === 0) return;
429
+ const source = transform.frame;
430
+ traceFrame(overlay, source).stroke({
431
+ color: brand,
432
+ width: 1.5 * inv,
433
+ alpha: .95
434
+ });
435
+ if (!controls) return;
436
+ if (arrowEndpoints && arrowEndpoints.length > 0) {
437
+ const r = 5 * inv;
438
+ for (const point of arrowEndpoints) overlay.circle(point.x, point.y, r).fill({
439
+ color: palette.surface,
440
+ alpha: 1
441
+ }).stroke({
442
+ color: brand,
443
+ width: 1.5 * inv
444
+ });
445
+ return;
446
+ }
447
+ if (hoveredControl?.kind === "resize" && transform.resizeMode === "free") {
448
+ const edge = frameEdgePoints(source, hoveredControl.handle);
449
+ if (edge) overlay.moveTo(edge[0].x, edge[0].y).lineTo(edge[1].x, edge[1].y).stroke({
450
+ color: brand,
451
+ width: 2.5 * inv,
452
+ alpha: 1
453
+ });
454
+ }
455
+ if (transform.resizeMode !== "none") for (const handle of CORNER_RESIZE_HANDLES) {
456
+ const position = frameHandlePosition(source, handle);
457
+ const hovered = hoveredControl?.kind === "resize" && hoveredControl.handle === handle;
458
+ overlay.circle(position.x, position.y, (hovered ? 5 : 4) * inv).fill({
459
+ color: hovered ? brand : palette.surface,
460
+ alpha: 1
461
+ }).stroke({
462
+ color: brand,
463
+ width: 1.5 * inv
464
+ });
465
+ }
466
+ if (transform.canRotate && (pointerType === "touch" || transform.resizeMode === "none")) {
467
+ const rotation = rotationPointer ?? rotationHandlePosition(source, zoom);
468
+ const anchor = rotationPointer ? frameRayIntersection(source, rotationPointer) : rotationHandleAnchor(source);
469
+ const hovered = rotationPointer !== null || hoveredControl?.kind === "rotate";
470
+ overlay.moveTo(anchor.x, anchor.y).lineTo(rotation.x, rotation.y).stroke({
471
+ color: brand,
472
+ width: inv,
473
+ alpha: .8
474
+ });
475
+ overlay.circle(rotation.x, rotation.y, (hovered ? 6 : 5) * inv).fill({ color: hovered ? brand : palette.surface }).stroke({
476
+ color: brand,
477
+ width: 1.5 * inv
478
+ });
479
+ }
480
+ }
481
+ function destroy(context) {
482
+ for (const entry of cards.values()) entry.renderer.destroy?.(entry.container, context);
483
+ cards.clear();
484
+ for (const pool of pools.values()) for (const container of pool) container.destroy({ children: true });
485
+ pools.clear();
486
+ for (const key of heldKeys.values()) context.releaseTexture(key);
487
+ heldKeys.clear();
488
+ orderById = /* @__PURE__ */ new Map();
489
+ for (const entry of clips.values()) entry.group.destroy({ children: true });
490
+ clips.clear();
491
+ animatedIds.clear();
492
+ for (const id of [...liveStrokes.keys()]) removeLiveStroke(id, context);
493
+ resetFarLayer();
494
+ farActive = false;
495
+ visibleMemo = null;
496
+ lastStructureVersion = -1;
497
+ }
498
+ return {
499
+ sync,
500
+ getNode: (itemId) => {
501
+ const entry = cards.get(itemId);
502
+ return entry ? {
503
+ item: entry.item,
504
+ container: entry.container
505
+ } : null;
506
+ },
507
+ get animated() {
508
+ return animatedIds.size > 0;
509
+ },
510
+ drawOverlay,
511
+ destroy
512
+ };
513
+ }
514
+ //#endregion
515
+ export { createBoardScene };
@@ -0,0 +1,19 @@
1
+ //#region src/board/stage/script-pointer.d.ts
2
+ type BoardScriptPointerTarget = {
3
+ owner: string;
4
+ target: string;
5
+ };
6
+ declare function createBoardScriptPointerRouter(): {
7
+ needsRouting(pointerId: number, scriptsEnabled: boolean): boolean;
8
+ owner(pointerId: number): "editor" | "script" | null;
9
+ route(phase: "down" | "move" | "up" | "leave", pointerId: number, target: BoardScriptPointerTarget | null, point: {
10
+ x: number;
11
+ y: number;
12
+ }, cancelled?: boolean): {
13
+ recipient: BoardScriptPointerTarget | null;
14
+ click: BoardScriptPointerTarget | null;
15
+ };
16
+ clear(): void;
17
+ };
18
+ //#endregion
19
+ export { BoardScriptPointerTarget, createBoardScriptPointerRouter };
@@ -0,0 +1,35 @@
1
+ //#region src/board/stage/script-pointer.ts
2
+ function createBoardScriptPointerRouter() {
3
+ const gestures = /* @__PURE__ */ new Map();
4
+ return {
5
+ needsRouting(pointerId, scriptsEnabled) {
6
+ return scriptsEnabled || gestures.has(pointerId);
7
+ },
8
+ owner(pointerId) {
9
+ const gesture = gestures.get(pointerId);
10
+ return gesture ? gesture.target ? "script" : "editor" : null;
11
+ },
12
+ route(phase, pointerId, target, point, cancelled = false) {
13
+ if (phase === "down") gestures.set(pointerId, {
14
+ target,
15
+ ...point
16
+ });
17
+ const gesture = gestures.get(pointerId);
18
+ const recipient = gesture ? gesture.target : phase === "move" ? target : null;
19
+ let click = null;
20
+ if (phase === "up") {
21
+ gestures.delete(pointerId);
22
+ if (!cancelled && gesture?.target && target?.owner === gesture.target.owner && target.target === gesture.target.target && Math.hypot(point.x - gesture.x, point.y - gesture.y) < 5) click = gesture.target;
23
+ }
24
+ return {
25
+ recipient,
26
+ click
27
+ };
28
+ },
29
+ clear() {
30
+ gestures.clear();
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ export { createBoardScriptPointerRouter };
@@ -0,0 +1,76 @@
1
+ import { BoardItem } from "../../protocol/dist/board-model.js";
2
+ import { BoardPlaybackSnapshot } from "../../protocol/dist/board.js";
3
+ import "../../protocol/dist/index.js";
4
+ import { BoardScriptOutput, BoardScriptSnapshot } from "../model/script.js";
5
+ import { BoardViewport, WorldPoint } from "../model/geometry.js";
6
+ import { BoardScene, BoardSceneItem } from "../model/scene.js";
7
+ import { BoardShapeColors } from "../model/palette.js";
8
+ import { BoardAssetSource } from "../model/media-playback.js";
9
+ import "../model/index.js";
10
+ import { BoardPointerEvent } from "../editor/interaction.js";
11
+ import { BoardEditor } from "../editor/editor.js";
12
+ import "../editor/index.js";
13
+ import { BoardCardRendererResolver, BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
14
+ import "../render/index.js";
15
+ import { BoardAssetManager } from "./assets.js";
16
+ import { BoardLiveStroke } from "./scene.js";
17
+ import { BoardItemView } from "./views.js";
18
+ import { Graphics, Renderer } from "pixi.js";
19
+ //#region src/board/stage/stage.d.ts
20
+ type BoardStageTheme = {
21
+ key: string;
22
+ palette: BoardRenderPalette;
23
+ colors: BoardShapeColors;
24
+ colorScheme: "dark" | "light";
25
+ imageBackground?: boolean;
26
+ };
27
+ type BoardStagePointerPhase = "down" | "move" | "up" | "leave";
28
+ type BoardStageOptions = {
29
+ editor: BoardEditor;
30
+ assets: BoardAssetManager;
31
+ source: BoardAssetSource;
32
+ renderers?: BoardCardRendererResolver;
33
+ views?: readonly BoardItemView[];
34
+ theme?: () => BoardStageTheme;
35
+ playback?: () => BoardPlaybackSnapshot | null;
36
+ fileState?: (path: string) => "ok" | "missing" | "unavailable";
37
+ previews?: () => ReadonlyMap<string, BoardItem>;
38
+ strokes?: () => readonly BoardLiveStroke[];
39
+ drawOverlay?: (overlay: Graphics, frame: {
40
+ zoom: number;
41
+ theme: BoardStageTheme;
42
+ scene: BoardScene;
43
+ }) => void;
44
+ onPointer?: (phase: BoardStagePointerPhase, event: PointerEvent, input: BoardPointerEvent) => boolean | undefined;
45
+ onDoubleClick?: (item: BoardSceneItem | null, point: WorldPoint) => boolean | undefined;
46
+ onSurfaceChange?: (size: {
47
+ width: number;
48
+ height: number;
49
+ }) => void;
50
+ onVisibleChange?: (ids: ReadonlySet<string> | null) => void;
51
+ scriptCapabilities?: Record<string, (id: string, input: unknown) => Promise<unknown>>;
52
+ };
53
+ type BoardStage = {
54
+ readonly ready: Promise<boolean>;
55
+ readonly renderer: Renderer | null;
56
+ readonly camera: BoardViewport;
57
+ readonly theme: BoardStageTheme;
58
+ setActive(active: boolean): void;
59
+ setScriptMode(mode: "edit" | "interact" | "paused"): void;
60
+ resetScripts(): void;
61
+ invalidateScript(path: string): void;
62
+ scriptError(id: string): string | null;
63
+ scriptOutput(id: string): BoardScriptOutput | null;
64
+ snapshotScripts(): Promise<Record<string, BoardScriptSnapshot>>;
65
+ restoreScript(id: string, snapshot: BoardScriptSnapshot): void;
66
+ subscribeScripts(listener: () => void): () => void;
67
+ invalidate(options?: {
68
+ cards?: boolean;
69
+ }): void;
70
+ isMaterialized(id: string): boolean;
71
+ toWorld(clientX: number, clientY: number): WorldPoint;
72
+ destroy(): void;
73
+ };
74
+ declare function mountBoardStage(host: HTMLElement, options: BoardStageOptions): BoardStage;
75
+ //#endregion
76
+ export { BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageTheme, mountBoardStage };