@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,36 +1,50 @@
|
|
|
1
1
|
import { DOMAdapter } from "pixi.js";
|
|
2
2
|
//#region src/board/render/css-color.ts
|
|
3
|
-
const CACHE_LIMIT = 256;
|
|
4
3
|
const cache = /* @__PURE__ */ new Map();
|
|
5
4
|
let canvas = null;
|
|
6
|
-
function
|
|
5
|
+
function parseBoardCssPaint(value) {
|
|
7
6
|
const key = value.trim();
|
|
8
7
|
if (!key) return null;
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
if (key.toLowerCase() === "transparent") return {
|
|
9
|
+
color: 0,
|
|
10
|
+
alpha: 0
|
|
11
|
+
};
|
|
12
|
+
const hex = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(key)?.[1];
|
|
13
|
+
if (hex) {
|
|
14
|
+
const digits = hex.length <= 4 ? [...hex].map((digit) => digit + digit).join("") : hex;
|
|
15
|
+
return {
|
|
16
|
+
color: Number.parseInt(digits.slice(0, 6), 16),
|
|
17
|
+
alpha: digits.length === 8 ? Number.parseInt(digits.slice(6), 16) / 255 : 1
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (cache.has(key)) return cache.get(key) ?? null;
|
|
11
21
|
let result = null;
|
|
12
22
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
canvas ??= DOMAdapter.get().createCanvas(1, 1);
|
|
24
|
+
const context = canvas.getContext("2d");
|
|
25
|
+
context.fillStyle = "#010203";
|
|
26
|
+
context.fillStyle = key;
|
|
27
|
+
const first = context.fillStyle;
|
|
28
|
+
context.fillStyle = "#040506";
|
|
29
|
+
context.fillStyle = key;
|
|
30
|
+
if (first === context.fillStyle) {
|
|
31
|
+
context.clearRect(0, 0, 1, 1);
|
|
32
|
+
context.fillRect(0, 0, 1, 1);
|
|
33
|
+
const [r = 0, g = 0, b = 0, a = 0] = context.getImageData(0, 0, 1, 1).data;
|
|
34
|
+
result = {
|
|
35
|
+
color: r << 16 | g << 8 | b,
|
|
36
|
+
alpha: a / 255
|
|
37
|
+
};
|
|
27
38
|
}
|
|
28
39
|
} catch {
|
|
29
40
|
result = null;
|
|
30
41
|
}
|
|
31
|
-
if (cache.size >=
|
|
42
|
+
if (cache.size >= 256) cache.clear();
|
|
32
43
|
cache.set(key, result);
|
|
33
44
|
return result;
|
|
34
45
|
}
|
|
46
|
+
function parseBoardCssColor(value) {
|
|
47
|
+
return parseBoardCssPaint(value)?.color ?? null;
|
|
48
|
+
}
|
|
35
49
|
//#endregion
|
|
36
|
-
export { parseBoardCssColor };
|
|
50
|
+
export { parseBoardCssColor, parseBoardCssPaint };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BoardTextItem } from "../../protocol/dist/board-model.js";
|
|
2
|
+
import "../../protocol/dist/index.js";
|
|
3
|
+
import { BoardScene } from "../model/scene.js";
|
|
4
|
+
//#region src/board/render/diagnostics.d.ts
|
|
5
|
+
type BoardRenderDiagnostic = {
|
|
6
|
+
code: "text-clipped" | "text-overlap" | "glyph-unavailable" | "effect-follow-unused";
|
|
7
|
+
item: string;
|
|
8
|
+
related?: string;
|
|
9
|
+
characters?: string;
|
|
10
|
+
};
|
|
11
|
+
declare function measureBoardText(props: BoardTextItem["props"]): {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
lines: string[];
|
|
15
|
+
lineHeight: number;
|
|
16
|
+
missingCharacters: string;
|
|
17
|
+
};
|
|
18
|
+
declare function inspectBoardScene(scene: BoardScene, limit?: number): BoardRenderDiagnostic[];
|
|
19
|
+
//#endregion
|
|
20
|
+
export { BoardRenderDiagnostic, inspectBoardScene, measureBoardText };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { layoutBoardText } from "../../protocol/dist/board-layout.js";
|
|
2
|
+
import { itemBounds, rectsIntersect } from "../model/geometry.js";
|
|
3
|
+
import { boardFontFamily } from "../model/text-metrics.js";
|
|
4
|
+
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement } from "./text-measurement.js";
|
|
5
|
+
import { DOMAdapter } from "pixi.js";
|
|
6
|
+
//#region src/board/render/diagnostics.ts
|
|
7
|
+
const glyphCache = /* @__PURE__ */ new Map();
|
|
8
|
+
let glyphContext;
|
|
9
|
+
function missingGlyph(character, font) {
|
|
10
|
+
if (/\s/u.test(character)) return false;
|
|
11
|
+
const key = `${font}:${character}`;
|
|
12
|
+
const cached = glyphCache.get(key);
|
|
13
|
+
if (cached !== void 0) return cached;
|
|
14
|
+
try {
|
|
15
|
+
glyphContext ??= DOMAdapter.get().createCanvas(64, 64).getContext("2d");
|
|
16
|
+
const ctx = glyphContext;
|
|
17
|
+
ctx.font = `32px ${font}`;
|
|
18
|
+
const absent = "";
|
|
19
|
+
if (ctx.measureText(character).width !== ctx.measureText(absent).width) {
|
|
20
|
+
if (glyphCache.size >= 2048) glyphCache.clear();
|
|
21
|
+
glyphCache.set(key, false);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const pixels = (text) => {
|
|
25
|
+
ctx.clearRect(0, 0, 64, 64);
|
|
26
|
+
ctx.fillStyle = "#000000";
|
|
27
|
+
ctx.fillText(text, 0, 40);
|
|
28
|
+
return ctx.getImageData(0, 0, 64, 64).data;
|
|
29
|
+
};
|
|
30
|
+
const a = pixels(character);
|
|
31
|
+
const b = pixels(absent);
|
|
32
|
+
const missing = a.some((n) => n !== 0) && a.every((n, i) => n === b[i]);
|
|
33
|
+
if (glyphCache.size >= 2048) glyphCache.clear();
|
|
34
|
+
glyphCache.set(key, missing);
|
|
35
|
+
return missing;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function measureBoardText(props) {
|
|
41
|
+
ensureBoardTextMeasurement();
|
|
42
|
+
const font = boardFontFamily(props.font, BOARD_FONT_STACKS);
|
|
43
|
+
const characters = [...new Set(props.text)].slice(0, 512).filter((char) => missingGlyph(char, font)).join("");
|
|
44
|
+
return {
|
|
45
|
+
...layoutBoardText(props),
|
|
46
|
+
missingCharacters: characters
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function inspectBoardScene(scene, limit = 100) {
|
|
50
|
+
ensureBoardTextMeasurement();
|
|
51
|
+
const diagnostics = [];
|
|
52
|
+
const texts = scene.items.filter((item) => item.type === "text" && scene.opacity(item.id) > .01);
|
|
53
|
+
const push = (entry) => {
|
|
54
|
+
if (diagnostics.length < limit) diagnostics.push(entry);
|
|
55
|
+
};
|
|
56
|
+
for (const item of scene.items) {
|
|
57
|
+
if (diagnostics.length >= limit) break;
|
|
58
|
+
if (item.type === "effect" && item.props.follow && item.props.kind !== "trail") push({
|
|
59
|
+
code: "effect-follow-unused",
|
|
60
|
+
item: item.id
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
let glyphBudget = 256;
|
|
64
|
+
for (const item of texts) {
|
|
65
|
+
if (diagnostics.length >= limit || item.type !== "text") break;
|
|
66
|
+
if (glyphBudget-- > 0) {
|
|
67
|
+
const measured = measureBoardText(item.props);
|
|
68
|
+
if (measured.missingCharacters) push({
|
|
69
|
+
code: "glyph-unavailable",
|
|
70
|
+
item: item.id,
|
|
71
|
+
characters: measured.missingCharacters
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const bounds = itemBounds(item.frame);
|
|
75
|
+
let parentId = item.parent;
|
|
76
|
+
while (parentId) {
|
|
77
|
+
const parent = scene.get(parentId);
|
|
78
|
+
if (!parent) break;
|
|
79
|
+
if (parent.type === "frame" && parent.props.clip) {
|
|
80
|
+
const clip = itemBounds(parent.frame);
|
|
81
|
+
if (bounds.x < clip.x - 1 || bounds.y < clip.y - 1 || bounds.x + bounds.width > clip.x + clip.width + 1 || bounds.y + bounds.height > clip.y + clip.height + 1) {
|
|
82
|
+
push({
|
|
83
|
+
code: "text-clipped",
|
|
84
|
+
item: item.id,
|
|
85
|
+
related: parentId
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
parentId = parent.parent;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const sorted = texts.map((item) => ({
|
|
94
|
+
item,
|
|
95
|
+
rect: itemBounds(item.frame)
|
|
96
|
+
})).sort((a, b) => a.rect.x - b.rect.x);
|
|
97
|
+
let comparisons = 0;
|
|
98
|
+
for (let i = 0; i < sorted.length && diagnostics.length < limit; i++) {
|
|
99
|
+
const a = sorted[i];
|
|
100
|
+
if (!a) continue;
|
|
101
|
+
for (let j = i + 1; j < sorted.length; j++) {
|
|
102
|
+
const b = sorted[j];
|
|
103
|
+
if (!b) continue;
|
|
104
|
+
if (b.rect.x >= a.rect.x + a.rect.width) break;
|
|
105
|
+
if (++comparisons > 1e4) return diagnostics;
|
|
106
|
+
if (a.item.parent === b.item.parent && rectsIntersect(a.rect, b.rect)) push({
|
|
107
|
+
code: "text-overlap",
|
|
108
|
+
item: a.item.id,
|
|
109
|
+
related: b.item.id
|
|
110
|
+
});
|
|
111
|
+
if (diagnostics.length >= limit) break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return diagnostics;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { inspectBoardScene, measureBoardText };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette,
|
|
1
|
+
import { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette, BoardScriptHost, createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
|
|
2
|
+
import { createBoardRenderSampler } from "./sampling.js";
|
|
3
|
+
import { BoardRenderDiagnostic, inspectBoardScene, measureBoardText } from "./diagnostics.js";
|
|
2
4
|
import { BoardBackgroundContext, createBoardBackground, updateBoardBackground } from "./board-background.js";
|
|
3
5
|
import { ClipGroup, clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
|
|
4
|
-
import { parseBoardCssColor } from "./css-color.js";
|
|
6
|
+
import { BoardCssPaint, parseBoardCssColor, parseBoardCssPaint } from "./css-color.js";
|
|
5
7
|
import { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
|
|
6
|
-
import { defaultBoardPalette, itemColor } from "./palette.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { defaultBoardPalette, itemAlpha, itemColor } from "./palette.js";
|
|
9
|
+
import { BoardScriptHostOptions, BrowserBoardScriptHost, createBoardScriptHost } from "./script-host.js";
|
|
10
|
+
import { prepareBrowserBoardScripts } from "./script-export.js";
|
|
11
|
+
import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
|
|
9
12
|
import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
|
|
10
13
|
import { TASK_CARD_FULL_DETAIL_ZOOM } from "./renderers/task-card-renderer.js";
|
|
11
14
|
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
|
|
12
15
|
import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
13
16
|
import { VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
|
|
14
|
-
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardMediaAction, BoardRenderContext, BoardRenderPalette,
|
|
17
|
+
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardCssPaint, BoardMediaAction, BoardRenderContext, BoardRenderDiagnostic, BoardRenderPalette, BoardScriptHost, BoardScriptHostOptions, BrowserBoardScriptHost, ClipGroup, TASK_CARD_FULL_DETAIL_ZOOM, VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, boardMediaActionAt, clippingAncestor, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, dashPattern, defaultBoardPalette, endAngle, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, roundRectPoints, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceFrameOutline, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { parseBoardCssColor } from "./css-color.js";
|
|
1
|
+
import { parseBoardCssColor, parseBoardCssPaint } from "./css-color.js";
|
|
2
|
+
import { defaultBoardPalette, itemAlpha, itemColor } from "./palette.js";
|
|
3
|
+
import { createBoardRenderSampler } from "./sampling.js";
|
|
4
|
+
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
|
|
5
|
+
import { inspectBoardScene, measureBoardText } from "./diagnostics.js";
|
|
2
6
|
import { createBoardBackground, updateBoardBackground } from "./board-background.js";
|
|
3
7
|
import { clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
|
|
4
8
|
import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
5
9
|
import { TASK_CARD_FULL_DETAIL_ZOOM } from "./renderers/task-card-renderer.js";
|
|
6
10
|
import { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
|
|
7
|
-
import {
|
|
8
|
-
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
|
|
9
|
-
import { dashPattern, endAngle, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
|
|
11
|
+
import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
|
|
10
12
|
import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
|
|
11
13
|
import { createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
|
|
12
|
-
import {
|
|
14
|
+
import { createBoardScriptHost } from "./script-host.js";
|
|
15
|
+
import { prepareBrowserBoardScripts } from "./script-export.js";
|
|
13
16
|
import { VIDEO_THUMBNAIL_MAX_EDGE, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
|
|
14
|
-
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, TASK_CARD_FULL_DETAIL_ZOOM, VIDEO_THUMBNAIL_MAX_EDGE, boardMediaActionAt, clippingAncestor, createBoardBackground, createBoardCardRendererResolver,
|
|
17
|
+
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, TASK_CARD_FULL_DETAIL_ZOOM, VIDEO_THUMBNAIL_MAX_EDGE, boardMediaActionAt, clippingAncestor, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, dashPattern, defaultBoardPalette, endAngle, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, roundRectPoints, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceFrameOutline, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize };
|
|
@@ -4,6 +4,7 @@ import { BoardColorId } from "../model/palette.js";
|
|
|
4
4
|
import { BoardRenderContext, BoardRenderPalette } from "./renderers/board-renderer-registry.js";
|
|
5
5
|
//#region src/board/render/palette.d.ts
|
|
6
6
|
declare function defaultBoardPalette(mode: "dark" | "light"): BoardRenderPalette;
|
|
7
|
+
declare function itemAlpha(context: Pick<BoardRenderContext, "colorScheme">, color: BoardColor | undefined): number;
|
|
7
8
|
declare function itemColor(context: Pick<BoardRenderContext, "colors" | "colorScheme" | "palette">, color: BoardColor | undefined, fallback: BoardColorId, part?: "stroke" | "fill" | "label"): number;
|
|
8
9
|
//#endregion
|
|
9
|
-
export { defaultBoardPalette, itemColor };
|
|
10
|
+
export { defaultBoardPalette, itemAlpha, itemColor };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveItemColor } from "../model/palette.js";
|
|
2
|
-
import { parseBoardCssColor } from "./css-color.js";
|
|
2
|
+
import { parseBoardCssColor, parseBoardCssPaint } from "./css-color.js";
|
|
3
3
|
//#region src/board/render/palette.ts
|
|
4
4
|
const DARK = {
|
|
5
5
|
bg: 1315860,
|
|
@@ -28,9 +28,13 @@ const LIGHT = {
|
|
|
28
28
|
function defaultBoardPalette(mode) {
|
|
29
29
|
return { ...mode === "light" ? LIGHT : DARK };
|
|
30
30
|
}
|
|
31
|
+
function itemAlpha(context, color) {
|
|
32
|
+
const value = typeof color === "object" && color ? color[context.colorScheme] : color;
|
|
33
|
+
return value ? parseBoardCssPaint(value)?.alpha ?? 1 : 1;
|
|
34
|
+
}
|
|
31
35
|
function itemColor(context, color, fallback, part = "stroke") {
|
|
32
36
|
if (((typeof color === "object" && color ? color[context.colorScheme] : color) ?? fallback) === "neutral" && part !== "fill") return context.palette.text;
|
|
33
37
|
return resolveItemColor(color, fallback, context.colors, context.colorScheme, parseBoardCssColor, part);
|
|
34
38
|
}
|
|
35
39
|
//#endregion
|
|
36
|
-
export { defaultBoardPalette, itemColor };
|
|
40
|
+
export { defaultBoardPalette, itemAlpha, itemColor };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
2
|
import { pathPointAt, resolveSceneArrow } from "../../model/arrow-geometry.js";
|
|
3
|
+
import { itemAlpha, itemColor } from "../palette.js";
|
|
3
4
|
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
4
5
|
import { drawFarStroke } from "./far-plate.js";
|
|
5
|
-
import { itemColor } from "../palette.js";
|
|
6
6
|
import { dashPattern, endAngle, traceArrowhead, tracePolyline, trimPolyline } from "../stroke.js";
|
|
7
7
|
import { Container, Graphics, Text } from "pixi.js";
|
|
8
8
|
//#region src/board/render/renderers/arrow-card-renderer.ts
|
|
@@ -16,6 +16,7 @@ function sync(container, item, context) {
|
|
|
16
16
|
const { props, style } = item;
|
|
17
17
|
const selected = context.selectedIds.has(item.id);
|
|
18
18
|
const hovered = context.hoveredId === item.id;
|
|
19
|
+
parts.line.alpha = itemAlpha(context, style.stroke);
|
|
19
20
|
const color = itemColor(context, style.stroke, "brand");
|
|
20
21
|
const size = style.strokeWidth ?? 2.5;
|
|
21
22
|
syncTextResolution(parts.label, parts, context.zoom);
|
|
@@ -130,7 +131,7 @@ const arrowCardRenderer = {
|
|
|
130
131
|
drawFarStroke(graphics, trimPolyline(resolved.path, arrow.style.trim), {
|
|
131
132
|
color: itemColor(context, arrow.style.stroke, "brand"),
|
|
132
133
|
width: arrow.style.strokeWidth ?? 2.5,
|
|
133
|
-
alpha: .85
|
|
134
|
+
alpha: .85 * itemAlpha(context, arrow.style.stroke)
|
|
134
135
|
});
|
|
135
136
|
},
|
|
136
137
|
destroy: (container) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
|
+
import { getMediaResourceTitle } from "../../model/media.js";
|
|
2
3
|
import { drawAudioWaveform } from "../audio-waveform.js";
|
|
3
4
|
import { syncTextResolution, syncTextWrapWidth, textResolutionForZoom } from "../text-resolution.js";
|
|
4
5
|
import { positionShell } from "./base-card-renderer.js";
|
|
@@ -15,7 +16,7 @@ function sync(container, item, context) {
|
|
|
15
16
|
const { width, height } = item.frame;
|
|
16
17
|
const selected = context.selectedIds.has(item.id);
|
|
17
18
|
const hovered = context.hoveredId === item.id;
|
|
18
|
-
const title = item.props.snapshot?.title ?? item.props.src
|
|
19
|
+
const title = item.props.snapshot?.title ?? getMediaResourceTitle(item.props.src);
|
|
19
20
|
syncTextResolution(parts.label, parts, context.zoom);
|
|
20
21
|
if (parts.label.text !== title) parts.label.text = title;
|
|
21
22
|
syncTextWrapWidth(parts.label, parts, Math.max(1, width - 24), false);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BoardCustomItem, BoardDocument, BoardSettings } from "../../../protocol/dist/board-model.js";
|
|
2
|
+
import { BoardFrame } from "../../../protocol/dist/board-layout.js";
|
|
2
3
|
import "../../../protocol/dist/index.js";
|
|
3
4
|
import { BoardScene, BoardSceneItem, SceneItem } from "../../model/scene.js";
|
|
4
5
|
import { BoardShapeColors } from "../../model/palette.js";
|
|
@@ -16,8 +17,11 @@ type BoardRenderPalette = {
|
|
|
16
17
|
epic: number;
|
|
17
18
|
legendary: number;
|
|
18
19
|
};
|
|
19
|
-
type
|
|
20
|
-
|
|
20
|
+
type BoardScriptHost = {
|
|
21
|
+
document?: (document: BoardDocument, time: number) => BoardDocument;
|
|
22
|
+
ready?: (id: string) => boolean;
|
|
23
|
+
revision?: (id: string) => number;
|
|
24
|
+
frame: (item: SceneItem<BoardCustomItem>, time: number, resolution: number) => Texture | null;
|
|
21
25
|
error: (id: string) => string | null;
|
|
22
26
|
release: (id: string) => void;
|
|
23
27
|
};
|
|
@@ -33,7 +37,8 @@ type BoardRenderContext = {
|
|
|
33
37
|
rendererType: "gpu" | "canvas";
|
|
34
38
|
zoom: number;
|
|
35
39
|
time: number;
|
|
36
|
-
|
|
40
|
+
frameAt?: (id: string, time: number) => BoardFrame | undefined;
|
|
41
|
+
scripts?: BoardScriptHost;
|
|
37
42
|
assetKey: (item: BoardSceneItem) => string | null;
|
|
38
43
|
getTexture: (key: string) => Texture | null;
|
|
39
44
|
hasError: (key: string) => boolean;
|
|
@@ -59,4 +64,4 @@ declare function createBoardCardRendererResolver(renderers?: Iterable<BoardCardR
|
|
|
59
64
|
/** The built-in renderers alone. */
|
|
60
65
|
declare const getBoardCardRenderer: BoardCardRendererResolver;
|
|
61
66
|
//#endregion
|
|
62
|
-
export { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette,
|
|
67
|
+
export { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette, BoardScriptHost, createBoardCardRendererResolver, getBoardCardRenderer };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { ensureBoardTextMeasurement } from "../text-measurement.js";
|
|
1
2
|
import { fileCardRenderer } from "./file-card-renderer.js";
|
|
2
3
|
import { taskCardRenderer } from "./task-card-renderer.js";
|
|
3
|
-
import { ensureBoardTextMeasurement } from "../text-measurement.js";
|
|
4
4
|
import { arrowCardRenderer } from "./arrow-card-renderer.js";
|
|
5
5
|
import { audioCardRenderer } from "./audio-card-renderer.js";
|
|
6
6
|
import { drawCardRenderer } from "./draw-card-renderer.js";
|
|
7
7
|
import { effectCardRenderer } from "./effect-card-renderer.js";
|
|
8
8
|
import { frameCardRenderer } from "./frame-card-renderer.js";
|
|
9
9
|
import { shapeCardRenderer } from "./shape-card-renderer.js";
|
|
10
|
-
import {
|
|
10
|
+
import { customCardRenderer } from "./custom-card-renderer.js";
|
|
11
11
|
import { imageCardRenderer } from "./image-card-renderer.js";
|
|
12
12
|
import { textCardRenderer } from "./text-card-renderer.js";
|
|
13
13
|
import { unknownCardRenderer } from "./unknown-card-renderer.js";
|
|
@@ -25,7 +25,7 @@ const builtinCardRenderers = [
|
|
|
25
25
|
arrowCardRenderer,
|
|
26
26
|
frameCardRenderer,
|
|
27
27
|
effectCardRenderer,
|
|
28
|
-
|
|
28
|
+
customCardRenderer
|
|
29
29
|
];
|
|
30
30
|
/**
|
|
31
31
|
* Pick a renderer per item: `renderers` first, then the built-ins. Items no
|
|
@@ -3,26 +3,27 @@ import { getBoardResolution } from "../text-resolution.js";
|
|
|
3
3
|
import { positionShell } from "./base-card-renderer.js";
|
|
4
4
|
import { drawFarPlate } from "./far-plate.js";
|
|
5
5
|
import { Container, Graphics, Sprite, Text, Texture } from "pixi.js";
|
|
6
|
-
//#region src/board/render/renderers/
|
|
6
|
+
//#region src/board/render/renderers/custom-card-renderer.ts
|
|
7
7
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
8
8
|
function sync(container, item, context) {
|
|
9
9
|
const parts = partsByContainer.get(container);
|
|
10
10
|
if (!parts) return;
|
|
11
11
|
if (parts.id !== item.id) {
|
|
12
|
-
context.
|
|
12
|
+
context.scripts?.release(parts.id);
|
|
13
13
|
parts.id = item.id;
|
|
14
14
|
}
|
|
15
15
|
positionShell(parts.root, item);
|
|
16
16
|
const { width, height } = item.frame;
|
|
17
17
|
const resolution = Math.min(4, Math.max(.25, context.zoom * getBoardResolution()));
|
|
18
|
-
const texture = context.
|
|
19
|
-
const error = context.
|
|
18
|
+
const texture = context.scripts?.frame(item, context.time, resolution) ?? null;
|
|
19
|
+
const error = context.scripts?.error(item.id) ?? null;
|
|
20
20
|
parts.sprite.visible = Boolean(texture);
|
|
21
21
|
if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
|
|
22
22
|
parts.sprite.width = width;
|
|
23
23
|
parts.sprite.height = height;
|
|
24
24
|
const selected = context.selectedIds.has(item.id);
|
|
25
|
-
const
|
|
25
|
+
const ready = Boolean(texture) || context.scripts?.ready?.(item.id) === true;
|
|
26
|
+
const message = error ? `${item.script.src}\n${error}` : ready ? "" : item.script.src;
|
|
26
27
|
const sig = [
|
|
27
28
|
width,
|
|
28
29
|
height,
|
|
@@ -35,21 +36,21 @@ function sync(container, item, context) {
|
|
|
35
36
|
if (sig === parts.sig) return;
|
|
36
37
|
parts.sig = sig;
|
|
37
38
|
parts.placeholder.clear();
|
|
38
|
-
if (!
|
|
39
|
+
if (!ready || selected || error) parts.placeholder.rect(0, 0, width, height).stroke({
|
|
39
40
|
color: selected ? context.palette.brand : context.palette.border,
|
|
40
41
|
width: selected ? 2 : 1,
|
|
41
42
|
alpha: .8
|
|
42
43
|
});
|
|
43
|
-
parts.label.visible = !
|
|
44
|
+
parts.label.visible = !ready || Boolean(error);
|
|
44
45
|
parts.label.text = message;
|
|
45
46
|
parts.label.style.fill = error ? context.colors.rose.stroke : context.palette.muted;
|
|
46
47
|
parts.label.style.wordWrapWidth = Math.max(40, width - 16);
|
|
47
48
|
parts.label.position.set(width / 2, height / 2);
|
|
48
49
|
}
|
|
49
|
-
const
|
|
50
|
-
id: "
|
|
51
|
-
canRender: (item) => item.type === "
|
|
52
|
-
animated:
|
|
50
|
+
const customCardRenderer = {
|
|
51
|
+
id: "custom-card",
|
|
52
|
+
canRender: (item) => item.type === "custom",
|
|
53
|
+
animated: false,
|
|
53
54
|
create: (item, context) => {
|
|
54
55
|
const root = new Container();
|
|
55
56
|
const sprite = new Sprite(Texture.EMPTY);
|
|
@@ -75,11 +76,11 @@ const sketchCardRenderer = {
|
|
|
75
76
|
label,
|
|
76
77
|
sig: ""
|
|
77
78
|
});
|
|
78
|
-
if (item.type === "
|
|
79
|
+
if (item.type === "custom") sync(root, item, context);
|
|
79
80
|
return root;
|
|
80
81
|
},
|
|
81
82
|
update: (container, item, context) => {
|
|
82
|
-
if (item.type === "
|
|
83
|
+
if (item.type === "custom") sync(container, item, context);
|
|
83
84
|
},
|
|
84
85
|
renderFar: (graphics, item, context) => {
|
|
85
86
|
drawFarPlate(graphics, item.frame, {
|
|
@@ -89,10 +90,10 @@ const sketchCardRenderer = {
|
|
|
89
90
|
},
|
|
90
91
|
destroy: (container, context) => {
|
|
91
92
|
const parts = partsByContainer.get(container);
|
|
92
|
-
if (parts) context.
|
|
93
|
+
if (parts) context.scripts?.release(parts.id);
|
|
93
94
|
parts?.root.destroy({ children: true });
|
|
94
95
|
partsByContainer.delete(container);
|
|
95
96
|
}
|
|
96
97
|
};
|
|
97
98
|
//#endregion
|
|
98
|
-
export {
|
|
99
|
+
export { customCardRenderer };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { applyMatrix } from "../../../protocol/dist/board-layout.js";
|
|
2
2
|
import { computeDrawBounds, createStrokeRibbonBuilder, trimDrawPoints } from "../../model/draw-geometry.js";
|
|
3
|
+
import { itemAlpha, itemColor } from "../palette.js";
|
|
3
4
|
import { positionShell } from "./base-card-renderer.js";
|
|
4
5
|
import { drawFarStroke, farStrokeSamples } from "./far-plate.js";
|
|
5
|
-
import { itemColor } from "../palette.js";
|
|
6
6
|
import { Container, Graphics, Mesh, MeshGeometry, Texture } from "pixi.js";
|
|
7
7
|
//#region src/board/render/renderers/draw-card-renderer.ts
|
|
8
8
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
@@ -77,6 +77,7 @@ function sync(container, item, context) {
|
|
|
77
77
|
}
|
|
78
78
|
const stroke = parts.stroke;
|
|
79
79
|
if (!stroke) return;
|
|
80
|
+
stroke.alpha = itemAlpha(context, item.style.stroke);
|
|
80
81
|
if (stroke instanceof Mesh) stroke.tint = color;
|
|
81
82
|
const { box } = parts;
|
|
82
83
|
const sx = item.frame.width / box.width;
|
|
@@ -120,7 +121,7 @@ const drawCardRenderer = {
|
|
|
120
121
|
drawFarStroke(graphics, samples.map((point) => applyMatrix(matrix, point)), {
|
|
121
122
|
color: itemColor(context, draw.style.stroke, "brand"),
|
|
122
123
|
width: draw.style.strokeWidth ?? 4,
|
|
123
|
-
alpha:
|
|
124
|
+
alpha: itemAlpha(context, draw.style.stroke)
|
|
124
125
|
});
|
|
125
126
|
},
|
|
126
127
|
destroy: (container) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { itemAlpha, itemColor } from "../palette.js";
|
|
1
2
|
import { positionShell } from "./base-card-renderer.js";
|
|
2
3
|
import { drawFarPlate } from "./far-plate.js";
|
|
3
|
-
import { itemColor } from "../palette.js";
|
|
4
4
|
import { Container, Graphics } from "pixi.js";
|
|
5
5
|
//#region src/board/render/renderers/effect-card-renderer.ts
|
|
6
6
|
const BOARD_EFFECT_MAX_PARTICLES = 4e3;
|
|
@@ -109,11 +109,27 @@ function drawTrail(graphics, parts, item, context, color) {
|
|
|
109
109
|
parts.trail.length = 0;
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
const angle = item.frame.rotation * Math.PI / 180;
|
|
113
|
+
const localPoint = (frame, t) => {
|
|
114
|
+
const dx = frame.x + frame.width / 2 - item.frame.x - item.frame.width / 2;
|
|
115
|
+
const dy = frame.y + frame.height / 2 - item.frame.y - item.frame.height / 2;
|
|
116
|
+
return {
|
|
117
|
+
x: dx * Math.cos(angle) + dy * Math.sin(angle) + item.frame.width / 2,
|
|
118
|
+
y: -dx * Math.sin(angle) + dy * Math.cos(angle) + item.frame.height / 2,
|
|
119
|
+
t
|
|
120
|
+
};
|
|
116
121
|
};
|
|
122
|
+
const head = localPoint(target.frame, time);
|
|
123
|
+
if (context.frameAt) {
|
|
124
|
+
parts.trail.length = 0;
|
|
125
|
+
const start = Math.max(0, time - item.props.life);
|
|
126
|
+
const step = Math.max(1e3 / 30, item.props.life / 63);
|
|
127
|
+
for (let t = Math.ceil(start / step) * step; t < time; t += step) {
|
|
128
|
+
const past = context.frameAt(target.id, t);
|
|
129
|
+
if (past) parts.trail.push(localPoint(past, t));
|
|
130
|
+
}
|
|
131
|
+
parts.trail.push(head);
|
|
132
|
+
}
|
|
117
133
|
const last = parts.trail.at(-1);
|
|
118
134
|
if (last && (time < last.t || time - last.t > item.props.life)) parts.trail.length = 0;
|
|
119
135
|
if (!last || Math.hypot(head.x - last.x, head.y - last.y) > .5) parts.trail.push(head);
|
|
@@ -140,6 +156,7 @@ function sync(container, item, context) {
|
|
|
140
156
|
const seed = hashString(props.seed ?? item.id);
|
|
141
157
|
const time = Math.max(0, context.time);
|
|
142
158
|
const graphics = parts.graphics;
|
|
159
|
+
graphics.alpha = itemAlpha(context, item.style.fill ?? item.style.stroke);
|
|
143
160
|
graphics.clear();
|
|
144
161
|
switch (props.kind) {
|
|
145
162
|
case "particles":
|