@neta-art/cohub 10.0.0 → 11.1.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 (155) hide show
  1. package/README.md +42 -7
  2. package/dist/board/editor/animation.d.ts +3 -0
  3. package/dist/board/editor/animation.js +16 -1
  4. package/dist/board/editor/clipboard.js +7 -0
  5. package/dist/board/editor/copy.js +23 -18
  6. package/dist/board/editor/creation.d.ts +7 -3
  7. package/dist/board/editor/creation.js +5 -4
  8. package/dist/board/editor/deletion.js +3 -3
  9. package/dist/board/editor/document.d.ts +8 -2
  10. package/dist/board/editor/document.js +54 -7
  11. package/dist/board/editor/editor.d.ts +22 -7
  12. package/dist/board/editor/editor.js +12 -2
  13. package/dist/board/editor/index.d.ts +2 -2
  14. package/dist/board/editor/interaction.d.ts +8 -2
  15. package/dist/board/editor/interaction.js +34 -1
  16. package/dist/board/editor/query.d.ts +5 -5
  17. package/dist/board/editor/query.js +14 -12
  18. package/dist/board/editor/selection.js +2 -1
  19. package/dist/board/editor/snapshots.d.ts +0 -2
  20. package/dist/board/editor/snapshots.js +4 -20
  21. package/dist/board/export/index.d.ts +25 -4
  22. package/dist/board/export/index.js +45 -12
  23. package/dist/board/export/node/index.d.ts +5 -4
  24. package/dist/board/export/node/index.js +16 -11
  25. package/dist/board/export/node/scripts.d.ts +16 -0
  26. package/dist/board/export/node/scripts.js +258 -0
  27. package/dist/board/export/scene.d.ts +2 -1
  28. package/dist/board/export/scene.js +24 -7
  29. package/dist/board/model/animation.d.ts +4 -1
  30. package/dist/board/model/animation.js +34 -7
  31. package/dist/board/model/authoring.d.ts +20 -0
  32. package/dist/board/model/authoring.js +185 -0
  33. package/dist/board/model/export-plan.js +1 -0
  34. package/dist/board/model/file-preview.js +1 -1
  35. package/dist/board/model/file-snapshot.js +3 -2
  36. package/dist/board/model/image-key.js +7 -3
  37. package/dist/board/model/index.d.ts +15 -7
  38. package/dist/board/model/index.js +16 -9
  39. package/dist/board/model/items/builtin.js +2 -2
  40. package/dist/board/model/items/create.d.ts +19 -4
  41. package/dist/board/model/items/create.js +130 -9
  42. package/dist/board/model/items/index.d.ts +2 -2
  43. package/dist/board/model/items/index.js +2 -2
  44. package/dist/board/model/media-playback.d.ts +2 -1
  45. package/dist/board/model/media-playback.js +15 -5
  46. package/dist/board/model/media.js +7 -1
  47. package/dist/board/model/overlay-placement.d.ts +25 -0
  48. package/dist/board/model/overlay-placement.js +35 -0
  49. package/dist/board/model/replay.d.ts +1 -1
  50. package/dist/board/model/replay.js +1 -0
  51. package/dist/board/model/sampling.d.ts +19 -0
  52. package/dist/board/model/sampling.js +96 -0
  53. package/dist/board/model/script-example.d.ts +4 -0
  54. package/dist/board/model/script-example.js +21 -0
  55. package/dist/board/model/script-hit.d.ts +27 -0
  56. package/dist/board/model/script-hit.js +109 -0
  57. package/dist/board/model/script-index.d.ts +7 -0
  58. package/dist/board/model/script-index.js +15 -0
  59. package/dist/board/model/script-observers.d.ts +18 -0
  60. package/dist/board/model/script-observers.js +92 -0
  61. package/dist/board/model/script-scene.d.ts +24 -0
  62. package/dist/board/model/script-scene.js +294 -0
  63. package/dist/board/model/script.d.ts +149 -0
  64. package/dist/board/model/script.js +1 -0
  65. package/dist/board/render/board-background.js +5 -4
  66. package/dist/board/render/css-color.d.ts +6 -1
  67. package/dist/board/render/css-color.js +34 -20
  68. package/dist/board/render/diagnostics.d.ts +20 -0
  69. package/dist/board/render/diagnostics.js +117 -0
  70. package/dist/board/render/index.d.ts +9 -6
  71. package/dist/board/render/index.js +10 -7
  72. package/dist/board/render/media-interaction.js +1 -1
  73. package/dist/board/render/palette.d.ts +2 -1
  74. package/dist/board/render/palette.js +6 -2
  75. package/dist/board/render/renderers/arrow-card-renderer.js +3 -2
  76. package/dist/board/render/renderers/audio-card-renderer.js +2 -1
  77. package/dist/board/render/renderers/board-renderer-registry.d.ts +25 -5
  78. package/dist/board/render/renderers/board-renderer-registry.js +11 -4
  79. package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
  80. package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +18 -16
  81. package/dist/board/render/renderers/draw-card-renderer.js +3 -2
  82. package/dist/board/render/renderers/effect-card-renderer.js +22 -5
  83. package/dist/board/render/renderers/frame-card-renderer.js +30 -14
  84. package/dist/board/render/renderers/image-card-renderer.js +43 -4
  85. package/dist/board/render/renderers/shape-card-renderer.js +10 -27
  86. package/dist/board/render/renderers/task-card-renderer.d.ts +2 -1
  87. package/dist/board/render/renderers/task-card-renderer.js +220 -71
  88. package/dist/board/render/renderers/text-card-renderer.js +5 -3
  89. package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
  90. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  91. package/dist/board/render/sampling.d.ts +22 -0
  92. package/dist/board/render/sampling.js +21 -0
  93. package/dist/board/render/script-export.d.ts +11 -0
  94. package/dist/board/render/script-export.js +86 -0
  95. package/dist/board/render/script-host.d.ts +31 -0
  96. package/dist/board/render/script-host.js +446 -0
  97. package/dist/board/render/script-modules.d.ts +2 -0
  98. package/dist/board/render/script-modules.js +2 -0
  99. package/dist/board/render/script-runtime.d.ts +29 -0
  100. package/dist/board/render/script-runtime.js +223 -0
  101. package/dist/board/render/script-sandbox.d.ts +4 -0
  102. package/dist/board/render/script-sandbox.js +173 -0
  103. package/dist/board/render/script-texture.d.ts +11 -0
  104. package/dist/board/render/script-texture.js +33 -0
  105. package/dist/board/render/stroke.d.ts +2 -1
  106. package/dist/board/render/stroke.js +22 -1
  107. package/dist/board/render/text-measurement.js +1 -1
  108. package/dist/board/stage/assets.js +7 -2
  109. package/dist/board/stage/maintenance.d.ts +5 -0
  110. package/dist/board/stage/maintenance.js +11 -0
  111. package/dist/board/stage/scene.d.ts +2 -0
  112. package/dist/board/stage/scene.js +21 -2
  113. package/dist/board/stage/script-pointer.d.ts +20 -0
  114. package/dist/board/stage/script-pointer.js +41 -0
  115. package/dist/board/stage/stage.d.ts +21 -3
  116. package/dist/board/stage/stage.js +350 -47
  117. package/dist/board/stage/texture-pool.js +2 -3
  118. package/dist/chunks/environment.d.ts +305 -27
  119. package/dist/chunks/environment.js +12 -4
  120. package/dist/chunks/host-bridge.d.ts +2 -2
  121. package/dist/chunks/http.d.ts +90 -7
  122. package/dist/chunks/http.js +157 -64
  123. package/dist/chunks/transport.js +4 -2
  124. package/dist/chunks/websocket.d.ts +9 -1
  125. package/dist/chunks/websocket.js +16 -2
  126. package/dist/host-bridge.js +2 -1
  127. package/dist/http.d.ts +3 -3
  128. package/dist/index.d.ts +182 -4
  129. package/dist/index.js +1559 -299
  130. package/dist/protocol/dist/board-assets.d.ts +1 -0
  131. package/dist/protocol/dist/board-constants.js +2 -2
  132. package/dist/protocol/dist/board-model.d.ts +236 -11
  133. package/dist/protocol/dist/board-model.js +46 -17
  134. package/dist/protocol/dist/board-patch.js +2 -1
  135. package/dist/protocol/dist/board-schema.d.ts +4 -2
  136. package/dist/protocol/dist/board-schema.js +18 -4
  137. package/dist/protocol/dist/board-script-modules.d.ts +25 -0
  138. package/dist/protocol/dist/board-script-modules.js +127 -0
  139. package/dist/protocol/dist/board-task.d.ts +22 -0
  140. package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
  141. package/dist/protocol/dist/display/index.d.ts +1 -0
  142. package/dist/protocol/dist/generation/blocks.d.ts +8 -0
  143. package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
  144. package/dist/protocol/dist/index.d.ts +7 -2
  145. package/dist/protocol/dist/realtime/types.d.ts +1 -0
  146. package/dist/protocol/dist/rtc/index.d.ts +1 -0
  147. package/dist/protocol/dist/task/index.d.ts +4 -0
  148. package/package.json +2 -1
  149. package/dist/board/export/node/sketch.d.ts +0 -19
  150. package/dist/board/export/node/sketch.js +0 -146
  151. package/dist/board/model/task.d.ts +0 -11
  152. package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
  153. package/dist/board/render/sketch-host.d.ts +0 -14
  154. package/dist/board/render/sketch-host.js +0 -216
  155. package/dist/types.d.ts +0 -36
@@ -0,0 +1,294 @@
1
+ import { parseBoardItem as parseBoardItem$1 } from "../../protocol/dist/board-model.js";
2
+ import { applyMatrix, createBoardLayout } from "../../protocol/dist/board-layout.js";
3
+ import { applyBoardPatchToDocument, mergePatch } from "../../protocol/dist/board-patch.js";
4
+ //#region src/board/model/script-scene.ts
5
+ function parseBoardItem(value) {
6
+ const parsed = parseBoardItem$1(value);
7
+ if (!parsed.ok) throw new Error(parsed.diagnostics[0]?.message ?? "Invalid script item.");
8
+ return parsed.item;
9
+ }
10
+ const DISPLAY_FIELDS = /* @__PURE__ */ new Set([
11
+ "position",
12
+ "size",
13
+ "rotation",
14
+ "scale",
15
+ "opacity",
16
+ "style",
17
+ "props"
18
+ ]);
19
+ function boardScriptScope(document, id) {
20
+ const scope = /* @__PURE__ */ new Set([id, ...Object.values(document.items[id]?.script?.targets ?? {}).filter((target) => Boolean(document.items[target]))]);
21
+ if (document.items[id]?.type !== "frame") return scope;
22
+ for (const candidate of Object.keys(document.items)) {
23
+ let parent = document.items[candidate]?.parent;
24
+ for (let depth = 0; parent && depth < 64; depth++) {
25
+ if (parent === id) {
26
+ scope.add(candidate);
27
+ break;
28
+ }
29
+ parent = document.items[parent]?.parent;
30
+ }
31
+ }
32
+ return scope;
33
+ }
34
+ function validateBoardScriptOutput(output, scope) {
35
+ if (!output || typeof output !== "object" || !output.patches || !output.items || !output.hits || !Array.isArray(output.listens)) throw new Error("Invalid script output.");
36
+ if (JSON.stringify(output).length > 1e6) throw new Error("Script output exceeds the 1 MB limit.");
37
+ if (output.space !== void 0 && output.space !== "world" && output.space !== "screen") throw new Error("Invalid script scene space.");
38
+ if (output.anchor) {
39
+ const { item, point } = output.anchor;
40
+ if (output.space !== "screen" || typeof item !== "string" || !item || point && ![point.x, point.y].every((n) => Number.isFinite(n) && n >= 0 && n <= 1)) throw new Error("Invalid screen scene anchor.");
41
+ }
42
+ if (output.parameters) {
43
+ if (Object.keys(output.parameters).length > 64) throw new Error("A script may declare at most 64 parameters.");
44
+ for (const spec of Object.values(output.parameters)) {
45
+ if (!spec || ![
46
+ "number",
47
+ "boolean",
48
+ "string",
49
+ "color"
50
+ ].includes(spec.type)) throw new Error("Invalid script parameter type.");
51
+ if (spec.label !== void 0 && typeof spec.label !== "string") throw new Error("Invalid script parameter label.");
52
+ if ([
53
+ spec.min,
54
+ spec.max,
55
+ spec.step
56
+ ].some((value) => value !== void 0 && !Number.isFinite(value))) throw new Error("Invalid script parameter range.");
57
+ }
58
+ }
59
+ if (Object.keys(output.items).length > 1e3 || Object.keys(output.hits).length > 1e3) throw new Error("A script may create at most 1000 temporary items or hit regions.");
60
+ for (const [id, patch] of Object.entries(output.patches)) {
61
+ if (!scope.has(id)) throw new Error(`Item is outside the script scope: ${id}`);
62
+ if (!patch || typeof patch !== "object" || Object.keys(patch).some((key) => !DISPLAY_FIELDS.has(key))) throw new Error("Scripts may only present display properties.");
63
+ }
64
+ for (const hit of Object.values(output.hits)) {
65
+ if (!hit || typeof hit !== "object") throw new Error("Invalid script hit region.");
66
+ if (hit.cursor && ![
67
+ "default",
68
+ "pointer",
69
+ "grab",
70
+ "grabbing",
71
+ "crosshair",
72
+ "text",
73
+ "move",
74
+ "ew-resize",
75
+ "ns-resize",
76
+ "not-allowed"
77
+ ].includes(hit.cursor)) throw new Error("Invalid script cursor.");
78
+ if ("target" in hit) {
79
+ if (hit.target !== "self" || "item" in hit || "x" in hit) throw new Error("Invalid script hit target.");
80
+ } else if ("item" in hit) {
81
+ if (typeof hit.item !== "string" || !Object.hasOwn(output.items, hit.item) || "x" in hit) throw new Error("Hit items must belong to the temporary scene.");
82
+ } else if (![
83
+ hit.x,
84
+ hit.y,
85
+ hit.width,
86
+ hit.height
87
+ ].every(Number.isFinite) || hit.width < 0 || hit.height < 0) throw new Error("Invalid script hit region.");
88
+ }
89
+ }
90
+ function validateBoardScriptCommit(document, id, patch) {
91
+ if (!patch || typeof patch !== "object" || Object.keys(patch).some((key) => key !== "items")) throw new Error("Scripts may only commit items in their scope.");
92
+ const scope = boardScriptScope(document, id);
93
+ for (const [target, value] of Object.entries(patch.items ?? {})) {
94
+ if (!scope.has(target)) {
95
+ if (document.items[target]) throw new Error(`Item is outside the script scope: ${target}`);
96
+ if (!value || typeof value !== "object" || !("parent" in value) || typeof value.parent !== "string" || !scope.has(value.parent) || document.items[value.parent]?.type !== "frame") throw new Error("New items must belong to a frame in the script scope.");
97
+ if ("script" in value) throw new Error("Scripts cannot start other scripts.");
98
+ continue;
99
+ }
100
+ if (value && typeof value === "object" && ("script" in value || "parent" in value || "type" in value)) throw new Error("A script cannot replace scripts, types, or parent relationships.");
101
+ }
102
+ const result = applyBoardPatchToDocument(document, patch);
103
+ if (!result.ok) throw new Error(result.diagnostics[0]?.message ?? "Invalid script commit.");
104
+ }
105
+ function scriptTemporaryPrefix(owner) {
106
+ let hash = 2166136261;
107
+ for (const char of owner) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
108
+ return `~script-${hash.toString(36)}-`;
109
+ }
110
+ const preparedOutputs = /* @__PURE__ */ new WeakMap();
111
+ const preparedScopes = /* @__PURE__ */ new WeakMap();
112
+ const itemOrders = /* @__PURE__ */ new WeakMap();
113
+ function orderedScriptOwners(document, outputs) {
114
+ let order = itemOrders.get(document.items);
115
+ if (!order) {
116
+ order = new Map(Object.keys(document.items).map((id, index) => [id, index]));
117
+ itemOrders.set(document.items, order);
118
+ }
119
+ return [...outputs.keys()].filter((id) => order.has(id)).sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
120
+ }
121
+ function prepareBoardScriptOutput(document, owner, output) {
122
+ const previous = preparedOutputs.get(output);
123
+ if (previous?.document === document && previous.owner === owner) return previous;
124
+ let scopes = preparedScopes.get(document);
125
+ if (!scopes) {
126
+ scopes = /* @__PURE__ */ new Map();
127
+ preparedScopes.set(document, scopes);
128
+ }
129
+ let scope = scopes.get(owner);
130
+ if (!scope) {
131
+ scope = boardScriptScope(document, owner);
132
+ scopes.set(owner, scope);
133
+ }
134
+ if (output.anchor && document.items[output.anchor.item] && !scope.has(output.anchor.item)) throw new Error("Scene anchor is outside the script scope.");
135
+ validateBoardScriptOutput(output, scope);
136
+ const presented = /* @__PURE__ */ new Map();
137
+ for (const [id, patch] of Object.entries(output.patches)) {
138
+ const base = document.items[id];
139
+ if (base) presented.set(id, {
140
+ base,
141
+ item: parseBoardItem(mergePatch(base, patch))
142
+ });
143
+ }
144
+ const prefix = scriptTemporaryPrefix(owner);
145
+ const rootId = `${prefix}root`;
146
+ if (document.items[rootId]) throw new Error("Temporary item ID conflicts with the document.");
147
+ const items = /* @__PURE__ */ new Map();
148
+ const parsed = new Map(Object.entries(output.items).map(([id, value]) => [id, parseBoardItem(value)]));
149
+ for (const [local, value] of parsed) {
150
+ if (!/^[a-zA-Z0-9_-]{1,80}$/.test(local) || local === "root") throw new Error("Temporary IDs must be 1–80 letters, digits, underscores or hyphens; root is reserved.");
151
+ if (value.script) throw new Error("Temporary items cannot start scripts.");
152
+ if (value.parent && !(value.parent in output.items)) throw new Error("Temporary parents must belong to the same instance.");
153
+ let cursor = value.parent;
154
+ const visited = /* @__PURE__ */ new Set([local]);
155
+ while (cursor) {
156
+ if (visited.has(cursor)) throw new Error("Temporary parents must not form a cycle.");
157
+ visited.add(cursor);
158
+ const parentItem = parsed.get(cursor);
159
+ if (!parentItem) throw new Error("Temporary parent is missing.");
160
+ if (parentItem.type !== "frame") throw new Error("Only frames can contain temporary items.");
161
+ cursor = parentItem.parent;
162
+ }
163
+ const id = `${prefix}${local}`;
164
+ if (document.items[id]) throw new Error("Temporary item ID conflicts with the document.");
165
+ const parent = value.parent ? `${prefix}${value.parent}` : rootId;
166
+ if (value.type === "arrow") {
167
+ const remap = (endpoint) => {
168
+ if (!("item" in endpoint)) return endpoint;
169
+ if (!(endpoint.item in output.items) && !scope.has(endpoint.item)) throw new Error("Arrow target is outside the script scope.");
170
+ return endpoint.item in output.items ? {
171
+ ...endpoint,
172
+ item: `${prefix}${endpoint.item}`
173
+ } : endpoint;
174
+ };
175
+ items.set(id, {
176
+ ...value,
177
+ parent,
178
+ props: {
179
+ ...value.props,
180
+ start: remap(value.props.start),
181
+ end: remap(value.props.end)
182
+ }
183
+ });
184
+ } else items.set(id, {
185
+ ...value,
186
+ parent
187
+ });
188
+ }
189
+ const prepared = {
190
+ document,
191
+ owner,
192
+ rootId,
193
+ items,
194
+ presented
195
+ };
196
+ preparedOutputs.set(output, prepared);
197
+ return prepared;
198
+ }
199
+ function composeBoardScriptItems(document, scene, outputs, incoming = /* @__PURE__ */ new Map(), screen, onError, gestureLayout) {
200
+ const result = new Map(incoming);
201
+ const screenAnchors = /* @__PURE__ */ new Map();
202
+ if (outputs.size === 0) return result;
203
+ for (const owner of orderedScriptOwners(document, outputs)) {
204
+ const output = outputs.get(owner);
205
+ if (!output) continue;
206
+ try {
207
+ const changes = /* @__PURE__ */ new Map();
208
+ const prepared = prepareBoardScriptOutput(document, owner, output);
209
+ for (const [id, patch] of Object.entries(output.patches)) {
210
+ const base = result.get(id) ?? document.items[id];
211
+ if (!base) continue;
212
+ let cached = prepared.presented.get(id);
213
+ if (cached?.base !== base) {
214
+ cached = {
215
+ base,
216
+ item: parseBoardItem(mergePatch(base, patch))
217
+ };
218
+ prepared.presented.set(id, cached);
219
+ }
220
+ changes.set(id, cached.item);
221
+ }
222
+ if (!prepared.items.size || output.space === "screen" && !screen) {
223
+ for (const [id, item] of changes) result.set(id, item);
224
+ continue;
225
+ }
226
+ const rootId = prepared.rootId;
227
+ const item = changes.get(owner) ?? result.get(owner) ?? document.items[owner];
228
+ if (!item) continue;
229
+ const box = scene.layout.box(owner);
230
+ const overlay = output.space === "screen" && screen ? {
231
+ parent: void 0,
232
+ position: {
233
+ x: 0,
234
+ y: 0
235
+ },
236
+ scale: 1 / screen.zoom,
237
+ rotation: 0,
238
+ origin: {
239
+ x: 0,
240
+ y: 0
241
+ }
242
+ } : {};
243
+ changes.set(rootId, parseBoardItem({
244
+ type: "frame",
245
+ parent: item.parent,
246
+ position: item.position,
247
+ rotation: item.rotation,
248
+ scale: item.scale,
249
+ origin: item.origin,
250
+ opacity: item.opacity,
251
+ size: {
252
+ width: Math.max(1, box.width),
253
+ height: Math.max(1, box.height)
254
+ },
255
+ z: (item.z ?? 0) + .001,
256
+ props: {
257
+ decoration: "none",
258
+ clip: false
259
+ },
260
+ ...overlay
261
+ }));
262
+ for (const [id, value] of prepared.items) changes.set(id, value);
263
+ for (const [id, item] of changes) result.set(id, item);
264
+ if (output.space === "screen") screenAnchors.set(owner, rootId);
265
+ } catch (error) {
266
+ if (!onError) throw error;
267
+ onError(owner, error);
268
+ }
269
+ }
270
+ if (screenAnchors.size) {
271
+ const layout = gestureLayout ?? createBoardLayout((id) => result.get(id) ?? document.items[id]);
272
+ for (const [owner, rootId] of screenAnchors) {
273
+ const root = result.get(rootId);
274
+ const anchor = outputs.get(owner)?.anchor;
275
+ const target = anchor?.item ?? owner;
276
+ const exists = Boolean(document.items[target]);
277
+ const box = exists ? layout.box(target) : {
278
+ width: 0,
279
+ height: 0
280
+ };
281
+ if (root) result.set(rootId, {
282
+ ...root,
283
+ opacity: exists ? root.opacity : 0,
284
+ position: exists ? applyMatrix(layout.matrix(target), {
285
+ x: (anchor?.point?.x ?? 0) * box.width,
286
+ y: (anchor?.point?.y ?? 0) * box.height
287
+ }) : root.position
288
+ });
289
+ }
290
+ }
291
+ return result;
292
+ }
293
+ //#endregion
294
+ export { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, scriptTemporaryPrefix, validateBoardScriptCommit, validateBoardScriptOutput };
@@ -0,0 +1,149 @@
1
+ import { BoardItem, BoardPatch, BoardScript, BoardVec2 } from "../../protocol/dist/board-model.js";
2
+ import "../../protocol/dist/index.js";
3
+ //#region src/board/model/script.d.ts
4
+ type BoardScriptView = {
5
+ camera: {
6
+ x: number;
7
+ y: number;
8
+ zoom: number;
9
+ };
10
+ width: number;
11
+ height: number;
12
+ safeRect: {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ };
18
+ };
19
+ type BoardScriptAnchor = {
20
+ item: string;
21
+ point?: BoardVec2;
22
+ };
23
+ type BoardScriptSceneOptions = {
24
+ space?: "world" | "screen";
25
+ anchor?: BoardScriptAnchor;
26
+ };
27
+ type BoardScriptEvent = {
28
+ type: string;
29
+ target?: string;
30
+ relatedTarget?: string;
31
+ relatedOwner?: string;
32
+ point?: BoardVec2;
33
+ pointerId?: number;
34
+ pointerType?: string;
35
+ buttons?: number;
36
+ key?: string;
37
+ shiftKey?: boolean;
38
+ altKey?: boolean;
39
+ ctrlKey?: boolean;
40
+ metaKey?: boolean;
41
+ data?: unknown;
42
+ };
43
+ type BoardScriptHostEvents = {
44
+ viewchange: BoardScriptView;
45
+ selectionchange: {
46
+ ids: string[];
47
+ };
48
+ transformend: {
49
+ ids: string[];
50
+ operation: "move" | "resize" | "rotate" | "arrow";
51
+ cancelled: boolean;
52
+ };
53
+ };
54
+ type BoardScriptEventOf<Type extends string> = BoardScriptEvent & {
55
+ type: Type;
56
+ } & (Type extends keyof BoardScriptHostEvents ? {
57
+ data: BoardScriptHostEvents[Type];
58
+ } : unknown);
59
+ type BoardScriptFrame = {
60
+ t: number;
61
+ dt: number;
62
+ width: number;
63
+ height: number;
64
+ seed: string;
65
+ params: BoardScript["params"];
66
+ state: Record<string, unknown>;
67
+ };
68
+ type BoardScriptItemPatch = Partial<Pick<BoardItem, "position" | "scale" | "rotation" | "opacity" | "style">> & {
69
+ size?: {
70
+ width?: number;
71
+ height?: number;
72
+ };
73
+ props?: Record<string, unknown>;
74
+ };
75
+ type BoardScriptHit = ({
76
+ x: number;
77
+ y: number;
78
+ width: number;
79
+ height: number;
80
+ } | {
81
+ target: "self";
82
+ } | {
83
+ item: string;
84
+ }) & {
85
+ label?: string;
86
+ cursor?: string;
87
+ };
88
+ type BoardScriptSnapshot = {
89
+ state: Record<string, unknown>;
90
+ time: number;
91
+ random: number;
92
+ configuration?: string;
93
+ effects?: Pick<BoardScriptOutput, "patches" | "items" | "hits" | "space" | "anchor">;
94
+ };
95
+ type BoardScriptParameter = {
96
+ type: "number" | "boolean" | "string" | "color";
97
+ label?: string;
98
+ default?: string | number | boolean;
99
+ min?: number;
100
+ max?: number;
101
+ step?: number;
102
+ };
103
+ type BoardScriptOutput = {
104
+ parameters?: Record<string, BoardScriptParameter>;
105
+ space?: "world" | "screen";
106
+ anchor?: BoardScriptAnchor;
107
+ patches: Record<string, Record<string, unknown>>;
108
+ items: Record<string, unknown>;
109
+ hits: Record<string, BoardScriptHit>;
110
+ listens: string[];
111
+ animated: boolean;
112
+ updating?: boolean;
113
+ version: number;
114
+ };
115
+ type BoardScriptContext = {
116
+ readonly self: {
117
+ readonly id: string;
118
+ readonly base: BoardItem;
119
+ present(patch: BoardScriptItemPatch | null): void;
120
+ };
121
+ readonly params: BoardScript["params"];
122
+ readonly targets: Readonly<Record<string, string>>;
123
+ emit(target: string, type: string, data?: unknown): Promise<void>;
124
+ readonly state: Record<string, unknown>;
125
+ readonly frame: BoardScriptFrame;
126
+ readonly view: BoardScriptView | null;
127
+ random(): number;
128
+ on<Type extends string>(type: Type, handler: (event: BoardScriptEventOf<Type>) => void): () => void;
129
+ onFrame(handler: (frame: BoardScriptFrame) => void): () => void;
130
+ onDispose(handler: () => void): void;
131
+ invalidate(): void;
132
+ get(id: string): BoardItem | undefined;
133
+ present(id: string, patch: BoardScriptItemPatch | null): void;
134
+ scene(items: Record<string, unknown>, options?: BoardScriptSceneOptions): void;
135
+ hitRegions(hits: Record<string, BoardScriptHit>): void;
136
+ commit(patch: BoardPatch): Promise<void>;
137
+ capabilities: {
138
+ has(name: string): boolean;
139
+ call(name: string, input?: unknown): Promise<unknown>;
140
+ };
141
+ };
142
+ type BoardScriptModule = {
143
+ parameters?: Record<string, BoardScriptParameter>;
144
+ default?: ((context: BoardScriptContext) => void) | ((context: BoardScriptContext) => () => void);
145
+ draw?: (context: CanvasRenderingContext2D, frame: BoardScriptFrame) => void;
146
+ animated?: boolean;
147
+ };
148
+ //#endregion
149
+ export { BoardScriptAnchor, BoardScriptContext, BoardScriptEvent, BoardScriptEventOf, BoardScriptFrame, BoardScriptHit, BoardScriptHostEvents, BoardScriptItemPatch, BoardScriptModule, BoardScriptOutput, BoardScriptParameter, BoardScriptSceneOptions, BoardScriptSnapshot, BoardScriptView };
@@ -0,0 +1 @@
1
+ export {};
@@ -12,7 +12,7 @@ function buildGridTexture(context, size, color, opacity, kind) {
12
12
  width: 1,
13
13
  alpha: opacity
14
14
  });
15
- else graphics.circle(.5, .5, .9).fill({
15
+ else graphics.circle(size / 2, size / 2, 1.1).fill({
16
16
  color,
17
17
  alpha: opacity
18
18
  });
@@ -49,8 +49,9 @@ function sync(parts, context) {
49
49
  const kind = settings.background.kind === "grid" ? "grid" : "dots";
50
50
  const visible = settings.grid?.visible === true;
51
51
  const size = Math.max(4, settings.grid?.size ?? 24);
52
- const opacity = .12;
53
- const key = `${kind}|${size}|${palette.border}|${opacity}`;
52
+ const color = kind === "grid" ? palette.border : palette.muted;
53
+ const opacity = kind === "grid" ? .9 : .4;
54
+ const key = `${kind}|${size}|${color}|${opacity}`;
54
55
  if (!visible) {
55
56
  if (parts.sprite) parts.sprite.visible = false;
56
57
  return;
@@ -61,7 +62,7 @@ function sync(parts, context) {
61
62
  parts.sprite.destroy();
62
63
  previousTexture.destroy(true);
63
64
  }
64
- const texture = buildGridTexture(context, size, palette.border, opacity, kind);
65
+ const texture = buildGridTexture(context, size, color, opacity, kind);
65
66
  parts.sprite = new TilingSprite({
66
67
  texture,
67
68
  width,
@@ -1,4 +1,9 @@
1
1
  //#region src/board/render/css-color.d.ts
2
+ type BoardCssPaint = {
3
+ color: number;
4
+ alpha: number;
5
+ };
6
+ declare function parseBoardCssPaint(value: string): BoardCssPaint | null;
2
7
  declare function parseBoardCssColor(value: string): number | null;
3
8
  //#endregion
4
- export { parseBoardCssColor };
9
+ export { BoardCssPaint, parseBoardCssColor, parseBoardCssPaint };
@@ -1,36 +1,50 @@
1
1
  import { DOMAdapter } from "pixi.js";
2
2
  //#region src/board/render/css-color.ts
3
- const CACHE_LIMIT = 256;
4
3
  const cache = /* @__PURE__ */ new Map();
5
4
  let canvas = null;
6
- function parseBoardCssColor(value) {
5
+ function parseBoardCssPaint(value) {
7
6
  const key = value.trim();
8
7
  if (!key) return null;
9
- const cached = cache.get(key);
10
- if (cached !== void 0 || cache.has(key)) return cached ?? null;
8
+ if (key.toLowerCase() === "transparent") return {
9
+ color: 0,
10
+ alpha: 0
11
+ };
12
+ const hex = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(key)?.[1];
13
+ if (hex) {
14
+ const digits = hex.length <= 4 ? [...hex].map((digit) => digit + digit).join("") : hex;
15
+ return {
16
+ color: Number.parseInt(digits.slice(0, 6), 16),
17
+ alpha: digits.length === 8 ? Number.parseInt(digits.slice(6), 16) / 255 : 1
18
+ };
19
+ }
20
+ if (cache.has(key)) return cache.get(key) ?? null;
11
21
  let result = null;
12
22
  try {
13
- const direct = /^#([0-9a-f]{6})$/i.exec(key);
14
- if (direct?.[1]) result = Number.parseInt(direct[1], 16);
15
- else {
16
- canvas ??= DOMAdapter.get().createCanvas(1, 1);
17
- const context = canvas.getContext("2d");
18
- if (context) {
19
- context.fillStyle = "#010203";
20
- context.fillStyle = key;
21
- const first = String(context.fillStyle);
22
- context.fillStyle = "#040506";
23
- context.fillStyle = key;
24
- const match = first === String(context.fillStyle) ? /^#([0-9a-f]{6})$/i.exec(first) : null;
25
- if (match?.[1]) result = Number.parseInt(match[1], 16);
26
- }
23
+ canvas ??= DOMAdapter.get().createCanvas(1, 1);
24
+ const context = canvas.getContext("2d");
25
+ context.fillStyle = "#010203";
26
+ context.fillStyle = key;
27
+ const first = context.fillStyle;
28
+ context.fillStyle = "#040506";
29
+ context.fillStyle = key;
30
+ if (first === context.fillStyle) {
31
+ context.clearRect(0, 0, 1, 1);
32
+ context.fillRect(0, 0, 1, 1);
33
+ const [r = 0, g = 0, b = 0, a = 0] = context.getImageData(0, 0, 1, 1).data;
34
+ result = {
35
+ color: r << 16 | g << 8 | b,
36
+ alpha: a / 255
37
+ };
27
38
  }
28
39
  } catch {
29
40
  result = null;
30
41
  }
31
- if (cache.size >= CACHE_LIMIT) cache.clear();
42
+ if (cache.size >= 256) cache.clear();
32
43
  cache.set(key, result);
33
44
  return result;
34
45
  }
46
+ function parseBoardCssColor(value) {
47
+ return parseBoardCssPaint(value)?.color ?? null;
48
+ }
35
49
  //#endregion
36
- export { parseBoardCssColor };
50
+ export { parseBoardCssColor, parseBoardCssPaint };
@@ -0,0 +1,20 @@
1
+ import { BoardTextItem } from "../../protocol/dist/board-model.js";
2
+ import "../../protocol/dist/index.js";
3
+ import { BoardScene } from "../model/scene.js";
4
+ //#region src/board/render/diagnostics.d.ts
5
+ type BoardRenderDiagnostic = {
6
+ code: "text-clipped" | "text-overlap" | "glyph-unavailable" | "effect-follow-unused";
7
+ item: string;
8
+ related?: string;
9
+ characters?: string;
10
+ };
11
+ declare function measureBoardText(props: BoardTextItem["props"]): {
12
+ width: number;
13
+ height: number;
14
+ lines: string[];
15
+ lineHeight: number;
16
+ missingCharacters: string;
17
+ };
18
+ declare function inspectBoardScene(scene: BoardScene, limit?: number): BoardRenderDiagnostic[];
19
+ //#endregion
20
+ export { BoardRenderDiagnostic, inspectBoardScene, measureBoardText };