@neta-art/cohub 8.24.0 → 9.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/dist/board/animation.d.ts +57 -181
- package/dist/board/animation.js +374 -220
- package/dist/board/core/arrow-geometry.d.ts +33 -18
- package/dist/board/core/arrow-geometry.js +283 -52
- package/dist/board/core/draw-geometry.d.ts +22 -36
- package/dist/board/core/draw-geometry.js +148 -85
- package/dist/board/core/export-assets.d.ts +3 -6
- package/dist/board/core/export-assets.js +0 -2
- package/dist/board/core/export-plan.d.ts +12 -63
- package/dist/board/core/export-plan.js +55 -94
- package/dist/board/core/palette.d.ts +4 -21
- package/dist/board/core/palette.js +8 -13
- package/dist/board/core/scene.d.ts +33 -0
- package/dist/board/core/scene.js +302 -0
- package/dist/board/core/shape-definition.d.ts +12 -24
- package/dist/board/core/shape-definition.js +0 -19
- package/dist/board/core/shape-types.d.ts +6 -120
- package/dist/board/core/shape-types.js +5 -5
- package/dist/board/core/text-metrics.d.ts +8 -11
- package/dist/board/core/text-metrics.js +5 -34
- package/dist/board/core/tool-styles.d.ts +6 -16
- package/dist/board/core/tool-styles.js +17 -25
- package/dist/board/export/index.d.ts +10 -17
- package/dist/board/export/index.js +22 -21
- package/dist/board/export/scene.d.ts +10 -16
- package/dist/board/export/scene.js +17 -30
- package/dist/board/geometry.d.ts +27 -56
- package/dist/board/geometry.js +7 -67
- package/dist/board/headless/index.d.ts +6 -27
- package/dist/board/headless/index.js +4 -19
- package/dist/board/headless/sketch.d.ts +19 -0
- package/dist/board/headless/sketch.js +146 -0
- package/dist/board/image-key.d.ts +2 -12
- package/dist/board/image-key.js +3 -13
- package/dist/board/index.d.ts +18 -20
- package/dist/board/index.js +17 -17
- package/dist/board/media-playback.d.ts +3 -6
- package/dist/board/media-playback.js +6 -6
- package/dist/board/presets.d.ts +150 -0
- package/dist/board/presets.js +226 -0
- package/dist/board/render/board-background.d.ts +5 -3
- package/dist/board/render/board-background.js +8 -7
- package/dist/board/render/clip.d.ts +14 -0
- package/dist/board/render/clip.js +47 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +4 -5
- package/dist/board/render/media-interaction.js +1 -2
- package/dist/board/render/palette.d.ts +6 -2
- package/dist/board/render/palette.js +7 -1
- package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
- package/dist/board/render/renderers/audio-card-renderer.js +3 -3
- package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
- package/dist/board/render/renderers/base-card-renderer.js +1 -24
- package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
- package/dist/board/render/renderers/board-renderer-registry.js +6 -7
- package/dist/board/render/renderers/draw-card-renderer.js +89 -56
- package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
- package/dist/board/render/renderers/effect-card-renderer.js +197 -0
- package/dist/board/render/renderers/far-plate.d.ts +4 -21
- package/dist/board/render/renderers/far-plate.js +15 -34
- package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
- package/dist/board/render/renderers/file-card-renderer.js +8 -58
- package/dist/board/render/renderers/frame-card-renderer.js +28 -28
- package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
- package/dist/board/render/renderers/image-card-renderer.js +0 -9
- package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
- package/dist/board/render/renderers/shape-card-renderer.js +240 -0
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
- package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
- package/dist/board/render/renderers/task-card-renderer.js +14 -16
- package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
- package/dist/board/render/renderers/text-card-renderer.js +56 -46
- package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
- package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sketch-host.d.ts +14 -0
- package/dist/board/render/sketch-host.js +216 -0
- package/dist/board/render/stroke.d.ts +12 -0
- package/dist/board/render/stroke.js +90 -0
- package/dist/board/render/text-measurement.d.ts +6 -26
- package/dist/board/render/text-measurement.js +11 -30
- package/dist/board/replay.d.ts +6 -31
- package/dist/board/replay.js +45 -203
- package/dist/board/runtime/board-player.d.ts +24 -0
- package/dist/board/runtime/board-player.js +161 -0
- package/dist/board/task.d.ts +2 -10
- package/dist/board/task.js +1 -10
- package/dist/chunks/environment.d.ts +2291 -3864
- package/dist/chunks/host-bridge.d.ts +253 -0
- package/dist/chunks/http.d.ts +233 -58
- package/dist/chunks/http.js +1046 -1028
- package/dist/chunks/transport.js +113 -59
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/host-bridge.d.ts +54 -0
- package/dist/host-bridge.js +322 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +74 -191
- package/dist/index.js +2032 -1186
- package/dist/protocol/dist/board-constants.d.ts +1 -29
- package/dist/protocol/dist/board-constants.js +2 -115
- package/dist/protocol/dist/board-layout.d.ts +59 -0
- package/dist/protocol/dist/board-layout.js +482 -0
- package/dist/protocol/dist/board-model.d.ts +2052 -0
- package/dist/protocol/dist/board-model.js +592 -0
- package/dist/protocol/dist/board-patch.d.ts +86 -0
- package/dist/protocol/dist/board-patch.js +734 -0
- package/dist/protocol/dist/board-schema.d.ts +33 -0
- package/dist/protocol/dist/board-schema.js +59 -0
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-url.d.ts +1 -3
- package/dist/protocol/dist/board-url.js +2 -2
- package/dist/protocol/dist/board.d.ts +37 -232
- package/dist/protocol/dist/board.js +62 -160
- package/dist/protocol/dist/display/actions.d.ts +1 -0
- package/dist/protocol/dist/display/index.d.ts +2 -0
- package/dist/protocol/dist/index.d.ts +11 -12
- package/dist/protocol/dist/model/metrics.d.ts +2 -0
- package/dist/protocol/dist/model/session.d.ts +2 -1
- package/dist/protocol/dist/realtime/types.d.ts +2 -2
- package/dist/protocol/dist/runtime/index.d.ts +1 -0
- package/dist/space-picker.d.ts +1 -0
- package/dist/space-picker.js +1 -0
- package/dist/types.d.ts +2 -5
- package/package.json +9 -1
- package/dist/board/core/connections.d.ts +0 -108
- package/dist/board/core/connections.js +0 -455
- package/dist/board/mutation.d.ts +0 -6
- package/dist/board/mutation.js +0 -14
- package/dist/board/render/connection-layer.d.ts +0 -53
- package/dist/board/render/connection-layer.js +0 -375
- package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
- package/dist/board/render/renderers/geo-card-renderer.js +0 -127
- package/dist/board/semantic-document.d.ts +0 -37
- package/dist/board/semantic-document.js +0 -288
- package/dist/board/semantic-mutation.d.ts +0 -10
- package/dist/board/semantic-mutation.js +0 -209
- package/dist/protocol/dist/board-animation.js +0 -34
- package/dist/protocol/dist/board-authoring.d.ts +0 -1326
- package/dist/protocol/dist/board-authoring.js +0 -302
- package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
- package/dist/protocol/dist/board-capability-registry.js +0 -123
- package/dist/protocol/dist/board-codec.d.ts +0 -3
- package/dist/protocol/dist/board-codec.js +0 -383
- package/dist/protocol/dist/board-composition.d.ts +0 -383
- package/dist/protocol/dist/board-composition.js +0 -310
- package/dist/protocol/dist/board-connection.d.ts +0 -317
- package/dist/protocol/dist/board-connection.js +0 -219
- package/dist/protocol/dist/board-document.d.ts +0 -1566
- package/dist/protocol/dist/board-document.js +0 -456
- package/dist/protocol/dist/board-effect.d.ts +0 -96
- package/dist/protocol/dist/board-effect.js +0 -54
- package/dist/protocol/dist/board-geometry.d.ts +0 -15
- package/dist/protocol/dist/board-geometry.js +0 -137
- package/dist/protocol/dist/board-node.d.ts +0 -8
- package/dist/protocol/dist/board-node.js +0 -252
- /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
|
+
import { getBoardResolution } from "../text-resolution.js";
|
|
3
|
+
import { positionShell } from "./base-card-renderer.js";
|
|
4
|
+
import { drawFarPlate } from "./far-plate.js";
|
|
5
|
+
import { Container, Graphics, Sprite, Text, Texture } from "pixi.js";
|
|
6
|
+
//#region src/board/render/renderers/sketch-card-renderer.ts
|
|
7
|
+
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
function sync(container, item, context) {
|
|
9
|
+
const parts = partsByContainer.get(container);
|
|
10
|
+
if (!parts) return;
|
|
11
|
+
if (parts.id !== item.id) {
|
|
12
|
+
context.sketches?.release(parts.id);
|
|
13
|
+
parts.id = item.id;
|
|
14
|
+
}
|
|
15
|
+
positionShell(parts.root, item);
|
|
16
|
+
const { width, height } = item.frame;
|
|
17
|
+
const resolution = Math.min(4, Math.max(.25, context.zoom * getBoardResolution()));
|
|
18
|
+
const texture = context.sketches?.frame(item, context.time, resolution) ?? null;
|
|
19
|
+
const error = context.sketches?.error(item.id) ?? null;
|
|
20
|
+
parts.sprite.visible = Boolean(texture);
|
|
21
|
+
if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
|
|
22
|
+
parts.sprite.width = width;
|
|
23
|
+
parts.sprite.height = height;
|
|
24
|
+
const selected = context.selectedIds.has(item.id);
|
|
25
|
+
const message = texture ? "" : error ? `${item.props.src}\n${error}` : item.props.src;
|
|
26
|
+
const sig = [
|
|
27
|
+
width,
|
|
28
|
+
height,
|
|
29
|
+
selected,
|
|
30
|
+
Boolean(texture),
|
|
31
|
+
message,
|
|
32
|
+
context.palette.border,
|
|
33
|
+
context.palette.muted
|
|
34
|
+
].join("|");
|
|
35
|
+
if (sig === parts.sig) return;
|
|
36
|
+
parts.sig = sig;
|
|
37
|
+
parts.placeholder.clear();
|
|
38
|
+
if (!texture || selected) parts.placeholder.rect(0, 0, width, height).stroke({
|
|
39
|
+
color: selected ? context.palette.brand : context.palette.border,
|
|
40
|
+
width: selected ? 2 : 1,
|
|
41
|
+
alpha: .8
|
|
42
|
+
});
|
|
43
|
+
parts.label.visible = !texture;
|
|
44
|
+
parts.label.text = message;
|
|
45
|
+
parts.label.style.fill = error ? context.colors.rose.stroke : context.palette.muted;
|
|
46
|
+
parts.label.style.wordWrapWidth = Math.max(40, width - 16);
|
|
47
|
+
parts.label.position.set(width / 2, height / 2);
|
|
48
|
+
}
|
|
49
|
+
const sketchCardRenderer = {
|
|
50
|
+
id: "sketch-card",
|
|
51
|
+
canRender: (item) => item.type === "sketch",
|
|
52
|
+
animated: true,
|
|
53
|
+
create: (item, context) => {
|
|
54
|
+
const root = new Container();
|
|
55
|
+
const sprite = new Sprite(Texture.EMPTY);
|
|
56
|
+
const placeholder = new Graphics();
|
|
57
|
+
const label = new Text({
|
|
58
|
+
text: "",
|
|
59
|
+
style: {
|
|
60
|
+
fontFamily: BOARD_MONO_FONT_STACK,
|
|
61
|
+
fontSize: 12,
|
|
62
|
+
align: "center",
|
|
63
|
+
wordWrap: true,
|
|
64
|
+
breakWords: true
|
|
65
|
+
},
|
|
66
|
+
resolution: getBoardResolution()
|
|
67
|
+
});
|
|
68
|
+
label.anchor.set(.5);
|
|
69
|
+
root.addChild(sprite, placeholder, label);
|
|
70
|
+
partsByContainer.set(root, {
|
|
71
|
+
id: item.id,
|
|
72
|
+
root,
|
|
73
|
+
sprite,
|
|
74
|
+
placeholder,
|
|
75
|
+
label,
|
|
76
|
+
sig: ""
|
|
77
|
+
});
|
|
78
|
+
if (item.type === "sketch") sync(root, item, context);
|
|
79
|
+
return root;
|
|
80
|
+
},
|
|
81
|
+
update: (container, item, context) => {
|
|
82
|
+
if (item.type === "sketch") sync(container, item, context);
|
|
83
|
+
},
|
|
84
|
+
renderFar: (graphics, item, context) => {
|
|
85
|
+
drawFarPlate(graphics, item.frame, {
|
|
86
|
+
fill: context.palette.muted,
|
|
87
|
+
fillAlpha: .12
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
destroy: (container, context) => {
|
|
91
|
+
const parts = partsByContainer.get(container);
|
|
92
|
+
if (parts) context.sketches?.release(parts.id);
|
|
93
|
+
parts?.root.destroy({ children: true });
|
|
94
|
+
partsByContainer.delete(container);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
//#endregion
|
|
98
|
+
export { sketchCardRenderer };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BoardCardRenderer } from "./board-renderer-registry.js";
|
|
2
2
|
//#region src/board/render/renderers/task-card-renderer.d.ts
|
|
3
3
|
declare const TASK_CARD_FULL_DETAIL_ZOOM = 0.55;
|
|
4
|
-
/** Scale a texture into a frame without cropping or distortion. */
|
|
5
4
|
declare function containTaskPreviewRect(width: number, height: number, imageWidth: number, imageHeight: number): {
|
|
6
5
|
x: number;
|
|
7
6
|
y: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BOARD_FONT_STACK, BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
2
|
import { featuredTaskArtifact } from "../../task.js";
|
|
3
|
-
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
4
3
|
import { drawAudioWaveform } from "../audio-waveform.js";
|
|
4
|
+
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
5
5
|
import { positionShell } from "./base-card-renderer.js";
|
|
6
6
|
import { drawFarPlate } from "./far-plate.js";
|
|
7
7
|
import { fitTextToLines } from "./file-card-renderer.js";
|
|
@@ -21,13 +21,13 @@ function previewKindFor(artifact) {
|
|
|
21
21
|
}
|
|
22
22
|
function stateSurfaceFor(item, kind, hasTexture, previewFailed) {
|
|
23
23
|
if (kind === "texture" && !hasTexture) {
|
|
24
|
-
if (item.snapshot.status === "failed") return "failed";
|
|
24
|
+
if (item.props.snapshot.status === "failed") return "failed";
|
|
25
25
|
return previewFailed ? "unavailable" : "media-loading";
|
|
26
26
|
}
|
|
27
27
|
if (kind !== "empty") return null;
|
|
28
|
-
if (item.snapshot.status === "failed") return "failed";
|
|
29
|
-
if (item.snapshot.status === "running") return "generating";
|
|
30
|
-
if (item.snapshot.status === "pending") return "queued";
|
|
28
|
+
if (item.props.snapshot.status === "failed") return "failed";
|
|
29
|
+
if (item.props.snapshot.status === "running") return "generating";
|
|
30
|
+
if (item.props.snapshot.status === "pending") return "queued";
|
|
31
31
|
return "empty";
|
|
32
32
|
}
|
|
33
33
|
function stateLabel(surface) {
|
|
@@ -41,16 +41,15 @@ function stateLabel(surface) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
function statusColor(item, context) {
|
|
44
|
-
if (item.snapshot.status === "failed") return context.colors.rose.stroke;
|
|
45
|
-
if (item.snapshot.status === "running") return context.colors.brand.stroke;
|
|
44
|
+
if (item.props.snapshot.status === "failed") return context.colors.rose.stroke;
|
|
45
|
+
if (item.props.snapshot.status === "running") return context.colors.brand.stroke;
|
|
46
46
|
return context.colors.neutral.stroke;
|
|
47
47
|
}
|
|
48
48
|
function metadata(item) {
|
|
49
|
-
const { artifactCount, artifacts } = item.snapshot;
|
|
49
|
+
const { artifactCount, artifacts } = item.props.snapshot;
|
|
50
50
|
const extra = artifactCount > artifacts.length ? `${artifacts.length}/${artifactCount}` : artifactCount > 1 ? `+${artifactCount - 1}` : null;
|
|
51
|
-
return [item.snapshot.model, extra].filter(Boolean).join(" · ");
|
|
51
|
+
return [item.props.snapshot.model, extra].filter(Boolean).join(" · ");
|
|
52
52
|
}
|
|
53
|
-
/** Scale a texture into a frame without cropping or distortion. */
|
|
54
53
|
function containTaskPreviewRect(width, height, imageWidth, imageHeight) {
|
|
55
54
|
if (width <= 0 || height <= 0 || imageWidth <= 0 || imageHeight <= 0) return {
|
|
56
55
|
x: 0,
|
|
@@ -82,7 +81,6 @@ function drawPlayBadge(graphics, rect, context) {
|
|
|
82
81
|
alpha: .94
|
|
83
82
|
});
|
|
84
83
|
}
|
|
85
|
-
/** Static state marks avoid implying measurable progress or keeping Pixi awake. */
|
|
86
84
|
function drawStateMark(graphics, surface, cx, cy, color) {
|
|
87
85
|
if (surface === "generating" || surface === "media-loading") {
|
|
88
86
|
graphics.circle(cx, cy, 4).fill({
|
|
@@ -169,7 +167,7 @@ function sync(container, item, context) {
|
|
|
169
167
|
}
|
|
170
168
|
const texture = key ? context.getTexture(key) : null;
|
|
171
169
|
const previewFailed = Boolean(key && !texture && context.hasError(key));
|
|
172
|
-
const artifact = featuredTaskArtifact(item.snapshot.artifacts);
|
|
170
|
+
const artifact = featuredTaskArtifact(item.props.snapshot.artifacts);
|
|
173
171
|
const kind = previewKindFor(artifact);
|
|
174
172
|
const surface = stateSurfaceFor(item, kind, Boolean(texture), previewFailed);
|
|
175
173
|
const frame = {
|
|
@@ -180,7 +178,7 @@ function sync(container, item, context) {
|
|
|
180
178
|
};
|
|
181
179
|
const metaText = artifact ? metadata(item) : "";
|
|
182
180
|
const showMeta = full && Boolean(metaText);
|
|
183
|
-
const failedWithOutput = item.snapshot.status === "failed" && kind !== "empty";
|
|
181
|
+
const failedWithOutput = item.props.snapshot.status === "failed" && kind !== "empty";
|
|
184
182
|
syncTextResolution(parts.body, parts, context.zoom);
|
|
185
183
|
syncTextResolution(parts.meta, parts, context.zoom);
|
|
186
184
|
const visualSig = [
|
|
@@ -196,7 +194,7 @@ function sync(container, item, context) {
|
|
|
196
194
|
failedWithOutput,
|
|
197
195
|
artifact?.type ?? "none",
|
|
198
196
|
artifact?.id ?? "none",
|
|
199
|
-
item.taskRunId,
|
|
197
|
+
item.props.taskRunId,
|
|
200
198
|
texture ? `${texture.width}x${texture.height}` : "none",
|
|
201
199
|
context.palette.surface,
|
|
202
200
|
context.palette.hover,
|
|
@@ -222,7 +220,7 @@ function sync(container, item, context) {
|
|
|
222
220
|
parts.previewArt.clear();
|
|
223
221
|
if (kind === "waveform") {
|
|
224
222
|
const bottomInset = showMeta ? META_HEIGHT : 0;
|
|
225
|
-
drawAudioWaveform(parts.previewArt, item.taskRunId, {
|
|
223
|
+
drawAudioWaveform(parts.previewArt, item.props.taskRunId, {
|
|
226
224
|
x: frame.x + PADDING,
|
|
227
225
|
y: frame.y + PADDING,
|
|
228
226
|
width: Math.max(1, frame.width - 24),
|
|
@@ -335,7 +333,7 @@ const taskCardRenderer = {
|
|
|
335
333
|
},
|
|
336
334
|
renderFar: (graphics, item, context) => {
|
|
337
335
|
if (item.type !== "task") return;
|
|
338
|
-
const active = item.snapshot.status === "failed" || item.snapshot.status === "running" || item.snapshot.status === "pending";
|
|
336
|
+
const active = item.props.snapshot.status === "failed" || item.props.snapshot.status === "running" || item.props.snapshot.status === "pending";
|
|
339
337
|
drawFarPlate(graphics, item.frame, {
|
|
340
338
|
fill: context.palette.surface,
|
|
341
339
|
fillAlpha: .82,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { BoardItem, BoardTextItem } from "../../../protocol/dist/board-document.js";
|
|
2
1
|
import { BoardCardRenderer } from "./board-renderer-registry.js";
|
|
3
2
|
//#region src/board/render/renderers/text-card-renderer.d.ts
|
|
4
3
|
declare const textCardRenderer: BoardCardRenderer;
|
|
5
|
-
declare function isTextItem(item: BoardItem): item is BoardTextItem;
|
|
6
4
|
//#endregion
|
|
7
|
-
export {
|
|
5
|
+
export { textCardRenderer };
|
|
@@ -1,38 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { TEXT_FONT_SIZE, boardTextLineHeight, clampBoardTextFontSize, measureBoardText } from "../../core/text-metrics.js";
|
|
1
|
+
import { layoutBoardText } from "../../../protocol/dist/board-layout.js";
|
|
2
|
+
import { boardFontFamily } from "../../core/text-metrics.js";
|
|
4
3
|
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
5
4
|
import { positionShell } from "./base-card-renderer.js";
|
|
6
5
|
import { drawFarPlate } from "./far-plate.js";
|
|
7
|
-
import {
|
|
6
|
+
import { itemColor } from "../palette.js";
|
|
7
|
+
import { BOARD_FONT_STACKS } from "../text-measurement.js";
|
|
8
|
+
import { Container, Graphics, Text } from "pixi.js";
|
|
8
9
|
//#region src/board/render/renderers/text-card-renderer.ts
|
|
9
10
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
function revealed(text, reveal) {
|
|
12
|
+
if (reveal >= 1) return text;
|
|
13
|
+
return [...text].slice(0, Math.round(text.length * Math.max(0, reveal))).join("");
|
|
14
|
+
}
|
|
15
|
+
function syncCaret(parts, item, context) {
|
|
16
|
+
const { props } = item;
|
|
17
|
+
const visible = props.caret && props.reveal < 1 && Math.floor(context.time / 500) % 2 === 0;
|
|
18
|
+
parts.caret.visible = visible;
|
|
19
|
+
if (!visible) return;
|
|
20
|
+
const anchorX = props.align === "center" ? .5 : props.align === "right" ? 1 : 0;
|
|
21
|
+
const x = parts.body.position.x + parts.body.width * (1 - anchorX) + 2;
|
|
22
|
+
parts.caret.clear().rect(x, 1, Math.max(1, 1.5 / Math.max(.25, context.zoom)), Math.max(12, parts.body.height - 2)).fill({ color: itemColor(context, item.style.fill, "neutral") });
|
|
23
|
+
}
|
|
10
24
|
function sync(container, item, context) {
|
|
11
25
|
const parts = partsByContainer.get(container);
|
|
12
26
|
if (!parts) return;
|
|
13
27
|
positionShell(parts.root, item);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const previewScale = Math.max(1e-4, item.frame.width / Math.max(1e-4,
|
|
28
|
+
const { props } = item;
|
|
29
|
+
const layout = layoutBoardText(props);
|
|
30
|
+
const previewScale = Math.max(1e-4, item.frame.width / Math.max(1e-4, layout.width));
|
|
17
31
|
parts.body.scale.set(previewScale);
|
|
18
|
-
|
|
32
|
+
const anchorX = props.align === "center" ? .5 : props.align === "right" ? 1 : 0;
|
|
33
|
+
parts.body.anchor.set(anchorX, 0);
|
|
34
|
+
parts.body.position.set(item.frame.width * anchorX, 0);
|
|
19
35
|
syncTextResolution(parts.body, parts, context.zoom * Math.max(1, previewScale));
|
|
20
|
-
const
|
|
21
|
-
const
|
|
36
|
+
const ink = itemColor(context, item.style.fill, "neutral");
|
|
37
|
+
const text = revealed(props.text, props.reveal);
|
|
22
38
|
const contentSig = [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
fontSize,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
text,
|
|
40
|
+
ink,
|
|
41
|
+
props.fontSize,
|
|
42
|
+
props.fontWeight,
|
|
43
|
+
props.font,
|
|
44
|
+
props.align,
|
|
45
|
+
props.lineHeight,
|
|
46
|
+
props.width ?? ""
|
|
29
47
|
].join("|");
|
|
30
|
-
if (contentSig
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
if (contentSig !== parts.contentSig) {
|
|
49
|
+
parts.contentSig = contentSig;
|
|
50
|
+
parts.body.text = text;
|
|
51
|
+
Object.assign(parts.body.style, {
|
|
52
|
+
fill: ink,
|
|
53
|
+
fontSize: props.fontSize,
|
|
54
|
+
fontWeight: String(Math.round(props.fontWeight / 100) * 100),
|
|
55
|
+
fontFamily: boardFontFamily(props.font, BOARD_FONT_STACKS),
|
|
56
|
+
align: props.align,
|
|
57
|
+
lineHeight: layout.lineHeight,
|
|
58
|
+
wordWrap: Boolean(props.width),
|
|
59
|
+
wordWrapWidth: props.width ?? 0,
|
|
60
|
+
breakWords: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
syncCaret(parts, item, context);
|
|
36
64
|
}
|
|
37
65
|
const textCardRenderer = {
|
|
38
66
|
id: "text-card",
|
|
@@ -40,26 +68,18 @@ const textCardRenderer = {
|
|
|
40
68
|
create: (item, context) => {
|
|
41
69
|
const root = new Container();
|
|
42
70
|
const resolution = textResolutionForZoom(context.zoom);
|
|
43
|
-
const color = pickBoardColor(context.colors, item.type === "text" ? item.color || "neutral" : "neutral", context.colorScheme);
|
|
44
|
-
const ink = item.type === "text" && (item.color === "neutral" || !item.color) ? context.palette.text : color.stroke;
|
|
45
71
|
const body = new Text({
|
|
46
72
|
text: "",
|
|
47
|
-
style: {
|
|
48
|
-
fill: ink,
|
|
49
|
-
fontFamily: BOARD_FONT_STACK,
|
|
50
|
-
fontSize: TEXT_FONT_SIZE,
|
|
51
|
-
fontWeight: "500",
|
|
52
|
-
lineHeight: boardTextLineHeight(TEXT_FONT_SIZE),
|
|
53
|
-
wordWrap: false
|
|
54
|
-
},
|
|
73
|
+
style: { fontFamily: BOARD_FONT_STACKS.sans },
|
|
55
74
|
resolution,
|
|
56
75
|
roundPixels: true
|
|
57
76
|
});
|
|
58
|
-
|
|
59
|
-
root.addChild(body);
|
|
77
|
+
const caret = new Graphics();
|
|
78
|
+
root.addChild(body, caret);
|
|
60
79
|
partsByContainer.set(root, {
|
|
61
80
|
root,
|
|
62
81
|
body,
|
|
82
|
+
caret,
|
|
63
83
|
resolution,
|
|
64
84
|
contentSig: ""
|
|
65
85
|
});
|
|
@@ -69,17 +89,10 @@ const textCardRenderer = {
|
|
|
69
89
|
update: (container, item, context) => {
|
|
70
90
|
if (item.type === "text") sync(container, item, context);
|
|
71
91
|
},
|
|
72
|
-
/**
|
|
73
|
-
* Far LOD: a low-alpha ink bar. Body text is well under a pixel tall at these
|
|
74
|
-
* zooms, so a rasterised glyph texture would only ever resolve to a smudge —
|
|
75
|
-
* this approximates that smudge for free and skips the texture entirely.
|
|
76
|
-
*/
|
|
77
92
|
renderFar: (graphics, item, context) => {
|
|
78
93
|
if (item.type !== "text") return;
|
|
79
|
-
const color = pickBoardColor(context.colors, item.color || "neutral", context.colorScheme);
|
|
80
|
-
const ink = item.color === "neutral" || !item.color ? context.palette.text : color.stroke;
|
|
81
94
|
drawFarPlate(graphics, item.frame, {
|
|
82
|
-
fill:
|
|
95
|
+
fill: itemColor(context, item.style.fill, "neutral"),
|
|
83
96
|
fillAlpha: .35
|
|
84
97
|
});
|
|
85
98
|
},
|
|
@@ -88,8 +101,5 @@ const textCardRenderer = {
|
|
|
88
101
|
partsByContainer.delete(container);
|
|
89
102
|
}
|
|
90
103
|
};
|
|
91
|
-
function isTextItem(item) {
|
|
92
|
-
return item.type === "text";
|
|
93
|
-
}
|
|
94
104
|
//#endregion
|
|
95
|
-
export {
|
|
105
|
+
export { textCardRenderer };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
|
-
import { unknownRealType } from "../../../protocol/dist/board-document.js";
|
|
3
2
|
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
4
3
|
import { positionShell } from "./base-card-renderer.js";
|
|
5
4
|
import { drawFarPlate } from "./far-plate.js";
|
|
@@ -7,18 +6,13 @@ import { Container, Graphics, Text } from "pixi.js";
|
|
|
7
6
|
//#region src/board/render/renderers/unknown-card-renderer.ts
|
|
8
7
|
const RADIUS = 10;
|
|
9
8
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
10
|
-
/**
|
|
11
|
-
* Neutral placeholder for shape types this client does not recognise. The shape
|
|
12
|
-
* stays fully interactive (select/move/resize via the generic box definition) and
|
|
13
|
-
* its data is preserved elsewhere — this is only its display.
|
|
14
|
-
*/
|
|
15
9
|
function sync(container, item, context) {
|
|
16
10
|
const parts = partsByContainer.get(container);
|
|
17
11
|
if (!parts) return;
|
|
18
12
|
positionShell(parts.root, item);
|
|
19
13
|
const { width, height } = item.frame;
|
|
20
14
|
const selected = context.selectedIds.has(item.id);
|
|
21
|
-
const realType =
|
|
15
|
+
const realType = item.type;
|
|
22
16
|
syncTextResolution(parts.label, parts, context.zoom);
|
|
23
17
|
const visualSig = [
|
|
24
18
|
width,
|
|
@@ -56,7 +50,7 @@ function sync(container, item, context) {
|
|
|
56
50
|
}
|
|
57
51
|
const unknownCardRenderer = {
|
|
58
52
|
id: "unknown-card",
|
|
59
|
-
canRender: (
|
|
53
|
+
canRender: () => true,
|
|
60
54
|
create: (item, context) => {
|
|
61
55
|
const root = new Container();
|
|
62
56
|
const box = new Graphics();
|
|
@@ -81,11 +75,11 @@ const unknownCardRenderer = {
|
|
|
81
75
|
textSig: "",
|
|
82
76
|
resolution
|
|
83
77
|
});
|
|
84
|
-
|
|
78
|
+
sync(root, item, context);
|
|
85
79
|
return root;
|
|
86
80
|
},
|
|
87
81
|
update: (container, item, context) => {
|
|
88
|
-
|
|
82
|
+
sync(container, item, context);
|
|
89
83
|
},
|
|
90
84
|
renderFar: (graphics, item, context) => {
|
|
91
85
|
drawFarPlate(graphics, item.frame, {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BoardVideoItem } from "../../../protocol/dist/board-model.js";
|
|
2
|
+
import "../../../protocol/dist/index.js";
|
|
3
|
+
import { BoardSceneItem, SceneItem } from "../../core/scene.js";
|
|
2
4
|
import { BoardCardRenderer } from "./board-renderer-registry.js";
|
|
3
5
|
//#region src/board/render/renderers/video-card-renderer.d.ts
|
|
4
6
|
declare const videoCardRenderer: BoardCardRenderer;
|
|
5
|
-
declare function isVideoItem(item:
|
|
7
|
+
declare function isVideoItem(item: BoardSceneItem): item is SceneItem<BoardVideoItem>;
|
|
6
8
|
//#endregion
|
|
7
9
|
export { isVideoItem, videoCardRenderer };
|
|
@@ -23,7 +23,7 @@ function sync(container, item, context) {
|
|
|
23
23
|
const selected = context.selectedIds.has(item.id);
|
|
24
24
|
const hovered = context.hoveredId === item.id;
|
|
25
25
|
const resizing = context.resizingIds.has(item.id);
|
|
26
|
-
const title = item.snapshot?.title ?? item.
|
|
26
|
+
const title = item.props.snapshot?.title ?? item.props.src.split("/").pop() ?? "Video";
|
|
27
27
|
const key = context.assetKey(item);
|
|
28
28
|
const texture = key ? context.getTexture(key) : null;
|
|
29
29
|
const failed = Boolean(key && !texture && context.hasError(key));
|
|
@@ -40,6 +40,7 @@ function sync(container, item, context) {
|
|
|
40
40
|
hovered,
|
|
41
41
|
failed,
|
|
42
42
|
title,
|
|
43
|
+
item.props.time ?? 0,
|
|
43
44
|
parts.label.height,
|
|
44
45
|
context.palette.surface,
|
|
45
46
|
context.palette.brand,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BoardSketchHost } from "./renderers/board-renderer-registry.js";
|
|
2
|
+
//#region src/board/render/sketch-host.d.ts
|
|
3
|
+
type BoardSketchHostOptions = {
|
|
4
|
+
readModule: (src: string) => Promise<string>;
|
|
5
|
+
onFrame?: () => void;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
};
|
|
8
|
+
type BrowserBoardSketchHost = BoardSketchHost & {
|
|
9
|
+
invalidate: (src?: string) => void;
|
|
10
|
+
destroy: () => void;
|
|
11
|
+
};
|
|
12
|
+
declare function createBoardSketchHost(options: BoardSketchHostOptions): BrowserBoardSketchHost;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { BoardSketchHostOptions, BrowserBoardSketchHost, createBoardSketchHost };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { Texture } from "pixi.js";
|
|
2
|
+
//#region src/board/render/sketch-host.ts
|
|
3
|
+
const MAX_EDGE = 2048;
|
|
4
|
+
const FRAME_SOURCE = `<!doctype html><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' blob:; worker-src blob:">
|
|
5
|
+
<script>
|
|
6
|
+
const workerUrl = URL.createObjectURL(new Blob([${JSON.stringify(`
|
|
7
|
+
const modules = new Map();
|
|
8
|
+
for (const name of ["fetch", "XMLHttpRequest", "WebSocket", "EventSource", "importScripts", "indexedDB", "caches"]) {
|
|
9
|
+
try { Object.defineProperty(self, name, { value: undefined, configurable: false }); } catch {}
|
|
10
|
+
}
|
|
11
|
+
self.onmessage = async (event) => {
|
|
12
|
+
const { id, src, code, t, width, height, scale, seed, params, request } = event.data;
|
|
13
|
+
try {
|
|
14
|
+
let entry = modules.get(id);
|
|
15
|
+
if (!entry || entry.code !== code) {
|
|
16
|
+
const url = URL.createObjectURL(new Blob([code], { type: "text/javascript" }));
|
|
17
|
+
try { entry = { code, module: await import(url) }; } finally { URL.revokeObjectURL(url); }
|
|
18
|
+
modules.set(id, entry);
|
|
19
|
+
}
|
|
20
|
+
if (typeof entry.module.draw !== "function") throw new Error(src + " does not export draw(ctx, frame)");
|
|
21
|
+
const canvas = new OffscreenCanvas(Math.max(1, Math.round(width * scale)), Math.max(1, Math.round(height * scale)));
|
|
22
|
+
const ctx = canvas.getContext("2d");
|
|
23
|
+
ctx.scale(scale, scale);
|
|
24
|
+
await entry.module.draw(ctx, { t, width, height, seed, params });
|
|
25
|
+
const bitmap = canvas.transferToImageBitmap();
|
|
26
|
+
self.postMessage({ id, request, bitmap }, [bitmap]);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
self.postMessage({ id, request, error: String(error && error.message || error) });
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
`)}], { type: "text/javascript" }));
|
|
32
|
+
const makeWorker = () => {
|
|
33
|
+
const next = new Worker(workerUrl, { type: "module" });
|
|
34
|
+
next.onmessage = (event) => parent.postMessage(event.data, "*", event.data.bitmap ? [event.data.bitmap] : []);
|
|
35
|
+
return next;
|
|
36
|
+
};
|
|
37
|
+
let worker = makeWorker();
|
|
38
|
+
addEventListener("message", (event) => {
|
|
39
|
+
if (event.source !== parent) return;
|
|
40
|
+
if (event.data?.reset) {
|
|
41
|
+
worker.terminate();
|
|
42
|
+
worker = makeWorker();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
worker.postMessage(event.data);
|
|
46
|
+
});
|
|
47
|
+
parent.postMessage({ ready: true }, "*");
|
|
48
|
+
<\/script>`;
|
|
49
|
+
function frameKey(item, time, width, height, scale) {
|
|
50
|
+
return `${time}|${width}|${height}|${scale}|${item.props.seed ?? ""}|${JSON.stringify(item.props.params)}`;
|
|
51
|
+
}
|
|
52
|
+
function createBoardSketchHost(options) {
|
|
53
|
+
const timeoutMs = options.timeoutMs ?? 2e3;
|
|
54
|
+
const entries = /* @__PURE__ */ new Map();
|
|
55
|
+
const sources = /* @__PURE__ */ new Map();
|
|
56
|
+
const iframe = document.createElement("iframe");
|
|
57
|
+
iframe.setAttribute("sandbox", "allow-scripts");
|
|
58
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
59
|
+
iframe.style.cssText = "position:fixed;width:0;height:0;border:0;visibility:hidden";
|
|
60
|
+
iframe.srcdoc = FRAME_SOURCE;
|
|
61
|
+
let ready = false;
|
|
62
|
+
const queue = [];
|
|
63
|
+
const post = (message) => {
|
|
64
|
+
if (ready) iframe.contentWindow?.postMessage(message, "*");
|
|
65
|
+
else queue.push(message);
|
|
66
|
+
};
|
|
67
|
+
const resetWorker = () => {
|
|
68
|
+
for (const entry of entries.values()) {
|
|
69
|
+
if (entry.pending === null) continue;
|
|
70
|
+
if (entry.timeout) clearTimeout(entry.timeout);
|
|
71
|
+
entry.timeout = null;
|
|
72
|
+
entry.pending = null;
|
|
73
|
+
entry.shown = "";
|
|
74
|
+
entry.request += 1;
|
|
75
|
+
}
|
|
76
|
+
post({ reset: true });
|
|
77
|
+
};
|
|
78
|
+
const onMessage = (event) => {
|
|
79
|
+
if (event.source !== iframe.contentWindow) return;
|
|
80
|
+
const data = event.data;
|
|
81
|
+
if (data.ready) {
|
|
82
|
+
ready = true;
|
|
83
|
+
for (const message of queue.splice(0)) iframe.contentWindow?.postMessage(message, "*");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const entry = data.id ? entries.get(data.id) : void 0;
|
|
87
|
+
if (!entry || data.request !== entry.request) {
|
|
88
|
+
data.bitmap?.close();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (data.bitmap) {
|
|
92
|
+
if (entry.timeout) clearTimeout(entry.timeout);
|
|
93
|
+
entry.timeout = null;
|
|
94
|
+
entry.texture?.destroy(true);
|
|
95
|
+
entry.texture = Texture.from(data.bitmap);
|
|
96
|
+
entry.error = null;
|
|
97
|
+
} else {
|
|
98
|
+
if (entry.timeout) clearTimeout(entry.timeout);
|
|
99
|
+
entry.timeout = null;
|
|
100
|
+
entry.error = data.error ?? "Sketch failed.";
|
|
101
|
+
}
|
|
102
|
+
entry.shown = entry.pending ?? entry.shown;
|
|
103
|
+
entry.pending = null;
|
|
104
|
+
options.onFrame?.();
|
|
105
|
+
};
|
|
106
|
+
window.addEventListener("message", onMessage);
|
|
107
|
+
document.body.appendChild(iframe);
|
|
108
|
+
function source(src) {
|
|
109
|
+
let promise = sources.get(src);
|
|
110
|
+
if (!promise) {
|
|
111
|
+
promise = options.readModule(src);
|
|
112
|
+
sources.set(src, promise);
|
|
113
|
+
promise.catch(() => sources.delete(src));
|
|
114
|
+
}
|
|
115
|
+
return promise;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
frame(item, time, resolution) {
|
|
119
|
+
let entry = entries.get(item.id);
|
|
120
|
+
if (!entry) {
|
|
121
|
+
entry = {
|
|
122
|
+
texture: null,
|
|
123
|
+
error: null,
|
|
124
|
+
shown: "",
|
|
125
|
+
pending: null,
|
|
126
|
+
request: 0,
|
|
127
|
+
code: null,
|
|
128
|
+
loading: false,
|
|
129
|
+
timeout: null,
|
|
130
|
+
sourceError: null
|
|
131
|
+
};
|
|
132
|
+
entries.set(item.id, entry);
|
|
133
|
+
}
|
|
134
|
+
const { width, height } = item.frame;
|
|
135
|
+
const scale = Math.min(resolution, MAX_EDGE / Math.max(1, width), MAX_EDGE / Math.max(1, height));
|
|
136
|
+
const key = frameKey(item, time, width, height, scale);
|
|
137
|
+
if (key !== entry.shown && entry.pending === null) {
|
|
138
|
+
const current = entry;
|
|
139
|
+
if (current.code === null) {
|
|
140
|
+
if (!current.sourceError && !current.loading) {
|
|
141
|
+
current.loading = true;
|
|
142
|
+
source(item.props.src).then((code) => {
|
|
143
|
+
current.code = code;
|
|
144
|
+
current.sourceError = null;
|
|
145
|
+
current.loading = false;
|
|
146
|
+
options.onFrame?.();
|
|
147
|
+
}, (error) => {
|
|
148
|
+
current.loading = false;
|
|
149
|
+
current.sourceError = error instanceof Error ? error.message : String(error);
|
|
150
|
+
current.error = current.sourceError;
|
|
151
|
+
options.onFrame?.();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
current.pending = key;
|
|
156
|
+
current.request += 1;
|
|
157
|
+
const request = current.request;
|
|
158
|
+
current.timeout = setTimeout(() => {
|
|
159
|
+
if (current.request !== request || current.pending !== key) return;
|
|
160
|
+
resetWorker();
|
|
161
|
+
current.shown = key;
|
|
162
|
+
current.pending = null;
|
|
163
|
+
current.timeout = null;
|
|
164
|
+
current.error = `${item.props.src} exceeded the ${timeoutMs}ms sketch timeout.`;
|
|
165
|
+
options.onFrame?.();
|
|
166
|
+
}, timeoutMs);
|
|
167
|
+
post({
|
|
168
|
+
id: item.id,
|
|
169
|
+
src: item.props.src,
|
|
170
|
+
code: current.code,
|
|
171
|
+
t: time,
|
|
172
|
+
width,
|
|
173
|
+
height,
|
|
174
|
+
scale,
|
|
175
|
+
seed: item.props.seed ?? item.id,
|
|
176
|
+
params: item.props.params,
|
|
177
|
+
request: current.request
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return entry.texture;
|
|
182
|
+
},
|
|
183
|
+
error: (id) => entries.get(id)?.error ?? null,
|
|
184
|
+
release(id) {
|
|
185
|
+
const entry = entries.get(id);
|
|
186
|
+
if (entry?.timeout) clearTimeout(entry.timeout);
|
|
187
|
+
entry?.texture?.destroy(true);
|
|
188
|
+
entries.delete(id);
|
|
189
|
+
},
|
|
190
|
+
invalidate(src) {
|
|
191
|
+
if (src) sources.delete(src);
|
|
192
|
+
else sources.clear();
|
|
193
|
+
for (const entry of entries.values()) {
|
|
194
|
+
entry.code = null;
|
|
195
|
+
entry.sourceError = null;
|
|
196
|
+
entry.pending = null;
|
|
197
|
+
entry.request += 1;
|
|
198
|
+
if (entry.timeout) clearTimeout(entry.timeout);
|
|
199
|
+
entry.timeout = null;
|
|
200
|
+
entry.shown = "";
|
|
201
|
+
}
|
|
202
|
+
options.onFrame?.();
|
|
203
|
+
},
|
|
204
|
+
destroy() {
|
|
205
|
+
window.removeEventListener("message", onMessage);
|
|
206
|
+
for (const entry of entries.values()) {
|
|
207
|
+
if (entry.timeout) clearTimeout(entry.timeout);
|
|
208
|
+
entry.texture?.destroy(true);
|
|
209
|
+
}
|
|
210
|
+
entries.clear();
|
|
211
|
+
iframe.remove();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
export { createBoardSketchHost };
|