@neta-art/cohub 10.0.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- 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 +5 -1
- package/dist/board/editor/creation.js +2 -1
- 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 +18 -3
- package/dist/board/editor/editor.js +11 -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/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 +20 -6
- 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 +6 -2
- package/dist/board/model/index.d.ts +13 -6
- package/dist/board/model/index.js +14 -8
- package/dist/board/model/items/builtin.js +2 -2
- package/dist/board/model/items/create.d.ts +4 -2
- package/dist/board/model/items/create.js +6 -5
- 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 +14 -4
- package/dist/board/model/media.js +7 -1
- 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 +10 -0
- package/dist/board/model/script-hit.js +52 -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-scene.d.ts +24 -0
- package/dist/board/model/script-scene.js +267 -0
- package/dist/board/model/script.d.ts +105 -0
- package/dist/board/model/script.js +1 -0
- 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 +9 -6
- 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 +10 -5
- package/dist/board/render/renderers/board-renderer-registry.js +3 -3
- 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} +16 -15
- 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/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 +421 -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 +213 -0
- package/dist/board/render/script-sandbox.d.ts +4 -0
- package/dist/board/render/script-sandbox.js +173 -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 +6 -1
- package/dist/board/stage/maintenance.d.ts +5 -0
- package/dist/board/stage/maintenance.js +11 -0
- package/dist/board/stage/scene.js +5 -2
- package/dist/board/stage/script-pointer.d.ts +19 -0
- package/dist/board/stage/script-pointer.js +35 -0
- package/dist/board/stage/stage.d.ts +10 -0
- package/dist/board/stage/stage.js +249 -43
- package/dist/board/stage/texture-pool.js +2 -3
- package/dist/chunks/environment.d.ts +266 -23
- package/dist/chunks/host-bridge.d.ts +2 -2
- package/dist/chunks/http.d.ts +88 -7
- package/dist/chunks/http.js +155 -63
- package/dist/chunks/transport.js +3 -1
- 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 +2 -2
- package/dist/index.d.ts +182 -4
- package/dist/index.js +1374 -126
- 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 +201 -11
- package/dist/protocol/dist/board-model.js +33 -14
- 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/display/index.d.ts +1 -0
- package/dist/protocol/dist/index.d.ts +4 -2
- package/dist/protocol/dist/realtime/types.d.ts +1 -0
- package/dist/protocol/dist/rtc/index.d.ts +1 -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/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
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyMatrix, invertMatrix } from "../../protocol/dist/board-layout.js";
|
|
2
|
+
import { screenPoint, screenToWorld, stableCullRect, visibleWorldRect, worldPoint } from "../model/geometry.js";
|
|
3
|
+
import { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, validateBoardScriptCommit } from "../model/script-scene.js";
|
|
4
|
+
import { boardScriptTarget } from "../model/script-hit.js";
|
|
5
|
+
import { createBoardAnimationSampler, resolveBoardAnimationCamera } from "../model/sampling.js";
|
|
2
6
|
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
3
7
|
import { buildFallbackShapeColors, pickBoardColor, resolveItemColor } from "../model/palette.js";
|
|
4
8
|
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 { defaultBoardPalette } from "../render/palette.js";
|
|
11
15
|
import { getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
12
|
-
import {
|
|
16
|
+
import { createBoardScriptHost } from "../render/script-host.js";
|
|
13
17
|
import { createBoardPlayer } from "../player/index.js";
|
|
14
18
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
15
19
|
import { createBoardScene } from "./scene.js";
|
|
20
|
+
import { scheduleBoardMaintenance } from "./maintenance.js";
|
|
21
|
+
import { createBoardScriptPointerRouter } from "./script-pointer.js";
|
|
16
22
|
import { createBoardItemViewLayer } from "./views.js";
|
|
17
23
|
import { Application, Container, Graphics, RendererType } from "pixi.js";
|
|
18
24
|
//#region src/board/stage/stage.ts
|
|
@@ -45,7 +51,21 @@ function mountBoardStage(host, options) {
|
|
|
45
51
|
let overlay = null;
|
|
46
52
|
let background = null;
|
|
47
53
|
let scene = null;
|
|
48
|
-
let
|
|
54
|
+
let scripts = null;
|
|
55
|
+
let scriptMode = editor.readonly ? "interact" : "edit";
|
|
56
|
+
let scriptTime = 0;
|
|
57
|
+
let scriptLastTick = performance.now();
|
|
58
|
+
const scriptOutputs = /* @__PURE__ */ new Map();
|
|
59
|
+
const scriptListeners = /* @__PURE__ */ new Set();
|
|
60
|
+
let scriptVersion = 0;
|
|
61
|
+
let composedVersion = -1;
|
|
62
|
+
let composedZoom = 0;
|
|
63
|
+
let composedPlayhead = "";
|
|
64
|
+
let composedMoving = false;
|
|
65
|
+
let composedDocument = null;
|
|
66
|
+
let scriptHover = null;
|
|
67
|
+
const scriptPointers = createBoardScriptPointerRouter();
|
|
68
|
+
let stopMaintenance = null;
|
|
49
69
|
let active = true;
|
|
50
70
|
let disposed = false;
|
|
51
71
|
let renderFrame = 0;
|
|
@@ -63,7 +83,10 @@ function mountBoardStage(host, options) {
|
|
|
63
83
|
const views = createBoardItemViewLayer(host, editor, options.views ?? [], renderCamera);
|
|
64
84
|
const player = createBoardPlayer({ resolveColor: (color) => {
|
|
65
85
|
const current = theme();
|
|
66
|
-
return
|
|
86
|
+
return {
|
|
87
|
+
color: resolveItemColor(color, "brand", current.colors, current.colorScheme, parseBoardCssColor),
|
|
88
|
+
alpha: itemAlpha(current, color)
|
|
89
|
+
};
|
|
67
90
|
} });
|
|
68
91
|
let cull = {
|
|
69
92
|
rect: null,
|
|
@@ -77,7 +100,7 @@ function mountBoardStage(host, options) {
|
|
|
77
100
|
if (same && cull.scene === editor.scene) return cull.ids;
|
|
78
101
|
cull = {
|
|
79
102
|
rect: same ? cull.rect : rect,
|
|
80
|
-
ids: new Set(editor.idsInRect(rect)),
|
|
103
|
+
ids: new Set(editor.idsInRect(rect, true)),
|
|
81
104
|
scene: editor.scene
|
|
82
105
|
};
|
|
83
106
|
options.onVisibleChange?.(cull.ids);
|
|
@@ -112,7 +135,7 @@ function mountBoardStage(host, options) {
|
|
|
112
135
|
settings: editor.settings,
|
|
113
136
|
scene: renderScene,
|
|
114
137
|
time,
|
|
115
|
-
...
|
|
138
|
+
...scripts ? { scripts } : {},
|
|
116
139
|
selectedIds: new Set(editor.selection),
|
|
117
140
|
hoveredId: editor.hoverId,
|
|
118
141
|
resizingIds: interaction.type === "resizing" ? new Set(interaction.origin.keys()) : /* @__PURE__ */ new Set(),
|
|
@@ -175,28 +198,11 @@ function mountBoardStage(host, options) {
|
|
|
175
198
|
return strokes;
|
|
176
199
|
}
|
|
177
200
|
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;
|
|
201
|
+
return resolveBoardAnimationCamera(frame.camera, (id) => renderScene.get(id)?.frame, surface, frame.time, editor.camera);
|
|
199
202
|
}
|
|
203
|
+
let effectSampler;
|
|
204
|
+
let effectDocument;
|
|
205
|
+
let effectAnimation = null;
|
|
200
206
|
function arrowHead(target, from, to, color, width, inv, alpha) {
|
|
201
207
|
const angle = Math.atan2(to.y - from.y, to.x - from.x);
|
|
202
208
|
const head = Math.max(14, 16 * inv);
|
|
@@ -248,6 +254,10 @@ function mountBoardStage(host, options) {
|
|
|
248
254
|
}
|
|
249
255
|
}
|
|
250
256
|
function cursorFor() {
|
|
257
|
+
if (scriptMode === "interact" && scriptHover) {
|
|
258
|
+
const output = scriptOutputs.get(scriptHover.owner);
|
|
259
|
+
return output?.hits[scriptHover.target]?.cursor ?? (output?.listens.includes("click") ? "pointer" : "default");
|
|
260
|
+
}
|
|
251
261
|
const interaction = editor.interaction;
|
|
252
262
|
switch (interaction.type) {
|
|
253
263
|
case "panning":
|
|
@@ -279,9 +289,63 @@ function mountBoardStage(host, options) {
|
|
|
279
289
|
sync();
|
|
280
290
|
});
|
|
281
291
|
}
|
|
292
|
+
function createScripts() {
|
|
293
|
+
return createBoardScriptHost({
|
|
294
|
+
host,
|
|
295
|
+
capabilities: {
|
|
296
|
+
...options.scriptCapabilities,
|
|
297
|
+
"board.emit": async (id, value) => {
|
|
298
|
+
const message = value;
|
|
299
|
+
if (!message || typeof message.target !== "string" || typeof message.type !== "string" || !boardScriptScope(editor.document, id).has(message.target)) throw new Error("Invalid script event target.");
|
|
300
|
+
scripts?.event(message.target, {
|
|
301
|
+
type: message.type,
|
|
302
|
+
data: message.data
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
...!editor.readonly ? { "board.commit": async (id, value) => {
|
|
306
|
+
validateBoardScriptCommit(editor.document, id, value);
|
|
307
|
+
await editor.commitPatch(value);
|
|
308
|
+
} } : {}
|
|
309
|
+
},
|
|
310
|
+
onOutput: (id, output) => {
|
|
311
|
+
if (output) {
|
|
312
|
+
prepareBoardScriptOutput(editor.document, id, output);
|
|
313
|
+
scriptOutputs.set(id, output);
|
|
314
|
+
} else scriptOutputs.delete(id);
|
|
315
|
+
scriptVersion++;
|
|
316
|
+
scheduleTick();
|
|
317
|
+
},
|
|
318
|
+
readModule: async (src) => {
|
|
319
|
+
const url = await options.source.resolveFileUrl(src);
|
|
320
|
+
if (!url) throw new Error(`${src} is not available.`);
|
|
321
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(15e3) });
|
|
322
|
+
if (!response.ok) throw new Error(`${src} could not be read.`);
|
|
323
|
+
return response.text();
|
|
324
|
+
},
|
|
325
|
+
onFrame: () => {
|
|
326
|
+
scheduleTick();
|
|
327
|
+
for (const listener of scriptListeners) listener();
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
282
331
|
function sync() {
|
|
283
332
|
if (!app || !world || !scene || !overlay) return;
|
|
284
333
|
const current = theme();
|
|
334
|
+
const hasScripts = editor.hasScripts;
|
|
335
|
+
if (hasScripts && !scripts) {
|
|
336
|
+
scripts = createScripts();
|
|
337
|
+
scriptTime = 0;
|
|
338
|
+
scriptLastTick = performance.now();
|
|
339
|
+
} else if (!hasScripts && scripts) {
|
|
340
|
+
const previous = scripts;
|
|
341
|
+
scripts = null;
|
|
342
|
+
previous.destroy();
|
|
343
|
+
scriptOutputs.clear();
|
|
344
|
+
editor.setScriptItems(/* @__PURE__ */ new Map());
|
|
345
|
+
scriptHover = null;
|
|
346
|
+
composedVersion = -1;
|
|
347
|
+
composedDocument = null;
|
|
348
|
+
}
|
|
285
349
|
syncBackground(current);
|
|
286
350
|
player.enter(editor.consumeRecentlyAdded(), editor.document);
|
|
287
351
|
const playback = options.playback?.() ?? null;
|
|
@@ -290,15 +354,51 @@ function mountBoardStage(host, options) {
|
|
|
290
354
|
const moving = new Map(previews);
|
|
291
355
|
for (const [id, item] of frame.items) moving.set(id, item);
|
|
292
356
|
editor.setPlayedItems(moving);
|
|
357
|
+
if (scripts && (scriptOutputs.size || composedVersion !== scriptVersion)) {
|
|
358
|
+
const playheadKey = `${editor.playhead?.animationId ?? ""}:${editor.playhead?.time ?? ""}`;
|
|
359
|
+
if (scriptVersion !== composedVersion || composedDocument !== editor.document || moving.size || composedMoving || composedZoom !== editor.camera.zoom || composedPlayhead !== playheadKey) {
|
|
360
|
+
const animationItems = editor.animationItems();
|
|
361
|
+
const composed = composeBoardScriptItems(editor.document, editor.scene, scriptOutputs, animationItems, editor.camera, (owner, error) => scripts?.fault(owner, error instanceof Error ? error.message : String(error)));
|
|
362
|
+
const scriptedIds = new Set([...scriptOutputs.values()].flatMap((output) => Object.keys(output.patches)));
|
|
363
|
+
for (const id of animationItems.keys()) if (!scriptedIds.has(id)) composed.delete(id);
|
|
364
|
+
editor.setScriptItems(composed);
|
|
365
|
+
composedMoving = moving.size > 0;
|
|
366
|
+
composedPlayhead = playheadKey;
|
|
367
|
+
composedZoom = editor.camera.zoom;
|
|
368
|
+
composedVersion = scriptVersion;
|
|
369
|
+
composedDocument = editor.document;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
293
372
|
const renderScene = editor.scene;
|
|
373
|
+
const animationId = editor.playhead?.animationId ?? (frame.playbackTime !== null ? playback?.animationId : null) ?? null;
|
|
374
|
+
if (animationId && (effectDocument !== editor.document || effectAnimation !== animationId)) {
|
|
375
|
+
effectDocument = editor.document;
|
|
376
|
+
effectAnimation = animationId;
|
|
377
|
+
effectSampler = createBoardAnimationSampler(editor.document, animationId);
|
|
378
|
+
}
|
|
379
|
+
if (editor.playhead && effectSampler) {
|
|
380
|
+
frame.camera = effectSampler.at(editor.playhead.time).evaluation.camera;
|
|
381
|
+
frame.time = editor.playhead.time;
|
|
382
|
+
}
|
|
294
383
|
playbackCamera = editor.cameraPolicy === "follow" && Object.keys(frame.camera).length > 0 ? followCamera(frame, renderScene) : null;
|
|
295
384
|
const camera = renderCamera();
|
|
385
|
+
if (scripts) {
|
|
386
|
+
const now = performance.now();
|
|
387
|
+
if (scriptMode !== "paused") scriptTime += Math.min(250, Math.max(0, now - scriptLastTick));
|
|
388
|
+
scriptLastTick = now;
|
|
389
|
+
scripts.sync(editor.document, renderScene, camera, scriptTime, scriptMode === "interact", visibleIds());
|
|
390
|
+
}
|
|
296
391
|
world.position.set(camera.x, camera.y);
|
|
297
392
|
world.scale.set(camera.zoom);
|
|
298
393
|
const ids = visibleIds();
|
|
299
394
|
syncAssets(ids);
|
|
300
395
|
const context = renderContext(current, renderScene, frame.time);
|
|
396
|
+
if (animationId && effectSampler) {
|
|
397
|
+
context.time = editor.playhead?.time ?? frame.time;
|
|
398
|
+
context.frameAt = effectSampler.frameAt;
|
|
399
|
+
}
|
|
301
400
|
const pinnedIds = new Set(editor.selection);
|
|
401
|
+
for (const item of renderScene.items) if (item.type === "effect" && item.props.kind === "trail" && renderScene.opacity(item.id) > 0) pinnedIds.add(item.id);
|
|
302
402
|
for (const id of previews.keys()) pinnedIds.add(id);
|
|
303
403
|
if (editor.editingId) pinnedIds.add(editor.editingId);
|
|
304
404
|
for (const id of frame.items.keys()) {
|
|
@@ -366,7 +466,7 @@ function mountBoardStage(host, options) {
|
|
|
366
466
|
const nextCursor = cursorFor();
|
|
367
467
|
if (nextCursor !== cursor) host.style.cursor = cursor = nextCursor;
|
|
368
468
|
scheduleRender();
|
|
369
|
-
if (frame.running || scene.animated) scheduleTick();
|
|
469
|
+
if (frame.running || scene.animated || scriptMode !== "paused" && scripts?.animated) scheduleTick();
|
|
370
470
|
}
|
|
371
471
|
function resize() {
|
|
372
472
|
cancelAnimationFrame(resizeFrame);
|
|
@@ -387,10 +487,75 @@ function mountBoardStage(host, options) {
|
|
|
387
487
|
function toInput(event) {
|
|
388
488
|
return toBoardPointerEvent(event, rectOf(), renderCamera(), event.type === "pointermove" && editor.interaction.type === "drawing");
|
|
389
489
|
}
|
|
490
|
+
function scriptTarget(input) {
|
|
491
|
+
if (!scriptOutputs.size) return null;
|
|
492
|
+
const point = input.world;
|
|
493
|
+
const candidates = editor.idsInRect({
|
|
494
|
+
x: point.x - .01,
|
|
495
|
+
y: point.y - .01,
|
|
496
|
+
width: .02,
|
|
497
|
+
height: .02
|
|
498
|
+
}, true);
|
|
499
|
+
return boardScriptTarget(editor.scene, candidates, point, scriptOutputs, editor.registry.hitTest);
|
|
500
|
+
}
|
|
501
|
+
function sendScriptEvent(target, type, event, input, related) {
|
|
502
|
+
if (!scripts) return;
|
|
503
|
+
const point = applyMatrix(invertMatrix(editor.scene.layout.matrix(target.owner)), input.world);
|
|
504
|
+
scripts?.event(target.owner, {
|
|
505
|
+
type,
|
|
506
|
+
target: target.target,
|
|
507
|
+
relatedTarget: related?.target,
|
|
508
|
+
relatedOwner: related?.owner,
|
|
509
|
+
point,
|
|
510
|
+
pointerId: event.pointerId,
|
|
511
|
+
pointerType: event.pointerType,
|
|
512
|
+
buttons: event.buttons,
|
|
513
|
+
shiftKey: event.shiftKey,
|
|
514
|
+
altKey: event.altKey,
|
|
515
|
+
ctrlKey: event.ctrlKey,
|
|
516
|
+
metaKey: event.metaKey
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function scriptPointer(type, event, input) {
|
|
520
|
+
if (!scriptPointers.needsRouting(event.pointerId, scripts !== null)) return false;
|
|
521
|
+
const enabled = scriptMode === "interact" && !editor.gestureActive && !editor.spaceHeld && event.button !== 1;
|
|
522
|
+
const target = type === "leave" || !enabled || scriptPointers.owner(event.pointerId) === "editor" ? null : scriptTarget(input);
|
|
523
|
+
if (target?.owner !== scriptHover?.owner || target?.target !== scriptHover?.target) {
|
|
524
|
+
if (scriptHover) sendScriptEvent(scriptHover, "pointerleave", event, input, target);
|
|
525
|
+
if (target) sendScriptEvent(target, "pointerenter", event, input, scriptHover);
|
|
526
|
+
scriptHover = target;
|
|
527
|
+
}
|
|
528
|
+
const { recipient, click } = scriptPointers.route(type, event.pointerId, target, {
|
|
529
|
+
x: event.clientX,
|
|
530
|
+
y: event.clientY
|
|
531
|
+
}, event.type !== "pointerup");
|
|
532
|
+
if (type === "down" && recipient) {
|
|
533
|
+
host.setPointerCapture(event.pointerId);
|
|
534
|
+
host.focus({ preventScroll: true });
|
|
535
|
+
}
|
|
536
|
+
if (recipient && type !== "leave") sendScriptEvent(recipient, input.cancelled ? "pointercancel" : `pointer${type}`, event, input);
|
|
537
|
+
if (click) sendScriptEvent(click, "click", event, input);
|
|
538
|
+
return recipient !== null;
|
|
539
|
+
}
|
|
540
|
+
function handleScriptKey(event) {
|
|
541
|
+
if (scriptMode !== "interact" || !scriptHover || event.target !== host) return;
|
|
542
|
+
const input = {
|
|
543
|
+
type: event.type,
|
|
544
|
+
target: scriptHover.target,
|
|
545
|
+
key: event.key,
|
|
546
|
+
shiftKey: event.shiftKey,
|
|
547
|
+
altKey: event.altKey,
|
|
548
|
+
ctrlKey: event.ctrlKey,
|
|
549
|
+
metaKey: event.metaKey
|
|
550
|
+
};
|
|
551
|
+
scripts?.event(scriptHover.owner, input);
|
|
552
|
+
event.stopPropagation();
|
|
553
|
+
}
|
|
390
554
|
function handlePointerDown(event) {
|
|
391
555
|
if (inViews(event)) return;
|
|
392
556
|
editor.takeCameraControl();
|
|
393
557
|
const input = toInput(event);
|
|
558
|
+
if (scriptPointer("down", event, input)) return;
|
|
394
559
|
if (options.onPointer?.("down", event, input) === false) return;
|
|
395
560
|
host.setPointerCapture(event.pointerId);
|
|
396
561
|
editor.pointerDown(input);
|
|
@@ -398,18 +563,21 @@ function mountBoardStage(host, options) {
|
|
|
398
563
|
function handlePointerMove(event) {
|
|
399
564
|
if (inViews(event)) return;
|
|
400
565
|
const input = toInput(event);
|
|
566
|
+
if (scriptPointer("move", event, input)) return;
|
|
401
567
|
if (options.onPointer?.("move", event, input) === false) return;
|
|
402
568
|
editor.pointerMove(input);
|
|
403
569
|
}
|
|
404
570
|
function handlePointerUp(event) {
|
|
405
571
|
if (inViews(event)) return;
|
|
406
572
|
const input = toInput(event);
|
|
573
|
+
if (scriptPointer("up", event, input)) return;
|
|
407
574
|
if (options.onPointer?.("up", event, input) === false) return;
|
|
408
575
|
editor.pointerUp(input);
|
|
409
576
|
if (event.type === "pointercancel" || event.pointerType !== "mouse") editor.pointerLeave();
|
|
410
577
|
}
|
|
411
578
|
function handlePointerLeave(event) {
|
|
412
579
|
if (event.buttons !== 0) return;
|
|
580
|
+
scriptPointer("leave", event, toInput(event));
|
|
413
581
|
if (options.onPointer?.("leave", event, toInput(event)) === false) return;
|
|
414
582
|
editor.pointerLeave();
|
|
415
583
|
}
|
|
@@ -436,6 +604,8 @@ function mountBoardStage(host, options) {
|
|
|
436
604
|
else if (!item) editor.beginTextDraft(point);
|
|
437
605
|
}
|
|
438
606
|
const listeners = [
|
|
607
|
+
["keydown", handleScriptKey],
|
|
608
|
+
["keyup", handleScriptKey],
|
|
439
609
|
["pointerdown", handlePointerDown],
|
|
440
610
|
["pointermove", handlePointerMove],
|
|
441
611
|
["pointerup", handlePointerUp],
|
|
@@ -478,13 +648,22 @@ function mountBoardStage(host, options) {
|
|
|
478
648
|
return false;
|
|
479
649
|
}
|
|
480
650
|
app = instance;
|
|
481
|
-
|
|
651
|
+
stopMaintenance = scheduleBoardMaintenance(instance.renderer);
|
|
652
|
+
Object.assign(instance.canvas.style, {
|
|
653
|
+
position: "relative",
|
|
654
|
+
zIndex: "1",
|
|
655
|
+
display: "block",
|
|
656
|
+
touchAction: "none"
|
|
657
|
+
});
|
|
482
658
|
host.insertBefore(instance.canvas, views.element);
|
|
483
659
|
world = new Container({
|
|
484
660
|
isRenderGroup: true,
|
|
485
661
|
label: "board-world"
|
|
486
662
|
});
|
|
487
|
-
const nodes = new Container({
|
|
663
|
+
const nodes = new Container({
|
|
664
|
+
isRenderGroup: true,
|
|
665
|
+
label: "board-nodes"
|
|
666
|
+
});
|
|
488
667
|
overlay = new Graphics({ label: "board-interaction-overlay" });
|
|
489
668
|
world.addChild(nodes, overlay);
|
|
490
669
|
instance.stage.addChild(world);
|
|
@@ -494,16 +673,6 @@ function mountBoardStage(host, options) {
|
|
|
494
673
|
getRenderer: resolveRenderer,
|
|
495
674
|
onFarLayerFrame: scheduleTick
|
|
496
675
|
});
|
|
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
676
|
for (const [type, listener, listenerOptions] of listeners) host.addEventListener(type, listener, listenerOptions);
|
|
508
677
|
resizeObserver.observe(host);
|
|
509
678
|
resize();
|
|
@@ -518,6 +687,38 @@ function mountBoardStage(host, options) {
|
|
|
518
687
|
get theme() {
|
|
519
688
|
return theme();
|
|
520
689
|
},
|
|
690
|
+
setScriptMode(mode) {
|
|
691
|
+
if (mode === scriptMode) return;
|
|
692
|
+
scriptMode = mode;
|
|
693
|
+
scriptLastTick = performance.now();
|
|
694
|
+
scheduleTick();
|
|
695
|
+
},
|
|
696
|
+
resetScripts() {
|
|
697
|
+
scriptTime = 0;
|
|
698
|
+
scripts?.invalidate();
|
|
699
|
+
},
|
|
700
|
+
invalidateScript(path) {
|
|
701
|
+
scripts?.invalidate(path);
|
|
702
|
+
},
|
|
703
|
+
scriptOutput: (id) => scripts?.output(id) ?? null,
|
|
704
|
+
async snapshotScripts() {
|
|
705
|
+
const result = {};
|
|
706
|
+
await Promise.all([...scriptOutputs.keys()].map(async (id) => {
|
|
707
|
+
const snapshot = await scripts?.snapshot(id);
|
|
708
|
+
if (snapshot) result[id] = snapshot;
|
|
709
|
+
}));
|
|
710
|
+
return result;
|
|
711
|
+
},
|
|
712
|
+
restoreScript(id, snapshot) {
|
|
713
|
+
scripts?.restore(id, snapshot);
|
|
714
|
+
},
|
|
715
|
+
subscribeScripts(listener) {
|
|
716
|
+
scriptListeners.add(listener);
|
|
717
|
+
return () => {
|
|
718
|
+
scriptListeners.delete(listener);
|
|
719
|
+
};
|
|
720
|
+
},
|
|
721
|
+
scriptError: (id) => scripts?.error(id) ?? null,
|
|
521
722
|
setActive(next) {
|
|
522
723
|
if (active === next) return;
|
|
523
724
|
active = next;
|
|
@@ -529,6 +730,7 @@ function mountBoardStage(host, options) {
|
|
|
529
730
|
return;
|
|
530
731
|
}
|
|
531
732
|
player.reset();
|
|
733
|
+
scriptLastTick = performance.now();
|
|
532
734
|
resize();
|
|
533
735
|
},
|
|
534
736
|
invalidate(invalidation) {
|
|
@@ -542,6 +744,9 @@ function mountBoardStage(host, options) {
|
|
|
542
744
|
toWorld: (clientX, clientY) => screenToWorld(clientX, clientY, rectOf(), renderCamera()),
|
|
543
745
|
destroy() {
|
|
544
746
|
disposed = true;
|
|
747
|
+
scriptPointers.clear();
|
|
748
|
+
scriptListeners.clear();
|
|
749
|
+
stopMaintenance?.();
|
|
545
750
|
stopEditor();
|
|
546
751
|
stopAssets();
|
|
547
752
|
resizeObserver.disconnect();
|
|
@@ -550,10 +755,11 @@ function mountBoardStage(host, options) {
|
|
|
550
755
|
cancelAnimationFrame(tickFrame);
|
|
551
756
|
for (const [type, listener] of listeners) host.removeEventListener(type, listener);
|
|
552
757
|
editor.setPlayedItems(/* @__PURE__ */ new Map());
|
|
758
|
+
editor.setScriptItems(/* @__PURE__ */ new Map());
|
|
553
759
|
scene?.destroy(renderContext(theme(), editor.scene, 0));
|
|
554
760
|
scene = null;
|
|
555
|
-
|
|
556
|
-
|
|
761
|
+
scripts?.destroy();
|
|
762
|
+
scripts = null;
|
|
557
763
|
background?.destroy({ children: true });
|
|
558
764
|
background = null;
|
|
559
765
|
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 {
|