@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
package/README.md CHANGED
@@ -46,18 +46,39 @@ Development uses:
46
46
  - API: `https://api-dev.cohub.live`
47
47
  - WebSocket: `wss://gateway-dev.cohub.live/ws`
48
48
 
49
- Custom endpoints are still supported when needed:
49
+ Override endpoints for self-hosted deployments or proxies:
50
50
 
51
51
  ```ts
52
52
  const client = createCohubClient({
53
53
  baseUrl: "https://api.example.com",
54
+ webBaseUrl: "https://cohub.example.com",
54
55
  getAccessToken: async () => localStorage.getItem("token"),
55
56
  websocket: {
56
57
  url: "https://gateway.example.com",
57
58
  },
59
+ voice: {
60
+ url: "https://gateway.example.com",
61
+ },
58
62
  });
59
63
  ```
60
64
 
65
+ In Node.js, the SDK and CLI share these optional environment variables:
66
+
67
+ | Variable | Endpoint |
68
+ | --- | --- |
69
+ | `COHUB_BASE_URL` | API base URL |
70
+ | `COHUB_WS_URL` | Realtime gateway URL (`http(s)` or `ws(s)`; `/ws` is added when omitted) |
71
+ | `COHUB_WEB_URL` | Web origin for App authorization and CLI links |
72
+ | `COHUB_VOICE_WS_URL` | Voice input gateway URL; falls back to `COHUB_WS_URL` with `/asr/ws` |
73
+
74
+ Explicit client options take precedence over environment variables. Unconfigured
75
+ endpoints use the selected environment's defaults. API, web, and gateway addresses
76
+ are configured separately; changing `baseUrl` does not change the gateway.
77
+
78
+ ```bash
79
+ COHUB_BASE_URL=http://localhost:3000 COHUB_WS_URL=ws://localhost:8080 node app.js
80
+ ```
81
+
61
82
  ## Spaces and sessions
62
83
 
63
84
  A **Space** is a live, isolated working environment where users and agents create together.
@@ -101,10 +122,15 @@ const stop = board.subscribe({
101
122
  });
102
123
  ```
103
124
 
104
- Task Items keep a small, replaceable display snapshot beside their stable
105
- `taskRunId`; build it from an authoritative TaskRun with
106
- `taskRunToBoardTaskSnapshot(taskRun)` from `@neta-art/cohub/board`, and restore
107
- a Board's TaskRuns in one request with `client.tasks.getMany(ids, { spaceId })`.
125
+ Task Items show a run by its `taskRunId`. Write only the id; the server fills
126
+ the display snapshot from the run and rewrites it when the run finishes, on
127
+ every Board in the Space that shows it. Lay a run's results out as media with
128
+ `createTaskResultItems`; each result keeps an `origin` leading back to the run.
129
+
130
+ ```ts
131
+ const { taskRunId } = await client.generations.create({ spaceId, model, content });
132
+ await board.apply({ items: { hero: { type: "task", position: { x: 80, y: 80 }, size: { width: 480, height: 270 }, props: { taskRunId } } } });
133
+ ```
108
134
 
109
135
  ### Layers
110
136
 
@@ -131,7 +157,7 @@ The editor has no framework. Read state through its getters and listen with
131
157
  change, so they work directly with `useSyncExternalStore`:
132
158
 
133
159
  ```ts
134
- import { createBoardRegistry, defineBoardItem } from "@neta-art/cohub/board";
160
+ import { createBoardRegistry, defineBoardItem, worldPoint } from "@neta-art/cohub/board";
135
161
  import { createBoardEditor } from "@neta-art/cohub/board/editor";
136
162
  import { createBoardReplica } from "@neta-art/cohub/board/replica";
137
163
  import { createBoardAssetManager, mountBoardStage } from "@neta-art/cohub/board/stage";
@@ -157,7 +183,16 @@ const editor = createBoardEditor({
157
183
  });
158
184
  replica.subscribe((state) => state.document && editor.loadDocument(state.document, boardId));
159
185
 
160
- // Where Space file paths resolve; a published copy could resolve elsewhere.
186
+ // Prefer stable media URLs. Existing Space paths remain supported by addFile.
187
+ editor.addMedia("https://cdn.example.com/cover.webp", worldPoint(0, 0));
188
+ // Supply a kind for sources without a file extension.
189
+ editor.addMedia("https://cdn.example.com/media?id=42", worldPoint(400, 0), {
190
+ kind: "video",
191
+ snapshot: { title: "Launch", naturalWidth: 1920, naturalHeight: 1080 },
192
+ });
193
+
194
+ // Media URLs load directly; these callbacks only receive Space paths.
195
+ // A published copy can resolve paths against captured artifacts instead.
161
196
  const source = {
162
197
  resolveFileUrl: (path: string) => space.files.resolveUrl(path, { purpose: "preview" }),
163
198
  resolvePlaybackUrl: (path: string) => space.files.resolveUrl(path, { purpose: "playback" }),
@@ -1,3 +1,5 @@
1
+ import { BoardChapter } from "../model/authoring.js";
2
+ import "../model/index.js";
1
3
  import { BoardPlayhead, EditorContext } from "./context.js";
2
4
  //#region src/board/editor/animation.d.ts
3
5
  declare function createAnimationModule(ctx: EditorContext): {
@@ -8,6 +10,7 @@ declare function createAnimationModule(ctx: EditorContext): {
8
10
  removeKeyframe: (trackId: string, at: number) => void;
9
11
  setKeyframeEase: (trackId: string, at: number, ease: string | null) => void;
10
12
  createAnimation: (name: string, duration?: number) => string;
13
+ createSequence: (name: string, chapters: BoardChapter[]) => string | null;
11
14
  };
12
15
  type AnimationModule = ReturnType<typeof createAnimationModule>;
13
16
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { boardSequence } from "../model/authoring.js";
1
2
  import { createBoardItemId } from "../model/items/id.js";
2
3
  import { readPath, sameJson } from "./document-edits.js";
3
4
  //#region src/board/editor/animation.ts
@@ -158,6 +159,19 @@ function createAnimationModule(ctx) {
158
159
  });
159
160
  return id;
160
161
  }
162
+ function createSequence(name, chapters) {
163
+ if (options.readonly) return null;
164
+ const id = createBoardItemId();
165
+ commitAnimation(id, {
166
+ ...boardSequence(state.base, chapters),
167
+ name
168
+ });
169
+ setPlayhead({
170
+ animationId: id,
171
+ time: 0
172
+ });
173
+ return id;
174
+ }
161
175
  return {
162
176
  setPlayhead,
163
177
  setRecording,
@@ -165,7 +179,8 @@ function createAnimationModule(ctx) {
165
179
  toggleKeyframe,
166
180
  removeKeyframe,
167
181
  setKeyframeEase,
168
- createAnimation
182
+ createAnimation,
183
+ createSequence
169
184
  };
170
185
  }
171
186
  //#endregion
@@ -65,6 +65,13 @@ function remapItems(items, idMap, place = (item) => item, resolveEnd = () => nul
65
65
  let item = place(source);
66
66
  const { locked: _locked, ...unlocked } = item;
67
67
  item = unlocked;
68
+ if (item.script) item = {
69
+ ...item,
70
+ script: {
71
+ ...item.script,
72
+ targets: Object.fromEntries(Object.entries(item.script.targets).map(([name, target]) => [name, idMap.get(target) ?? target]))
73
+ }
74
+ };
68
75
  if (item.parent) {
69
76
  const parent = idMap.get(item.parent);
70
77
  if (parent) item = {
@@ -7,35 +7,39 @@ function createCopyModule(ctx) {
7
7
  const { state, options } = ctx;
8
8
  let internalClipboard = null;
9
9
  let pasteCount = 0;
10
- function detachedSubtrees(ids) {
11
- const { scene } = state;
10
+ function detachedSubtrees(ids, scene) {
12
11
  const items = {};
13
12
  for (const root of ctx.query.rootsOf(ids)) {
14
13
  const item = scene.get(root);
15
- if (!item) continue;
14
+ if (!item || !state.base.items[root]) continue;
16
15
  const { id: _id, frame, ...stored } = item;
17
16
  items[root] = ctx.document.reparent(stored, frame, void 0, scene.layout.box(root));
18
17
  for (const child of scene.descendants(root)) {
19
- const value = ctx.document.displayItem(child);
20
- if (value) items[child] = value;
18
+ if (!state.base.items[child]) continue;
19
+ const shown = scene.get(child);
20
+ if (shown) {
21
+ const { id: _id, frame: _frame, ...value } = shown;
22
+ items[child] = value;
23
+ }
21
24
  }
22
25
  }
23
26
  return items;
24
27
  }
25
- function arrowEndPoint(id, which) {
26
- const arrow = state.scene.get(id);
28
+ function arrowEndPoint(scene, id, which) {
29
+ const arrow = scene.get(id);
27
30
  if (arrow?.type !== "arrow") return null;
28
- const resolved = resolveSceneArrow(arrow, state.scene);
29
- return applyMatrix(invertMatrix(state.scene.layout.matrix(id)), (which === "start" ? resolved.start : resolved.end).point);
31
+ const resolved = resolveSceneArrow(arrow, scene);
32
+ return applyMatrix(invertMatrix(scene.layout.matrix(id)), (which === "start" ? resolved.start : resolved.end).point);
30
33
  }
31
34
  function duplicates(ids, offset) {
32
- const { scene } = state;
33
- const copied = detachedSubtrees(ids);
35
+ const scene = ctx.document.copyScene();
36
+ const copied = detachedSubtrees(ids, scene);
34
37
  for (const id of Object.keys(copied)) for (const arrowId of scene.binders(id)) {
35
- const arrow = ctx.document.displayItem(arrowId);
38
+ if (!state.base.items[arrowId]) continue;
36
39
  const shown = scene.get(arrowId);
37
- if (copied[arrowId] || !arrow || !shown || !arrowBindings(arrow).every((bound) => copied[bound])) continue;
38
- copied[arrowId] = ctx.document.reparent(arrow, shown.frame, void 0, scene.layout.box(arrowId));
40
+ if (copied[arrowId] || !shown || !arrowBindings(shown).every((bound) => copied[bound])) continue;
41
+ const { id: _id, frame, ...arrow } = shown;
42
+ copied[arrowId] = ctx.document.reparent(arrow, frame, void 0, scene.layout.box(arrowId));
39
43
  }
40
44
  const idMap = new Map(Object.keys(copied).map((id) => [id, createBoardItemId()]));
41
45
  const moved = remapItems(copied, idMap, (item) => item.parent && copied[item.parent] ? item : {
@@ -44,8 +48,8 @@ function createCopyModule(ctx) {
44
48
  x: item.position.x + offset,
45
49
  y: item.position.y + offset
46
50
  }
47
- }, arrowEndPoint);
48
- let z = ctx.document.topZ(void 0);
51
+ }, (id, which) => arrowEndPoint(scene, id, which));
52
+ let z = ctx.document.topZ(void 0, scene);
49
53
  return new Map(Object.entries(moved).map(([id, item]) => [id, item.parent ? item : {
50
54
  ...item,
51
55
  z: z++
@@ -66,8 +70,9 @@ function createCopyModule(ctx) {
66
70
  function copySelection() {
67
71
  const { selection } = state;
68
72
  if (selection.length === 0) return null;
69
- const copied = detachedSubtrees(selection);
70
- const origin = ctx.query.contentBounds(ctx.query.rootsOf(selection));
73
+ const scene = ctx.document.copyScene();
74
+ const copied = detachedSubtrees(selection, scene);
75
+ const origin = ctx.query.contentBounds(ctx.query.rootsOf(selection), scene);
71
76
  const payload = origin ? encodeClipboard(copied, {
72
77
  x: origin.x,
73
78
  y: origin.y
@@ -3,7 +3,7 @@ import { BoardMatrix } from "../../protocol/dist/board-layout.js";
3
3
  import { WorldPoint } from "../model/geometry.js";
4
4
  import { BoardFileSnapshotFacts } from "../model/file-snapshot.js";
5
5
  import { ShapeKind } from "../model/shape-types.js";
6
- import { BoardItemEntry, createAppBoardItem } from "../model/items/create.js";
6
+ import { BoardItemEntry, BoardMediaItemKind, createAppBoardItem } from "../model/items/create.js";
7
7
  import { BoardItemInput } from "../model/items/registry.js";
8
8
  import "../model/index.js";
9
9
  import { BoardInteraction } from "./interaction.js";
@@ -23,9 +23,13 @@ declare function createCreationModule(ctx: EditorContext): {
23
23
  }) => void;
24
24
  addItem: (type: string, input: BoardItemInput) => string | null;
25
25
  addApp: (app: BoardAppMetadata, at: WorldPoint) => string;
26
+ addMedia: (src: string, at: WorldPoint, options?: {
27
+ kind?: BoardMediaItemKind;
28
+ snapshot?: BoardMediaSnapshot;
29
+ }) => string;
26
30
  addFile: (path: string, at: WorldPoint, snapshot?: BoardMediaSnapshot & BoardFileSnapshotFacts) => string;
27
- addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>) => string;
28
- addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>) => string | null;
31
+ addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>, aspect?: number) => string;
32
+ addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>, aspect?: number) => string | null;
29
33
  addText: (text: string, at: WorldPoint) => void;
30
34
  addShape: (at: WorldPoint, geometry?: ShapeKind) => void;
31
35
  addFrame: (at: WorldPoint) => void;
@@ -3,7 +3,7 @@ import { rectCenter } from "../model/geometry.js";
3
3
  import { buildBoardScene } from "../model/scene.js";
4
4
  import { isShapeKind } from "../model/shape-types.js";
5
5
  import { createBoardItemId } from "../model/items/id.js";
6
- import { createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem } from "../model/items/create.js";
6
+ import { createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createMediaBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem } from "../model/items/create.js";
7
7
  import { sameJson } from "./document-edits.js";
8
8
  import { isContinuousBoardTool } from "./tool.js";
9
9
  //#region src/board/editor/creation.ts
@@ -53,10 +53,10 @@ function createCreationModule(ctx) {
53
53
  if (options.readonly) return null;
54
54
  return addOne(ctx.registry.create(type, input));
55
55
  }
56
- function addTaskWithSources(taskRunId, snapshot, at, sources, metadata) {
56
+ function addTaskWithSources(taskRunId, snapshot, at, sources, metadata, aspect) {
57
57
  if (options.readonly) return null;
58
58
  const { arrow } = state.toolStyles;
59
- const task = createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata);
59
+ const task = createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata, void 0, aspect);
60
60
  add([task, ...sources.filter((source) => source.itemId !== task.id && state.scene.get(source.itemId)).map((source) => {
61
61
  const entry = createArrowBoardItem({
62
62
  x: 0,
@@ -197,8 +197,9 @@ function createCreationModule(ctx) {
197
197
  add,
198
198
  addItem,
199
199
  addApp: (app, at) => addOne(createAppBoardItem(app, at.x, at.y)),
200
+ addMedia: (src, at, options = {}) => addOne(createMediaBoardItem(src, at.x, at.y, options.snapshot, void 0, options.kind)),
200
201
  addFile: (path, at, snapshot) => addOne(createFileNodeForPath(path, at.x, at.y, snapshot)),
201
- addTask: (taskRunId, snapshot, at, metadata) => addOne(createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata)),
202
+ addTask: (taskRunId, snapshot, at, metadata, aspect) => addOne(createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata, void 0, aspect)),
202
203
  addTaskWithSources,
203
204
  addText: (text, at) => {
204
205
  add([createTextBoardItem(text, at.x, at.y, styles().text.color)]);
@@ -8,14 +8,14 @@ function createDeletionModule(ctx) {
8
8
  const { scene, base } = state;
9
9
  const doomed = /* @__PURE__ */ new Set();
10
10
  for (const id of ids) {
11
- if (!scene.get(id)) continue;
11
+ if (!base.items[id]) continue;
12
12
  doomed.add(id);
13
- for (const child of scene.descendants(id)) doomed.add(child);
13
+ for (const child of scene.descendants(id)) if (base.items[child]) doomed.add(child);
14
14
  }
15
15
  const changes = /* @__PURE__ */ new Map();
16
16
  for (const id of doomed) changes.set(id, null);
17
17
  for (const id of doomed) for (const arrowId of scene.binders(id)) {
18
- if (doomed.has(arrowId) || changes.has(arrowId)) continue;
18
+ if (!base.items[arrowId] || doomed.has(arrowId) || changes.has(arrowId)) continue;
19
19
  const arrow = scene.get(arrowId);
20
20
  if (!arrow) continue;
21
21
  const resolved = resolveSceneArrow(arrow, scene);
@@ -1,6 +1,7 @@
1
- import { BoardAnimation, BoardDocument, BoardItem, BoardSettings } from "../../protocol/dist/board-model.js";
1
+ import { BoardAnimation, BoardDocument, BoardItem, BoardPatch, BoardSettings } from "../../protocol/dist/board-model.js";
2
2
  import { BoardFrame } from "../../protocol/dist/board-layout.js";
3
3
  import { Rect } from "../model/geometry.js";
4
+ import { BoardScene } from "../model/scene.js";
4
5
  import "../model/index.js";
5
6
  import { EditorContext } from "./context.js";
6
7
  //#region src/board/editor/document.d.ts
@@ -8,8 +9,13 @@ declare function createDocumentModule(ctx: EditorContext): {
8
9
  markAdded: (ids: readonly string[]) => void;
9
10
  consumeRecentlyAdded: () => string[];
10
11
  displayItem: (id: string) => BoardItem | undefined;
12
+ copyScene: () => BoardScene;
11
13
  reevaluate: () => void;
12
14
  setPlayedItems: (items: ReadonlyMap<string, BoardItem>) => void;
15
+ readonly hasScriptItems: boolean;
16
+ setScriptItems: (items: ReadonlyMap<string, BoardItem>) => void;
17
+ animationItems: () => Map<string, BoardItem>;
18
+ commitPatch: (patch: BoardPatch) => Promise<void> | undefined;
13
19
  rescene: (ids?: Iterable<string>) => void;
14
20
  reset: (next: BoardDocument) => void;
15
21
  setDraftItem: (id: string, item: BoardItem | null) => void;
@@ -28,7 +34,7 @@ declare function createDocumentModule(ctx: EditorContext): {
28
34
  previewSettings: (settings: BoardSettings) => void;
29
35
  setSettings: (settings: BoardSettings) => void;
30
36
  reparent: (item: BoardItem, frame: BoardFrame, parent: string | undefined, box: Rect) => BoardItem;
31
- topZ: (parent: string | undefined) => number;
37
+ topZ: (parent: string | undefined, scene?: BoardScene) => number;
32
38
  };
33
39
  type DocumentModule = ReturnType<typeof createDocumentModule>;
34
40
  //#endregion
@@ -11,6 +11,7 @@ function createDocumentModule(ctx) {
11
11
  const recentlyAdded = /* @__PURE__ */ new Set();
12
12
  let evaluated = /* @__PURE__ */ new Map();
13
13
  let played = /* @__PURE__ */ new Map();
14
+ let scripted = /* @__PURE__ */ new Map();
14
15
  let compiled = null;
15
16
  function markAdded(ids) {
16
17
  if (ids.length === 0) return;
@@ -24,14 +25,15 @@ function createDocumentModule(ctx) {
24
25
  }
25
26
  function displayItem(id) {
26
27
  if (draft.has(id)) return draft.get(id) ?? void 0;
27
- return evaluated.get(id) ?? played.get(id) ?? state.base.items[id];
28
+ return scripted.get(id) ?? evaluated.get(id) ?? played.get(id) ?? state.base.items[id];
28
29
  }
29
- function displayDocument() {
30
+ function displayDocument(includeScripts = true) {
30
31
  const base = state.base;
31
- if (draft.size === 0 && evaluated.size === 0 && played.size === 0) return base;
32
+ if (draft.size === 0 && evaluated.size === 0 && played.size === 0 && (!includeScripts || scripted.size === 0)) return base;
32
33
  const items = { ...base.items };
33
34
  for (const [id, item] of played) items[id] = item;
34
35
  for (const [id, item] of evaluated) items[id] = item;
36
+ if (includeScripts) for (const [id, item] of scripted) items[id] = item;
35
37
  for (const [id, item] of draft) if (item) items[id] = item;
36
38
  else delete items[id];
37
39
  return {
@@ -59,6 +61,17 @@ function createDocumentModule(ctx) {
59
61
  for (const id of changed) if (draft.has(id) || evaluated.has(id) || sameJson(previous.get(id), played.get(id))) changed.delete(id);
60
62
  if (changed.size) rescene(changed);
61
63
  }
64
+ function setScriptItems(items) {
65
+ const previous = scripted;
66
+ scripted = items;
67
+ const changed = [.../* @__PURE__ */ new Set([...previous.keys(), ...items.keys()])].filter((id) => !sameJson(previous.get(id), items.get(id)));
68
+ if (!changed.length) return;
69
+ rescene(changed.some((id) => {
70
+ const before = previous.get(id) ?? state.base.items[id];
71
+ const after = items.get(id) ?? state.base.items[id];
72
+ return !before || !after || before.parent !== after.parent || before.z !== after.z || !sameJson(arrowBindings(before), arrowBindings(after));
73
+ }) ? void 0 : changed);
74
+ }
62
75
  function rescene(ids) {
63
76
  if (ids) {
64
77
  const changed = [...ids];
@@ -76,6 +89,7 @@ function createDocumentModule(ctx) {
76
89
  draft.clear();
77
90
  draftOrigins.clear();
78
91
  played = /* @__PURE__ */ new Map();
92
+ scripted = /* @__PURE__ */ new Map();
79
93
  reevaluate();
80
94
  state.scene = buildBoardScene(next);
81
95
  ctx.query.invalidate();
@@ -114,12 +128,20 @@ function createDocumentModule(ctx) {
114
128
  rescene();
115
129
  }
116
130
  function send(patch) {
117
- if (options.readonly || isEmptyBoardPatch(patch)) return;
131
+ if (options.readonly || isEmptyBoardPatch(patch)) return Promise.resolve(null);
118
132
  state.pendingCommits += 1;
119
- Promise.resolve(options.onCommit(patch)).then(() => {
133
+ let completion;
134
+ try {
135
+ completion = Promise.resolve(options.onCommit(patch));
136
+ } catch (error) {
137
+ completion = Promise.reject(error);
138
+ }
139
+ return completion.then(() => {
120
140
  state.saveError = null;
141
+ return null;
121
142
  }).catch((error) => {
122
143
  state.saveError = error instanceof Error ? error.message : "Failed to sync board";
144
+ return error instanceof Error ? error : new Error(state.saveError);
123
145
  }).finally(() => {
124
146
  state.pendingCommits -= 1;
125
147
  });
@@ -183,6 +205,24 @@ function createDocumentModule(ctx) {
183
205
  for (const [id, item] of changes) setDraftItem(id, item);
184
206
  commit({}, record);
185
207
  }
208
+ function commitPatch(patch) {
209
+ if (options.readonly) throw new Error("Board is read-only.");
210
+ const before = state.base;
211
+ const result = applyBoardPatchToDocument(before, patch);
212
+ if (!result.ok) throw new Error(result.diagnostics[0]?.message ?? "Invalid board patch.");
213
+ const ids = Object.keys(patch.items ?? {});
214
+ const redo = diffBoardEdits(before, result.document, ids);
215
+ if (isEmptyBoardPatch(redo)) return;
216
+ state.undoStack = [...state.undoStack.slice(-199), {
217
+ undo: diffBoardEdits(result.document, before, ids),
218
+ redo
219
+ }];
220
+ state.redoStack = [];
221
+ adopt(result.document, ids);
222
+ return send(redo).then((error) => {
223
+ if (error) throw error;
224
+ });
225
+ }
186
226
  function applyPatch(patch) {
187
227
  const result = applyBoardPatchToDocument(state.base, patch);
188
228
  if (!result.ok) {
@@ -222,17 +262,24 @@ function createDocumentModule(ctx) {
222
262
  const matrix = parent ? state.scene.layout.matrix(parent) : IDENTITY_MATRIX;
223
263
  return placeItem(next, frame, matrix, box);
224
264
  }
225
- function topZ(parent) {
265
+ function topZ(parent, scene = state.scene) {
226
266
  let z = 0;
227
- for (const id of state.scene.children(parent)) z = Math.max(z, displayItem(id)?.z ?? 0);
267
+ for (const id of scene.children(parent)) z = Math.max(z, scene.get(id)?.z ?? 0);
228
268
  return z + 1;
229
269
  }
230
270
  return {
231
271
  markAdded,
232
272
  consumeRecentlyAdded,
233
273
  displayItem,
274
+ copyScene: () => scripted.size ? buildBoardScene(displayDocument(false)) : state.scene,
234
275
  reevaluate,
235
276
  setPlayedItems,
277
+ get hasScriptItems() {
278
+ return scripted.size > 0;
279
+ },
280
+ setScriptItems,
281
+ animationItems: () => new Map([...played, ...evaluated]),
282
+ commitPatch,
236
283
  rescene,
237
284
  reset,
238
285
  setDraftItem,
@@ -1,15 +1,17 @@
1
- import { BoardDocument, BoardItem, BoardMediaSnapshot, BoardSettings, BoardTaskSnapshot } from "../../protocol/dist/board-model.js";
1
+ import { BoardDocument, BoardItem, BoardMediaSnapshot, BoardPatch, BoardSettings, BoardTaskSnapshot } from "../../protocol/dist/board-model.js";
2
2
  import { BoardFrame } from "../../protocol/dist/board-layout.js";
3
3
  import { BoardViewport, Rect, ScreenPoint, WorldPoint } from "../model/geometry.js";
4
4
  import { BoardScene, BoardSceneItem } from "../model/scene.js";
5
+ import { BoardChapter } from "../model/authoring.js";
5
6
  import { BoardFileSnapshotFacts } from "../model/file-snapshot.js";
6
7
  import { ShapeKind } from "../model/shape-types.js";
8
+ import { BoardItemEntry, BoardMediaItemKind } from "../model/items/create.js";
7
9
  import { BoardItemInput, BoardRegistry } from "../model/items/registry.js";
8
10
  import "../model/index.js";
9
11
  import { AlignMode, DistributeAxis } from "./align.js";
10
12
  import { BoardFocusOptions } from "./camera.js";
11
13
  import { BoardSelectionTransform, BoardTransformControl } from "./selection-transform.js";
12
- import { BoardInteraction, BoardPointerEvent } from "./interaction.js";
14
+ import { BoardInteraction, BoardPointerEvent, BoardTransformEnd } from "./interaction.js";
13
15
  import { BoardAppMetadata, BoardTaskSource } from "./creation.js";
14
16
  import { BoardClipboardPayload } from "./clipboard.js";
15
17
  import { BoardNaturalSize } from "./snapshots.js";
@@ -22,6 +24,7 @@ declare function createBoardEditor(options: BoardEditorOptions): {
22
24
  registry: BoardRegistry;
23
25
  readonly: boolean;
24
26
  subscribe: (channels: BoardEditorChannel | readonly BoardEditorChannel[], listener: BoardEditorListener) => () => void;
27
+ readonly hasScripts: boolean;
25
28
  readonly document: BoardDocument;
26
29
  readonly settings: {
27
30
  title?: string | undefined;
@@ -130,11 +133,14 @@ declare function createBoardEditor(options: BoardEditorOptions): {
130
133
  setSettings: (settings: BoardSettings) => void;
131
134
  previewSettings: (settings: BoardSettings) => void;
132
135
  setPlayedItems: (items: ReadonlyMap<string, BoardItem>) => void;
136
+ setScriptItems: (items: ReadonlyMap<string, BoardItem>) => void;
137
+ animationItems: () => Map<string, BoardItem>;
138
+ commitPatch: (patch: BoardPatch) => Promise<void> | undefined;
133
139
  consumeRecentlyAdded: () => string[];
134
- itemAt: (point: WorldPoint, exclude?: ReadonlySet<string> | undefined) => BoardSceneItem | null;
140
+ itemAt: (point: WorldPoint, exclude?: ReadonlySet<string> | undefined, includeTemporary?: boolean | undefined) => BoardSceneItem | null;
135
141
  itemById: (id: string) => BoardSceneItem | null;
136
142
  labelItemAt: (point: WorldPoint) => BoardSceneItem | null;
137
- idsInRect: (rect: Rect) => string[];
143
+ idsInRect: (rect: Rect, includeTemporary?: boolean | undefined) => string[];
138
144
  setCamera: (viewport: BoardViewport) => void;
139
145
  setCameraPolicy: (policy: BoardCameraPolicy) => void;
140
146
  takeCameraControl: () => void;
@@ -162,10 +168,18 @@ declare function createBoardEditor(options: BoardEditorOptions): {
162
168
  bringToFront: () => void;
163
169
  sendToBack: () => void;
164
170
  addItem: (type: string, input: BoardItemInput) => string | null;
171
+ addItems: (entries: BoardItemEntry[], { select, enter }?: {
172
+ select?: boolean;
173
+ enter?: boolean;
174
+ }) => void;
165
175
  addApp: (app: BoardAppMetadata, at: WorldPoint) => string;
166
176
  addFile: (path: string, at: WorldPoint, snapshot?: BoardMediaSnapshot & BoardFileSnapshotFacts) => string;
167
- addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>) => string;
168
- addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>) => string | null;
177
+ addMedia: (src: string, at: WorldPoint, options?: {
178
+ kind?: BoardMediaItemKind;
179
+ snapshot?: BoardMediaSnapshot;
180
+ }) => string;
181
+ addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>, aspect?: number) => string;
182
+ addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>, aspect?: number) => string | null;
169
183
  addText: (text: string, at: WorldPoint) => void;
170
184
  addShape: (at: WorldPoint, geometry?: ShapeKind) => void;
171
185
  addFrame: (at: WorldPoint) => void;
@@ -190,7 +204,6 @@ declare function createBoardEditor(options: BoardEditorOptions): {
190
204
  snapshot: BoardFileSnapshotFacts;
191
205
  replace?: boolean;
192
206
  }>) => void;
193
- applyTaskSnapshots: (snapshots: ReadonlyMap<string, BoardTaskSnapshot>) => void;
194
207
  setPlayhead: (next: BoardPlayhead | null) => void;
195
208
  setRecording: (value: boolean) => void;
196
209
  keyframeState: (property: string) => "animated" | "keyframe" | "none";
@@ -198,10 +211,12 @@ declare function createBoardEditor(options: BoardEditorOptions): {
198
211
  removeKeyframe: (trackId: string, at: number) => void;
199
212
  setKeyframeEase: (trackId: string, at: number, ease: string | null) => void;
200
213
  createAnimation: (name: string, duration?: number) => string;
214
+ createSequence: (name: string, chapters: BoardChapter[]) => string | null;
201
215
  pointerDown: (event: BoardPointerEvent) => void;
202
216
  pointerMove: (event: BoardPointerEvent) => void;
203
217
  pointerUp: (event: BoardPointerEvent) => void;
204
218
  pointerLeave: () => void;
219
+ subscribeTransforms: (listener: (event: BoardTransformEnd) => void) => () => void;
205
220
  cancelPointerInteraction: () => void;
206
221
  destroy(): void;
207
222
  };
@@ -1,7 +1,8 @@
1
1
  import { clampBoardStrokeSize } from "../../protocol/dist/board-constants.js";
2
+ import { hasBoardScripts } from "../model/script-index.js";
2
3
  import { normalizeViewport } from "../model/geometry.js";
3
- import { isBoardColorId } from "../model/palette.js";
4
4
  import { buildBoardScene } from "../model/scene.js";
5
+ import { isBoardColorId } from "../model/palette.js";
5
6
  import { isShapeKind } from "../model/shape-types.js";
6
7
  import { createBoardToolStyles } from "../model/tool-styles.js";
7
8
  import { titleForBoardItem } from "../model/items/create.js";
@@ -145,6 +146,9 @@ function createBoardEditor(options) {
145
146
  registry,
146
147
  readonly: options.readonly === true,
147
148
  subscribe: channels.subscribe,
149
+ get hasScripts() {
150
+ return read("document", () => hasBoardScripts(state.base));
151
+ },
148
152
  get document() {
149
153
  return read("document", () => state.base);
150
154
  },
@@ -344,6 +348,9 @@ function createBoardEditor(options) {
344
348
  setSettings: document.setSettings,
345
349
  previewSettings: document.previewSettings,
346
350
  setPlayedItems: document.setPlayedItems,
351
+ setScriptItems: document.setScriptItems,
352
+ animationItems: document.animationItems,
353
+ commitPatch: document.commitPatch,
347
354
  consumeRecentlyAdded: document.consumeRecentlyAdded,
348
355
  itemAt: tracked(["scene", "camera"], query.itemAt),
349
356
  itemById: tracked(["scene"], query.itemById),
@@ -380,8 +387,10 @@ function createBoardEditor(options) {
380
387
  bringToFront: selection.bringToFront,
381
388
  sendToBack: selection.sendToBack,
382
389
  addItem: creation.addItem,
390
+ addItems: creation.add,
383
391
  addApp: creation.addApp,
384
392
  addFile: creation.addFile,
393
+ addMedia: creation.addMedia,
385
394
  addTask: creation.addTask,
386
395
  addTaskWithSources: creation.addTaskWithSources,
387
396
  addText: creation.addText,
@@ -400,7 +409,6 @@ function createBoardEditor(options) {
400
409
  applyMediaFileChange: snapshots.applyMediaFileChange,
401
410
  adoptMediaNaturalSizes: snapshots.adoptMediaNaturalSizes,
402
411
  applyFileSnapshots: snapshots.applyFileSnapshots,
403
- applyTaskSnapshots: snapshots.applyTaskSnapshots,
404
412
  setPlayhead: animation.setPlayhead,
405
413
  setRecording: animation.setRecording,
406
414
  keyframeState: tracked([
@@ -413,10 +421,12 @@ function createBoardEditor(options) {
413
421
  removeKeyframe: animation.removeKeyframe,
414
422
  setKeyframeEase: animation.setKeyframeEase,
415
423
  createAnimation: animation.createAnimation,
424
+ createSequence: animation.createSequence,
416
425
  pointerDown: interaction.pointerDown,
417
426
  pointerMove: interaction.pointerMove,
418
427
  pointerUp: interaction.pointerUp,
419
428
  pointerLeave: interaction.pointerLeave,
429
+ subscribeTransforms: interaction.subscribeTransforms,
420
430
  cancelPointerInteraction: interaction.cancel,
421
431
  destroy() {
422
432
  stopViewState();
@@ -2,7 +2,7 @@ import { BOARD_CLIPBOARD_MIME } from "../../protocol/dist/board.js";
2
2
  import { AlignMode, DistributeAxis, alignFrames, distributeFrames, framesBounds } from "./align.js";
3
3
  import { BoardFocusOptions } from "./camera.js";
4
4
  import { BoardSelectionTransform, BoardTransformControl, resizeCursorForHandle, resolveSelectionTransform, selectionTransformControlAt } from "./selection-transform.js";
5
- import { BoardInteraction, BoardPointerEvent } from "./interaction.js";
5
+ import { BoardInteraction, BoardPointerEvent, BoardTransformEnd } from "./interaction.js";
6
6
  import { BoardAppMetadata, BoardTaskSource } from "./creation.js";
7
7
  import { BOARD_CLIPBOARD_VERSION, BoardClipboardPayload, DUPLICATE_OFFSET, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, defaultPasteOffset, encodeClipboard, materializeClipboard, parseClipboard, remapItems } from "./clipboard.js";
8
8
  import { BoardNaturalSize } from "./snapshots.js";
@@ -15,4 +15,4 @@ import { BoardEditPlayhead, diffBoardEdits, isEmptyBoardPatch, readPath, routeBo
15
15
  import { BoardEditor, createBoardEditor } from "./editor.js";
16
16
  import { SpatialEntry, SpatialIndex, createSpatialIndex } from "./spatial.js";
17
17
  import { normalizeWheelDelta, wheelZoomFactor } from "./wheel.js";
18
- export { AlignMode, BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, BOARD_HAND_TAP_SLOP, type BoardAppMetadata, type BoardCameraPolicy, BoardClipboardPayload, BoardEditPlayhead, type BoardEditor, type BoardEditorChannel, type BoardEditorListener, type BoardEditorOptions, type BoardEditorPreferences, type BoardFocusOptions, type BoardInteraction, type BoardNaturalSize, type BoardPlayhead, type BoardPointerEvent, BoardSelectionTransform, type BoardTaskSource, BoardToolId, BoardTransformControl, type BoardViewState, CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, ConnectionPort, DUPLICATE_OFFSET, DistributeAxis, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, SnapAxis, SnapGuide, SnapOptions, SnapResult, SpatialEntry, SpatialIndex, alignFrames, canTapSelectWithHand, computeSnap, connectionPortAt, connectionPorts, createBoardEditor, createSpatialIndex, defaultBoardTool, defaultPasteOffset, diffBoardEdits, distributeFrames, encodeClipboard, framesBounds, isContinuousBoardTool, isCreationBoardTool, isEmptyBoardPatch, isWithinHandTapSlop, materializeClipboard, normalizeWheelDelta, parseClipboard, portHitRadius, readPath, remapItems, resizeCursorForHandle, resolveSelectionTransform, routeBoardEdits, sameJson, selectionTransformControlAt, wheelZoomFactor, writePath };
18
+ export { AlignMode, BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, BOARD_HAND_TAP_SLOP, type BoardAppMetadata, type BoardCameraPolicy, BoardClipboardPayload, BoardEditPlayhead, type BoardEditor, type BoardEditorChannel, type BoardEditorListener, type BoardEditorOptions, type BoardEditorPreferences, type BoardFocusOptions, type BoardInteraction, type BoardNaturalSize, type BoardPlayhead, type BoardPointerEvent, BoardSelectionTransform, type BoardTaskSource, BoardToolId, BoardTransformControl, type BoardTransformEnd, type BoardViewState, CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, ConnectionPort, DUPLICATE_OFFSET, DistributeAxis, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, SnapAxis, SnapGuide, SnapOptions, SnapResult, SpatialEntry, SpatialIndex, alignFrames, canTapSelectWithHand, computeSnap, connectionPortAt, connectionPorts, createBoardEditor, createSpatialIndex, defaultBoardTool, defaultPasteOffset, diffBoardEdits, distributeFrames, encodeClipboard, framesBounds, isContinuousBoardTool, isCreationBoardTool, isEmptyBoardPatch, isWithinHandTapSlop, materializeClipboard, normalizeWheelDelta, parseClipboard, portHitRadius, readPath, remapItems, resizeCursorForHandle, resolveSelectionTransform, routeBoardEdits, sameJson, selectionTransformControlAt, wheelZoomFactor, writePath };