@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,734 @@
|
|
|
1
|
+
import { BOARD_CAMERA_TARGET, BoardAnimationHeaderSchema, BoardColorSchema, BoardPatchSchema, BoardSettingsSchema, BoardTrackSchema, BoardVec2Schema, boardItemSchema, isBuiltinItemType, parseBoardItem } from "./board-model.js";
|
|
2
|
+
import { boardJsonEquals } from "./board-json.js";
|
|
3
|
+
import { applyMatrix, arrowBindings, createBoardLayout, invertMatrix, isArrowBinding } from "./board-layout.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region ../protocol/dist/board-patch.js
|
|
6
|
+
function slot(map, key) {
|
|
7
|
+
let value = map[key];
|
|
8
|
+
if (!value) {
|
|
9
|
+
value = {};
|
|
10
|
+
map[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function emptyDelta() {
|
|
15
|
+
return {
|
|
16
|
+
items: {},
|
|
17
|
+
animations: {},
|
|
18
|
+
tracks: {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function withoutEmpty(value) {
|
|
22
|
+
for (const [key, entry] of Object.entries(value)) if (entry === void 0 || isRecord(entry) && Object.keys(entry).length === 0) delete value[key];
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function mergePatch(target, patch) {
|
|
29
|
+
if (!isRecord(patch)) return patch;
|
|
30
|
+
const result = isRecord(target) ? { ...target } : {};
|
|
31
|
+
for (const [key, value] of Object.entries(patch)) if (value === null) delete result[key];
|
|
32
|
+
else result[key] = mergePatch(result[key], value);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function diffMergePatch(before, after) {
|
|
36
|
+
if (boardJsonEquals(before, after)) return void 0;
|
|
37
|
+
if (after === void 0) return null;
|
|
38
|
+
if (!isRecord(before) || !isRecord(after)) return after;
|
|
39
|
+
const patch = {};
|
|
40
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
|
|
41
|
+
const value = diffMergePatch(before[key], after[key]);
|
|
42
|
+
if (value !== void 0) patch[key] = value;
|
|
43
|
+
}
|
|
44
|
+
return patch;
|
|
45
|
+
}
|
|
46
|
+
function def(schema) {
|
|
47
|
+
return schema._zod.def;
|
|
48
|
+
}
|
|
49
|
+
function unwrap(schema) {
|
|
50
|
+
let current = schema;
|
|
51
|
+
for (let guard = 0; guard < 16; guard += 1) {
|
|
52
|
+
const d = def(current);
|
|
53
|
+
if (d.type === "optional" || d.type === "default" || d.type === "nullable" || d.type === "readonly" || d.type === "prefault") current = d.innerType;
|
|
54
|
+
else if (d.type === "pipe") current = d.in;
|
|
55
|
+
else return current;
|
|
56
|
+
}
|
|
57
|
+
return current;
|
|
58
|
+
}
|
|
59
|
+
function unwrapObjectShape(schema) {
|
|
60
|
+
const d = def(unwrap(schema));
|
|
61
|
+
return d.type === "object" ? d.shape : null;
|
|
62
|
+
}
|
|
63
|
+
function schemaAtPath(schema, path) {
|
|
64
|
+
let current = schema;
|
|
65
|
+
for (const segment of path) {
|
|
66
|
+
const d = def(unwrap(current));
|
|
67
|
+
if (d.type === "object") {
|
|
68
|
+
const shape = d.shape;
|
|
69
|
+
if (!Object.hasOwn(shape, segment)) return null;
|
|
70
|
+
current = shape[segment];
|
|
71
|
+
} else if (d.type === "record") current = d.valueType;
|
|
72
|
+
else if (d.type === "union") {
|
|
73
|
+
const branch = d.options.find((option) => unwrapObjectShape(option)?.[segment]);
|
|
74
|
+
if (!branch) return null;
|
|
75
|
+
current = unwrapObjectShape(branch)[segment];
|
|
76
|
+
} else return null;
|
|
77
|
+
}
|
|
78
|
+
return current;
|
|
79
|
+
}
|
|
80
|
+
const NOT_ANIMATABLE = /* @__PURE__ */ new Set([
|
|
81
|
+
"type",
|
|
82
|
+
"parent",
|
|
83
|
+
"z",
|
|
84
|
+
"locked",
|
|
85
|
+
"metadata"
|
|
86
|
+
]);
|
|
87
|
+
const CAMERA_PROPERTIES = {
|
|
88
|
+
focus: {
|
|
89
|
+
kind: "discrete",
|
|
90
|
+
schema: z.union([z.string().min(1), z.object({
|
|
91
|
+
x: z.number().finite(),
|
|
92
|
+
y: z.number().finite(),
|
|
93
|
+
width: z.number().finite().positive(),
|
|
94
|
+
height: z.number().finite().positive()
|
|
95
|
+
}).strict()])
|
|
96
|
+
},
|
|
97
|
+
zoom: {
|
|
98
|
+
kind: "number",
|
|
99
|
+
schema: z.number().finite().positive(),
|
|
100
|
+
multiplicative: true
|
|
101
|
+
},
|
|
102
|
+
center: {
|
|
103
|
+
kind: "vec2",
|
|
104
|
+
schema: BoardVec2Schema
|
|
105
|
+
},
|
|
106
|
+
shake: {
|
|
107
|
+
kind: "number",
|
|
108
|
+
schema: z.number().finite().nonnegative()
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const ANIMATION_PROPERTIES = { time: {
|
|
112
|
+
kind: "number",
|
|
113
|
+
schema: z.number().finite().nonnegative()
|
|
114
|
+
} };
|
|
115
|
+
function kindOf(schema, path) {
|
|
116
|
+
const leaf = path[path.length - 1];
|
|
117
|
+
if (path.length === 1 && leaf === "scale") return "scale";
|
|
118
|
+
if (schema === BoardColorSchema || unwrap(schema) === BoardColorSchema) return "color";
|
|
119
|
+
if (def(unwrap(schema)).type === "number") return "number";
|
|
120
|
+
const shape = unwrapObjectShape(schema);
|
|
121
|
+
if (shape && Object.keys(shape).length === 2 && "x" in shape && "y" in shape) return "vec2";
|
|
122
|
+
return "discrete";
|
|
123
|
+
}
|
|
124
|
+
function resolveBoardProperty(targetType, property) {
|
|
125
|
+
if (targetType === "camera") {
|
|
126
|
+
const spec = CAMERA_PROPERTIES[property];
|
|
127
|
+
return spec ? {
|
|
128
|
+
ok: true,
|
|
129
|
+
spec
|
|
130
|
+
} : {
|
|
131
|
+
ok: false,
|
|
132
|
+
message: `camera has no property ${property}; expected ${Object.keys(CAMERA_PROPERTIES).join(", ")}`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (targetType === "animation") {
|
|
136
|
+
const spec = ANIMATION_PROPERTIES[property];
|
|
137
|
+
return spec ? {
|
|
138
|
+
ok: true,
|
|
139
|
+
spec
|
|
140
|
+
} : {
|
|
141
|
+
ok: false,
|
|
142
|
+
message: `animations are driven through time`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const path = property.split(".");
|
|
146
|
+
if (path.some((segment) => !segment) || NOT_ANIMATABLE.has(path[0])) return {
|
|
147
|
+
ok: false,
|
|
148
|
+
message: `${property} cannot be animated`
|
|
149
|
+
};
|
|
150
|
+
const schema = boardItemSchema(targetType);
|
|
151
|
+
if (!schema) return {
|
|
152
|
+
ok: false,
|
|
153
|
+
message: `unknown item type ${targetType}`
|
|
154
|
+
};
|
|
155
|
+
if (!isBuiltinItemType(targetType) && path[0] === "props") return {
|
|
156
|
+
ok: true,
|
|
157
|
+
spec: {
|
|
158
|
+
kind: "discrete",
|
|
159
|
+
schema: z.unknown()
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const leaf = schemaAtPath(schema, path);
|
|
163
|
+
if (!leaf) return {
|
|
164
|
+
ok: false,
|
|
165
|
+
message: `${targetType} has no property ${property}`
|
|
166
|
+
};
|
|
167
|
+
const kind = kindOf(leaf, path);
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
spec: {
|
|
171
|
+
kind,
|
|
172
|
+
schema: unwrap(leaf),
|
|
173
|
+
...property === "opacity" || kind === "scale" ? { multiplicative: true } : {}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function listBoardProperties(targetType) {
|
|
178
|
+
if (targetType === "camera") return Object.entries(CAMERA_PROPERTIES).map(([property, spec]) => ({
|
|
179
|
+
property,
|
|
180
|
+
kind: spec.kind
|
|
181
|
+
}));
|
|
182
|
+
if (targetType === "animation") return [{
|
|
183
|
+
property: "time",
|
|
184
|
+
kind: "number"
|
|
185
|
+
}];
|
|
186
|
+
const schema = boardItemSchema(targetType);
|
|
187
|
+
const shape = schema ? unwrapObjectShape(schema) : null;
|
|
188
|
+
const result = [];
|
|
189
|
+
const visit = (field, path) => {
|
|
190
|
+
const kind = kindOf(field, path);
|
|
191
|
+
if (kind !== "discrete") {
|
|
192
|
+
result.push({
|
|
193
|
+
property: path.join("."),
|
|
194
|
+
kind
|
|
195
|
+
});
|
|
196
|
+
if (kind === "vec2" || kind === "scale") return;
|
|
197
|
+
}
|
|
198
|
+
const nested = unwrapObjectShape(field);
|
|
199
|
+
if (nested && kind !== "color") for (const [key, child] of Object.entries(nested)) visit(child, [...path, key]);
|
|
200
|
+
else if (kind === "discrete") result.push({
|
|
201
|
+
property: path.join("."),
|
|
202
|
+
kind
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
for (const [key, field] of Object.entries(shape ?? {})) {
|
|
206
|
+
if (NOT_ANIMATABLE.has(key) || key === "type") continue;
|
|
207
|
+
visit(field, [key]);
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
function lastKeyframeAt(trackPatches) {
|
|
212
|
+
let end = 0;
|
|
213
|
+
for (const track of trackPatches) {
|
|
214
|
+
const keyframes = track?.keyframes;
|
|
215
|
+
if (!Array.isArray(keyframes)) continue;
|
|
216
|
+
for (const keyframe of keyframes) {
|
|
217
|
+
const at = keyframe?.at;
|
|
218
|
+
if (typeof at === "number" && Number.isFinite(at)) end = Math.max(end, at);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return end > 0 ? end : void 0;
|
|
222
|
+
}
|
|
223
|
+
function trackReferences(track) {
|
|
224
|
+
const refs = /* @__PURE__ */ new Set([track.target]);
|
|
225
|
+
if (track.target === "camera" && track.property === "focus") {
|
|
226
|
+
for (const keyframe of track.keyframes) if (typeof keyframe.value === "string") refs.add(keyframe.value);
|
|
227
|
+
}
|
|
228
|
+
refs.delete(BOARD_CAMERA_TARGET);
|
|
229
|
+
return [...refs];
|
|
230
|
+
}
|
|
231
|
+
function pruneBoardTrack(track, deleted) {
|
|
232
|
+
if (deleted.has(track.target)) return null;
|
|
233
|
+
if (track.target !== "camera" || track.property !== "focus") return track;
|
|
234
|
+
const keyframes = track.keyframes.filter((entry) => typeof entry.value !== "string" || !deleted.has(entry.value));
|
|
235
|
+
if (keyframes.length === track.keyframes.length) return track;
|
|
236
|
+
return keyframes.length ? {
|
|
237
|
+
...track,
|
|
238
|
+
keyframes
|
|
239
|
+
} : null;
|
|
240
|
+
}
|
|
241
|
+
function boardPatchTouches(patch) {
|
|
242
|
+
const items = /* @__PURE__ */ new Set();
|
|
243
|
+
const deletedItems = [];
|
|
244
|
+
const parents = /* @__PURE__ */ new Set();
|
|
245
|
+
const references = /* @__PURE__ */ new Set();
|
|
246
|
+
for (const [id, value] of Object.entries(patch.items ?? {})) {
|
|
247
|
+
items.add(id);
|
|
248
|
+
if (value === null) {
|
|
249
|
+
deletedItems.push(id);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (typeof value.parent === "string") parents.add(value.parent);
|
|
253
|
+
const props = isRecord(value.props) ? value.props : null;
|
|
254
|
+
for (const end of [props?.start, props?.end]) if (isRecord(end) && typeof end.item === "string") references.add(end.item);
|
|
255
|
+
}
|
|
256
|
+
const animations = /* @__PURE__ */ new Set();
|
|
257
|
+
const deletedAnimations = [];
|
|
258
|
+
const tracks = [];
|
|
259
|
+
for (const [id, value] of Object.entries(patch.animations ?? {})) {
|
|
260
|
+
animations.add(id);
|
|
261
|
+
if (value === null) {
|
|
262
|
+
deletedAnimations.push(id);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
for (const [trackId, track] of Object.entries(value.tracks ?? {})) {
|
|
266
|
+
tracks.push({
|
|
267
|
+
animation: id,
|
|
268
|
+
track: trackId
|
|
269
|
+
});
|
|
270
|
+
if (!isRecord(track)) continue;
|
|
271
|
+
if (typeof track.target === "string") references.add(track.target);
|
|
272
|
+
if (Array.isArray(track.keyframes)) {
|
|
273
|
+
for (const keyframe of track.keyframes) if (isRecord(keyframe) && typeof keyframe.value === "string") references.add(keyframe.value);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
references.delete(BOARD_CAMERA_TARGET);
|
|
278
|
+
return {
|
|
279
|
+
items: [...items],
|
|
280
|
+
animations: [...animations],
|
|
281
|
+
tracks,
|
|
282
|
+
deletedItems,
|
|
283
|
+
deletedAnimations,
|
|
284
|
+
parents: [...parents],
|
|
285
|
+
references: [...references]
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function error(path, code, message) {
|
|
289
|
+
return {
|
|
290
|
+
severity: "error",
|
|
291
|
+
code,
|
|
292
|
+
path,
|
|
293
|
+
message
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function stripId(value) {
|
|
297
|
+
if (!("id" in value)) return value;
|
|
298
|
+
const { id: _id, ...rest } = value;
|
|
299
|
+
return rest;
|
|
300
|
+
}
|
|
301
|
+
function applyBoardPatch(state, input, options = {}) {
|
|
302
|
+
const parsedPatch = BoardPatchSchema.safeParse(input);
|
|
303
|
+
if (!parsedPatch.success) return {
|
|
304
|
+
ok: false,
|
|
305
|
+
diagnostics: parsedPatch.error.issues.map((issue) => error(issue.path.map(String).join("."), "INVALID_PATCH", issue.message))
|
|
306
|
+
};
|
|
307
|
+
const patch = parsedPatch.data;
|
|
308
|
+
const diagnostics = [];
|
|
309
|
+
const nextItems = /* @__PURE__ */ new Map();
|
|
310
|
+
const nextAnimations = /* @__PURE__ */ new Map();
|
|
311
|
+
const nextTracks = /* @__PURE__ */ new Map();
|
|
312
|
+
let nextSettings;
|
|
313
|
+
const item = (id) => nextItems.has(id) ? nextItems.get(id) ?? void 0 : state.item(id);
|
|
314
|
+
const animation = (id) => nextAnimations.has(id) ? nextAnimations.get(id) ?? void 0 : state.animation(id);
|
|
315
|
+
const setTrack = (animationId, trackId, value) => {
|
|
316
|
+
const tracks = nextTracks.get(animationId) ?? /* @__PURE__ */ new Map();
|
|
317
|
+
tracks.set(trackId, value);
|
|
318
|
+
nextTracks.set(animationId, tracks);
|
|
319
|
+
};
|
|
320
|
+
const track = (animationId, trackId) => {
|
|
321
|
+
const pending = nextTracks.get(animationId);
|
|
322
|
+
if (pending?.has(trackId)) return pending.get(trackId) ?? void 0;
|
|
323
|
+
return state.track(animationId, trackId);
|
|
324
|
+
};
|
|
325
|
+
if (patch.board !== void 0) {
|
|
326
|
+
const merged = patch.board === null ? {} : mergePatch(state.settings(), patch.board);
|
|
327
|
+
const parsed = BoardSettingsSchema.safeParse(merged);
|
|
328
|
+
if (parsed.success) nextSettings = parsed.data;
|
|
329
|
+
else for (const issue of parsed.error.issues) diagnostics.push(error(["board", ...issue.path.map(String)].join("."), "INVALID_BOARD", issue.message));
|
|
330
|
+
}
|
|
331
|
+
const zByParent = /* @__PURE__ */ new Map();
|
|
332
|
+
for (const [id, value] of Object.entries(patch.items ?? {})) {
|
|
333
|
+
if (value === null) {
|
|
334
|
+
if (state.item(id)) nextItems.set(id, null);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const current = state.item(id);
|
|
338
|
+
const body = stripId(value);
|
|
339
|
+
const candidate = !current || typeof body.type === "string" && body.type !== current.type ? { ...body } : mergePatch(current, body);
|
|
340
|
+
if (candidate.z === void 0) {
|
|
341
|
+
const parent = typeof candidate.parent === "string" ? candidate.parent : void 0;
|
|
342
|
+
const key = parent ?? "";
|
|
343
|
+
const top = zByParent.get(key) ?? state.maxZ(parent);
|
|
344
|
+
candidate.z = current?.z ?? top + 1;
|
|
345
|
+
if (!current) zByParent.set(key, top + 1);
|
|
346
|
+
}
|
|
347
|
+
const parsed = parseBoardItem(candidate, `items.${id}`);
|
|
348
|
+
if (!parsed.ok) {
|
|
349
|
+
diagnostics.push(...parsed.diagnostics);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (current && boardJsonEquals(current, parsed.item)) continue;
|
|
353
|
+
nextItems.set(id, parsed.item);
|
|
354
|
+
}
|
|
355
|
+
for (const [id, value] of Object.entries(patch.animations ?? {})) {
|
|
356
|
+
if (value === null) {
|
|
357
|
+
if (!state.animation(id)) continue;
|
|
358
|
+
nextAnimations.set(id, null);
|
|
359
|
+
for (const trackId of state.trackIds(id)) setTrack(id, trackId, null);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const { tracks, ...headerPatch } = stripId(value);
|
|
363
|
+
if (state.item(id) || nextItems.get(id)) {
|
|
364
|
+
diagnostics.push(error(`animations.${id}`, "ID_CONFLICT", `${id} is already an item id`));
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const current = state.animation(id);
|
|
368
|
+
if (!current || Object.keys(headerPatch).length > 0) {
|
|
369
|
+
const header = current ? mergePatch(current, headerPatch) : {
|
|
370
|
+
...headerPatch,
|
|
371
|
+
...headerPatch.duration === void 0 ? { duration: lastKeyframeAt([...Object.values(tracks ?? {}), ...state.trackIds(id).map((trackId) => state.track(id, trackId))]) } : {}
|
|
372
|
+
};
|
|
373
|
+
const parsed = BoardAnimationHeaderSchema.safeParse(header);
|
|
374
|
+
if (!parsed.success) {
|
|
375
|
+
for (const issue of parsed.error.issues) diagnostics.push(error([
|
|
376
|
+
"animations",
|
|
377
|
+
id,
|
|
378
|
+
...issue.path.map(String)
|
|
379
|
+
].join("."), "INVALID_ANIMATION", issue.message));
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (!current || !boardJsonEquals(current, parsed.data)) nextAnimations.set(id, parsed.data);
|
|
383
|
+
}
|
|
384
|
+
for (const [trackId, trackPatch] of Object.entries(tracks ?? {})) {
|
|
385
|
+
const existing = state.track(id, trackId);
|
|
386
|
+
if (trackPatch === null) {
|
|
387
|
+
if (existing) setTrack(id, trackId, null);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const parsed = BoardTrackSchema.safeParse(existing ? mergePatch(existing, trackPatch) : trackPatch);
|
|
391
|
+
if (!parsed.success) {
|
|
392
|
+
for (const issue of parsed.error.issues) diagnostics.push(error([
|
|
393
|
+
"animations",
|
|
394
|
+
id,
|
|
395
|
+
"tracks",
|
|
396
|
+
trackId,
|
|
397
|
+
...issue.path.map(String)
|
|
398
|
+
].join("."), "INVALID_TRACK", issue.message));
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (!existing || !boardJsonEquals(existing, parsed.data)) setTrack(id, trackId, parsed.data);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const id of nextItems.keys()) if (nextItems.get(id) && animation(id)) diagnostics.push(error(`items.${id}`, "ID_CONFLICT", `${id} is already an animation id`));
|
|
405
|
+
if (diagnostics.length) return {
|
|
406
|
+
ok: false,
|
|
407
|
+
diagnostics
|
|
408
|
+
};
|
|
409
|
+
const deletedItems = [...nextItems].filter(([, value]) => value === null).map(([id]) => id);
|
|
410
|
+
const deletionLayout = createBoardLayout((id) => item(id) ?? state.item(id));
|
|
411
|
+
const pendingDeletes = [...deletedItems];
|
|
412
|
+
while (pendingDeletes.length) {
|
|
413
|
+
const id = pendingDeletes.pop();
|
|
414
|
+
for (const child of state.children(id)) {
|
|
415
|
+
if (nextItems.has(child) && nextItems.get(child)?.parent !== id) continue;
|
|
416
|
+
if (options.cascade) {
|
|
417
|
+
if (nextItems.get(child) !== null) {
|
|
418
|
+
nextItems.set(child, null);
|
|
419
|
+
pendingDeletes.push(child);
|
|
420
|
+
}
|
|
421
|
+
} else diagnostics.push(error(`items.${id}`, "ITEM_REFERENCED", `${id} still has child ${child}; delete it too or use cascade`));
|
|
422
|
+
}
|
|
423
|
+
for (const arrowId of state.binders(id)) {
|
|
424
|
+
const arrow = item(arrowId);
|
|
425
|
+
if (arrow?.type !== "arrow") continue;
|
|
426
|
+
const props = { ...arrow.props };
|
|
427
|
+
for (const which of ["start", "end"]) {
|
|
428
|
+
const end = props[which];
|
|
429
|
+
if (!isArrowBinding(end) || end.item !== id) continue;
|
|
430
|
+
const world = options.resolveArrowEnd?.(arrowId, which) ?? deletionLayout.arrowEnd(arrowId, which);
|
|
431
|
+
props[which] = applyMatrix(invertMatrix(deletionLayout.matrix(arrowId)), world);
|
|
432
|
+
}
|
|
433
|
+
nextItems.set(arrowId, {
|
|
434
|
+
...arrow,
|
|
435
|
+
props
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
for (const ref of state.trackRefs(id)) {
|
|
439
|
+
const current = track(ref.animation, ref.track);
|
|
440
|
+
if (!current || !trackReferences(current).includes(id)) continue;
|
|
441
|
+
if (options.cascade) setTrack(ref.animation, ref.track, pruneBoardTrack(current, /* @__PURE__ */ new Set([id])));
|
|
442
|
+
else diagnostics.push(error(`items.${id}`, "ITEM_REFERENCED", `${id} is animated by ${ref.animation}.${ref.track}; delete the track or use cascade`));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
for (const [id, value] of nextAnimations) {
|
|
446
|
+
if (value !== null) continue;
|
|
447
|
+
for (const ref of state.trackRefs(id)) {
|
|
448
|
+
if (ref.animation === id || track(ref.animation, ref.track) === void 0) continue;
|
|
449
|
+
if (options.cascade) setTrack(ref.animation, ref.track, null);
|
|
450
|
+
else diagnostics.push(error(`animations.${id}`, "ANIMATION_REFERENCED", `${id} is nested in ${ref.animation}.${ref.track}; delete the track or use cascade`));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const validationState = {
|
|
454
|
+
...state,
|
|
455
|
+
trackIds: (id) => [.../* @__PURE__ */ new Set([...state.trackIds(id), ...nextTracks.get(id)?.keys() ?? []])]
|
|
456
|
+
};
|
|
457
|
+
const validationItems = new Map(nextItems);
|
|
458
|
+
const descendants = [];
|
|
459
|
+
for (const [id, value] of nextItems) if (value && value.parent !== state.item(id)?.parent) descendants.push(...state.children(id));
|
|
460
|
+
const seenDescendants = /* @__PURE__ */ new Set();
|
|
461
|
+
while (descendants.length) {
|
|
462
|
+
const id = descendants.pop();
|
|
463
|
+
if (seenDescendants.has(id)) continue;
|
|
464
|
+
seenDescendants.add(id);
|
|
465
|
+
validationItems.set(id, item(id) ?? null);
|
|
466
|
+
descendants.push(...state.children(id));
|
|
467
|
+
}
|
|
468
|
+
for (const [id, value] of validationItems) {
|
|
469
|
+
if (!value) continue;
|
|
470
|
+
if (value.parent !== void 0) {
|
|
471
|
+
const parent = item(value.parent);
|
|
472
|
+
if (!parent) diagnostics.push(error(`items.${id}.parent`, "PARENT_NOT_FOUND", `parent ${value.parent} does not exist`));
|
|
473
|
+
else if (parent.type !== "frame") diagnostics.push(error(`items.${id}.parent`, "INVALID_PARENT", `parent ${value.parent} is a ${parent.type}; only frames contain items`));
|
|
474
|
+
else {
|
|
475
|
+
let cursor = value.parent;
|
|
476
|
+
for (let depth = 0; cursor; depth += 1) {
|
|
477
|
+
if (depth >= 64) {
|
|
478
|
+
diagnostics.push(error(`items.${id}.parent`, "PARENT_DEPTH", "parent hierarchy exceeds 64 levels"));
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
if (cursor === id) {
|
|
482
|
+
diagnostics.push(error(`items.${id}.parent`, "PARENT_CYCLE", `${id} cannot be inside itself`));
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
cursor = item(cursor)?.parent;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
for (const bound of arrowBindings(value)) {
|
|
490
|
+
const target = item(bound);
|
|
491
|
+
if (!target) diagnostics.push(error(`items.${id}.props`, "BINDING_NOT_FOUND", `bound item ${bound} does not exist`));
|
|
492
|
+
else if (bound === id || target.type === "arrow") diagnostics.push(error(`items.${id}.props`, "INVALID_BINDING", "arrows bind to items other than arrows"));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
for (const [id, value] of nextItems) {
|
|
496
|
+
if (!value || !state.item(id) || state.item(id)?.type === value.type) continue;
|
|
497
|
+
if (value.type !== "frame") {
|
|
498
|
+
for (const child of state.children(id)) if (item(child)?.parent === id) diagnostics.push(error(`items.${child}.parent`, "INVALID_PARENT", `${id} is not a frame`));
|
|
499
|
+
}
|
|
500
|
+
if (value.type === "arrow") for (const binder of state.binders(id)) {
|
|
501
|
+
const arrow = item(binder);
|
|
502
|
+
if (arrow && arrowBindings(arrow).includes(id)) diagnostics.push(error(`items.${binder}.props`, "INVALID_BINDING", "arrows cannot bind to arrows"));
|
|
503
|
+
}
|
|
504
|
+
for (const ref of state.trackRefs(id)) {
|
|
505
|
+
const current = track(ref.animation, ref.track);
|
|
506
|
+
if (current) diagnostics.push(...validateTrack(`animations.${ref.animation}.tracks.${ref.track}`, ref.animation, current, item, animation, track, validationState));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
for (const [animationId, tracks] of nextTracks) {
|
|
510
|
+
if (!animation(animationId)) {
|
|
511
|
+
for (const [trackId, value] of tracks) if (value) diagnostics.push(error(`animations.${animationId}.tracks.${trackId}`, "ANIMATION_NOT_FOUND", `animation ${animationId} does not exist`));
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
for (const [trackId, value] of tracks) {
|
|
515
|
+
if (!value) continue;
|
|
516
|
+
diagnostics.push(...validateTrack(`animations.${animationId}.tracks.${trackId}`, animationId, value, item, animation, track, validationState));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (diagnostics.length) return {
|
|
520
|
+
ok: false,
|
|
521
|
+
diagnostics
|
|
522
|
+
};
|
|
523
|
+
const before = emptyDelta();
|
|
524
|
+
const after = emptyDelta();
|
|
525
|
+
if (nextSettings && !boardJsonEquals(nextSettings, state.settings())) {
|
|
526
|
+
before.board = state.settings();
|
|
527
|
+
after.board = nextSettings;
|
|
528
|
+
}
|
|
529
|
+
for (const [id, value] of nextItems) {
|
|
530
|
+
const previous = state.item(id) ?? null;
|
|
531
|
+
if (boardJsonEquals(previous, value)) continue;
|
|
532
|
+
before.items[id] = previous;
|
|
533
|
+
after.items[id] = value;
|
|
534
|
+
}
|
|
535
|
+
for (const [id, value] of nextAnimations) {
|
|
536
|
+
before.animations[id] = state.animation(id) ?? null;
|
|
537
|
+
after.animations[id] = value;
|
|
538
|
+
}
|
|
539
|
+
for (const [animationId, tracks] of nextTracks) for (const [trackId, value] of tracks) {
|
|
540
|
+
const previous = state.track(animationId, trackId) ?? null;
|
|
541
|
+
if (boardJsonEquals(previous, value)) continue;
|
|
542
|
+
slot(before.tracks, animationId)[trackId] = previous;
|
|
543
|
+
slot(after.tracks, animationId)[trackId] = value;
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
ok: true,
|
|
547
|
+
before: withoutEmpty(before),
|
|
548
|
+
after: withoutEmpty(after),
|
|
549
|
+
diagnostics: []
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function validateTrack(path, animationId, value, item, animation, track, state) {
|
|
553
|
+
const diagnostics = [];
|
|
554
|
+
let targetType;
|
|
555
|
+
if (value.target === "camera") targetType = BOARD_CAMERA_TARGET;
|
|
556
|
+
else if (item(value.target)) targetType = item(value.target).type;
|
|
557
|
+
else if (animation(value.target)) {
|
|
558
|
+
targetType = "animation";
|
|
559
|
+
if (value.target === animationId || nestsInto(value.target, animationId, animation, track, state)) return [error(`${path}.target`, "ANIMATION_CYCLE", `${animationId} cannot contain itself through ${value.target}`)];
|
|
560
|
+
} else return [error(`${path}.target`, "TARGET_NOT_FOUND", `target ${value.target} is not an item, animation or camera`)];
|
|
561
|
+
const resolved = resolveBoardProperty(targetType, value.property);
|
|
562
|
+
if (!resolved.ok) return [error(`${path}.property`, "INVALID_PROPERTY", resolved.message)];
|
|
563
|
+
for (const [index, keyframe] of value.keyframes.entries()) {
|
|
564
|
+
const parsed = resolved.spec.schema.safeParse(keyframe.value);
|
|
565
|
+
if (!parsed.success) {
|
|
566
|
+
diagnostics.push(error(`${path}.keyframes.${index}.value`, "INVALID_KEYFRAME", parsed.error.issues[0]?.message ?? "invalid value"));
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (value.property === "focus" && typeof keyframe.value === "string" && !item(keyframe.value)) diagnostics.push(error(`${path}.keyframes.${index}.value`, "TARGET_NOT_FOUND", `focus item ${keyframe.value} does not exist`));
|
|
570
|
+
}
|
|
571
|
+
if (value.interpolation === "spline" && resolved.spec.kind !== "vec2") diagnostics.push(error(`${path}.interpolation`, "INVALID_TRACK", "spline interpolation needs a position-like property"));
|
|
572
|
+
return diagnostics;
|
|
573
|
+
}
|
|
574
|
+
function nestsInto(from, into, animation, track, state) {
|
|
575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
576
|
+
const stack = [from];
|
|
577
|
+
while (stack.length) {
|
|
578
|
+
const current = stack.pop();
|
|
579
|
+
if (current === into) return true;
|
|
580
|
+
if (seen.has(current) || !animation(current)) continue;
|
|
581
|
+
seen.add(current);
|
|
582
|
+
for (const trackId of state.trackIds(current)) {
|
|
583
|
+
const value = track(current, trackId);
|
|
584
|
+
if (value && value.property === "time" && animation(value.target)) stack.push(value.target);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
function createDocumentState(document) {
|
|
590
|
+
let children = null;
|
|
591
|
+
let binders = null;
|
|
592
|
+
let refs = null;
|
|
593
|
+
const index = () => {
|
|
594
|
+
if (children) return;
|
|
595
|
+
children = /* @__PURE__ */ new Map();
|
|
596
|
+
binders = /* @__PURE__ */ new Map();
|
|
597
|
+
refs = /* @__PURE__ */ new Map();
|
|
598
|
+
for (const [id, value] of Object.entries(document.items)) {
|
|
599
|
+
if (value.parent) children.set(value.parent, [...children.get(value.parent) ?? [], id]);
|
|
600
|
+
for (const bound of arrowBindings(value)) binders.set(bound, [...binders.get(bound) ?? [], id]);
|
|
601
|
+
}
|
|
602
|
+
for (const [animationId, value] of Object.entries(document.animations)) for (const [trackId, track] of Object.entries(value.tracks)) for (const ref of trackReferences(track)) refs.set(ref, [...refs.get(ref) ?? [], {
|
|
603
|
+
animation: animationId,
|
|
604
|
+
track: trackId
|
|
605
|
+
}]);
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
settings: () => document.board,
|
|
609
|
+
item: (id) => document.items[id],
|
|
610
|
+
animation: (id) => {
|
|
611
|
+
const value = document.animations[id];
|
|
612
|
+
if (!value) return void 0;
|
|
613
|
+
const { tracks: _tracks, ...header } = value;
|
|
614
|
+
return header;
|
|
615
|
+
},
|
|
616
|
+
track: (animationId, trackId) => document.animations[animationId]?.tracks[trackId],
|
|
617
|
+
trackIds: (animationId) => Object.keys(document.animations[animationId]?.tracks ?? {}),
|
|
618
|
+
children: (id) => {
|
|
619
|
+
index();
|
|
620
|
+
return children?.get(id) ?? [];
|
|
621
|
+
},
|
|
622
|
+
binders: (id) => {
|
|
623
|
+
index();
|
|
624
|
+
return binders?.get(id) ?? [];
|
|
625
|
+
},
|
|
626
|
+
trackRefs: (id) => {
|
|
627
|
+
index();
|
|
628
|
+
return refs?.get(id) ?? [];
|
|
629
|
+
},
|
|
630
|
+
maxZ: (parent) => {
|
|
631
|
+
let max = 0;
|
|
632
|
+
for (const value of Object.values(document.items)) if (value.parent === parent && (value.z ?? 0) > max) max = value.z ?? 0;
|
|
633
|
+
return max;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function applyBoardDelta(document, delta) {
|
|
638
|
+
const items = delta.items ? { ...document.items } : document.items;
|
|
639
|
+
for (const [id, value] of Object.entries(delta.items ?? {})) if (value) items[id] = value;
|
|
640
|
+
else delete items[id];
|
|
641
|
+
const animations = delta.animations || delta.tracks ? { ...document.animations } : document.animations;
|
|
642
|
+
for (const [id, value] of Object.entries(delta.animations ?? {})) if (value) animations[id] = {
|
|
643
|
+
...value,
|
|
644
|
+
tracks: animations[id]?.tracks ?? {}
|
|
645
|
+
};
|
|
646
|
+
else delete animations[id];
|
|
647
|
+
for (const [animationId, tracks] of Object.entries(delta.tracks ?? {})) {
|
|
648
|
+
const current = animations[animationId];
|
|
649
|
+
if (!current) continue;
|
|
650
|
+
const nextTracks = { ...current.tracks };
|
|
651
|
+
for (const [trackId, value] of Object.entries(tracks)) if (value) nextTracks[trackId] = value;
|
|
652
|
+
else delete nextTracks[trackId];
|
|
653
|
+
animations[animationId] = {
|
|
654
|
+
...current,
|
|
655
|
+
tracks: nextTracks
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
return {
|
|
659
|
+
board: delta.board ?? document.board,
|
|
660
|
+
items,
|
|
661
|
+
animations
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function applyBoardPatchToDocument(document, patch, options = {}) {
|
|
665
|
+
const result = applyBoardPatch(createDocumentState(document), patch, options);
|
|
666
|
+
return result.ok ? {
|
|
667
|
+
ok: true,
|
|
668
|
+
document: applyBoardDelta(document, result.after),
|
|
669
|
+
before: result.before,
|
|
670
|
+
after: result.after
|
|
671
|
+
} : result;
|
|
672
|
+
}
|
|
673
|
+
function diffBoardDocuments(before, after) {
|
|
674
|
+
const items = {};
|
|
675
|
+
for (const id of /* @__PURE__ */ new Set([...Object.keys(before.items), ...Object.keys(after.items)])) {
|
|
676
|
+
const previous = before.items[id];
|
|
677
|
+
const next = after.items[id];
|
|
678
|
+
if (previous === next) continue;
|
|
679
|
+
const value = !next ? null : previous && previous.type === next.type ? diffMergePatch(previous, next) : next;
|
|
680
|
+
if (value !== void 0) items[id] = value;
|
|
681
|
+
}
|
|
682
|
+
const animations = {};
|
|
683
|
+
for (const id of /* @__PURE__ */ new Set([...Object.keys(before.animations), ...Object.keys(after.animations)])) {
|
|
684
|
+
const previous = before.animations[id];
|
|
685
|
+
const next = after.animations[id];
|
|
686
|
+
if (previous === next) continue;
|
|
687
|
+
if (!next || !previous) {
|
|
688
|
+
animations[id] = next ?? null;
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
const { tracks: previousTracks, ...previousHeader } = previous;
|
|
692
|
+
const { tracks: nextTracks, ...nextHeader } = next;
|
|
693
|
+
const header = diffMergePatch(previousHeader, nextHeader);
|
|
694
|
+
const tracks = {};
|
|
695
|
+
for (const trackId of /* @__PURE__ */ new Set([...Object.keys(previousTracks), ...Object.keys(nextTracks)])) {
|
|
696
|
+
const value = diffMergePatch(previousTracks[trackId], nextTracks[trackId]);
|
|
697
|
+
if (value !== void 0) tracks[trackId] = value;
|
|
698
|
+
}
|
|
699
|
+
const entry = withoutEmpty({
|
|
700
|
+
...isRecord(header) ? header : {},
|
|
701
|
+
tracks
|
|
702
|
+
});
|
|
703
|
+
if (Object.keys(entry).length) animations[id] = entry;
|
|
704
|
+
}
|
|
705
|
+
return withoutEmpty({
|
|
706
|
+
board: diffMergePatch(before.board, after.board),
|
|
707
|
+
items,
|
|
708
|
+
animations
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
function isEmptyBoardPatch(patch) {
|
|
712
|
+
return patch.board === void 0 && Object.keys(patch.items ?? {}).length === 0 && Object.keys(patch.animations ?? {}).length === 0;
|
|
713
|
+
}
|
|
714
|
+
function parseBoardDocument(input) {
|
|
715
|
+
if (!isRecord(input)) return {
|
|
716
|
+
ok: false,
|
|
717
|
+
diagnostics: [error("", "INVALID_DOCUMENT", "expected an object")]
|
|
718
|
+
};
|
|
719
|
+
const result = applyBoardPatchToDocument({
|
|
720
|
+
board: BoardSettingsSchema.parse({}),
|
|
721
|
+
items: {},
|
|
722
|
+
animations: {}
|
|
723
|
+
}, {
|
|
724
|
+
...isRecord(input.board) ? { board: input.board } : {},
|
|
725
|
+
...isRecord(input.items) ? { items: input.items } : {},
|
|
726
|
+
...isRecord(input.animations) ? { animations: input.animations } : {}
|
|
727
|
+
});
|
|
728
|
+
return result.ok ? {
|
|
729
|
+
ok: true,
|
|
730
|
+
document: result.document
|
|
731
|
+
} : result;
|
|
732
|
+
}
|
|
733
|
+
//#endregion
|
|
734
|
+
export { applyBoardDelta, applyBoardPatch, applyBoardPatchToDocument, boardPatchTouches, createDocumentState, diffBoardDocuments, diffMergePatch, isEmptyBoardPatch, listBoardProperties, mergePatch, parseBoardDocument, pruneBoardTrack, resolveBoardProperty, trackReferences };
|