@neta-art/cohub 11.0.0 → 11.2.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 (115) hide show
  1. package/README.md +31 -5
  2. package/dist/board/editor/context.d.ts +1 -0
  3. package/dist/board/editor/context.js +1 -0
  4. package/dist/board/editor/creation.d.ts +2 -2
  5. package/dist/board/editor/creation.js +3 -3
  6. package/dist/board/editor/editor.d.ts +5 -4
  7. package/dist/board/editor/editor.js +8 -1
  8. package/dist/board/editor/index.d.ts +2 -2
  9. package/dist/board/editor/interaction.d.ts +12 -2
  10. package/dist/board/editor/interaction.js +50 -8
  11. package/dist/board/editor/selection-transform.d.ts +1 -1
  12. package/dist/board/editor/selection-transform.js +13 -2
  13. package/dist/board/editor/snapshots.d.ts +0 -2
  14. package/dist/board/editor/snapshots.js +4 -20
  15. package/dist/board/export/index.d.ts +2 -0
  16. package/dist/board/export/index.js +1 -0
  17. package/dist/board/export/scene.d.ts +2 -0
  18. package/dist/board/export/scene.js +8 -1
  19. package/dist/board/model/file-preview.d.ts +0 -1
  20. package/dist/board/model/file-preview.js +10 -1
  21. package/dist/board/model/image-key.js +1 -1
  22. package/dist/board/model/index.d.ts +8 -7
  23. package/dist/board/model/index.js +6 -5
  24. package/dist/board/model/items/create.d.ts +16 -3
  25. package/dist/board/model/items/create.js +125 -5
  26. package/dist/board/model/items/index.d.ts +2 -2
  27. package/dist/board/model/items/index.js +2 -2
  28. package/dist/board/model/media-playback.js +1 -1
  29. package/dist/board/model/overlay-placement.d.ts +25 -0
  30. package/dist/board/model/overlay-placement.js +35 -0
  31. package/dist/board/model/replay.d.ts +1 -1
  32. package/dist/board/model/replay.js +1 -0
  33. package/dist/board/model/script-hit.d.ts +20 -3
  34. package/dist/board/model/script-hit.js +81 -24
  35. package/dist/board/model/script-observers.d.ts +18 -0
  36. package/dist/board/model/script-observers.js +92 -0
  37. package/dist/board/model/script-scene.d.ts +1 -1
  38. package/dist/board/model/script-scene.js +35 -8
  39. package/dist/board/model/script.d.ts +51 -7
  40. package/dist/board/render/audio-waveform.d.ts +1 -1
  41. package/dist/board/render/audio-waveform.js +8 -5
  42. package/dist/board/render/board-background.js +5 -4
  43. package/dist/board/render/card-tag.d.ts +24 -0
  44. package/dist/board/render/card-tag.js +205 -0
  45. package/dist/board/render/index.d.ts +8 -5
  46. package/dist/board/render/index.js +8 -5
  47. package/dist/board/render/media-interaction.d.ts +2 -1
  48. package/dist/board/render/media-interaction.js +15 -7
  49. package/dist/board/render/palette.js +2 -2
  50. package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
  51. package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
  52. package/dist/board/render/renderers/audio-card-renderer.js +64 -60
  53. package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
  54. package/dist/board/render/renderers/base-card-renderer.js +6 -92
  55. package/dist/board/render/renderers/board-renderer-registry.d.ts +25 -2
  56. package/dist/board/render/renderers/board-renderer-registry.js +11 -4
  57. package/dist/board/render/renderers/custom-card-renderer.js +6 -8
  58. package/dist/board/render/renderers/effect-card-renderer.js +0 -5
  59. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
  60. package/dist/board/render/renderers/file-card-renderer.js +43 -105
  61. package/dist/board/render/renderers/frame-card-renderer.js +14 -15
  62. package/dist/board/render/renderers/image-card-renderer.js +62 -67
  63. package/dist/board/render/renderers/media-key.d.ts +6 -0
  64. package/dist/board/render/renderers/media-key.js +39 -0
  65. package/dist/board/render/renderers/shape-card-renderer.js +6 -12
  66. package/dist/board/render/renderers/task-card-renderer.d.ts +1 -8
  67. package/dist/board/render/renderers/task-card-renderer.js +286 -111
  68. package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
  69. package/dist/board/render/renderers/video-card-renderer.js +73 -80
  70. package/dist/board/render/script-host.d.ts +1 -1
  71. package/dist/board/render/script-host.js +53 -28
  72. package/dist/board/render/script-runtime.js +13 -3
  73. package/dist/board/render/script-texture.d.ts +11 -0
  74. package/dist/board/render/script-texture.js +33 -0
  75. package/dist/board/render/stroke.d.ts +1 -1
  76. package/dist/board/render/stroke.js +5 -4
  77. package/dist/board/render/style.d.ts +29 -0
  78. package/dist/board/render/style.js +54 -0
  79. package/dist/board/render/surface.d.ts +34 -0
  80. package/dist/board/render/surface.js +132 -0
  81. package/dist/board/render/text-fit.d.ts +8 -0
  82. package/dist/board/render/text-fit.js +60 -0
  83. package/dist/board/render/text-resolution.d.ts +2 -1
  84. package/dist/board/render/text-resolution.js +4 -1
  85. package/dist/board/stage/assets.js +1 -1
  86. package/dist/board/stage/index.d.ts +3 -2
  87. package/dist/board/stage/index.js +2 -1
  88. package/dist/board/stage/overlay.d.ts +41 -0
  89. package/dist/board/stage/overlay.js +182 -0
  90. package/dist/board/stage/scene.d.ts +4 -43
  91. package/dist/board/stage/scene.js +38 -130
  92. package/dist/board/stage/script-pointer.d.ts +1 -0
  93. package/dist/board/stage/script-pointer.js +8 -2
  94. package/dist/board/stage/stage.d.ts +13 -3
  95. package/dist/board/stage/stage.js +217 -53
  96. package/dist/chunks/environment.d.ts +1037 -8
  97. package/dist/chunks/environment.js +12 -4
  98. package/dist/chunks/http.d.ts +51 -2
  99. package/dist/chunks/http.js +10 -1
  100. package/dist/chunks/transport.js +1 -1
  101. package/dist/chunks/websocket.d.ts +1 -1
  102. package/dist/http.d.ts +3 -3
  103. package/dist/index.d.ts +3 -3
  104. package/dist/index.js +151 -139
  105. package/dist/protocol/dist/board-model.d.ts +36 -1
  106. package/dist/protocol/dist/board-model.js +14 -4
  107. package/dist/protocol/dist/board-task.d.ts +22 -0
  108. package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
  109. package/dist/protocol/dist/generation/blocks.d.ts +8 -0
  110. package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
  111. package/dist/protocol/dist/index.d.ts +5 -2
  112. package/dist/protocol/dist/task/index.d.ts +4 -0
  113. package/package.json +2 -1
  114. package/dist/board/model/task.d.ts +0 -11
  115. package/dist/types.d.ts +0 -36
@@ -2,11 +2,75 @@ import { applyMatrix, invertMatrix } from "../../protocol/dist/board-layout.js";
2
2
  import { frameContainsPoint } from "./geometry.js";
3
3
  import { scriptTemporaryPrefix } from "./script-scene.js";
4
4
  //#region src/board/model/script-hit.ts
5
+ function resolveBoardScriptHit(scene, owner, hit) {
6
+ const id = "item" in hit ? `${scriptTemporaryPrefix(owner)}${hit.item}` : owner;
7
+ const item = scene.get(id);
8
+ if (!item || scene.opacity(id) <= 0) return null;
9
+ const box = scene.layout.box(id);
10
+ return {
11
+ id,
12
+ item,
13
+ rect: "x" in hit ? hit : {
14
+ x: 0,
15
+ y: 0,
16
+ width: box.width,
17
+ height: box.height
18
+ }
19
+ };
20
+ }
21
+ function boardScriptHitVisible(scene, hit, camera, surface) {
22
+ const { x, y, width, height } = hit.rect;
23
+ if (width <= 0 || height <= 0) return false;
24
+ const matrix = scene.layout.matrix(hit.id);
25
+ const corners = [
26
+ applyMatrix(matrix, {
27
+ x,
28
+ y
29
+ }),
30
+ applyMatrix(matrix, {
31
+ x: x + width,
32
+ y
33
+ }),
34
+ applyMatrix(matrix, {
35
+ x,
36
+ y: y + height
37
+ }),
38
+ applyMatrix(matrix, {
39
+ x: x + width,
40
+ y: y + height
41
+ })
42
+ ];
43
+ const xs = corners.map((point) => point.x * camera.zoom + camera.x);
44
+ const ys = corners.map((point) => point.y * camera.zoom + camera.y);
45
+ return Math.max(...xs) > 0 && Math.min(...xs) < surface.width && Math.max(...ys) > 0 && Math.min(...ys) < surface.height;
46
+ }
47
+ const bindings = /* @__PURE__ */ new WeakMap();
48
+ function bindingsFor(owner, output) {
49
+ const cached = bindings.get(output);
50
+ if (cached?.owner === owner) return cached.byItem;
51
+ const byItem = /* @__PURE__ */ new Map();
52
+ for (const [target, hit] of Object.entries(output.hits)) {
53
+ const id = "item" in hit ? `${scriptTemporaryPrefix(owner)}${hit.item}` : owner;
54
+ const list = byItem.get(id) ?? [];
55
+ list.push({
56
+ owner,
57
+ target,
58
+ hit
59
+ });
60
+ byItem.set(id, list);
61
+ }
62
+ bindings.set(output, {
63
+ owner,
64
+ byItem
65
+ });
66
+ return byItem;
67
+ }
5
68
  function boardScriptTarget(scene, candidates, point, outputs, hitTest) {
6
- const owners = new Map([...outputs.keys()].map((id) => [scriptTemporaryPrefix(id), id]));
69
+ const declared = [...outputs].map(([owner, output]) => bindingsFor(owner, output));
70
+ const prefixes = [...outputs.keys()].map(scriptTemporaryPrefix);
7
71
  for (const id of [...candidates].sort((a, b) => scene.indexOf(b) - scene.indexOf(a))) {
8
72
  const item = scene.get(id);
9
- if (!item || scene.opacity(id) <= 0 || id.endsWith("root") && owners.has(id.slice(0, -4))) continue;
73
+ if (!item || scene.opacity(id) <= 0) continue;
10
74
  let parent = item.parent;
11
75
  let clipped = false;
12
76
  for (let depth = 0; parent && depth < 64; depth++) {
@@ -19,34 +83,27 @@ function boardScriptTarget(scene, candidates, point, outputs, hitTest) {
19
83
  parent = ancestor.parent;
20
84
  }
21
85
  if (clipped) continue;
22
- const output = outputs.get(id);
23
- if (output) {
86
+ for (const byItem of declared) {
87
+ const hits = byItem.get(id);
88
+ if (!hits) continue;
24
89
  const local = applyMatrix(invertMatrix(scene.layout.matrix(id)), point);
25
- for (const [target, region] of Object.entries(output.hits).reverse()) if (local.x >= region.x && local.y >= region.y && local.x <= region.x + region.width && local.y <= region.y + region.height) return {
26
- owner: id,
27
- target
28
- };
29
- }
30
- if (!hitTest(item, point)) continue;
31
- let current = item;
32
- for (let depth = 0; current && depth < 64; depth++) {
33
- if (outputs.has(current.id)) return {
34
- owner: current.id,
35
- target: id
36
- };
37
- for (const [prefix, owner] of owners) {
38
- if (current.id === `${prefix}root`) break;
39
- if (current.id.startsWith(prefix)) return {
40
- owner,
41
- target: current.id.slice(prefix.length)
90
+ for (let i = hits.length - 1; i >= 0; i--) {
91
+ const binding = hits[i];
92
+ if (!binding) continue;
93
+ const resolved = resolveBoardScriptHit(scene, binding.owner, binding.hit);
94
+ if (!resolved) continue;
95
+ const r = resolved.rect;
96
+ if (local.x >= r.x && local.y >= r.y && local.x <= r.x + r.width && local.y <= r.y + r.height && ("x" in binding.hit || hitTest(item, point))) return {
97
+ owner: binding.owner,
98
+ target: binding.target
42
99
  };
43
100
  }
44
- current = current.parent ? scene.get(current.parent) : void 0;
45
101
  }
102
+ if (prefixes.some((prefix) => id.startsWith(prefix))) continue;
46
103
  if (item.type === "frame" && item.props.decoration === "none") continue;
47
- return null;
104
+ if (hitTest(item, point)) return null;
48
105
  }
49
106
  return null;
50
107
  }
51
108
  //#endregion
52
- export { boardScriptTarget };
109
+ export { boardScriptHitVisible, boardScriptTarget, resolveBoardScriptHit };
@@ -0,0 +1,18 @@
1
+ import { BoardDocument } from "../../protocol/dist/board-model.js";
2
+ import "../../protocol/dist/index.js";
3
+ import { BoardScriptEvent, BoardScriptOutput, BoardScriptView } from "./script.js";
4
+ //#region src/board/model/script-observers.d.ts
5
+ declare function createBoardScriptObservers(send: (owner: string, event: BoardScriptEvent) => void): {
6
+ readonly size: number;
7
+ readonly transforms: boolean;
8
+ update(owner: string, output: BoardScriptOutput | null): void;
9
+ sync(document: BoardDocument, selection: readonly string[], readView: () => BoardScriptView): void;
10
+ transform(document: BoardDocument, event: {
11
+ ids: readonly string[];
12
+ operation: string;
13
+ cancelled: boolean;
14
+ }): void;
15
+ clear(): void;
16
+ };
17
+ //#endregion
18
+ export { createBoardScriptObservers };
@@ -0,0 +1,92 @@
1
+ import { boardScriptScope } from "./script-scene.js";
2
+ //#region src/board/model/script-observers.ts
3
+ const EVENTS = [
4
+ "viewchange",
5
+ "selectionchange",
6
+ "transformend"
7
+ ];
8
+ function createBoardScriptObservers(send) {
9
+ const subscribers = /* @__PURE__ */ new Map();
10
+ function scope(owner, entry, document) {
11
+ if (entry.document !== document) {
12
+ entry.scope = boardScriptScope(document, owner);
13
+ entry.document = document;
14
+ entry.selection = void 0;
15
+ }
16
+ return entry.scope;
17
+ }
18
+ return {
19
+ get size() {
20
+ return subscribers.size;
21
+ },
22
+ get transforms() {
23
+ return [...subscribers.values()].some((entry) => entry.listens.has("transformend"));
24
+ },
25
+ update(owner, output) {
26
+ const listens = new Set(EVENTS.filter((type) => output?.listens.includes(type)));
27
+ if (!listens.size) {
28
+ subscribers.delete(owner);
29
+ return;
30
+ }
31
+ const entry = subscribers.get(owner) ?? {
32
+ listens,
33
+ scope: /* @__PURE__ */ new Set()
34
+ };
35
+ if (!listens.has("viewchange")) entry.view = void 0;
36
+ if (!listens.has("selectionchange")) {
37
+ entry.selection = void 0;
38
+ entry.selected = void 0;
39
+ }
40
+ entry.listens = listens;
41
+ subscribers.set(owner, entry);
42
+ },
43
+ sync(document, selection, readView) {
44
+ let view;
45
+ let key;
46
+ for (const [owner, entry] of subscribers) {
47
+ if (entry.listens.has("viewchange")) {
48
+ view ??= readView();
49
+ key ??= JSON.stringify(view);
50
+ if (entry.view !== key) {
51
+ entry.view = key;
52
+ send(owner, {
53
+ type: "viewchange",
54
+ data: view
55
+ });
56
+ }
57
+ }
58
+ if (entry.listens.has("selectionchange")) {
59
+ const allowed = scope(owner, entry, document);
60
+ if (entry.selection === selection) continue;
61
+ entry.selection = selection;
62
+ const ids = selection.filter((id) => allowed.has(id));
63
+ if (entry.selected && ids.length === entry.selected.length && ids.every((id, i) => id === entry.selected?.[i])) continue;
64
+ entry.selected = ids;
65
+ send(owner, {
66
+ type: "selectionchange",
67
+ data: { ids }
68
+ });
69
+ }
70
+ }
71
+ },
72
+ transform(document, event) {
73
+ for (const [owner, entry] of subscribers) {
74
+ if (!entry.listens.has("transformend")) continue;
75
+ const allowed = scope(owner, entry, document);
76
+ const ids = event.ids.filter((id) => allowed.has(id));
77
+ if (ids.length) send(owner, {
78
+ type: "transformend",
79
+ data: {
80
+ ...event,
81
+ ids
82
+ }
83
+ });
84
+ }
85
+ },
86
+ clear() {
87
+ subscribers.clear();
88
+ }
89
+ };
90
+ }
91
+ //#endregion
92
+ export { createBoardScriptObservers };
@@ -19,6 +19,6 @@ type PreparedOutput = {
19
19
  declare function prepareBoardScriptOutput(document: BoardDocument, owner: string, output: BoardScriptOutput): PreparedOutput;
20
20
  declare function composeBoardScriptItems(document: BoardDocument, scene: BoardScene, outputs: ReadonlyMap<string, BoardScriptOutput>, incoming?: ReadonlyMap<string, BoardItem>, screen?: {
21
21
  zoom: number;
22
- }, onError?: (owner: string, error: unknown) => void): Map<string, BoardItem>;
22
+ }, onError?: (owner: string, error: unknown) => void, gestureLayout?: BoardScene["layout"]): Map<string, BoardItem>;
23
23
  //#endregion
24
24
  export { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, scriptTemporaryPrefix, validateBoardScriptCommit, validateBoardScriptOutput };
@@ -35,6 +35,10 @@ function validateBoardScriptOutput(output, scope) {
35
35
  if (!output || typeof output !== "object" || !output.patches || !output.items || !output.hits || !Array.isArray(output.listens)) throw new Error("Invalid script output.");
36
36
  if (JSON.stringify(output).length > 1e6) throw new Error("Script output exceeds the 1 MB limit.");
37
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
+ }
38
42
  if (output.parameters) {
39
43
  if (Object.keys(output.parameters).length > 64) throw new Error("A script may declare at most 64 parameters.");
40
44
  for (const spec of Object.values(output.parameters)) {
@@ -58,6 +62,7 @@ function validateBoardScriptOutput(output, scope) {
58
62
  if (!patch || typeof patch !== "object" || Object.keys(patch).some((key) => !DISPLAY_FIELDS.has(key))) throw new Error("Scripts may only present display properties.");
59
63
  }
60
64
  for (const hit of Object.values(output.hits)) {
65
+ if (!hit || typeof hit !== "object") throw new Error("Invalid script hit region.");
61
66
  if (hit.cursor && ![
62
67
  "default",
63
68
  "pointer",
@@ -70,7 +75,11 @@ function validateBoardScriptOutput(output, scope) {
70
75
  "ns-resize",
71
76
  "not-allowed"
72
77
  ].includes(hit.cursor)) throw new Error("Invalid script cursor.");
73
- if (![
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 (![
74
83
  hit.x,
75
84
  hit.y,
76
85
  hit.width,
@@ -100,6 +109,15 @@ function scriptTemporaryPrefix(owner) {
100
109
  }
101
110
  const preparedOutputs = /* @__PURE__ */ new WeakMap();
102
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
+ }
103
121
  function prepareBoardScriptOutput(document, owner, output) {
104
122
  const previous = preparedOutputs.get(output);
105
123
  if (previous?.document === document && previous.owner === owner) return previous;
@@ -113,6 +131,7 @@ function prepareBoardScriptOutput(document, owner, output) {
113
131
  scope = boardScriptScope(document, owner);
114
132
  scopes.set(owner, scope);
115
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.");
116
135
  validateBoardScriptOutput(output, scope);
117
136
  const presented = /* @__PURE__ */ new Map();
118
137
  for (const [id, patch] of Object.entries(output.patches)) {
@@ -177,11 +196,11 @@ function prepareBoardScriptOutput(document, owner, output) {
177
196
  preparedOutputs.set(output, prepared);
178
197
  return prepared;
179
198
  }
180
- function composeBoardScriptItems(document, scene, outputs, incoming = /* @__PURE__ */ new Map(), screen, onError) {
199
+ function composeBoardScriptItems(document, scene, outputs, incoming = /* @__PURE__ */ new Map(), screen, onError, gestureLayout) {
181
200
  const result = new Map(incoming);
182
201
  const screenAnchors = /* @__PURE__ */ new Map();
183
202
  if (outputs.size === 0) return result;
184
- for (const owner of Object.keys(document.items)) {
203
+ for (const owner of orderedScriptOwners(document, outputs)) {
185
204
  const output = outputs.get(owner);
186
205
  if (!output) continue;
187
206
  try {
@@ -249,15 +268,23 @@ function composeBoardScriptItems(document, scene, outputs, incoming = /* @__PURE
249
268
  }
250
269
  }
251
270
  if (screenAnchors.size) {
252
- const layout = createBoardLayout((id) => result.get(id) ?? document.items[id]);
271
+ const layout = gestureLayout ?? createBoardLayout((id) => result.get(id) ?? document.items[id]);
253
272
  for (const [owner, rootId] of screenAnchors) {
254
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
+ };
255
281
  if (root) result.set(rootId, {
256
282
  ...root,
257
- position: applyMatrix(layout.matrix(owner), {
258
- x: 0,
259
- y: 0
260
- })
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
261
288
  });
262
289
  }
263
290
  }
@@ -1,6 +1,29 @@
1
1
  import { BoardItem, BoardPatch, BoardScript, BoardVec2 } from "../../protocol/dist/board-model.js";
2
2
  import "../../protocol/dist/index.js";
3
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
+ };
4
27
  type BoardScriptEvent = {
5
28
  type: string;
6
29
  target?: string;
@@ -17,6 +40,22 @@ type BoardScriptEvent = {
17
40
  metaKey?: boolean;
18
41
  data?: unknown;
19
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);
20
59
  type BoardScriptFrame = {
21
60
  t: number;
22
61
  dt: number;
@@ -33,11 +72,16 @@ type BoardScriptItemPatch = Partial<Pick<BoardItem, "position" | "scale" | "rota
33
72
  };
34
73
  props?: Record<string, unknown>;
35
74
  };
36
- type BoardScriptHit = {
75
+ type BoardScriptHit = ({
37
76
  x: number;
38
77
  y: number;
39
78
  width: number;
40
79
  height: number;
80
+ } | {
81
+ target: "self";
82
+ } | {
83
+ item: string;
84
+ }) & {
41
85
  label?: string;
42
86
  cursor?: string;
43
87
  };
@@ -46,7 +90,7 @@ type BoardScriptSnapshot = {
46
90
  time: number;
47
91
  random: number;
48
92
  configuration?: string;
49
- effects?: Pick<BoardScriptOutput, "patches" | "items" | "hits" | "space">;
93
+ effects?: Pick<BoardScriptOutput, "patches" | "items" | "hits" | "space" | "anchor">;
50
94
  };
51
95
  type BoardScriptParameter = {
52
96
  type: "number" | "boolean" | "string" | "color";
@@ -59,6 +103,7 @@ type BoardScriptParameter = {
59
103
  type BoardScriptOutput = {
60
104
  parameters?: Record<string, BoardScriptParameter>;
61
105
  space?: "world" | "screen";
106
+ anchor?: BoardScriptAnchor;
62
107
  patches: Record<string, Record<string, unknown>>;
63
108
  items: Record<string, unknown>;
64
109
  hits: Record<string, BoardScriptHit>;
@@ -78,16 +123,15 @@ type BoardScriptContext = {
78
123
  emit(target: string, type: string, data?: unknown): Promise<void>;
79
124
  readonly state: Record<string, unknown>;
80
125
  readonly frame: BoardScriptFrame;
126
+ readonly view: BoardScriptView | null;
81
127
  random(): number;
82
- on(type: string, handler: (event: BoardScriptEvent) => void): () => void;
128
+ on<Type extends string>(type: Type, handler: (event: BoardScriptEventOf<Type>) => void): () => void;
83
129
  onFrame(handler: (frame: BoardScriptFrame) => void): () => void;
84
130
  onDispose(handler: () => void): void;
85
131
  invalidate(): void;
86
132
  get(id: string): BoardItem | undefined;
87
133
  present(id: string, patch: BoardScriptItemPatch | null): void;
88
- scene(items: Record<string, unknown>, options?: {
89
- space?: "world" | "screen";
90
- }): void;
134
+ scene(items: Record<string, unknown>, options?: BoardScriptSceneOptions): void;
91
135
  hitRegions(hits: Record<string, BoardScriptHit>): void;
92
136
  commit(patch: BoardPatch): Promise<void>;
93
137
  capabilities: {
@@ -102,4 +146,4 @@ type BoardScriptModule = {
102
146
  animated?: boolean;
103
147
  };
104
148
  //#endregion
105
- export { BoardScriptContext, BoardScriptEvent, BoardScriptFrame, BoardScriptHit, BoardScriptItemPatch, BoardScriptModule, BoardScriptOutput, BoardScriptParameter, BoardScriptSnapshot };
149
+ export { BoardScriptAnchor, BoardScriptContext, BoardScriptEvent, BoardScriptEventOf, BoardScriptFrame, BoardScriptHit, BoardScriptHostEvents, BoardScriptItemPatch, BoardScriptModule, BoardScriptOutput, BoardScriptParameter, BoardScriptSceneOptions, BoardScriptSnapshot, BoardScriptView };
@@ -7,6 +7,6 @@ declare function drawAudioWaveform(graphics: Graphics, seed: string, rect: {
7
7
  y: number;
8
8
  width: number;
9
9
  height: number;
10
- }, color: number, alpha?: number): void;
10
+ }, color: number, alpha?: number, square?: boolean): void;
11
11
  //#endregion
12
12
  export { audioWaveformBars, drawAudioWaveform };
@@ -17,7 +17,7 @@ function audioWaveformBars(seed, count) {
17
17
  }
18
18
  return bars;
19
19
  }
20
- function drawAudioWaveform(graphics, seed, rect, color, alpha = .72) {
20
+ function drawAudioWaveform(graphics, seed, rect, color, alpha = .72, square = false) {
21
21
  const count = Math.max(8, Math.min(DEFAULT_BARS, Math.floor(rect.width / 4)));
22
22
  const step = rect.width / count;
23
23
  const barWidth = Math.max(1.5, step - BAR_GAP);
@@ -26,11 +26,14 @@ function drawAudioWaveform(graphics, seed, rect, color, alpha = .72) {
26
26
  const bars = audioWaveformBars(seed, count);
27
27
  for (let index = 0; index < count; index += 1) {
28
28
  const height = Math.max(2, (bars[index] ?? .4) * maxHeight);
29
- graphics.roundRect(rect.x + index * step, centerY - height / 2, barWidth, height, barWidth / 2).fill({
30
- color,
31
- alpha
32
- });
29
+ const x = rect.x + index * step;
30
+ if (square) graphics.rect(x, centerY - height / 2, barWidth, height);
31
+ else graphics.roundRect(x, centerY - height / 2, barWidth, height, barWidth / 2);
33
32
  }
33
+ graphics.fill({
34
+ color,
35
+ alpha
36
+ });
34
37
  }
35
38
  //#endregion
36
39
  export { audioWaveformBars, drawAudioWaveform };
@@ -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,
@@ -0,0 +1,24 @@
1
+ import { BoardRenderContext } from "./renderers/board-renderer-registry.js";
2
+ import { Container, Graphics, Text } from "pixi.js";
3
+ //#region src/board/render/card-tag.d.ts
4
+ type CardGlyph = "image" | "video" | "audio" | "file" | "task";
5
+ declare const CARD_TAG_SIZE = 11;
6
+ declare function formatBoardDuration(ms: number): string;
7
+ declare function drawCardGlyph(graphics: Graphics, glyph: CardGlyph, color: number, pixel: boolean): void;
8
+ type CardTag = {
9
+ root: Container;
10
+ icon: Graphics;
11
+ text: Text | null;
12
+ sig: string;
13
+ resolution: number;
14
+ width: number;
15
+ };
16
+ declare function createCardTag(): CardTag;
17
+ declare function zoomCardTag(tag: CardTag, zoom: number): void;
18
+ declare function syncCardTag(tag: CardTag, input: {
19
+ glyph: CardGlyph;
20
+ text: string;
21
+ width: number;
22
+ }, context: BoardRenderContext): void;
23
+ //#endregion
24
+ export { CARD_TAG_SIZE, CardGlyph, CardTag, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag };