@glissade/core 0.39.0 → 0.40.0-pre.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/clips.js +2 -1
- package/dist/expr.d.ts +32 -0
- package/dist/expr.js +344 -0
- package/dist/index.js +3 -16
- package/dist/rng.js +16 -0
- package/dist/sidecar.js +2 -1
- package/dist/targetRef.js +1 -1155
- package/dist/timeline.js +1 -1
- package/dist/track.d.ts +15 -0
- package/dist/track.js +1194 -0
- package/package.json +8 -2
package/dist/clips.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as inferValueType, d as track, n as key } from "./track.js";
|
|
2
|
+
import { i as resolveTweenTarget, t as TARGET_PATH } from "./targetRef.js";
|
|
2
3
|
//#region src/clip.ts
|
|
3
4
|
/**
|
|
4
5
|
* Motion clips (DESIGN.md §2 build-time sugar): `clip()` captures a relative-time
|
package/dist/expr.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { i as Track } from "./track.js";
|
|
2
|
+
|
|
3
|
+
//#region src/expr.d.ts
|
|
4
|
+
|
|
5
|
+
declare class ExprError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** A compiled expression: evaluate at a scope (must include `t`). */
|
|
9
|
+
interface CompiledExpr {
|
|
10
|
+
/** the original source (serializable — a Track stores this). */
|
|
11
|
+
readonly source: string;
|
|
12
|
+
/** evaluate the formula; `scope.t` is the playhead time. Pure in its scope. */
|
|
13
|
+
eval(scope: Record<string, number>): number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Compile an expression source to a {@link CompiledExpr}. Parses ONCE (fails loud
|
|
17
|
+
* on syntax / unknown function / arity / trailing tokens); `eval(scope)` is a fast
|
|
18
|
+
* pure closure. `scope.t` is the playhead time; add more vars (e.g. `i`, `n`) as
|
|
19
|
+
* the binding provides them.
|
|
20
|
+
*/
|
|
21
|
+
declare function compileExpr(source: string): CompiledExpr;
|
|
22
|
+
/** The names available to an expression (for docs / a describe() surface). */
|
|
23
|
+
declare const EXPR_FUNCTIONS: string[];
|
|
24
|
+
declare const EXPR_CONSTANTS: string[];
|
|
25
|
+
/**
|
|
26
|
+
* A raw formula-driven numeric track — `exprTrack('circle/opacity', '0.5 +
|
|
27
|
+
* 0.5*sin(t*3)')`. Sampled by evaluating the formula at the playhead `t` (no
|
|
28
|
+
* keys); compile-validated now (fail loud on bad syntax / unknown fn / arity).
|
|
29
|
+
*/
|
|
30
|
+
declare function exprTrack(target: string, formula: string): Track<number>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { CompiledExpr, EXPR_CONSTANTS, EXPR_FUNCTIONS, ExprError, compileExpr, exprTrack };
|
package/dist/expr.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { c as setExprCompiler, r as makeExprTrack } from "./track.js";
|
|
2
|
+
import { t as random } from "./rng.js";
|
|
3
|
+
//#region src/expr.ts
|
|
4
|
+
/**
|
|
5
|
+
* `Expr` (0.40) — a deterministic math-expression evaluator for animating a prop
|
|
6
|
+
* by a FORMULA of time: `expr('100 + 50*sin(t*2)')`. Compiles a source string ONCE
|
|
7
|
+
* to a fast closure `(scope) => number`; the Track sampler evaluates it at the
|
|
8
|
+
* playhead `t` through the SAME channel keyframe interpolation uses (so it's a pure
|
|
9
|
+
* function of time, byte-identical run-to-run, and needs no ambient-time design —
|
|
10
|
+
* glissade already threads `t` to the sampler).
|
|
11
|
+
*
|
|
12
|
+
* Grammar (arithmetic; no side effects): numbers, the variable `t` (+ any scope
|
|
13
|
+
* var), `+ - * / % ^` (^ = pow, right-assoc), unary ±, parens, and a WHITELIST of
|
|
14
|
+
* pure functions/constants. Determinism is enforced by construction: the only
|
|
15
|
+
* randomness is `rand(x)` (a seeded hash → [0,1)); there is no `Date`/`Math.random`
|
|
16
|
+
* reachable, and an unknown identifier/function fails loud at COMPILE time.
|
|
17
|
+
*/
|
|
18
|
+
var ExprError = class extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "ExprError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const fract = (x) => x - Math.floor(x);
|
|
25
|
+
const clamp = (x, lo, hi) => x < lo ? lo : x > hi ? hi : x;
|
|
26
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
27
|
+
const smoothstep = (e0, e1, x) => {
|
|
28
|
+
const t = clamp(e1 === e0 ? 0 : (x - e0) / (e1 - e0), 0, 1);
|
|
29
|
+
return t * t * (3 - 2 * t);
|
|
30
|
+
};
|
|
31
|
+
/** deterministic hash of `x` → [0, 1) — the ONLY randomness (seeded, pure). */
|
|
32
|
+
const rand = (x) => {
|
|
33
|
+
const buf = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(8));
|
|
34
|
+
buf.setFloat64(0, x);
|
|
35
|
+
return random((buf.getUint32(0) ^ buf.getUint32(4)) >>> 0)();
|
|
36
|
+
};
|
|
37
|
+
const CONSTS = {
|
|
38
|
+
PI: Math.PI,
|
|
39
|
+
TAU: Math.PI * 2,
|
|
40
|
+
E: Math.E
|
|
41
|
+
};
|
|
42
|
+
const FNS = {
|
|
43
|
+
sin: {
|
|
44
|
+
fn: Math.sin,
|
|
45
|
+
arity: 1
|
|
46
|
+
},
|
|
47
|
+
cos: {
|
|
48
|
+
fn: Math.cos,
|
|
49
|
+
arity: 1
|
|
50
|
+
},
|
|
51
|
+
tan: {
|
|
52
|
+
fn: Math.tan,
|
|
53
|
+
arity: 1
|
|
54
|
+
},
|
|
55
|
+
asin: {
|
|
56
|
+
fn: Math.asin,
|
|
57
|
+
arity: 1
|
|
58
|
+
},
|
|
59
|
+
acos: {
|
|
60
|
+
fn: Math.acos,
|
|
61
|
+
arity: 1
|
|
62
|
+
},
|
|
63
|
+
atan: {
|
|
64
|
+
fn: Math.atan,
|
|
65
|
+
arity: 1
|
|
66
|
+
},
|
|
67
|
+
abs: {
|
|
68
|
+
fn: Math.abs,
|
|
69
|
+
arity: 1
|
|
70
|
+
},
|
|
71
|
+
sqrt: {
|
|
72
|
+
fn: Math.sqrt,
|
|
73
|
+
arity: 1
|
|
74
|
+
},
|
|
75
|
+
exp: {
|
|
76
|
+
fn: Math.exp,
|
|
77
|
+
arity: 1
|
|
78
|
+
},
|
|
79
|
+
log: {
|
|
80
|
+
fn: Math.log,
|
|
81
|
+
arity: 1
|
|
82
|
+
},
|
|
83
|
+
floor: {
|
|
84
|
+
fn: Math.floor,
|
|
85
|
+
arity: 1
|
|
86
|
+
},
|
|
87
|
+
ceil: {
|
|
88
|
+
fn: Math.ceil,
|
|
89
|
+
arity: 1
|
|
90
|
+
},
|
|
91
|
+
round: {
|
|
92
|
+
fn: Math.round,
|
|
93
|
+
arity: 1
|
|
94
|
+
},
|
|
95
|
+
sign: {
|
|
96
|
+
fn: Math.sign,
|
|
97
|
+
arity: 1
|
|
98
|
+
},
|
|
99
|
+
fract: {
|
|
100
|
+
fn: fract,
|
|
101
|
+
arity: 1
|
|
102
|
+
},
|
|
103
|
+
rand: {
|
|
104
|
+
fn: rand,
|
|
105
|
+
arity: 1
|
|
106
|
+
},
|
|
107
|
+
atan2: {
|
|
108
|
+
fn: Math.atan2,
|
|
109
|
+
arity: 2
|
|
110
|
+
},
|
|
111
|
+
pow: {
|
|
112
|
+
fn: Math.pow,
|
|
113
|
+
arity: 2
|
|
114
|
+
},
|
|
115
|
+
mod: {
|
|
116
|
+
fn: (a, b) => (a % b + b) % b,
|
|
117
|
+
arity: 2
|
|
118
|
+
},
|
|
119
|
+
step: {
|
|
120
|
+
fn: (edge, x) => x < edge ? 0 : 1,
|
|
121
|
+
arity: 2
|
|
122
|
+
},
|
|
123
|
+
min: {
|
|
124
|
+
fn: Math.min,
|
|
125
|
+
arity: "variadic"
|
|
126
|
+
},
|
|
127
|
+
max: {
|
|
128
|
+
fn: Math.max,
|
|
129
|
+
arity: "variadic"
|
|
130
|
+
},
|
|
131
|
+
clamp: {
|
|
132
|
+
fn: clamp,
|
|
133
|
+
arity: 3
|
|
134
|
+
},
|
|
135
|
+
lerp: {
|
|
136
|
+
fn: lerp,
|
|
137
|
+
arity: 3
|
|
138
|
+
},
|
|
139
|
+
mix: {
|
|
140
|
+
fn: lerp,
|
|
141
|
+
arity: 3
|
|
142
|
+
},
|
|
143
|
+
smoothstep: {
|
|
144
|
+
fn: smoothstep,
|
|
145
|
+
arity: 3
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
function tokenize(src) {
|
|
149
|
+
const toks = [];
|
|
150
|
+
let i = 0;
|
|
151
|
+
const n = src.length;
|
|
152
|
+
while (i < n) {
|
|
153
|
+
const c = src[i];
|
|
154
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
155
|
+
i++;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (c === "(") {
|
|
159
|
+
toks.push({ k: "(" });
|
|
160
|
+
i++;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (c === ")") {
|
|
164
|
+
toks.push({ k: ")" });
|
|
165
|
+
i++;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (c === ",") {
|
|
169
|
+
toks.push({ k: "," });
|
|
170
|
+
i++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if ("+-*/%^".includes(c)) {
|
|
174
|
+
toks.push({
|
|
175
|
+
k: "op",
|
|
176
|
+
v: c
|
|
177
|
+
});
|
|
178
|
+
i++;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (c >= "0" && c <= "9" || c === ".") {
|
|
182
|
+
let j = i + 1;
|
|
183
|
+
while (j < n && /[0-9.eE+\-]/.test(src[j])) {
|
|
184
|
+
if ((src[j] === "+" || src[j] === "-") && !/[eE]/.test(src[j - 1])) break;
|
|
185
|
+
j++;
|
|
186
|
+
}
|
|
187
|
+
const num = Number(src.slice(i, j));
|
|
188
|
+
if (!Number.isFinite(num)) throw new ExprError(`invalid number '${src.slice(i, j)}' in expr`);
|
|
189
|
+
toks.push({
|
|
190
|
+
k: "num",
|
|
191
|
+
v: num
|
|
192
|
+
});
|
|
193
|
+
i = j;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (/[a-zA-Z_]/.test(c)) {
|
|
197
|
+
let j = i + 1;
|
|
198
|
+
while (j < n && /[a-zA-Z0-9_]/.test(src[j])) j++;
|
|
199
|
+
toks.push({
|
|
200
|
+
k: "id",
|
|
201
|
+
v: src.slice(i, j)
|
|
202
|
+
});
|
|
203
|
+
i = j;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
throw new ExprError(`unexpected character '${c}' in expr '${src}'`);
|
|
207
|
+
}
|
|
208
|
+
return toks;
|
|
209
|
+
}
|
|
210
|
+
const BINOP = {
|
|
211
|
+
"+": {
|
|
212
|
+
prec: 1,
|
|
213
|
+
apply: (a, b) => a + b
|
|
214
|
+
},
|
|
215
|
+
"-": {
|
|
216
|
+
prec: 1,
|
|
217
|
+
apply: (a, b) => a - b
|
|
218
|
+
},
|
|
219
|
+
"*": {
|
|
220
|
+
prec: 2,
|
|
221
|
+
apply: (a, b) => a * b
|
|
222
|
+
},
|
|
223
|
+
"/": {
|
|
224
|
+
prec: 2,
|
|
225
|
+
apply: (a, b) => a / b
|
|
226
|
+
},
|
|
227
|
+
"%": {
|
|
228
|
+
prec: 2,
|
|
229
|
+
apply: (a, b) => (a % b + b) % b
|
|
230
|
+
},
|
|
231
|
+
"^": {
|
|
232
|
+
prec: 4,
|
|
233
|
+
rightAssoc: true,
|
|
234
|
+
apply: (a, b) => Math.pow(a, b)
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function parse(src) {
|
|
238
|
+
const toks = tokenize(src);
|
|
239
|
+
if (toks.length === 0) throw new ExprError(`empty expr`);
|
|
240
|
+
let p = 0;
|
|
241
|
+
const peek = () => toks[p];
|
|
242
|
+
const next = () => toks[p++];
|
|
243
|
+
function parsePrimary() {
|
|
244
|
+
const t = peek();
|
|
245
|
+
if (!t) throw new ExprError(`unexpected end of expr '${src}'`);
|
|
246
|
+
if (t.k === "op" && (t.v === "-" || t.v === "+")) {
|
|
247
|
+
next();
|
|
248
|
+
const operand = parseUnary();
|
|
249
|
+
return t.v === "-" ? (s) => -operand(s) : operand;
|
|
250
|
+
}
|
|
251
|
+
if (t.k === "num") {
|
|
252
|
+
next();
|
|
253
|
+
return () => t.v;
|
|
254
|
+
}
|
|
255
|
+
if (t.k === "(") {
|
|
256
|
+
next();
|
|
257
|
+
const e = parseExpr(0);
|
|
258
|
+
if (peek()?.k !== ")") throw new ExprError(`missing ')' in expr '${src}'`);
|
|
259
|
+
next();
|
|
260
|
+
return e;
|
|
261
|
+
}
|
|
262
|
+
if (t.k === "id") {
|
|
263
|
+
next();
|
|
264
|
+
const name = t.v;
|
|
265
|
+
if (peek()?.k === "(") {
|
|
266
|
+
next();
|
|
267
|
+
const args = [];
|
|
268
|
+
if (peek()?.k !== ")") {
|
|
269
|
+
args.push(parseExpr(0));
|
|
270
|
+
while (peek()?.k === ",") {
|
|
271
|
+
next();
|
|
272
|
+
args.push(parseExpr(0));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (peek()?.k !== ")") throw new ExprError(`missing ')' after ${name}(… in expr '${src}'`);
|
|
276
|
+
next();
|
|
277
|
+
const spec = FNS[name];
|
|
278
|
+
if (!spec) throw new ExprError(`unknown function '${name}' in expr '${src}' (have: ${Object.keys(FNS).join(", ")})`);
|
|
279
|
+
if (spec.arity !== "variadic" && spec.arity !== args.length) throw new ExprError(`${name}() takes ${spec.arity} arg(s), got ${args.length}`);
|
|
280
|
+
return (s) => spec.fn(...args.map((a) => a(s)));
|
|
281
|
+
}
|
|
282
|
+
if (name in CONSTS) {
|
|
283
|
+
const v = CONSTS[name];
|
|
284
|
+
return () => v;
|
|
285
|
+
}
|
|
286
|
+
return (s) => {
|
|
287
|
+
const v = s[name];
|
|
288
|
+
if (v === void 0) throw new ExprError(`unknown variable '${name}' in expr '${src}' (scope has: ${Object.keys(s).join(", ") || "nothing"})`);
|
|
289
|
+
return v;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
throw new ExprError(`unexpected token in expr '${src}'`);
|
|
293
|
+
}
|
|
294
|
+
function parseUnary() {
|
|
295
|
+
return parsePrimary();
|
|
296
|
+
}
|
|
297
|
+
function parseExpr(minPrec) {
|
|
298
|
+
let left = parseUnary();
|
|
299
|
+
for (;;) {
|
|
300
|
+
const t = peek();
|
|
301
|
+
if (!t || t.k !== "op") break;
|
|
302
|
+
const op = BINOP[t.v];
|
|
303
|
+
if (!op || op.prec < minPrec) break;
|
|
304
|
+
next();
|
|
305
|
+
const right = parseExpr(op.rightAssoc ? op.prec : op.prec + 1);
|
|
306
|
+
const l = left;
|
|
307
|
+
left = (s) => op.apply(l(s), right(s));
|
|
308
|
+
}
|
|
309
|
+
return left;
|
|
310
|
+
}
|
|
311
|
+
const root = parseExpr(0);
|
|
312
|
+
if (p !== toks.length) throw new ExprError(`unexpected trailing tokens in expr '${src}'`);
|
|
313
|
+
return root;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Compile an expression source to a {@link CompiledExpr}. Parses ONCE (fails loud
|
|
317
|
+
* on syntax / unknown function / arity / trailing tokens); `eval(scope)` is a fast
|
|
318
|
+
* pure closure. `scope.t` is the playhead time; add more vars (e.g. `i`, `n`) as
|
|
319
|
+
* the binding provides them.
|
|
320
|
+
*/
|
|
321
|
+
function compileExpr(source) {
|
|
322
|
+
const node = parse(source);
|
|
323
|
+
return {
|
|
324
|
+
source,
|
|
325
|
+
eval: (scope) => node(scope)
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/** The names available to an expression (for docs / a describe() surface). */
|
|
329
|
+
const EXPR_FUNCTIONS = Object.keys(FNS);
|
|
330
|
+
const EXPR_CONSTANTS = Object.keys(CONSTS);
|
|
331
|
+
/**
|
|
332
|
+
* A raw formula-driven numeric track — `exprTrack('circle/opacity', '0.5 +
|
|
333
|
+
* 0.5*sin(t*3)')`. Sampled by evaluating the formula at the playhead `t` (no
|
|
334
|
+
* keys); compile-validated now (fail loud on bad syntax / unknown fn / arity).
|
|
335
|
+
*/
|
|
336
|
+
function exprTrack(target, formula) {
|
|
337
|
+
return makeExprTrack(target, formula);
|
|
338
|
+
}
|
|
339
|
+
setExprCompiler((src) => {
|
|
340
|
+
const c = compileExpr(src);
|
|
341
|
+
return (t) => c.eval({ t });
|
|
342
|
+
});
|
|
343
|
+
//#endregion
|
|
344
|
+
export { EXPR_CONSTANTS, EXPR_FUNCTIONS, ExprError, compileExpr, exprTrack };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as vec2Type, B as springEasing, C as paintType, D as stringType, E as reprOf, F as parseColor, G as cubicBezier, H as springPresets, I as rgbaToOklab, J as easings, K as cubicBezierDerivative, L as emitDevWarning, M as formatColor, N as lerpColor, O as vec2ArcType, P as oklabToRgba, R as setDevWarning, S as numberType, T as registerValueType, U as DEFAULT_EASE, V as springEasingDerivative, W as UnknownEasingError, Y as namedEasing, _ as colorType, a as resolveEaseDerivative, b as inferValueType, d as track, f as validateTrack, g as booleanType, h as ValueTypeInferenceError, i as resolveEase, j as ColorParseError, k as vec2Equals, l as springTo, m as UnknownValueTypeError, n as key, o as retime, p as velocityAt, q as easingDerivatives, s as sampleTrack, t as TrackValidationError, u as stagger, v as fontAxesType, w as pathType, x as listValueTypes, y as getValueType, z as spring } from "./track.js";
|
|
2
|
+
import { a as targetNodeId, i as resolveTweenTarget, n as UnresolvableTargetError, r as isEditableNodeId, t as TARGET_PATH } from "./targetRef.js";
|
|
3
|
+
import { t as random } from "./rng.js";
|
|
2
4
|
import { t as parseCmap } from "./cmap.js";
|
|
3
5
|
import { a as isDurationEditable, i as compileTimeline, n as audioOffsetSamples, o as namespaceCallName, r as callMarkerPrefix, s as timeline$1, t as TimelineValidationError } from "./timeline.js";
|
|
4
6
|
//#region src/ticker.ts
|
|
@@ -1002,21 +1004,6 @@ function evaluateAt(playhead, t, read) {
|
|
|
1002
1004
|
}
|
|
1003
1005
|
}
|
|
1004
1006
|
//#endregion
|
|
1005
|
-
//#region src/rng.ts
|
|
1006
|
-
/** Returns a deterministic [0,1) generator for the given integer seed. */
|
|
1007
|
-
function random(seed) {
|
|
1008
|
-
let a = seed >>> 0;
|
|
1009
|
-
return () => {
|
|
1010
|
-
a = a + 2654435769 | 0;
|
|
1011
|
-
let t = a ^ a >>> 16;
|
|
1012
|
-
t = Math.imul(t, 569420461);
|
|
1013
|
-
t = t ^ t >>> 15;
|
|
1014
|
-
t = Math.imul(t, 1935289751);
|
|
1015
|
-
t = t ^ t >>> 15;
|
|
1016
|
-
return (t >>> 0) / 4294967296;
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
//#endregion
|
|
1020
1007
|
//#region src/bake.ts
|
|
1021
1008
|
/**
|
|
1022
1009
|
* bake() (DESIGN.md §2.8): stateful simulation as a compilation step. Run the
|
package/dist/rng.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/rng.ts
|
|
2
|
+
/** Returns a deterministic [0,1) generator for the given integer seed. */
|
|
3
|
+
function random(seed) {
|
|
4
|
+
let a = seed >>> 0;
|
|
5
|
+
return () => {
|
|
6
|
+
a = a + 2654435769 | 0;
|
|
7
|
+
let t = a ^ a >>> 16;
|
|
8
|
+
t = Math.imul(t, 569420461);
|
|
9
|
+
t = t ^ t >>> 15;
|
|
10
|
+
t = Math.imul(t, 1935289751);
|
|
11
|
+
t = t ^ t >>> 15;
|
|
12
|
+
return (t >>> 0) / 4294967296;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { random as t };
|
package/dist/sidecar.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { L as emitDevWarning, t as TrackValidationError, z as spring } from "./track.js";
|
|
2
|
+
import { a as targetNodeId, r as isEditableNodeId } from "./targetRef.js";
|
|
2
3
|
//#region src/sidecar.ts
|
|
3
4
|
/**
|
|
4
5
|
* The editor sidecar (DESIGN.md §6.2): code declares scene structure and
|