@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.
- package/README.md +42 -7
- package/dist/board/editor/animation.d.ts +3 -0
- package/dist/board/editor/animation.js +16 -1
- package/dist/board/editor/clipboard.js +7 -0
- package/dist/board/editor/copy.js +23 -18
- package/dist/board/editor/creation.d.ts +7 -3
- package/dist/board/editor/creation.js +5 -4
- package/dist/board/editor/deletion.js +3 -3
- package/dist/board/editor/document.d.ts +8 -2
- package/dist/board/editor/document.js +54 -7
- package/dist/board/editor/editor.d.ts +22 -7
- package/dist/board/editor/editor.js +12 -2
- package/dist/board/editor/index.d.ts +2 -2
- package/dist/board/editor/interaction.d.ts +8 -2
- package/dist/board/editor/interaction.js +34 -1
- package/dist/board/editor/query.d.ts +5 -5
- package/dist/board/editor/query.js +14 -12
- package/dist/board/editor/selection.js +2 -1
- package/dist/board/editor/snapshots.d.ts +0 -2
- package/dist/board/editor/snapshots.js +4 -20
- package/dist/board/export/index.d.ts +25 -4
- package/dist/board/export/index.js +45 -12
- package/dist/board/export/node/index.d.ts +5 -4
- package/dist/board/export/node/index.js +16 -11
- package/dist/board/export/node/scripts.d.ts +16 -0
- package/dist/board/export/node/scripts.js +258 -0
- package/dist/board/export/scene.d.ts +2 -1
- package/dist/board/export/scene.js +24 -7
- package/dist/board/model/animation.d.ts +4 -1
- package/dist/board/model/animation.js +34 -7
- package/dist/board/model/authoring.d.ts +20 -0
- package/dist/board/model/authoring.js +185 -0
- package/dist/board/model/export-plan.js +1 -0
- package/dist/board/model/file-preview.js +1 -1
- package/dist/board/model/file-snapshot.js +3 -2
- package/dist/board/model/image-key.js +7 -3
- package/dist/board/model/index.d.ts +15 -7
- package/dist/board/model/index.js +16 -9
- package/dist/board/model/items/builtin.js +2 -2
- package/dist/board/model/items/create.d.ts +19 -4
- package/dist/board/model/items/create.js +130 -9
- package/dist/board/model/items/index.d.ts +2 -2
- package/dist/board/model/items/index.js +2 -2
- package/dist/board/model/media-playback.d.ts +2 -1
- package/dist/board/model/media-playback.js +15 -5
- package/dist/board/model/media.js +7 -1
- package/dist/board/model/overlay-placement.d.ts +25 -0
- package/dist/board/model/overlay-placement.js +35 -0
- package/dist/board/model/replay.d.ts +1 -1
- package/dist/board/model/replay.js +1 -0
- package/dist/board/model/sampling.d.ts +19 -0
- package/dist/board/model/sampling.js +96 -0
- package/dist/board/model/script-example.d.ts +4 -0
- package/dist/board/model/script-example.js +21 -0
- package/dist/board/model/script-hit.d.ts +27 -0
- package/dist/board/model/script-hit.js +109 -0
- package/dist/board/model/script-index.d.ts +7 -0
- package/dist/board/model/script-index.js +15 -0
- package/dist/board/model/script-observers.d.ts +18 -0
- package/dist/board/model/script-observers.js +92 -0
- package/dist/board/model/script-scene.d.ts +24 -0
- package/dist/board/model/script-scene.js +294 -0
- package/dist/board/model/script.d.ts +149 -0
- package/dist/board/model/script.js +1 -0
- package/dist/board/render/board-background.js +5 -4
- package/dist/board/render/css-color.d.ts +6 -1
- package/dist/board/render/css-color.js +34 -20
- package/dist/board/render/diagnostics.d.ts +20 -0
- package/dist/board/render/diagnostics.js +117 -0
- package/dist/board/render/index.d.ts +9 -6
- package/dist/board/render/index.js +10 -7
- package/dist/board/render/media-interaction.js +1 -1
- package/dist/board/render/palette.d.ts +2 -1
- package/dist/board/render/palette.js +6 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +3 -2
- package/dist/board/render/renderers/audio-card-renderer.js +2 -1
- package/dist/board/render/renderers/board-renderer-registry.d.ts +25 -5
- package/dist/board/render/renderers/board-renderer-registry.js +11 -4
- package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +18 -16
- package/dist/board/render/renderers/draw-card-renderer.js +3 -2
- package/dist/board/render/renderers/effect-card-renderer.js +22 -5
- package/dist/board/render/renderers/frame-card-renderer.js +30 -14
- package/dist/board/render/renderers/image-card-renderer.js +43 -4
- package/dist/board/render/renderers/shape-card-renderer.js +10 -27
- package/dist/board/render/renderers/task-card-renderer.d.ts +2 -1
- package/dist/board/render/renderers/task-card-renderer.js +220 -71
- package/dist/board/render/renderers/text-card-renderer.js +5 -3
- package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sampling.d.ts +22 -0
- package/dist/board/render/sampling.js +21 -0
- package/dist/board/render/script-export.d.ts +11 -0
- package/dist/board/render/script-export.js +86 -0
- package/dist/board/render/script-host.d.ts +31 -0
- package/dist/board/render/script-host.js +446 -0
- package/dist/board/render/script-modules.d.ts +2 -0
- package/dist/board/render/script-modules.js +2 -0
- package/dist/board/render/script-runtime.d.ts +29 -0
- package/dist/board/render/script-runtime.js +223 -0
- package/dist/board/render/script-sandbox.d.ts +4 -0
- package/dist/board/render/script-sandbox.js +173 -0
- package/dist/board/render/script-texture.d.ts +11 -0
- package/dist/board/render/script-texture.js +33 -0
- package/dist/board/render/stroke.d.ts +2 -1
- package/dist/board/render/stroke.js +22 -1
- package/dist/board/render/text-measurement.js +1 -1
- package/dist/board/stage/assets.js +7 -2
- package/dist/board/stage/maintenance.d.ts +5 -0
- package/dist/board/stage/maintenance.js +11 -0
- package/dist/board/stage/scene.d.ts +2 -0
- package/dist/board/stage/scene.js +21 -2
- package/dist/board/stage/script-pointer.d.ts +20 -0
- package/dist/board/stage/script-pointer.js +41 -0
- package/dist/board/stage/stage.d.ts +21 -3
- package/dist/board/stage/stage.js +350 -47
- package/dist/board/stage/texture-pool.js +2 -3
- package/dist/chunks/environment.d.ts +305 -27
- package/dist/chunks/environment.js +12 -4
- package/dist/chunks/host-bridge.d.ts +2 -2
- package/dist/chunks/http.d.ts +90 -7
- package/dist/chunks/http.js +157 -64
- package/dist/chunks/transport.js +4 -2
- package/dist/chunks/websocket.d.ts +9 -1
- package/dist/chunks/websocket.js +16 -2
- package/dist/host-bridge.js +2 -1
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +182 -4
- package/dist/index.js +1559 -299
- package/dist/protocol/dist/board-assets.d.ts +1 -0
- package/dist/protocol/dist/board-constants.js +2 -2
- package/dist/protocol/dist/board-model.d.ts +236 -11
- package/dist/protocol/dist/board-model.js +46 -17
- package/dist/protocol/dist/board-patch.js +2 -1
- package/dist/protocol/dist/board-schema.d.ts +4 -2
- package/dist/protocol/dist/board-schema.js +18 -4
- package/dist/protocol/dist/board-script-modules.d.ts +25 -0
- package/dist/protocol/dist/board-script-modules.js +127 -0
- package/dist/protocol/dist/board-task.d.ts +22 -0
- package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
- package/dist/protocol/dist/display/index.d.ts +1 -0
- package/dist/protocol/dist/generation/blocks.d.ts +8 -0
- package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
- package/dist/protocol/dist/index.d.ts +7 -2
- package/dist/protocol/dist/realtime/types.d.ts +1 -0
- package/dist/protocol/dist/rtc/index.d.ts +1 -0
- package/dist/protocol/dist/task/index.d.ts +4 -0
- package/package.json +2 -1
- package/dist/board/export/node/sketch.d.ts +0 -19
- package/dist/board/export/node/sketch.js +0 -146
- package/dist/board/model/task.d.ts +0 -11
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
- package/dist/board/render/sketch-host.d.ts +0 -14
- package/dist/board/render/sketch-host.js +0 -216
- package/dist/types.d.ts +0 -36
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyMatrix, invertMatrix } from "../../protocol/dist/board-layout.js";
|
|
2
|
+
import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
|
|
3
|
+
import { screenPoint, screenToWorld, stableCullRect, visibleWorldRect, worldPoint } from "../model/geometry.js";
|
|
4
|
+
import { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, validateBoardScriptCommit } from "../model/script-scene.js";
|
|
5
|
+
import { boardScriptTarget } from "../model/script-hit.js";
|
|
6
|
+
import { createBoardAnimationSampler, resolveBoardAnimationCamera } from "../model/sampling.js";
|
|
2
7
|
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
3
8
|
import { buildFallbackShapeColors, pickBoardColor, resolveItemColor } from "../model/palette.js";
|
|
4
|
-
import { featuredTaskArtifact } from "../model/task.js";
|
|
5
9
|
import { isCreationBoardTool } from "../editor/tool.js";
|
|
6
10
|
import { resizeCursorForHandle } from "../editor/selection-transform.js";
|
|
7
11
|
import { parseBoardCssColor } from "../render/css-color.js";
|
|
12
|
+
import { defaultBoardPalette, itemAlpha } from "../render/palette.js";
|
|
8
13
|
import { createBoardBackground, updateBoardBackground } from "../render/board-background.js";
|
|
9
14
|
import { getBoardResolution, textZoomBucket } from "../render/text-resolution.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { createBoardSketchHost } from "../render/sketch-host.js";
|
|
15
|
+
import { DEFAULT_BOARD_RENDER_LABELS, getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
16
|
+
import { createBoardScriptHost } from "../render/script-host.js";
|
|
13
17
|
import { createBoardPlayer } from "../player/index.js";
|
|
18
|
+
import { createBoardScriptObservers } from "../model/script-observers.js";
|
|
14
19
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
15
20
|
import { createBoardScene } from "./scene.js";
|
|
21
|
+
import { scheduleBoardMaintenance } from "./maintenance.js";
|
|
22
|
+
import { createBoardScriptPointerRouter } from "./script-pointer.js";
|
|
16
23
|
import { createBoardItemViewLayer } from "./views.js";
|
|
17
24
|
import { Application, Container, Graphics, RendererType } from "pixi.js";
|
|
18
25
|
//#region src/board/stage/stage.ts
|
|
@@ -45,11 +52,43 @@ function mountBoardStage(host, options) {
|
|
|
45
52
|
let overlay = null;
|
|
46
53
|
let background = null;
|
|
47
54
|
let scene = null;
|
|
48
|
-
let
|
|
55
|
+
let scripts = null;
|
|
56
|
+
let scriptMode = editor.readonly ? "interact" : "edit";
|
|
57
|
+
let scriptTime = 0;
|
|
58
|
+
let scriptLastTick = performance.now();
|
|
59
|
+
const scriptOutputs = /* @__PURE__ */ new Map();
|
|
60
|
+
const interactiveScripts = /* @__PURE__ */ new Set();
|
|
61
|
+
const screenScripts = /* @__PURE__ */ new Set();
|
|
62
|
+
const scriptObservers = createBoardScriptObservers((owner, event) => scripts?.event(owner, event));
|
|
63
|
+
let stopTransforms = null;
|
|
64
|
+
function syncTransformSubscription() {
|
|
65
|
+
if (scriptObservers.transforms && !stopTransforms) stopTransforms = editor.subscribeTransforms((event) => {
|
|
66
|
+
if (active && scriptMode !== "paused") scriptObservers.transform(editor.document, event);
|
|
67
|
+
});
|
|
68
|
+
else if (!scriptObservers.transforms && stopTransforms) {
|
|
69
|
+
stopTransforms();
|
|
70
|
+
stopTransforms = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const scriptListeners = /* @__PURE__ */ new Set();
|
|
74
|
+
let scriptVersion = 0;
|
|
75
|
+
let composedVersion = -1;
|
|
76
|
+
let composedZoom = 0;
|
|
77
|
+
let composedPlayhead = "";
|
|
78
|
+
let composedMoving = false;
|
|
79
|
+
let composedGesture = false;
|
|
80
|
+
let composedDocument = null;
|
|
81
|
+
let scriptHover = null;
|
|
82
|
+
const scriptPointers = createBoardScriptPointerRouter();
|
|
83
|
+
let stopMaintenance = null;
|
|
49
84
|
let active = true;
|
|
50
85
|
let disposed = false;
|
|
51
86
|
let renderFrame = 0;
|
|
52
87
|
let tickFrame = 0;
|
|
88
|
+
let cardTickFrame = 0;
|
|
89
|
+
let cardTickTimer = null;
|
|
90
|
+
let cardTickDue = 0;
|
|
91
|
+
const staleIds = /* @__PURE__ */ new Set();
|
|
53
92
|
let resizeFrame = 0;
|
|
54
93
|
let cardEpoch = 0;
|
|
55
94
|
let assetVersion = 0;
|
|
@@ -63,7 +102,10 @@ function mountBoardStage(host, options) {
|
|
|
63
102
|
const views = createBoardItemViewLayer(host, editor, options.views ?? [], renderCamera);
|
|
64
103
|
const player = createBoardPlayer({ resolveColor: (color) => {
|
|
65
104
|
const current = theme();
|
|
66
|
-
return
|
|
105
|
+
return {
|
|
106
|
+
color: resolveItemColor(color, "brand", current.colors, current.colorScheme, parseBoardCssColor),
|
|
107
|
+
alpha: itemAlpha(current, color)
|
|
108
|
+
};
|
|
67
109
|
} });
|
|
68
110
|
let cull = {
|
|
69
111
|
rect: null,
|
|
@@ -77,7 +119,7 @@ function mountBoardStage(host, options) {
|
|
|
77
119
|
if (same && cull.scene === editor.scene) return cull.ids;
|
|
78
120
|
cull = {
|
|
79
121
|
rect: same ? cull.rect : rect,
|
|
80
|
-
ids: new Set(editor.idsInRect(rect)),
|
|
122
|
+
ids: new Set(editor.idsInRect(rect, true)),
|
|
81
123
|
scene: editor.scene
|
|
82
124
|
};
|
|
83
125
|
options.onVisibleChange?.(cull.ids);
|
|
@@ -112,7 +154,7 @@ function mountBoardStage(host, options) {
|
|
|
112
154
|
settings: editor.settings,
|
|
113
155
|
scene: renderScene,
|
|
114
156
|
time,
|
|
115
|
-
...
|
|
157
|
+
...scripts ? { scripts } : {},
|
|
116
158
|
selectedIds: new Set(editor.selection),
|
|
117
159
|
hoveredId: editor.hoverId,
|
|
118
160
|
resizingIds: interaction.type === "resizing" ? new Set(interaction.origin.keys()) : /* @__PURE__ */ new Set(),
|
|
@@ -121,6 +163,9 @@ function mountBoardStage(host, options) {
|
|
|
121
163
|
colorScheme: current.colorScheme,
|
|
122
164
|
rendererType: app?.renderer.type === RendererType.CANVAS ? "canvas" : "gpu",
|
|
123
165
|
zoom: renderCamera().zoom,
|
|
166
|
+
reducedMotion: options.reducedMotion?.() ?? false,
|
|
167
|
+
labels: options.labels?.() ?? DEFAULT_BOARD_RENDER_LABELS,
|
|
168
|
+
taskRun: options.taskRun ?? (() => null),
|
|
124
169
|
assetKey: assets.assetKey,
|
|
125
170
|
getTexture: (key) => assets.getTexture(key),
|
|
126
171
|
hasError: (key) => assets.hasError(key),
|
|
@@ -175,28 +220,11 @@ function mountBoardStage(host, options) {
|
|
|
175
220
|
return strokes;
|
|
176
221
|
}
|
|
177
222
|
function followCamera(frame, renderScene) {
|
|
178
|
-
|
|
179
|
-
const focus = frame.camera.focus;
|
|
180
|
-
if (focus !== void 0 && surface.width > 0 && surface.height > 0) camera = cameraForFocus(focus, (id) => renderScene.get(id)?.frame, surface, { padding: 48 }) ?? camera;
|
|
181
|
-
if (frame.camera.zoom !== void 0) camera = {
|
|
182
|
-
...camera,
|
|
183
|
-
zoom: frame.camera.zoom
|
|
184
|
-
};
|
|
185
|
-
if (frame.camera.center) camera = cameraForState({
|
|
186
|
-
centerX: frame.camera.center.x,
|
|
187
|
-
centerY: frame.camera.center.y,
|
|
188
|
-
zoom: camera.zoom
|
|
189
|
-
}, surface);
|
|
190
|
-
if (frame.camera.shake) {
|
|
191
|
-
const t = frame.time / 1e3;
|
|
192
|
-
camera = {
|
|
193
|
-
...camera,
|
|
194
|
-
x: camera.x + Math.sin(t * 91.3) * frame.camera.shake,
|
|
195
|
-
y: camera.y + Math.cos(t * 77.9) * frame.camera.shake
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
return camera;
|
|
223
|
+
return resolveBoardAnimationCamera(frame.camera, (id) => renderScene.get(id)?.frame, surface, frame.time, editor.camera);
|
|
199
224
|
}
|
|
225
|
+
let effectSampler;
|
|
226
|
+
let effectDocument;
|
|
227
|
+
let effectAnimation = null;
|
|
200
228
|
function arrowHead(target, from, to, color, width, inv, alpha) {
|
|
201
229
|
const angle = Math.atan2(to.y - from.y, to.x - from.x);
|
|
202
230
|
const head = Math.max(14, 16 * inv);
|
|
@@ -248,6 +276,10 @@ function mountBoardStage(host, options) {
|
|
|
248
276
|
}
|
|
249
277
|
}
|
|
250
278
|
function cursorFor() {
|
|
279
|
+
if (scriptMode === "interact" && scriptHover) {
|
|
280
|
+
const output = scriptOutputs.get(scriptHover.owner);
|
|
281
|
+
return output?.hits[scriptHover.target]?.cursor ?? (output?.listens.includes("click") ? "pointer" : "default");
|
|
282
|
+
}
|
|
251
283
|
const interaction = editor.interaction;
|
|
252
284
|
switch (interaction.type) {
|
|
253
285
|
case "panning":
|
|
@@ -272,6 +304,28 @@ function mountBoardStage(host, options) {
|
|
|
272
304
|
app?.render();
|
|
273
305
|
});
|
|
274
306
|
}
|
|
307
|
+
function scheduleCardTick(wait) {
|
|
308
|
+
if (!active || disposed) return;
|
|
309
|
+
const due = performance.now() + wait;
|
|
310
|
+
if (cardTickFrame || cardTickTimer !== null && cardTickDue <= due) return;
|
|
311
|
+
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
312
|
+
cardTickTimer = null;
|
|
313
|
+
if (wait > 0) {
|
|
314
|
+
cardTickDue = due;
|
|
315
|
+
cardTickTimer = setTimeout(() => {
|
|
316
|
+
cardTickTimer = null;
|
|
317
|
+
scheduleCardTick(0);
|
|
318
|
+
}, wait);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
cardTickFrame = requestAnimationFrame(() => {
|
|
322
|
+
cardTickFrame = 0;
|
|
323
|
+
if (!scene) return;
|
|
324
|
+
const next = scene.tick(performance.now());
|
|
325
|
+
scheduleRender();
|
|
326
|
+
if (next !== null) scheduleCardTick(next);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
275
329
|
function scheduleTick() {
|
|
276
330
|
if (tickFrame || !active || disposed) return;
|
|
277
331
|
tickFrame = requestAnimationFrame(() => {
|
|
@@ -279,9 +333,89 @@ function mountBoardStage(host, options) {
|
|
|
279
333
|
sync();
|
|
280
334
|
});
|
|
281
335
|
}
|
|
336
|
+
function cancelScriptPointers(owner, retained) {
|
|
337
|
+
scriptPointers.cancelScripts((pointerId, target) => {
|
|
338
|
+
scripts?.event(target.owner, {
|
|
339
|
+
type: "pointercancel",
|
|
340
|
+
target: target.target,
|
|
341
|
+
pointerId
|
|
342
|
+
});
|
|
343
|
+
}, owner, retained);
|
|
344
|
+
if (!retained || scriptHover && scriptHover.owner === owner && !Object.hasOwn(retained, scriptHover.target)) scriptHover = null;
|
|
345
|
+
}
|
|
346
|
+
function createScripts() {
|
|
347
|
+
return createBoardScriptHost({
|
|
348
|
+
host,
|
|
349
|
+
capabilities: {
|
|
350
|
+
...options.scriptCapabilities,
|
|
351
|
+
"board.emit": async (id, value) => {
|
|
352
|
+
const message = value;
|
|
353
|
+
if (!message || typeof message.target !== "string" || typeof message.type !== "string" || !boardScriptScope(editor.document, id).has(message.target)) throw new Error("Invalid script event target.");
|
|
354
|
+
scripts?.event(message.target, {
|
|
355
|
+
type: message.type,
|
|
356
|
+
data: message.data
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
...!editor.readonly ? { "board.commit": async (id, value) => {
|
|
360
|
+
validateBoardScriptCommit(editor.document, id, value);
|
|
361
|
+
await editor.commitPatch(value);
|
|
362
|
+
} } : {}
|
|
363
|
+
},
|
|
364
|
+
onOutput: (id, output) => {
|
|
365
|
+
if (output) {
|
|
366
|
+
prepareBoardScriptOutput(editor.document, id, output);
|
|
367
|
+
cancelScriptPointers(id, output.hits);
|
|
368
|
+
scriptOutputs.set(id, output);
|
|
369
|
+
if (Object.keys(output.hits).length) interactiveScripts.add(id);
|
|
370
|
+
else interactiveScripts.delete(id);
|
|
371
|
+
if (output.space === "screen" && Object.keys(output.items).length) screenScripts.add(id);
|
|
372
|
+
else screenScripts.delete(id);
|
|
373
|
+
} else {
|
|
374
|
+
cancelScriptPointers(id);
|
|
375
|
+
scriptOutputs.delete(id);
|
|
376
|
+
interactiveScripts.delete(id);
|
|
377
|
+
screenScripts.delete(id);
|
|
378
|
+
}
|
|
379
|
+
scriptObservers.update(id, output);
|
|
380
|
+
syncTransformSubscription();
|
|
381
|
+
scriptVersion++;
|
|
382
|
+
scheduleTick();
|
|
383
|
+
},
|
|
384
|
+
readModule: async (src) => {
|
|
385
|
+
const url = await options.source.resolveFileUrl(src);
|
|
386
|
+
if (!url) throw new Error(`${src} is not available.`);
|
|
387
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(15e3) });
|
|
388
|
+
if (!response.ok) throw new Error(`${src} could not be read.`);
|
|
389
|
+
return response.text();
|
|
390
|
+
},
|
|
391
|
+
onFrame: () => {
|
|
392
|
+
scheduleTick();
|
|
393
|
+
for (const listener of scriptListeners) listener();
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
282
397
|
function sync() {
|
|
283
398
|
if (!app || !world || !scene || !overlay) return;
|
|
284
399
|
const current = theme();
|
|
400
|
+
const hasScripts = editor.hasScripts;
|
|
401
|
+
if (hasScripts && !scripts) {
|
|
402
|
+
scripts = createScripts();
|
|
403
|
+
scriptTime = 0;
|
|
404
|
+
scriptLastTick = performance.now();
|
|
405
|
+
} else if (!hasScripts && scripts) {
|
|
406
|
+
const previous = scripts;
|
|
407
|
+
scripts = null;
|
|
408
|
+
previous.destroy();
|
|
409
|
+
scriptObservers.clear();
|
|
410
|
+
syncTransformSubscription();
|
|
411
|
+
interactiveScripts.clear();
|
|
412
|
+
screenScripts.clear();
|
|
413
|
+
scriptOutputs.clear();
|
|
414
|
+
editor.setScriptItems(/* @__PURE__ */ new Map());
|
|
415
|
+
scriptHover = null;
|
|
416
|
+
composedVersion = -1;
|
|
417
|
+
composedDocument = null;
|
|
418
|
+
}
|
|
285
419
|
syncBackground(current);
|
|
286
420
|
player.enter(editor.consumeRecentlyAdded(), editor.document);
|
|
287
421
|
const playback = options.playback?.() ?? null;
|
|
@@ -290,15 +424,61 @@ function mountBoardStage(host, options) {
|
|
|
290
424
|
const moving = new Map(previews);
|
|
291
425
|
for (const [id, item] of frame.items) moving.set(id, item);
|
|
292
426
|
editor.setPlayedItems(moving);
|
|
293
|
-
const
|
|
294
|
-
|
|
427
|
+
const animationId = editor.playhead?.animationId ?? (frame.playbackTime !== null ? playback?.animationId : null) ?? null;
|
|
428
|
+
if (animationId && (effectDocument !== editor.document || effectAnimation !== animationId)) {
|
|
429
|
+
effectDocument = editor.document;
|
|
430
|
+
effectAnimation = animationId;
|
|
431
|
+
effectSampler = createBoardAnimationSampler(editor.document, animationId);
|
|
432
|
+
}
|
|
433
|
+
if (editor.playhead && effectSampler) {
|
|
434
|
+
frame.camera = effectSampler.at(editor.playhead.time).evaluation.camera;
|
|
435
|
+
frame.time = editor.playhead.time;
|
|
436
|
+
}
|
|
437
|
+
playbackCamera = editor.cameraPolicy === "follow" && Object.keys(frame.camera).length > 0 ? followCamera(frame, editor.scene) : null;
|
|
295
438
|
const camera = renderCamera();
|
|
439
|
+
if (scripts && (scriptOutputs.size || composedVersion !== scriptVersion)) {
|
|
440
|
+
const playheadKey = `${editor.playhead?.animationId ?? ""}:${editor.playhead?.time ?? ""}`;
|
|
441
|
+
if (scriptVersion !== composedVersion || composedDocument !== editor.document || screenScripts.size > 0 && editor.gestureActive || composedGesture || moving.size || composedMoving || composedZoom !== camera.zoom || composedPlayhead !== playheadKey) {
|
|
442
|
+
const animationItems = editor.animationItems();
|
|
443
|
+
const composed = composeBoardScriptItems(editor.document, editor.scene, scriptOutputs, animationItems, camera, (owner, error) => scripts?.fault(owner, error instanceof Error ? error.message : String(error)), editor.gestureActive ? editor.scene.layout : void 0);
|
|
444
|
+
const scriptedIds = new Set([...scriptOutputs.values()].flatMap((output) => Object.keys(output.patches)));
|
|
445
|
+
for (const id of animationItems.keys()) if (!scriptedIds.has(id)) composed.delete(id);
|
|
446
|
+
editor.setScriptItems(composed);
|
|
447
|
+
composedMoving = moving.size > 0;
|
|
448
|
+
composedPlayhead = playheadKey;
|
|
449
|
+
composedZoom = camera.zoom;
|
|
450
|
+
composedGesture = screenScripts.size > 0 && editor.gestureActive;
|
|
451
|
+
composedVersion = scriptVersion;
|
|
452
|
+
composedDocument = editor.document;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const renderScene = editor.scene;
|
|
456
|
+
if (scripts) {
|
|
457
|
+
const now = performance.now();
|
|
458
|
+
if (scriptMode !== "paused") scriptTime += Math.min(250, Math.max(0, now - scriptLastTick));
|
|
459
|
+
scriptLastTick = now;
|
|
460
|
+
if (scriptMode !== "paused" && scriptObservers.size) scriptObservers.sync(editor.document, editor.selection, () => ({
|
|
461
|
+
camera: { ...camera },
|
|
462
|
+
...surface,
|
|
463
|
+
safeRect: options.safeRect?.() ?? {
|
|
464
|
+
x: 0,
|
|
465
|
+
y: 0,
|
|
466
|
+
...surface
|
|
467
|
+
}
|
|
468
|
+
}));
|
|
469
|
+
scripts.sync(editor.document, renderScene, camera, scriptTime, scriptMode === "interact", visibleIds());
|
|
470
|
+
}
|
|
296
471
|
world.position.set(camera.x, camera.y);
|
|
297
472
|
world.scale.set(camera.zoom);
|
|
298
473
|
const ids = visibleIds();
|
|
299
474
|
syncAssets(ids);
|
|
300
475
|
const context = renderContext(current, renderScene, frame.time);
|
|
476
|
+
if (animationId && effectSampler) {
|
|
477
|
+
context.time = editor.playhead?.time ?? frame.time;
|
|
478
|
+
context.frameAt = effectSampler.frameAt;
|
|
479
|
+
}
|
|
301
480
|
const pinnedIds = new Set(editor.selection);
|
|
481
|
+
for (const item of renderScene.items) if (item.type === "effect" && item.props.kind === "trail" && renderScene.opacity(item.id) > 0) pinnedIds.add(item.id);
|
|
302
482
|
for (const id of previews.keys()) pinnedIds.add(id);
|
|
303
483
|
if (editor.editingId) pinnedIds.add(editor.editingId);
|
|
304
484
|
for (const id of frame.items.keys()) {
|
|
@@ -320,10 +500,12 @@ function mountBoardStage(host, options) {
|
|
|
320
500
|
current.key,
|
|
321
501
|
textZoomBucket(camera.zoom)
|
|
322
502
|
].join("|"),
|
|
503
|
+
staleIds,
|
|
323
504
|
structureVersion: editor.structureVersion,
|
|
324
505
|
geometryVersion: editor.geometryVersion,
|
|
325
506
|
gestureActive: editor.gestureActive && editor.interaction.type !== "panning"
|
|
326
507
|
});
|
|
508
|
+
staleIds.clear();
|
|
327
509
|
const single = editor.selection.length === 1 ? renderScene.get(editor.selection[0]) : null;
|
|
328
510
|
let arrowEndpoints;
|
|
329
511
|
if (single?.type === "arrow" && !single.locked) {
|
|
@@ -366,7 +548,9 @@ function mountBoardStage(host, options) {
|
|
|
366
548
|
const nextCursor = cursorFor();
|
|
367
549
|
if (nextCursor !== cursor) host.style.cursor = cursor = nextCursor;
|
|
368
550
|
scheduleRender();
|
|
369
|
-
if (frame.running || scene.animated) scheduleTick();
|
|
551
|
+
if (frame.running || scene.animated || scriptMode !== "paused" && scripts?.animated) scheduleTick();
|
|
552
|
+
const cardWait = scene.tick(performance.now());
|
|
553
|
+
if (cardWait !== null) scheduleCardTick(cardWait);
|
|
370
554
|
}
|
|
371
555
|
function resize() {
|
|
372
556
|
cancelAnimationFrame(resizeFrame);
|
|
@@ -387,10 +571,76 @@ function mountBoardStage(host, options) {
|
|
|
387
571
|
function toInput(event) {
|
|
388
572
|
return toBoardPointerEvent(event, rectOf(), renderCamera(), event.type === "pointermove" && editor.interaction.type === "drawing");
|
|
389
573
|
}
|
|
574
|
+
function scriptTarget(input) {
|
|
575
|
+
if (!scriptOutputs.size) return null;
|
|
576
|
+
const point = input.world;
|
|
577
|
+
const candidates = editor.idsInRect({
|
|
578
|
+
x: point.x - .01,
|
|
579
|
+
y: point.y - .01,
|
|
580
|
+
width: .02,
|
|
581
|
+
height: .02
|
|
582
|
+
}, true);
|
|
583
|
+
return boardScriptTarget(editor.scene, candidates, point, scriptOutputs, editor.registry.hitTest);
|
|
584
|
+
}
|
|
585
|
+
function sendScriptEvent(target, type, event, input, related) {
|
|
586
|
+
if (!scripts) return;
|
|
587
|
+
const point = applyMatrix(invertMatrix(editor.scene.layout.matrix(target.owner)), input.world);
|
|
588
|
+
scripts?.event(target.owner, {
|
|
589
|
+
type,
|
|
590
|
+
target: target.target,
|
|
591
|
+
relatedTarget: related?.target,
|
|
592
|
+
relatedOwner: related?.owner,
|
|
593
|
+
point,
|
|
594
|
+
pointerId: event.pointerId,
|
|
595
|
+
pointerType: event.pointerType,
|
|
596
|
+
buttons: event.buttons,
|
|
597
|
+
shiftKey: event.shiftKey,
|
|
598
|
+
altKey: event.altKey,
|
|
599
|
+
ctrlKey: event.ctrlKey,
|
|
600
|
+
metaKey: event.metaKey
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function scriptPointer(type, event, input) {
|
|
604
|
+
if (!scriptPointers.needsRouting(event.pointerId, interactiveScripts.size > 0)) return false;
|
|
605
|
+
const enabled = scriptMode === "interact" && !editor.gestureActive && !editor.spaceHeld && event.button !== 1;
|
|
606
|
+
const target = type === "leave" || !enabled || scriptPointers.owner(event.pointerId) === "editor" ? null : scriptTarget(input);
|
|
607
|
+
if (target?.owner !== scriptHover?.owner || target?.target !== scriptHover?.target) {
|
|
608
|
+
if (scriptHover) sendScriptEvent(scriptHover, "pointerleave", event, input, target);
|
|
609
|
+
if (target) sendScriptEvent(target, "pointerenter", event, input, scriptHover);
|
|
610
|
+
scriptHover = target;
|
|
611
|
+
}
|
|
612
|
+
const consumed = scriptPointers.owner(event.pointerId) === "script";
|
|
613
|
+
const { recipient, click } = scriptPointers.route(type, event.pointerId, target, {
|
|
614
|
+
x: event.clientX,
|
|
615
|
+
y: event.clientY
|
|
616
|
+
}, event.type !== "pointerup");
|
|
617
|
+
if (type === "down" && recipient) {
|
|
618
|
+
host.setPointerCapture(event.pointerId);
|
|
619
|
+
host.focus({ preventScroll: true });
|
|
620
|
+
}
|
|
621
|
+
if (recipient && type !== "leave") sendScriptEvent(recipient, input.cancelled ? "pointercancel" : `pointer${type}`, event, input);
|
|
622
|
+
if (click) sendScriptEvent(click, "click", event, input);
|
|
623
|
+
return consumed || recipient !== null;
|
|
624
|
+
}
|
|
625
|
+
function handleScriptKey(event) {
|
|
626
|
+
if (scriptMode !== "interact" || !scriptHover || event.target !== host || !scriptOutputs.get(scriptHover.owner)?.listens.includes(event.type)) return;
|
|
627
|
+
const input = {
|
|
628
|
+
type: event.type,
|
|
629
|
+
target: scriptHover.target,
|
|
630
|
+
key: event.key,
|
|
631
|
+
shiftKey: event.shiftKey,
|
|
632
|
+
altKey: event.altKey,
|
|
633
|
+
ctrlKey: event.ctrlKey,
|
|
634
|
+
metaKey: event.metaKey
|
|
635
|
+
};
|
|
636
|
+
scripts?.event(scriptHover.owner, input);
|
|
637
|
+
event.stopPropagation();
|
|
638
|
+
}
|
|
390
639
|
function handlePointerDown(event) {
|
|
391
640
|
if (inViews(event)) return;
|
|
392
641
|
editor.takeCameraControl();
|
|
393
642
|
const input = toInput(event);
|
|
643
|
+
if (scriptPointer("down", event, input)) return;
|
|
394
644
|
if (options.onPointer?.("down", event, input) === false) return;
|
|
395
645
|
host.setPointerCapture(event.pointerId);
|
|
396
646
|
editor.pointerDown(input);
|
|
@@ -398,18 +648,21 @@ function mountBoardStage(host, options) {
|
|
|
398
648
|
function handlePointerMove(event) {
|
|
399
649
|
if (inViews(event)) return;
|
|
400
650
|
const input = toInput(event);
|
|
651
|
+
if (scriptPointer("move", event, input)) return;
|
|
401
652
|
if (options.onPointer?.("move", event, input) === false) return;
|
|
402
653
|
editor.pointerMove(input);
|
|
403
654
|
}
|
|
404
655
|
function handlePointerUp(event) {
|
|
405
656
|
if (inViews(event)) return;
|
|
406
657
|
const input = toInput(event);
|
|
658
|
+
if (scriptPointer("up", event, input)) return;
|
|
407
659
|
if (options.onPointer?.("up", event, input) === false) return;
|
|
408
660
|
editor.pointerUp(input);
|
|
409
661
|
if (event.type === "pointercancel" || event.pointerType !== "mouse") editor.pointerLeave();
|
|
410
662
|
}
|
|
411
663
|
function handlePointerLeave(event) {
|
|
412
664
|
if (event.buttons !== 0) return;
|
|
665
|
+
scriptPointer("leave", event, toInput(event));
|
|
413
666
|
if (options.onPointer?.("leave", event, toInput(event)) === false) return;
|
|
414
667
|
editor.pointerLeave();
|
|
415
668
|
}
|
|
@@ -436,6 +689,8 @@ function mountBoardStage(host, options) {
|
|
|
436
689
|
else if (!item) editor.beginTextDraft(point);
|
|
437
690
|
}
|
|
438
691
|
const listeners = [
|
|
692
|
+
["keydown", handleScriptKey],
|
|
693
|
+
["keyup", handleScriptKey],
|
|
439
694
|
["pointerdown", handlePointerDown],
|
|
440
695
|
["pointermove", handlePointerMove],
|
|
441
696
|
["pointerup", handlePointerUp],
|
|
@@ -478,13 +733,22 @@ function mountBoardStage(host, options) {
|
|
|
478
733
|
return false;
|
|
479
734
|
}
|
|
480
735
|
app = instance;
|
|
481
|
-
|
|
736
|
+
stopMaintenance = scheduleBoardMaintenance(instance.renderer);
|
|
737
|
+
Object.assign(instance.canvas.style, {
|
|
738
|
+
position: "relative",
|
|
739
|
+
zIndex: "1",
|
|
740
|
+
display: "block",
|
|
741
|
+
touchAction: "none"
|
|
742
|
+
});
|
|
482
743
|
host.insertBefore(instance.canvas, views.element);
|
|
483
744
|
world = new Container({
|
|
484
745
|
isRenderGroup: true,
|
|
485
746
|
label: "board-world"
|
|
486
747
|
});
|
|
487
|
-
const nodes = new Container({
|
|
748
|
+
const nodes = new Container({
|
|
749
|
+
isRenderGroup: true,
|
|
750
|
+
label: "board-nodes"
|
|
751
|
+
});
|
|
488
752
|
overlay = new Graphics({ label: "board-interaction-overlay" });
|
|
489
753
|
world.addChild(nodes, overlay);
|
|
490
754
|
instance.stage.addChild(world);
|
|
@@ -494,16 +758,6 @@ function mountBoardStage(host, options) {
|
|
|
494
758
|
getRenderer: resolveRenderer,
|
|
495
759
|
onFarLayerFrame: scheduleTick
|
|
496
760
|
});
|
|
497
|
-
sketches = createBoardSketchHost({
|
|
498
|
-
readModule: async (src) => {
|
|
499
|
-
const url = await options.source.resolveFileUrl(src);
|
|
500
|
-
if (!url) throw new Error(`${src} is not available.`);
|
|
501
|
-
const response = await fetch(url);
|
|
502
|
-
if (!response.ok) throw new Error(`${src} could not be read.`);
|
|
503
|
-
return response.text();
|
|
504
|
-
},
|
|
505
|
-
onFrame: () => sync()
|
|
506
|
-
});
|
|
507
761
|
for (const [type, listener, listenerOptions] of listeners) host.addEventListener(type, listener, listenerOptions);
|
|
508
762
|
resizeObserver.observe(host);
|
|
509
763
|
resize();
|
|
@@ -518,17 +772,56 @@ function mountBoardStage(host, options) {
|
|
|
518
772
|
get theme() {
|
|
519
773
|
return theme();
|
|
520
774
|
},
|
|
775
|
+
setScriptMode(mode) {
|
|
776
|
+
if (mode === scriptMode) return;
|
|
777
|
+
if (mode !== "interact") cancelScriptPointers();
|
|
778
|
+
scriptMode = mode;
|
|
779
|
+
scriptLastTick = performance.now();
|
|
780
|
+
scheduleTick();
|
|
781
|
+
},
|
|
782
|
+
resetScripts() {
|
|
783
|
+
scriptTime = 0;
|
|
784
|
+
scripts?.invalidate();
|
|
785
|
+
},
|
|
786
|
+
invalidateScript(path) {
|
|
787
|
+
scripts?.invalidate(path);
|
|
788
|
+
},
|
|
789
|
+
scriptOutput: (id) => scripts?.output(id) ?? null,
|
|
790
|
+
async snapshotScripts() {
|
|
791
|
+
const result = {};
|
|
792
|
+
await Promise.all([...scriptOutputs.keys()].map(async (id) => {
|
|
793
|
+
const snapshot = await scripts?.snapshot(id);
|
|
794
|
+
if (snapshot) result[id] = snapshot;
|
|
795
|
+
}));
|
|
796
|
+
return result;
|
|
797
|
+
},
|
|
798
|
+
restoreScript(id, snapshot) {
|
|
799
|
+
scripts?.restore(id, snapshot);
|
|
800
|
+
},
|
|
801
|
+
subscribeScripts(listener) {
|
|
802
|
+
scriptListeners.add(listener);
|
|
803
|
+
return () => {
|
|
804
|
+
scriptListeners.delete(listener);
|
|
805
|
+
};
|
|
806
|
+
},
|
|
807
|
+
scriptError: (id) => scripts?.error(id) ?? null,
|
|
521
808
|
setActive(next) {
|
|
522
809
|
if (active === next) return;
|
|
523
810
|
active = next;
|
|
524
811
|
if (!active) {
|
|
812
|
+
cancelScriptPointers();
|
|
525
813
|
cancelAnimationFrame(renderFrame);
|
|
526
814
|
cancelAnimationFrame(tickFrame);
|
|
815
|
+
cancelAnimationFrame(cardTickFrame);
|
|
816
|
+
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
527
817
|
renderFrame = 0;
|
|
528
818
|
tickFrame = 0;
|
|
819
|
+
cardTickFrame = 0;
|
|
820
|
+
cardTickTimer = null;
|
|
529
821
|
return;
|
|
530
822
|
}
|
|
531
823
|
player.reset();
|
|
824
|
+
scriptLastTick = performance.now();
|
|
532
825
|
resize();
|
|
533
826
|
},
|
|
534
827
|
invalidate(invalidation) {
|
|
@@ -536,24 +829,34 @@ function mountBoardStage(host, options) {
|
|
|
536
829
|
cardEpoch += 1;
|
|
537
830
|
requestAssets = true;
|
|
538
831
|
}
|
|
832
|
+
for (const id of invalidation?.items ?? []) staleIds.add(id);
|
|
539
833
|
scheduleTick();
|
|
540
834
|
},
|
|
541
835
|
isMaterialized: (id) => Boolean(scene?.getNode(id)),
|
|
542
836
|
toWorld: (clientX, clientY) => screenToWorld(clientX, clientY, rectOf(), renderCamera()),
|
|
543
837
|
destroy() {
|
|
544
838
|
disposed = true;
|
|
839
|
+
scriptPointers.clear();
|
|
840
|
+
stopTransforms?.();
|
|
841
|
+
scriptObservers.clear();
|
|
842
|
+
scriptListeners.clear();
|
|
843
|
+
stopMaintenance?.();
|
|
545
844
|
stopEditor();
|
|
546
845
|
stopAssets();
|
|
547
846
|
resizeObserver.disconnect();
|
|
548
847
|
cancelAnimationFrame(resizeFrame);
|
|
549
848
|
cancelAnimationFrame(renderFrame);
|
|
550
849
|
cancelAnimationFrame(tickFrame);
|
|
850
|
+
cancelAnimationFrame(cardTickFrame);
|
|
851
|
+
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
852
|
+
cardTickTimer = null;
|
|
551
853
|
for (const [type, listener] of listeners) host.removeEventListener(type, listener);
|
|
552
854
|
editor.setPlayedItems(/* @__PURE__ */ new Map());
|
|
855
|
+
editor.setScriptItems(/* @__PURE__ */ new Map());
|
|
553
856
|
scene?.destroy(renderContext(theme(), editor.scene, 0));
|
|
554
857
|
scene = null;
|
|
555
|
-
|
|
556
|
-
|
|
858
|
+
scripts?.destroy();
|
|
859
|
+
scripts = null;
|
|
557
860
|
background?.destroy({ children: true });
|
|
558
861
|
background = null;
|
|
559
862
|
views.destroy();
|
|
@@ -34,11 +34,10 @@ async function loadUncachedTexture(url) {
|
|
|
34
34
|
*/
|
|
35
35
|
async function loadImageTexture(url) {
|
|
36
36
|
try {
|
|
37
|
-
|
|
37
|
+
return await Assets.load({
|
|
38
38
|
src: url,
|
|
39
39
|
parser: "texture"
|
|
40
|
-
}
|
|
41
|
-
return await Assets.load(asset);
|
|
40
|
+
});
|
|
42
41
|
} catch (workerError) {
|
|
43
42
|
const errors = [workerError];
|
|
44
43
|
for (const load of [loadImageElementTexture, loadUncachedTexture]) try {
|