@neta-art/cohub 10.0.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -7
- package/dist/board/editor/animation.d.ts +3 -0
- package/dist/board/editor/animation.js +16 -1
- package/dist/board/editor/clipboard.js +7 -0
- package/dist/board/editor/copy.js +23 -18
- package/dist/board/editor/creation.d.ts +7 -3
- package/dist/board/editor/creation.js +5 -4
- package/dist/board/editor/deletion.js +3 -3
- package/dist/board/editor/document.d.ts +8 -2
- package/dist/board/editor/document.js +54 -7
- package/dist/board/editor/editor.d.ts +22 -7
- package/dist/board/editor/editor.js +12 -2
- package/dist/board/editor/index.d.ts +2 -2
- package/dist/board/editor/interaction.d.ts +8 -2
- package/dist/board/editor/interaction.js +34 -1
- package/dist/board/editor/query.d.ts +5 -5
- package/dist/board/editor/query.js +14 -12
- package/dist/board/editor/selection.js +2 -1
- package/dist/board/editor/snapshots.d.ts +0 -2
- package/dist/board/editor/snapshots.js +4 -20
- package/dist/board/export/index.d.ts +25 -4
- package/dist/board/export/index.js +45 -12
- package/dist/board/export/node/index.d.ts +5 -4
- package/dist/board/export/node/index.js +16 -11
- package/dist/board/export/node/scripts.d.ts +16 -0
- package/dist/board/export/node/scripts.js +258 -0
- package/dist/board/export/scene.d.ts +2 -1
- package/dist/board/export/scene.js +24 -7
- package/dist/board/model/animation.d.ts +4 -1
- package/dist/board/model/animation.js +34 -7
- package/dist/board/model/authoring.d.ts +20 -0
- package/dist/board/model/authoring.js +185 -0
- package/dist/board/model/export-plan.js +1 -0
- package/dist/board/model/file-preview.js +1 -1
- package/dist/board/model/file-snapshot.js +3 -2
- package/dist/board/model/image-key.js +7 -3
- package/dist/board/model/index.d.ts +15 -7
- package/dist/board/model/index.js +16 -9
- package/dist/board/model/items/builtin.js +2 -2
- package/dist/board/model/items/create.d.ts +19 -4
- package/dist/board/model/items/create.js +130 -9
- package/dist/board/model/items/index.d.ts +2 -2
- package/dist/board/model/items/index.js +2 -2
- package/dist/board/model/media-playback.d.ts +2 -1
- package/dist/board/model/media-playback.js +15 -5
- package/dist/board/model/media.js +7 -1
- package/dist/board/model/overlay-placement.d.ts +25 -0
- package/dist/board/model/overlay-placement.js +35 -0
- package/dist/board/model/replay.d.ts +1 -1
- package/dist/board/model/replay.js +1 -0
- package/dist/board/model/sampling.d.ts +19 -0
- package/dist/board/model/sampling.js +96 -0
- package/dist/board/model/script-example.d.ts +4 -0
- package/dist/board/model/script-example.js +21 -0
- package/dist/board/model/script-hit.d.ts +27 -0
- package/dist/board/model/script-hit.js +109 -0
- package/dist/board/model/script-index.d.ts +7 -0
- package/dist/board/model/script-index.js +15 -0
- package/dist/board/model/script-observers.d.ts +18 -0
- package/dist/board/model/script-observers.js +92 -0
- package/dist/board/model/script-scene.d.ts +24 -0
- package/dist/board/model/script-scene.js +294 -0
- package/dist/board/model/script.d.ts +149 -0
- package/dist/board/model/script.js +1 -0
- package/dist/board/render/board-background.js +5 -4
- package/dist/board/render/css-color.d.ts +6 -1
- package/dist/board/render/css-color.js +34 -20
- package/dist/board/render/diagnostics.d.ts +20 -0
- package/dist/board/render/diagnostics.js +117 -0
- package/dist/board/render/index.d.ts +9 -6
- package/dist/board/render/index.js +10 -7
- package/dist/board/render/media-interaction.js +1 -1
- package/dist/board/render/palette.d.ts +2 -1
- package/dist/board/render/palette.js +6 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +3 -2
- package/dist/board/render/renderers/audio-card-renderer.js +2 -1
- package/dist/board/render/renderers/board-renderer-registry.d.ts +25 -5
- package/dist/board/render/renderers/board-renderer-registry.js +11 -4
- package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +18 -16
- package/dist/board/render/renderers/draw-card-renderer.js +3 -2
- package/dist/board/render/renderers/effect-card-renderer.js +22 -5
- package/dist/board/render/renderers/frame-card-renderer.js +30 -14
- package/dist/board/render/renderers/image-card-renderer.js +43 -4
- package/dist/board/render/renderers/shape-card-renderer.js +10 -27
- package/dist/board/render/renderers/task-card-renderer.d.ts +2 -1
- package/dist/board/render/renderers/task-card-renderer.js +220 -71
- package/dist/board/render/renderers/text-card-renderer.js +5 -3
- package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sampling.d.ts +22 -0
- package/dist/board/render/sampling.js +21 -0
- package/dist/board/render/script-export.d.ts +11 -0
- package/dist/board/render/script-export.js +86 -0
- package/dist/board/render/script-host.d.ts +31 -0
- package/dist/board/render/script-host.js +446 -0
- package/dist/board/render/script-modules.d.ts +2 -0
- package/dist/board/render/script-modules.js +2 -0
- package/dist/board/render/script-runtime.d.ts +29 -0
- package/dist/board/render/script-runtime.js +223 -0
- package/dist/board/render/script-sandbox.d.ts +4 -0
- package/dist/board/render/script-sandbox.js +173 -0
- package/dist/board/render/script-texture.d.ts +11 -0
- package/dist/board/render/script-texture.js +33 -0
- package/dist/board/render/stroke.d.ts +2 -1
- package/dist/board/render/stroke.js +22 -1
- package/dist/board/render/text-measurement.js +1 -1
- package/dist/board/stage/assets.js +7 -2
- package/dist/board/stage/maintenance.d.ts +5 -0
- package/dist/board/stage/maintenance.js +11 -0
- package/dist/board/stage/scene.d.ts +2 -0
- package/dist/board/stage/scene.js +21 -2
- package/dist/board/stage/script-pointer.d.ts +20 -0
- package/dist/board/stage/script-pointer.js +41 -0
- package/dist/board/stage/stage.d.ts +21 -3
- package/dist/board/stage/stage.js +350 -47
- package/dist/board/stage/texture-pool.js +2 -3
- package/dist/chunks/environment.d.ts +305 -27
- package/dist/chunks/environment.js +12 -4
- package/dist/chunks/host-bridge.d.ts +2 -2
- package/dist/chunks/http.d.ts +90 -7
- package/dist/chunks/http.js +157 -64
- package/dist/chunks/transport.js +4 -2
- package/dist/chunks/websocket.d.ts +9 -1
- package/dist/chunks/websocket.js +16 -2
- package/dist/host-bridge.js +2 -1
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +182 -4
- package/dist/index.js +1559 -299
- package/dist/protocol/dist/board-assets.d.ts +1 -0
- package/dist/protocol/dist/board-constants.js +2 -2
- package/dist/protocol/dist/board-model.d.ts +236 -11
- package/dist/protocol/dist/board-model.js +46 -17
- package/dist/protocol/dist/board-patch.js +2 -1
- package/dist/protocol/dist/board-schema.d.ts +4 -2
- package/dist/protocol/dist/board-schema.js +18 -4
- package/dist/protocol/dist/board-script-modules.d.ts +25 -0
- package/dist/protocol/dist/board-script-modules.js +127 -0
- package/dist/protocol/dist/board-task.d.ts +22 -0
- package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
- package/dist/protocol/dist/display/index.d.ts +1 -0
- package/dist/protocol/dist/generation/blocks.d.ts +8 -0
- package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
- package/dist/protocol/dist/index.d.ts +7 -2
- package/dist/protocol/dist/realtime/types.d.ts +1 -0
- package/dist/protocol/dist/rtc/index.d.ts +1 -0
- package/dist/protocol/dist/task/index.d.ts +4 -0
- package/package.json +2 -1
- package/dist/board/export/node/sketch.d.ts +0 -19
- package/dist/board/export/node/sketch.js +0 -146
- package/dist/board/model/task.d.ts +0 -11
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
- package/dist/board/render/sketch-host.d.ts +0 -14
- package/dist/board/render/sketch-host.js +0 -216
- package/dist/types.d.ts +0 -36
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { parseBoardItem as parseBoardItem$1 } from "../../protocol/dist/board-model.js";
|
|
2
|
+
import { applyMatrix, createBoardLayout } from "../../protocol/dist/board-layout.js";
|
|
3
|
+
import { applyBoardPatchToDocument, mergePatch } from "../../protocol/dist/board-patch.js";
|
|
4
|
+
//#region src/board/model/script-scene.ts
|
|
5
|
+
function parseBoardItem(value) {
|
|
6
|
+
const parsed = parseBoardItem$1(value);
|
|
7
|
+
if (!parsed.ok) throw new Error(parsed.diagnostics[0]?.message ?? "Invalid script item.");
|
|
8
|
+
return parsed.item;
|
|
9
|
+
}
|
|
10
|
+
const DISPLAY_FIELDS = /* @__PURE__ */ new Set([
|
|
11
|
+
"position",
|
|
12
|
+
"size",
|
|
13
|
+
"rotation",
|
|
14
|
+
"scale",
|
|
15
|
+
"opacity",
|
|
16
|
+
"style",
|
|
17
|
+
"props"
|
|
18
|
+
]);
|
|
19
|
+
function boardScriptScope(document, id) {
|
|
20
|
+
const scope = /* @__PURE__ */ new Set([id, ...Object.values(document.items[id]?.script?.targets ?? {}).filter((target) => Boolean(document.items[target]))]);
|
|
21
|
+
if (document.items[id]?.type !== "frame") return scope;
|
|
22
|
+
for (const candidate of Object.keys(document.items)) {
|
|
23
|
+
let parent = document.items[candidate]?.parent;
|
|
24
|
+
for (let depth = 0; parent && depth < 64; depth++) {
|
|
25
|
+
if (parent === id) {
|
|
26
|
+
scope.add(candidate);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
parent = document.items[parent]?.parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return scope;
|
|
33
|
+
}
|
|
34
|
+
function validateBoardScriptOutput(output, scope) {
|
|
35
|
+
if (!output || typeof output !== "object" || !output.patches || !output.items || !output.hits || !Array.isArray(output.listens)) throw new Error("Invalid script output.");
|
|
36
|
+
if (JSON.stringify(output).length > 1e6) throw new Error("Script output exceeds the 1 MB limit.");
|
|
37
|
+
if (output.space !== void 0 && output.space !== "world" && output.space !== "screen") throw new Error("Invalid script scene space.");
|
|
38
|
+
if (output.anchor) {
|
|
39
|
+
const { item, point } = output.anchor;
|
|
40
|
+
if (output.space !== "screen" || typeof item !== "string" || !item || point && ![point.x, point.y].every((n) => Number.isFinite(n) && n >= 0 && n <= 1)) throw new Error("Invalid screen scene anchor.");
|
|
41
|
+
}
|
|
42
|
+
if (output.parameters) {
|
|
43
|
+
if (Object.keys(output.parameters).length > 64) throw new Error("A script may declare at most 64 parameters.");
|
|
44
|
+
for (const spec of Object.values(output.parameters)) {
|
|
45
|
+
if (!spec || ![
|
|
46
|
+
"number",
|
|
47
|
+
"boolean",
|
|
48
|
+
"string",
|
|
49
|
+
"color"
|
|
50
|
+
].includes(spec.type)) throw new Error("Invalid script parameter type.");
|
|
51
|
+
if (spec.label !== void 0 && typeof spec.label !== "string") throw new Error("Invalid script parameter label.");
|
|
52
|
+
if ([
|
|
53
|
+
spec.min,
|
|
54
|
+
spec.max,
|
|
55
|
+
spec.step
|
|
56
|
+
].some((value) => value !== void 0 && !Number.isFinite(value))) throw new Error("Invalid script parameter range.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (Object.keys(output.items).length > 1e3 || Object.keys(output.hits).length > 1e3) throw new Error("A script may create at most 1000 temporary items or hit regions.");
|
|
60
|
+
for (const [id, patch] of Object.entries(output.patches)) {
|
|
61
|
+
if (!scope.has(id)) throw new Error(`Item is outside the script scope: ${id}`);
|
|
62
|
+
if (!patch || typeof patch !== "object" || Object.keys(patch).some((key) => !DISPLAY_FIELDS.has(key))) throw new Error("Scripts may only present display properties.");
|
|
63
|
+
}
|
|
64
|
+
for (const hit of Object.values(output.hits)) {
|
|
65
|
+
if (!hit || typeof hit !== "object") throw new Error("Invalid script hit region.");
|
|
66
|
+
if (hit.cursor && ![
|
|
67
|
+
"default",
|
|
68
|
+
"pointer",
|
|
69
|
+
"grab",
|
|
70
|
+
"grabbing",
|
|
71
|
+
"crosshair",
|
|
72
|
+
"text",
|
|
73
|
+
"move",
|
|
74
|
+
"ew-resize",
|
|
75
|
+
"ns-resize",
|
|
76
|
+
"not-allowed"
|
|
77
|
+
].includes(hit.cursor)) throw new Error("Invalid script cursor.");
|
|
78
|
+
if ("target" in hit) {
|
|
79
|
+
if (hit.target !== "self" || "item" in hit || "x" in hit) throw new Error("Invalid script hit target.");
|
|
80
|
+
} else if ("item" in hit) {
|
|
81
|
+
if (typeof hit.item !== "string" || !Object.hasOwn(output.items, hit.item) || "x" in hit) throw new Error("Hit items must belong to the temporary scene.");
|
|
82
|
+
} else if (![
|
|
83
|
+
hit.x,
|
|
84
|
+
hit.y,
|
|
85
|
+
hit.width,
|
|
86
|
+
hit.height
|
|
87
|
+
].every(Number.isFinite) || hit.width < 0 || hit.height < 0) throw new Error("Invalid script hit region.");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function validateBoardScriptCommit(document, id, patch) {
|
|
91
|
+
if (!patch || typeof patch !== "object" || Object.keys(patch).some((key) => key !== "items")) throw new Error("Scripts may only commit items in their scope.");
|
|
92
|
+
const scope = boardScriptScope(document, id);
|
|
93
|
+
for (const [target, value] of Object.entries(patch.items ?? {})) {
|
|
94
|
+
if (!scope.has(target)) {
|
|
95
|
+
if (document.items[target]) throw new Error(`Item is outside the script scope: ${target}`);
|
|
96
|
+
if (!value || typeof value !== "object" || !("parent" in value) || typeof value.parent !== "string" || !scope.has(value.parent) || document.items[value.parent]?.type !== "frame") throw new Error("New items must belong to a frame in the script scope.");
|
|
97
|
+
if ("script" in value) throw new Error("Scripts cannot start other scripts.");
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (value && typeof value === "object" && ("script" in value || "parent" in value || "type" in value)) throw new Error("A script cannot replace scripts, types, or parent relationships.");
|
|
101
|
+
}
|
|
102
|
+
const result = applyBoardPatchToDocument(document, patch);
|
|
103
|
+
if (!result.ok) throw new Error(result.diagnostics[0]?.message ?? "Invalid script commit.");
|
|
104
|
+
}
|
|
105
|
+
function scriptTemporaryPrefix(owner) {
|
|
106
|
+
let hash = 2166136261;
|
|
107
|
+
for (const char of owner) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
|
|
108
|
+
return `~script-${hash.toString(36)}-`;
|
|
109
|
+
}
|
|
110
|
+
const preparedOutputs = /* @__PURE__ */ new WeakMap();
|
|
111
|
+
const preparedScopes = /* @__PURE__ */ new WeakMap();
|
|
112
|
+
const itemOrders = /* @__PURE__ */ new WeakMap();
|
|
113
|
+
function orderedScriptOwners(document, outputs) {
|
|
114
|
+
let order = itemOrders.get(document.items);
|
|
115
|
+
if (!order) {
|
|
116
|
+
order = new Map(Object.keys(document.items).map((id, index) => [id, index]));
|
|
117
|
+
itemOrders.set(document.items, order);
|
|
118
|
+
}
|
|
119
|
+
return [...outputs.keys()].filter((id) => order.has(id)).sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
|
|
120
|
+
}
|
|
121
|
+
function prepareBoardScriptOutput(document, owner, output) {
|
|
122
|
+
const previous = preparedOutputs.get(output);
|
|
123
|
+
if (previous?.document === document && previous.owner === owner) return previous;
|
|
124
|
+
let scopes = preparedScopes.get(document);
|
|
125
|
+
if (!scopes) {
|
|
126
|
+
scopes = /* @__PURE__ */ new Map();
|
|
127
|
+
preparedScopes.set(document, scopes);
|
|
128
|
+
}
|
|
129
|
+
let scope = scopes.get(owner);
|
|
130
|
+
if (!scope) {
|
|
131
|
+
scope = boardScriptScope(document, owner);
|
|
132
|
+
scopes.set(owner, scope);
|
|
133
|
+
}
|
|
134
|
+
if (output.anchor && document.items[output.anchor.item] && !scope.has(output.anchor.item)) throw new Error("Scene anchor is outside the script scope.");
|
|
135
|
+
validateBoardScriptOutput(output, scope);
|
|
136
|
+
const presented = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const [id, patch] of Object.entries(output.patches)) {
|
|
138
|
+
const base = document.items[id];
|
|
139
|
+
if (base) presented.set(id, {
|
|
140
|
+
base,
|
|
141
|
+
item: parseBoardItem(mergePatch(base, patch))
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const prefix = scriptTemporaryPrefix(owner);
|
|
145
|
+
const rootId = `${prefix}root`;
|
|
146
|
+
if (document.items[rootId]) throw new Error("Temporary item ID conflicts with the document.");
|
|
147
|
+
const items = /* @__PURE__ */ new Map();
|
|
148
|
+
const parsed = new Map(Object.entries(output.items).map(([id, value]) => [id, parseBoardItem(value)]));
|
|
149
|
+
for (const [local, value] of parsed) {
|
|
150
|
+
if (!/^[a-zA-Z0-9_-]{1,80}$/.test(local) || local === "root") throw new Error("Temporary IDs must be 1–80 letters, digits, underscores or hyphens; root is reserved.");
|
|
151
|
+
if (value.script) throw new Error("Temporary items cannot start scripts.");
|
|
152
|
+
if (value.parent && !(value.parent in output.items)) throw new Error("Temporary parents must belong to the same instance.");
|
|
153
|
+
let cursor = value.parent;
|
|
154
|
+
const visited = /* @__PURE__ */ new Set([local]);
|
|
155
|
+
while (cursor) {
|
|
156
|
+
if (visited.has(cursor)) throw new Error("Temporary parents must not form a cycle.");
|
|
157
|
+
visited.add(cursor);
|
|
158
|
+
const parentItem = parsed.get(cursor);
|
|
159
|
+
if (!parentItem) throw new Error("Temporary parent is missing.");
|
|
160
|
+
if (parentItem.type !== "frame") throw new Error("Only frames can contain temporary items.");
|
|
161
|
+
cursor = parentItem.parent;
|
|
162
|
+
}
|
|
163
|
+
const id = `${prefix}${local}`;
|
|
164
|
+
if (document.items[id]) throw new Error("Temporary item ID conflicts with the document.");
|
|
165
|
+
const parent = value.parent ? `${prefix}${value.parent}` : rootId;
|
|
166
|
+
if (value.type === "arrow") {
|
|
167
|
+
const remap = (endpoint) => {
|
|
168
|
+
if (!("item" in endpoint)) return endpoint;
|
|
169
|
+
if (!(endpoint.item in output.items) && !scope.has(endpoint.item)) throw new Error("Arrow target is outside the script scope.");
|
|
170
|
+
return endpoint.item in output.items ? {
|
|
171
|
+
...endpoint,
|
|
172
|
+
item: `${prefix}${endpoint.item}`
|
|
173
|
+
} : endpoint;
|
|
174
|
+
};
|
|
175
|
+
items.set(id, {
|
|
176
|
+
...value,
|
|
177
|
+
parent,
|
|
178
|
+
props: {
|
|
179
|
+
...value.props,
|
|
180
|
+
start: remap(value.props.start),
|
|
181
|
+
end: remap(value.props.end)
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
} else items.set(id, {
|
|
185
|
+
...value,
|
|
186
|
+
parent
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const prepared = {
|
|
190
|
+
document,
|
|
191
|
+
owner,
|
|
192
|
+
rootId,
|
|
193
|
+
items,
|
|
194
|
+
presented
|
|
195
|
+
};
|
|
196
|
+
preparedOutputs.set(output, prepared);
|
|
197
|
+
return prepared;
|
|
198
|
+
}
|
|
199
|
+
function composeBoardScriptItems(document, scene, outputs, incoming = /* @__PURE__ */ new Map(), screen, onError, gestureLayout) {
|
|
200
|
+
const result = new Map(incoming);
|
|
201
|
+
const screenAnchors = /* @__PURE__ */ new Map();
|
|
202
|
+
if (outputs.size === 0) return result;
|
|
203
|
+
for (const owner of orderedScriptOwners(document, outputs)) {
|
|
204
|
+
const output = outputs.get(owner);
|
|
205
|
+
if (!output) continue;
|
|
206
|
+
try {
|
|
207
|
+
const changes = /* @__PURE__ */ new Map();
|
|
208
|
+
const prepared = prepareBoardScriptOutput(document, owner, output);
|
|
209
|
+
for (const [id, patch] of Object.entries(output.patches)) {
|
|
210
|
+
const base = result.get(id) ?? document.items[id];
|
|
211
|
+
if (!base) continue;
|
|
212
|
+
let cached = prepared.presented.get(id);
|
|
213
|
+
if (cached?.base !== base) {
|
|
214
|
+
cached = {
|
|
215
|
+
base,
|
|
216
|
+
item: parseBoardItem(mergePatch(base, patch))
|
|
217
|
+
};
|
|
218
|
+
prepared.presented.set(id, cached);
|
|
219
|
+
}
|
|
220
|
+
changes.set(id, cached.item);
|
|
221
|
+
}
|
|
222
|
+
if (!prepared.items.size || output.space === "screen" && !screen) {
|
|
223
|
+
for (const [id, item] of changes) result.set(id, item);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const rootId = prepared.rootId;
|
|
227
|
+
const item = changes.get(owner) ?? result.get(owner) ?? document.items[owner];
|
|
228
|
+
if (!item) continue;
|
|
229
|
+
const box = scene.layout.box(owner);
|
|
230
|
+
const overlay = output.space === "screen" && screen ? {
|
|
231
|
+
parent: void 0,
|
|
232
|
+
position: {
|
|
233
|
+
x: 0,
|
|
234
|
+
y: 0
|
|
235
|
+
},
|
|
236
|
+
scale: 1 / screen.zoom,
|
|
237
|
+
rotation: 0,
|
|
238
|
+
origin: {
|
|
239
|
+
x: 0,
|
|
240
|
+
y: 0
|
|
241
|
+
}
|
|
242
|
+
} : {};
|
|
243
|
+
changes.set(rootId, parseBoardItem({
|
|
244
|
+
type: "frame",
|
|
245
|
+
parent: item.parent,
|
|
246
|
+
position: item.position,
|
|
247
|
+
rotation: item.rotation,
|
|
248
|
+
scale: item.scale,
|
|
249
|
+
origin: item.origin,
|
|
250
|
+
opacity: item.opacity,
|
|
251
|
+
size: {
|
|
252
|
+
width: Math.max(1, box.width),
|
|
253
|
+
height: Math.max(1, box.height)
|
|
254
|
+
},
|
|
255
|
+
z: (item.z ?? 0) + .001,
|
|
256
|
+
props: {
|
|
257
|
+
decoration: "none",
|
|
258
|
+
clip: false
|
|
259
|
+
},
|
|
260
|
+
...overlay
|
|
261
|
+
}));
|
|
262
|
+
for (const [id, value] of prepared.items) changes.set(id, value);
|
|
263
|
+
for (const [id, item] of changes) result.set(id, item);
|
|
264
|
+
if (output.space === "screen") screenAnchors.set(owner, rootId);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (!onError) throw error;
|
|
267
|
+
onError(owner, error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (screenAnchors.size) {
|
|
271
|
+
const layout = gestureLayout ?? createBoardLayout((id) => result.get(id) ?? document.items[id]);
|
|
272
|
+
for (const [owner, rootId] of screenAnchors) {
|
|
273
|
+
const root = result.get(rootId);
|
|
274
|
+
const anchor = outputs.get(owner)?.anchor;
|
|
275
|
+
const target = anchor?.item ?? owner;
|
|
276
|
+
const exists = Boolean(document.items[target]);
|
|
277
|
+
const box = exists ? layout.box(target) : {
|
|
278
|
+
width: 0,
|
|
279
|
+
height: 0
|
|
280
|
+
};
|
|
281
|
+
if (root) result.set(rootId, {
|
|
282
|
+
...root,
|
|
283
|
+
opacity: exists ? root.opacity : 0,
|
|
284
|
+
position: exists ? applyMatrix(layout.matrix(target), {
|
|
285
|
+
x: (anchor?.point?.x ?? 0) * box.width,
|
|
286
|
+
y: (anchor?.point?.y ?? 0) * box.height
|
|
287
|
+
}) : root.position
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
export { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, scriptTemporaryPrefix, validateBoardScriptCommit, validateBoardScriptOutput };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { BoardItem, BoardPatch, BoardScript, BoardVec2 } from "../../protocol/dist/board-model.js";
|
|
2
|
+
import "../../protocol/dist/index.js";
|
|
3
|
+
//#region src/board/model/script.d.ts
|
|
4
|
+
type BoardScriptView = {
|
|
5
|
+
camera: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
zoom: number;
|
|
9
|
+
};
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
safeRect: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
type BoardScriptAnchor = {
|
|
20
|
+
item: string;
|
|
21
|
+
point?: BoardVec2;
|
|
22
|
+
};
|
|
23
|
+
type BoardScriptSceneOptions = {
|
|
24
|
+
space?: "world" | "screen";
|
|
25
|
+
anchor?: BoardScriptAnchor;
|
|
26
|
+
};
|
|
27
|
+
type BoardScriptEvent = {
|
|
28
|
+
type: string;
|
|
29
|
+
target?: string;
|
|
30
|
+
relatedTarget?: string;
|
|
31
|
+
relatedOwner?: string;
|
|
32
|
+
point?: BoardVec2;
|
|
33
|
+
pointerId?: number;
|
|
34
|
+
pointerType?: string;
|
|
35
|
+
buttons?: number;
|
|
36
|
+
key?: string;
|
|
37
|
+
shiftKey?: boolean;
|
|
38
|
+
altKey?: boolean;
|
|
39
|
+
ctrlKey?: boolean;
|
|
40
|
+
metaKey?: boolean;
|
|
41
|
+
data?: unknown;
|
|
42
|
+
};
|
|
43
|
+
type BoardScriptHostEvents = {
|
|
44
|
+
viewchange: BoardScriptView;
|
|
45
|
+
selectionchange: {
|
|
46
|
+
ids: string[];
|
|
47
|
+
};
|
|
48
|
+
transformend: {
|
|
49
|
+
ids: string[];
|
|
50
|
+
operation: "move" | "resize" | "rotate" | "arrow";
|
|
51
|
+
cancelled: boolean;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
type BoardScriptEventOf<Type extends string> = BoardScriptEvent & {
|
|
55
|
+
type: Type;
|
|
56
|
+
} & (Type extends keyof BoardScriptHostEvents ? {
|
|
57
|
+
data: BoardScriptHostEvents[Type];
|
|
58
|
+
} : unknown);
|
|
59
|
+
type BoardScriptFrame = {
|
|
60
|
+
t: number;
|
|
61
|
+
dt: number;
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
seed: string;
|
|
65
|
+
params: BoardScript["params"];
|
|
66
|
+
state: Record<string, unknown>;
|
|
67
|
+
};
|
|
68
|
+
type BoardScriptItemPatch = Partial<Pick<BoardItem, "position" | "scale" | "rotation" | "opacity" | "style">> & {
|
|
69
|
+
size?: {
|
|
70
|
+
width?: number;
|
|
71
|
+
height?: number;
|
|
72
|
+
};
|
|
73
|
+
props?: Record<string, unknown>;
|
|
74
|
+
};
|
|
75
|
+
type BoardScriptHit = ({
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
} | {
|
|
81
|
+
target: "self";
|
|
82
|
+
} | {
|
|
83
|
+
item: string;
|
|
84
|
+
}) & {
|
|
85
|
+
label?: string;
|
|
86
|
+
cursor?: string;
|
|
87
|
+
};
|
|
88
|
+
type BoardScriptSnapshot = {
|
|
89
|
+
state: Record<string, unknown>;
|
|
90
|
+
time: number;
|
|
91
|
+
random: number;
|
|
92
|
+
configuration?: string;
|
|
93
|
+
effects?: Pick<BoardScriptOutput, "patches" | "items" | "hits" | "space" | "anchor">;
|
|
94
|
+
};
|
|
95
|
+
type BoardScriptParameter = {
|
|
96
|
+
type: "number" | "boolean" | "string" | "color";
|
|
97
|
+
label?: string;
|
|
98
|
+
default?: string | number | boolean;
|
|
99
|
+
min?: number;
|
|
100
|
+
max?: number;
|
|
101
|
+
step?: number;
|
|
102
|
+
};
|
|
103
|
+
type BoardScriptOutput = {
|
|
104
|
+
parameters?: Record<string, BoardScriptParameter>;
|
|
105
|
+
space?: "world" | "screen";
|
|
106
|
+
anchor?: BoardScriptAnchor;
|
|
107
|
+
patches: Record<string, Record<string, unknown>>;
|
|
108
|
+
items: Record<string, unknown>;
|
|
109
|
+
hits: Record<string, BoardScriptHit>;
|
|
110
|
+
listens: string[];
|
|
111
|
+
animated: boolean;
|
|
112
|
+
updating?: boolean;
|
|
113
|
+
version: number;
|
|
114
|
+
};
|
|
115
|
+
type BoardScriptContext = {
|
|
116
|
+
readonly self: {
|
|
117
|
+
readonly id: string;
|
|
118
|
+
readonly base: BoardItem;
|
|
119
|
+
present(patch: BoardScriptItemPatch | null): void;
|
|
120
|
+
};
|
|
121
|
+
readonly params: BoardScript["params"];
|
|
122
|
+
readonly targets: Readonly<Record<string, string>>;
|
|
123
|
+
emit(target: string, type: string, data?: unknown): Promise<void>;
|
|
124
|
+
readonly state: Record<string, unknown>;
|
|
125
|
+
readonly frame: BoardScriptFrame;
|
|
126
|
+
readonly view: BoardScriptView | null;
|
|
127
|
+
random(): number;
|
|
128
|
+
on<Type extends string>(type: Type, handler: (event: BoardScriptEventOf<Type>) => void): () => void;
|
|
129
|
+
onFrame(handler: (frame: BoardScriptFrame) => void): () => void;
|
|
130
|
+
onDispose(handler: () => void): void;
|
|
131
|
+
invalidate(): void;
|
|
132
|
+
get(id: string): BoardItem | undefined;
|
|
133
|
+
present(id: string, patch: BoardScriptItemPatch | null): void;
|
|
134
|
+
scene(items: Record<string, unknown>, options?: BoardScriptSceneOptions): void;
|
|
135
|
+
hitRegions(hits: Record<string, BoardScriptHit>): void;
|
|
136
|
+
commit(patch: BoardPatch): Promise<void>;
|
|
137
|
+
capabilities: {
|
|
138
|
+
has(name: string): boolean;
|
|
139
|
+
call(name: string, input?: unknown): Promise<unknown>;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
type BoardScriptModule = {
|
|
143
|
+
parameters?: Record<string, BoardScriptParameter>;
|
|
144
|
+
default?: ((context: BoardScriptContext) => void) | ((context: BoardScriptContext) => () => void);
|
|
145
|
+
draw?: (context: CanvasRenderingContext2D, frame: BoardScriptFrame) => void;
|
|
146
|
+
animated?: boolean;
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { BoardScriptAnchor, BoardScriptContext, BoardScriptEvent, BoardScriptEventOf, BoardScriptFrame, BoardScriptHit, BoardScriptHostEvents, BoardScriptItemPatch, BoardScriptModule, BoardScriptOutput, BoardScriptParameter, BoardScriptSceneOptions, BoardScriptSnapshot, BoardScriptView };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,7 +12,7 @@ function buildGridTexture(context, size, color, opacity, kind) {
|
|
|
12
12
|
width: 1,
|
|
13
13
|
alpha: opacity
|
|
14
14
|
});
|
|
15
|
-
else graphics.circle(
|
|
15
|
+
else graphics.circle(size / 2, size / 2, 1.1).fill({
|
|
16
16
|
color,
|
|
17
17
|
alpha: opacity
|
|
18
18
|
});
|
|
@@ -49,8 +49,9 @@ function sync(parts, context) {
|
|
|
49
49
|
const kind = settings.background.kind === "grid" ? "grid" : "dots";
|
|
50
50
|
const visible = settings.grid?.visible === true;
|
|
51
51
|
const size = Math.max(4, settings.grid?.size ?? 24);
|
|
52
|
-
const
|
|
53
|
-
const
|
|
52
|
+
const color = kind === "grid" ? palette.border : palette.muted;
|
|
53
|
+
const opacity = kind === "grid" ? .9 : .4;
|
|
54
|
+
const key = `${kind}|${size}|${color}|${opacity}`;
|
|
54
55
|
if (!visible) {
|
|
55
56
|
if (parts.sprite) parts.sprite.visible = false;
|
|
56
57
|
return;
|
|
@@ -61,7 +62,7 @@ function sync(parts, context) {
|
|
|
61
62
|
parts.sprite.destroy();
|
|
62
63
|
previousTexture.destroy(true);
|
|
63
64
|
}
|
|
64
|
-
const texture = buildGridTexture(context, size,
|
|
65
|
+
const texture = buildGridTexture(context, size, color, opacity, kind);
|
|
65
66
|
parts.sprite = new TilingSprite({
|
|
66
67
|
texture,
|
|
67
68
|
width,
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
//#region src/board/render/css-color.d.ts
|
|
2
|
+
type BoardCssPaint = {
|
|
3
|
+
color: number;
|
|
4
|
+
alpha: number;
|
|
5
|
+
};
|
|
6
|
+
declare function parseBoardCssPaint(value: string): BoardCssPaint | null;
|
|
2
7
|
declare function parseBoardCssColor(value: string): number | null;
|
|
3
8
|
//#endregion
|
|
4
|
-
export { parseBoardCssColor };
|
|
9
|
+
export { BoardCssPaint, parseBoardCssColor, parseBoardCssPaint };
|
|
@@ -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 };
|