@clipkit/editor-core 1.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/LICENSE +201 -0
- package/README.md +20 -0
- package/dist/asset-store.d.ts +40 -0
- package/dist/asset-store.d.ts.map +1 -0
- package/dist/asset-store.js +181 -0
- package/dist/asset-store.js.map +1 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +23 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/node-graph.d.ts +42 -0
- package/dist/node-graph.d.ts.map +1 -0
- package/dist/node-graph.js +123 -0
- package/dist/node-graph.js.map +1 -0
- package/dist/registry/build.d.ts +3 -0
- package/dist/registry/build.d.ts.map +1 -0
- package/dist/registry/build.js +84 -0
- package/dist/registry/build.js.map +1 -0
- package/dist/registry/configuration.d.ts +51 -0
- package/dist/registry/configuration.d.ts.map +1 -0
- package/dist/registry/configuration.js +92 -0
- package/dist/registry/configuration.js.map +1 -0
- package/dist/registry/derive.d.ts +30 -0
- package/dist/registry/derive.d.ts.map +1 -0
- package/dist/registry/derive.js +212 -0
- package/dist/registry/derive.js.map +1 -0
- package/dist/registry/overrides.d.ts +7 -0
- package/dist/registry/overrides.d.ts.map +1 -0
- package/dist/registry/overrides.js +322 -0
- package/dist/registry/overrides.js.map +1 -0
- package/dist/registry/types.d.ts +58 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +7 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/source-diff.d.ts +8 -0
- package/dist/source-diff.d.ts.map +1 -0
- package/dist/source-diff.js +148 -0
- package/dist/source-diff.js.map +1 -0
- package/dist/stage-utils.d.ts +170 -0
- package/dist/stage-utils.d.ts.map +1 -0
- package/dist/stage-utils.js +476 -0
- package/dist/stage-utils.js.map +1 -0
- package/dist/store.d.ts +123 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +234 -0
- package/dist/store.js.map +1 -0
- package/dist/timeline-utils.d.ts +69 -0
- package/dist/timeline-utils.d.ts.map +1 -0
- package/dist/timeline-utils.js +211 -0
- package/dist/timeline-utils.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/useEditor.d.ts +77 -0
- package/dist/useEditor.d.ts.map +1 -0
- package/dist/useEditor.js +74 -0
- package/dist/useEditor.js.map +1 -0
- package/dist/useEditorStore.d.ts +3 -0
- package/dist/useEditorStore.d.ts.map +1 -0
- package/dist/useEditorStore.js +15 -0
- package/dist/useEditorStore.js.map +1 -0
- package/dist/usePlaybackSession.d.ts +20 -0
- package/dist/usePlaybackSession.d.ts.map +1 -0
- package/dist/usePlaybackSession.js +120 -0
- package/dist/usePlaybackSession.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Node-graph derivation — a LENS over the Source (never serialized).
|
|
2
|
+
//
|
|
3
|
+
// A Clipkit Source is a tree painted in track/z order, where each element
|
|
4
|
+
// carries an ordered `effects[]` chain. A node graph is just a different
|
|
5
|
+
// drawing of that same tree: element → node, effects → a chain of nodes,
|
|
6
|
+
// `group` → a merge node fed by its children, and the whole composite
|
|
7
|
+
// flows into one Output node. Everything here is DERIVED from the Source
|
|
8
|
+
// (the lens rule), so the editor can show/edit it without any protocol
|
|
9
|
+
// change. UI-free on purpose: colors/icons live in the renderer.
|
|
10
|
+
import { elementLabel } from './timeline-utils.js';
|
|
11
|
+
export const NODE_W = 152;
|
|
12
|
+
export const NODE_H = 38;
|
|
13
|
+
const COL_W = 196;
|
|
14
|
+
const ROW_GAP = 58;
|
|
15
|
+
const MARGIN = 28;
|
|
16
|
+
export const OUTPUT_ID = '__output__';
|
|
17
|
+
/**
|
|
18
|
+
* Derive the node graph for a Source. Deterministic: same Source → same
|
|
19
|
+
* layout. The graph is an anti-arborescence (every node has exactly one
|
|
20
|
+
* downstream edge, all flowing into Output), so ranks and a tidy layered
|
|
21
|
+
* layout fall out of a single longest-path pass.
|
|
22
|
+
*/
|
|
23
|
+
export function buildNodeGraph(source) {
|
|
24
|
+
const b = { nodes: new Map(), edges: [], out: new Map() };
|
|
25
|
+
b.nodes.set(OUTPUT_ID, {
|
|
26
|
+
id: OUTPUT_ID, kind: 'output', label: 'Output', rank: 0,
|
|
27
|
+
x: 0, y: 0, w: NODE_W, h: NODE_H,
|
|
28
|
+
});
|
|
29
|
+
let counter = 0;
|
|
30
|
+
const elements = Array.isArray(source.elements) ? source.elements : [];
|
|
31
|
+
// Higher track / z paints later (in front) → list it nearer the top so
|
|
32
|
+
// the vertical stack reads back-to-front top-down, matching the layers tree.
|
|
33
|
+
const ordered = [...elements];
|
|
34
|
+
for (const el of ordered)
|
|
35
|
+
addElement(el, OUTPUT_ID);
|
|
36
|
+
function addElement(el, consumer) {
|
|
37
|
+
const key = `el:${el.id ?? `_${counter++}`}`;
|
|
38
|
+
b.nodes.set(key, {
|
|
39
|
+
id: key, kind: 'element', elType: el.type,
|
|
40
|
+
label: elementLabel(el), elementId: el.id, rank: 0,
|
|
41
|
+
x: 0, y: 0, w: NODE_W, h: NODE_H,
|
|
42
|
+
});
|
|
43
|
+
// Effects chain hangs to the RIGHT of the element, between it and its
|
|
44
|
+
// consumer: element → fx0 → fx1 → … → consumer.
|
|
45
|
+
let tail = key;
|
|
46
|
+
const fx = el.effects;
|
|
47
|
+
if (Array.isArray(fx)) {
|
|
48
|
+
fx.forEach((e, i) => {
|
|
49
|
+
const fid = `fx:${key}:${i}`;
|
|
50
|
+
b.nodes.set(fid, {
|
|
51
|
+
id: fid, kind: 'effect', effectType: e?.type ?? 'effect',
|
|
52
|
+
label: e?.type ?? 'effect', elementId: el.id, rank: 0,
|
|
53
|
+
x: 0, y: 0, w: NODE_W, h: NODE_H,
|
|
54
|
+
});
|
|
55
|
+
link(tail, fid);
|
|
56
|
+
tail = fid;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
link(tail, consumer);
|
|
60
|
+
// Group children composite INTO the group node (before its effects).
|
|
61
|
+
if (el.type === 'group') {
|
|
62
|
+
const kids = el.elements;
|
|
63
|
+
if (Array.isArray(kids))
|
|
64
|
+
for (const child of kids)
|
|
65
|
+
addElement(child, key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function link(from, to) {
|
|
69
|
+
b.edges.push({ from, to });
|
|
70
|
+
b.out.set(from, to);
|
|
71
|
+
}
|
|
72
|
+
layout(b);
|
|
73
|
+
const nodes = [...b.nodes.values()];
|
|
74
|
+
const width = Math.max(...nodes.map((n) => n.x + n.w), COL_W) + MARGIN;
|
|
75
|
+
const height = Math.max(...nodes.map((n) => n.y + n.h), ROW_GAP) + MARGIN;
|
|
76
|
+
return { nodes, edges: b.edges, width, height };
|
|
77
|
+
}
|
|
78
|
+
/** Longest-path ranks + a tidy per-column vertical order (DFS pre-order). */
|
|
79
|
+
function layout(b) {
|
|
80
|
+
const rankMemo = new Map();
|
|
81
|
+
const rankOf = (id) => {
|
|
82
|
+
if (rankMemo.has(id))
|
|
83
|
+
return rankMemo.get(id);
|
|
84
|
+
const t = b.out.get(id);
|
|
85
|
+
const r = t === undefined ? 0 : 1 + rankOf(t);
|
|
86
|
+
rankMemo.set(id, r);
|
|
87
|
+
return r;
|
|
88
|
+
};
|
|
89
|
+
for (const n of b.nodes.values())
|
|
90
|
+
n.rank = rankOf(n.id);
|
|
91
|
+
const maxRank = Math.max(0, ...[...b.nodes.values()].map((n) => n.rank));
|
|
92
|
+
// Incoming adjacency, in insertion order, for a stable DFS.
|
|
93
|
+
const incoming = new Map();
|
|
94
|
+
for (const e of b.edges) {
|
|
95
|
+
const list = incoming.get(e.to) ?? [];
|
|
96
|
+
list.push(e.from);
|
|
97
|
+
incoming.set(e.to, list);
|
|
98
|
+
}
|
|
99
|
+
// DFS from Output over incoming edges assigns a vertical order index so
|
|
100
|
+
// siblings cluster and subtrees stay contiguous.
|
|
101
|
+
const order = new Map();
|
|
102
|
+
let k = 0;
|
|
103
|
+
const dfs = (id) => {
|
|
104
|
+
order.set(id, k++);
|
|
105
|
+
for (const src of incoming.get(id) ?? [])
|
|
106
|
+
dfs(src);
|
|
107
|
+
};
|
|
108
|
+
dfs(OUTPUT_ID);
|
|
109
|
+
const perRank = new Map();
|
|
110
|
+
for (const n of b.nodes.values()) {
|
|
111
|
+
const list = perRank.get(n.rank) ?? [];
|
|
112
|
+
list.push(n);
|
|
113
|
+
perRank.set(n.rank, list);
|
|
114
|
+
}
|
|
115
|
+
for (const [rank, list] of perRank) {
|
|
116
|
+
list.sort((a, c) => (order.get(a.id) ?? 0) - (order.get(c.id) ?? 0));
|
|
117
|
+
list.forEach((n, i) => {
|
|
118
|
+
n.x = (maxRank - rank) * COL_W + MARGIN;
|
|
119
|
+
n.y = i * ROW_GAP + MARGIN;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=node-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-graph.js","sourceRoot":"","sources":["../src/node-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/registry/build.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAEV,cAAc,EAIf,MAAM,YAAY,CAAC;AAuEpB,wBAAgB,mBAAmB,IAAI,cAAc,CAsBpD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// buildEditorRegistry — runs the derive → override pipeline over the
|
|
2
|
+
// protocol's exported zod schemas and returns the resolved registry
|
|
3
|
+
// every inspector renders from (EDITORS-PLAN D2).
|
|
4
|
+
import { animationSchema, audioElementSchema, captionElementSchema, effectSchema, groupElementSchema, imageElementSchema, particlesElementSchema, shapeElementSchema, sourceSchema, textElementSchema, videoElementSchema, } from '@clipkit/protocol';
|
|
5
|
+
import { deriveScope, unwrap } from './derive.js';
|
|
6
|
+
import { BASE_FIELD_OVERRIDES, COMPOSITES, FIELD_OVERRIDES, } from './overrides.js';
|
|
7
|
+
const ELEMENT_SCHEMAS = {
|
|
8
|
+
video: videoElementSchema,
|
|
9
|
+
image: imageElementSchema,
|
|
10
|
+
text: textElementSchema,
|
|
11
|
+
shape: shapeElementSchema,
|
|
12
|
+
audio: audioElementSchema,
|
|
13
|
+
group: groupElementSchema,
|
|
14
|
+
caption: captionElementSchema,
|
|
15
|
+
particles: particlesElementSchema,
|
|
16
|
+
};
|
|
17
|
+
function applyOverrides(fields, ...layers) {
|
|
18
|
+
return fields.map((f) => {
|
|
19
|
+
let merged = f;
|
|
20
|
+
for (const layer of layers) {
|
|
21
|
+
const o = layer?.[f.path];
|
|
22
|
+
if (!o)
|
|
23
|
+
continue;
|
|
24
|
+
merged = { ...merged, ...o, path: f.path, origin: 'override' };
|
|
25
|
+
}
|
|
26
|
+
return merged;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function resolveScope(scope, schema, opts = {}) {
|
|
30
|
+
const derived = deriveScope(scope, schema, opts.skip);
|
|
31
|
+
const fields = applyOverrides(derived, ...(opts.overrides ?? []));
|
|
32
|
+
const composites = opts.composites ?? [];
|
|
33
|
+
const claimed = new Set(composites.flatMap((c) => c.claims));
|
|
34
|
+
return {
|
|
35
|
+
scope,
|
|
36
|
+
// Composite claims beat field specs — claimed fields leave the
|
|
37
|
+
// flat list (their values are edited through the widget).
|
|
38
|
+
fields: fields.filter((f) => !claimed.has(f.path)),
|
|
39
|
+
composites,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Effect scopes from the discriminated union: one per `type` literal. */
|
|
43
|
+
function effectScopes() {
|
|
44
|
+
const out = {};
|
|
45
|
+
const def = unwrap(effectSchema)._def;
|
|
46
|
+
for (const option of def.options ?? []) {
|
|
47
|
+
const shape = unwrap(option)._def.shape?.();
|
|
48
|
+
const typeLiteral = shape?.type
|
|
49
|
+
? unwrap(shape.type)._def.value
|
|
50
|
+
: undefined;
|
|
51
|
+
if (!typeLiteral)
|
|
52
|
+
continue;
|
|
53
|
+
const scope = `effects.${typeLiteral}`;
|
|
54
|
+
out[typeLiteral] = resolveScope(scope, option, {
|
|
55
|
+
skip: new Set(['type']),
|
|
56
|
+
overrides: [FIELD_OVERRIDES[scope]],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
export function buildEditorRegistry() {
|
|
62
|
+
const elements = {};
|
|
63
|
+
for (const [type, schema] of Object.entries(ELEMENT_SCHEMAS)) {
|
|
64
|
+
elements[type] = resolveScope(type, schema, {
|
|
65
|
+
skip: new Set(['type']),
|
|
66
|
+
overrides: [BASE_FIELD_OVERRIDES, FIELD_OVERRIDES[type]],
|
|
67
|
+
composites: [...(COMPOSITES.__element__ ?? []), ...(COMPOSITES[type] ?? [])],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
source: resolveScope('source', sourceSchema, {
|
|
72
|
+
skip: new Set(['elements']),
|
|
73
|
+
overrides: [FIELD_OVERRIDES.source],
|
|
74
|
+
composites: COMPOSITES.source ?? [],
|
|
75
|
+
}),
|
|
76
|
+
elements,
|
|
77
|
+
effects: effectScopes(),
|
|
78
|
+
animation: resolveScope('animation', animationSchema, {
|
|
79
|
+
skip: new Set(['type']),
|
|
80
|
+
overrides: [FIELD_OVERRIDES.animation],
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/registry/build.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,oEAAoE;AACpE,kDAAkD;AAElD,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAChB,MAAM,gBAAgB,CAAC;AASxB,MAAM,eAAe,GAA4B;IAC/C,KAAK,EAAE,kBAAkB;IACzB,KAAK,EAAE,kBAAkB;IACzB,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,kBAAkB;IACzB,KAAK,EAAE,kBAAkB;IACzB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,oBAAoB;IAC7B,SAAS,EAAE,sBAAsB;CAClC,CAAC;AAEF,SAAS,cAAc,CACrB,MAAmB,EACnB,GAAG,MAAwD;IAE3D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACtB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CACnB,KAAa,EACb,MAAe,EACf,OAII,EAAE;IAEN,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,MAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,OAAO;QACL,KAAK;QACL,+DAA+D;QAC/D,0DAA0D;QAC1D,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClD,UAAU;KACX,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,SAAS,YAAY;IACnB,MAAM,GAAG,GAAkC,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAI,MAAM,CAAC,YAAuB,CAAa,CAAC,IAExD,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAI,MAAM,CAAC,MAAM,CAAC,CAAC,IAAkD,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3F,MAAM,WAAW,GAAG,KAAK,EAAE,IAAI;YAC7B,CAAC,CAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAA4B,CAAC,KAAgB;YACpE,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,CAAC,WAAW;YAAE,SAAS;QAC3B,MAAM,KAAK,GAAG,WAAW,WAAW,EAAE,CAAC;QACvC,GAAG,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE;YAC7C,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;YACvB,SAAS,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;SACpC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,QAAQ,GAAkC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7D,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE;YAC1C,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;YACvB,SAAS,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;YACxD,UAAU,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;SAC7E,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,MAAM,EAAE,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE;YAC3C,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;YAC3B,SAAS,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC;YACnC,UAAU,EAAE,UAAU,CAAC,MAAM,IAAI,EAAE;SACpC,CAAC;QACF,QAAQ;QACR,OAAO,EAAE,YAAY,EAAE;QACvB,SAAS,EAAE,YAAY,CAAC,WAAW,EAAE,eAAe,EAAE;YACpD,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;YACvB,SAAS,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC;SACvC,CAAC;KACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ToolId } from '../types.js';
|
|
2
|
+
import type { EditorRegistry, ScopeRegistry } from './types.js';
|
|
3
|
+
export interface EditorViewsConfiguration {
|
|
4
|
+
timeline: boolean;
|
|
5
|
+
/** Per-audio-track meters + master rail (advanced timeline). */
|
|
6
|
+
mixer: boolean;
|
|
7
|
+
/** Layers / element tree panel. */
|
|
8
|
+
layers: boolean;
|
|
9
|
+
/** Asset bin panel. */
|
|
10
|
+
assets: boolean;
|
|
11
|
+
/** Dockable bidirectional JSON pane. */
|
|
12
|
+
json: boolean;
|
|
13
|
+
/** Keyframe lanes under expanded clips. */
|
|
14
|
+
keyframeLanes: boolean;
|
|
15
|
+
/** Curve (graph) editor drawer. */
|
|
16
|
+
curveEditor: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface EditorConfiguration {
|
|
19
|
+
id: string;
|
|
20
|
+
views: EditorViewsConfiguration;
|
|
21
|
+
/** Dock tools, in order. */
|
|
22
|
+
dock: readonly ToolId[];
|
|
23
|
+
/**
|
|
24
|
+
* Knob exposure over the registry: 'all', or an allowlist of
|
|
25
|
+
* section ids and/or `scope.path` field ids. Composites are exposed
|
|
26
|
+
* when their section is allowed or any claimed field is listed.
|
|
27
|
+
*/
|
|
28
|
+
knobs: 'all' | {
|
|
29
|
+
sections?: readonly string[];
|
|
30
|
+
fields?: readonly string[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare const ADVANCED_CONFIGURATION: EditorConfiguration;
|
|
34
|
+
/** Mirrors what today's hand-coded basic panels expose — the curated
|
|
35
|
+
* consumer surface. (The basic shell adopts the registry renderer
|
|
36
|
+
* under this configuration in a later phase.) */
|
|
37
|
+
export declare const BASIC_CONFIGURATION: EditorConfiguration;
|
|
38
|
+
/** Is a knob (field spec or composite) exposed under a configuration? */
|
|
39
|
+
export declare function isKnobExposed(config: EditorConfiguration, scope: string, knob: {
|
|
40
|
+
path?: string;
|
|
41
|
+
section: string;
|
|
42
|
+
claims?: readonly string[];
|
|
43
|
+
}): boolean;
|
|
44
|
+
/** The scope's knobs filtered + sorted for one configuration. */
|
|
45
|
+
export declare function exposedKnobs(config: EditorConfiguration, registry: ScopeRegistry): {
|
|
46
|
+
fields: ScopeRegistry['fields'];
|
|
47
|
+
composites: ScopeRegistry['composites'];
|
|
48
|
+
};
|
|
49
|
+
/** Convenience: every scope of the registry filtered by a configuration. */
|
|
50
|
+
export declare function configurationView(config: EditorConfiguration, registry: EditorRegistry): Record<string, ReturnType<typeof exposedKnobs>>;
|
|
51
|
+
//# sourceMappingURL=configuration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/registry/configuration.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,mCAAmC;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,OAAO,CAAC;IACvB,mCAAmC;IACnC,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,wBAAwB,CAAC;IAChC,4BAA4B;IAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB;;;;OAIG;IACH,KAAK,EACD,KAAK,GACL;QACE,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC5B,CAAC;CACP;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAapC,CAAC;AAEF;;iDAEiD;AACjD,eAAO,MAAM,mBAAmB,EAAE,mBA8BjC,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACnE,OAAO,CAOT;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,aAAa,GACtB;IAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAAC,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;CAAE,CAW9E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,cAAc,GACvB,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAUjD"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// EditorConfiguration (EDITORS-PLAN D2 layer 3, ruled by Ian) — a
|
|
2
|
+
// declarative object that says what an editor instance IS: which
|
|
3
|
+
// views are mounted, which dock tools exist, which knobs the
|
|
4
|
+
// inspector exposes. The basic and advanced editors are PRESETS of
|
|
5
|
+
// the same machine; embedders fork a configuration, not components.
|
|
6
|
+
export const ADVANCED_CONFIGURATION = {
|
|
7
|
+
id: 'advanced',
|
|
8
|
+
views: {
|
|
9
|
+
timeline: true,
|
|
10
|
+
mixer: true,
|
|
11
|
+
layers: true,
|
|
12
|
+
assets: true,
|
|
13
|
+
json: true,
|
|
14
|
+
keyframeLanes: true,
|
|
15
|
+
curveEditor: true,
|
|
16
|
+
},
|
|
17
|
+
dock: ['text', 'shape', 'image', 'video', 'audio', 'caption'],
|
|
18
|
+
knobs: 'all',
|
|
19
|
+
};
|
|
20
|
+
/** Mirrors what today's hand-coded basic panels expose — the curated
|
|
21
|
+
* consumer surface. (The basic shell adopts the registry renderer
|
|
22
|
+
* under this configuration in a later phase.) */
|
|
23
|
+
export const BASIC_CONFIGURATION = {
|
|
24
|
+
id: 'basic',
|
|
25
|
+
views: {
|
|
26
|
+
timeline: true,
|
|
27
|
+
mixer: false,
|
|
28
|
+
layers: false,
|
|
29
|
+
assets: false,
|
|
30
|
+
json: true,
|
|
31
|
+
keyframeLanes: false,
|
|
32
|
+
curveEditor: false,
|
|
33
|
+
},
|
|
34
|
+
dock: ['text', 'shape', 'image', 'video', 'audio', 'caption'],
|
|
35
|
+
knobs: {
|
|
36
|
+
sections: ['identity', 'timing', 'transform', 'appearance'],
|
|
37
|
+
fields: [
|
|
38
|
+
'text.text', 'text.font_family', 'text.font_size', 'text.font_weight',
|
|
39
|
+
'text.line_height', 'text.letter_spacing', 'text.text_align',
|
|
40
|
+
'text.vertical_align', 'text.fill_color', 'text.stroke_color',
|
|
41
|
+
'text.stroke_width',
|
|
42
|
+
'shape.shape', 'shape.fill_color', 'shape.stroke_color',
|
|
43
|
+
'shape.stroke_width', 'shape.border_radius',
|
|
44
|
+
'image.source', 'image.fit', 'image.brightness', 'image.contrast',
|
|
45
|
+
'image.saturation', 'image.blur_radius',
|
|
46
|
+
'video.source', 'video.volume', 'video.playback_rate', 'video.loop',
|
|
47
|
+
'video.fit',
|
|
48
|
+
'audio.source', 'audio.volume', 'audio.loop',
|
|
49
|
+
'caption.words', 'caption.style', 'caption.font_family',
|
|
50
|
+
'caption.font_size', 'caption.fill_color', 'caption.highlight_color',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
/** Is a knob (field spec or composite) exposed under a configuration? */
|
|
55
|
+
export function isKnobExposed(config, scope, knob) {
|
|
56
|
+
if (config.knobs === 'all')
|
|
57
|
+
return true;
|
|
58
|
+
const { sections = [], fields = [] } = config.knobs;
|
|
59
|
+
if (sections.includes(knob.section))
|
|
60
|
+
return true;
|
|
61
|
+
if (knob.path && fields.includes(`${scope}.${knob.path}`))
|
|
62
|
+
return true;
|
|
63
|
+
if (knob.claims?.some((c) => fields.includes(`${scope}.${c}`)))
|
|
64
|
+
return true;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/** The scope's knobs filtered + sorted for one configuration. */
|
|
68
|
+
export function exposedKnobs(config, registry) {
|
|
69
|
+
const bySection = (a, b) => a.section === b.section ? a.order - b.order : a.section.localeCompare(b.section);
|
|
70
|
+
return {
|
|
71
|
+
fields: registry.fields
|
|
72
|
+
.filter((f) => isKnobExposed(config, registry.scope, f))
|
|
73
|
+
.sort(bySection),
|
|
74
|
+
composites: registry.composites
|
|
75
|
+
.filter((c) => isKnobExposed(config, registry.scope, c))
|
|
76
|
+
.sort(bySection),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Convenience: every scope of the registry filtered by a configuration. */
|
|
80
|
+
export function configurationView(config, registry) {
|
|
81
|
+
const out = {
|
|
82
|
+
source: exposedKnobs(config, registry.source),
|
|
83
|
+
animation: exposedKnobs(config, registry.animation),
|
|
84
|
+
};
|
|
85
|
+
for (const [k, v] of Object.entries(registry.elements))
|
|
86
|
+
out[k] = exposedKnobs(config, v);
|
|
87
|
+
for (const [k, v] of Object.entries(registry.effects)) {
|
|
88
|
+
out[`effects.${k}`] = exposedKnobs(config, v);
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../src/registry/configuration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FieldSpec } from './types.js';
|
|
2
|
+
interface ZodDefLike {
|
|
3
|
+
typeName?: string;
|
|
4
|
+
innerType?: ZodLike;
|
|
5
|
+
schema?: ZodLike;
|
|
6
|
+
type?: ZodLike;
|
|
7
|
+
items?: ZodLike[];
|
|
8
|
+
options?: ZodLike[] | Map<string, ZodLike>;
|
|
9
|
+
values?: string[];
|
|
10
|
+
value?: unknown;
|
|
11
|
+
shape?: () => Record<string, ZodLike>;
|
|
12
|
+
checks?: Array<{
|
|
13
|
+
kind: string;
|
|
14
|
+
value?: number;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export interface ZodLike {
|
|
18
|
+
_def: ZodDefLike;
|
|
19
|
+
}
|
|
20
|
+
/** Unwrap optional/default/nullable/effects/readonly wrappers. */
|
|
21
|
+
export declare function unwrap(t: ZodLike): ZodLike;
|
|
22
|
+
/** Object shape of a (possibly wrapped) ZodObject, or null. */
|
|
23
|
+
export declare function shapeOf(t: ZodLike): Record<string, ZodLike> | null;
|
|
24
|
+
/**
|
|
25
|
+
* Derive the default knob set for one object schema. `skip` drops
|
|
26
|
+
* structural keys a scope handles elsewhere (e.g. element `type`).
|
|
27
|
+
*/
|
|
28
|
+
export declare function deriveScope(scope: string, schema: ZodLike, skip?: ReadonlySet<string>): FieldSpec[];
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/registry/derive.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,UAAU,UAAU;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,UAAU,CAAC;CAClB;AAMD,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAoB1C;AAMD,+DAA+D;AAC/D,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAKlE;AAwJD;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,IAAI,GAAE,WAAW,CAAC,MAAM,CAAa,GACpC,SAAS,EAAE,CAsBb"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// The deriver — walks the protocol's zod schemas and produces a
|
|
2
|
+
// default FieldSpec for EVERY field, so the inspector grows with the
|
|
3
|
+
// protocol automatically (EDITORS-PLAN D2 layer 1). Recognition is
|
|
4
|
+
// STRUCTURAL (zod _def duck-typing — no zod dependency, no version
|
|
5
|
+
// coupling) plus name heuristics for semantics the schema can't carry
|
|
6
|
+
// (colors, urls, angles). Anything unrecognized falls back to 'json'
|
|
7
|
+
// and is flagged via origin: 'derived' + note for the polish triage.
|
|
8
|
+
function defOf(t) {
|
|
9
|
+
return t._def ?? {};
|
|
10
|
+
}
|
|
11
|
+
/** Unwrap optional/default/nullable/effects/readonly wrappers. */
|
|
12
|
+
export function unwrap(t) {
|
|
13
|
+
let cur = t;
|
|
14
|
+
for (let i = 0; i < 10; i++) {
|
|
15
|
+
const def = defOf(cur);
|
|
16
|
+
const name = def.typeName;
|
|
17
|
+
if (name === 'ZodOptional' ||
|
|
18
|
+
name === 'ZodDefault' ||
|
|
19
|
+
name === 'ZodNullable' ||
|
|
20
|
+
name === 'ZodReadonly' ||
|
|
21
|
+
name === 'ZodBranded') {
|
|
22
|
+
cur = def.innerType;
|
|
23
|
+
}
|
|
24
|
+
else if (name === 'ZodEffects') {
|
|
25
|
+
cur = def.schema;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
return cur;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return cur;
|
|
32
|
+
}
|
|
33
|
+
function typeName(t) {
|
|
34
|
+
return defOf(unwrap(t)).typeName ?? 'unknown';
|
|
35
|
+
}
|
|
36
|
+
/** Object shape of a (possibly wrapped) ZodObject, or null. */
|
|
37
|
+
export function shapeOf(t) {
|
|
38
|
+
const u = unwrap(t);
|
|
39
|
+
const def = defOf(u);
|
|
40
|
+
if (def.typeName !== 'ZodObject' || typeof def.shape !== 'function')
|
|
41
|
+
return null;
|
|
42
|
+
return def.shape();
|
|
43
|
+
}
|
|
44
|
+
function unionMembers(t) {
|
|
45
|
+
const def = defOf(unwrap(t));
|
|
46
|
+
if (def.typeName !== 'ZodUnion')
|
|
47
|
+
return [];
|
|
48
|
+
return def.options ?? [];
|
|
49
|
+
}
|
|
50
|
+
/** An array whose element is an object with `time` and `value` keys —
|
|
51
|
+
* structurally, a Keyframe[]. */
|
|
52
|
+
function isKeyframeArray(t) {
|
|
53
|
+
const def = defOf(unwrap(t));
|
|
54
|
+
if (def.typeName !== 'ZodArray')
|
|
55
|
+
return false;
|
|
56
|
+
const el = shapeOf(def.type);
|
|
57
|
+
return el !== null && 'time' in el && 'value' in el;
|
|
58
|
+
}
|
|
59
|
+
function numberBounds(t) {
|
|
60
|
+
const def = defOf(unwrap(t));
|
|
61
|
+
const out = {};
|
|
62
|
+
for (const c of def.checks ?? []) {
|
|
63
|
+
if (c.kind === 'min' && typeof c.value === 'number')
|
|
64
|
+
out.min = c.value;
|
|
65
|
+
if (c.kind === 'max' && typeof c.value === 'number')
|
|
66
|
+
out.max = c.value;
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
// ── Name heuristics ─────────────────────────────────────────────────
|
|
71
|
+
function looksLikeColor(path) {
|
|
72
|
+
return path === 'color' || path === 'tint' || path.endsWith('_color');
|
|
73
|
+
}
|
|
74
|
+
function looksLikeUrl(path) {
|
|
75
|
+
return path === 'source' || path === 'src';
|
|
76
|
+
}
|
|
77
|
+
function looksLikeAngle(path) {
|
|
78
|
+
// (`direction` on particles is also degrees but reads better as a
|
|
79
|
+
// plain number — override territory, not a heuristic.)
|
|
80
|
+
return path === 'angle' || path.includes('rotation') || path.endsWith('_skew');
|
|
81
|
+
}
|
|
82
|
+
// ── Default sections for the shared BaseElement fields ──────────────
|
|
83
|
+
const SECTION_BY_FIELD = {
|
|
84
|
+
id: 'identity', name: 'identity', layer: 'identity',
|
|
85
|
+
time: 'timing', duration: 'timing',
|
|
86
|
+
// visible gates rendering (opacity's hard on/off cousin), not WHEN
|
|
87
|
+
// the element plays — re-ruled into appearance by Ian 2026-06-11.
|
|
88
|
+
visible: 'appearance',
|
|
89
|
+
x: 'transform', y: 'transform', width: 'transform', height: 'transform',
|
|
90
|
+
aspect_ratio: 'transform', x_anchor: 'transform', y_anchor: 'transform',
|
|
91
|
+
rotation: 'transform', z_rotation: 'transform', x_rotation: 'transform',
|
|
92
|
+
y_rotation: 'transform', z: 'transform', scale: 'transform',
|
|
93
|
+
x_scale: 'transform', y_scale: 'transform', x_skew: 'transform',
|
|
94
|
+
y_skew: 'transform',
|
|
95
|
+
opacity: 'appearance', blend_mode: 'appearance',
|
|
96
|
+
blur_radius: 'filters', brightness: 'filters', contrast: 'filters',
|
|
97
|
+
saturation: 'filters', hue_rotate: 'filters',
|
|
98
|
+
effects: 'effects', animations: 'animations',
|
|
99
|
+
keyframe_animations: 'keyframes',
|
|
100
|
+
};
|
|
101
|
+
function labelOf(path) {
|
|
102
|
+
const words = path.split('_');
|
|
103
|
+
return words
|
|
104
|
+
.map((w, i) => (i === 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w))
|
|
105
|
+
.join(' ');
|
|
106
|
+
}
|
|
107
|
+
function classify(path, t) {
|
|
108
|
+
const u = unwrap(t);
|
|
109
|
+
const name = typeName(u);
|
|
110
|
+
if (name === 'ZodNumber') {
|
|
111
|
+
const bounds = numberBounds(u);
|
|
112
|
+
if (looksLikeAngle(path))
|
|
113
|
+
return { control: 'angle', animatable: false, ...bounds };
|
|
114
|
+
return { control: 'number', animatable: false, ...bounds };
|
|
115
|
+
}
|
|
116
|
+
if (name === 'ZodString') {
|
|
117
|
+
if (looksLikeColor(path))
|
|
118
|
+
return { control: 'color', animatable: false };
|
|
119
|
+
if (looksLikeUrl(path))
|
|
120
|
+
return { control: 'url', animatable: false };
|
|
121
|
+
return { control: 'text', animatable: false };
|
|
122
|
+
}
|
|
123
|
+
if (name === 'ZodBoolean')
|
|
124
|
+
return { control: 'toggle', animatable: false };
|
|
125
|
+
if (name === 'ZodEnum') {
|
|
126
|
+
const values = defOf(u).values ?? [];
|
|
127
|
+
return { control: 'select', animatable: false, options: [...values] };
|
|
128
|
+
}
|
|
129
|
+
if (name === 'ZodArray') {
|
|
130
|
+
if (isKeyframeArray(u))
|
|
131
|
+
return { control: 'keyframes', animatable: true };
|
|
132
|
+
return { control: 'list', animatable: false, note: 'derived list fallback' };
|
|
133
|
+
}
|
|
134
|
+
if (name === 'ZodUnion') {
|
|
135
|
+
const members = unionMembers(u);
|
|
136
|
+
const kinds = new Set(members.map((m) => typeName(m)));
|
|
137
|
+
const hasKeyframes = members.some((m) => isKeyframeArray(m));
|
|
138
|
+
const numberMember = members.find((m) => typeName(m) === 'ZodNumber');
|
|
139
|
+
const bounds = numberMember ? numberBounds(numberMember) : {};
|
|
140
|
+
// number | Keyframe[] (effect params, volume, …)
|
|
141
|
+
if (kinds.has('ZodNumber') && hasKeyframes && !kinds.has('ZodString')) {
|
|
142
|
+
const base = looksLikeAngle(path)
|
|
143
|
+
? { control: 'angle' }
|
|
144
|
+
: { control: 'number' };
|
|
145
|
+
return { ...base, animatable: true, ...bounds };
|
|
146
|
+
}
|
|
147
|
+
// number | string (| Keyframe[]) — length units
|
|
148
|
+
if (kinds.has('ZodNumber') && kinds.has('ZodString')) {
|
|
149
|
+
return { control: 'length', animatable: hasKeyframes, ...bounds };
|
|
150
|
+
}
|
|
151
|
+
// string | string[] (particles color palette)
|
|
152
|
+
if (kinds.has('ZodString') && kinds.has('ZodArray') && !hasKeyframes) {
|
|
153
|
+
if (looksLikeColor(path)) {
|
|
154
|
+
return { control: 'color', animatable: false, note: 'accepts an array (palette)' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// boolean | literal(s) (loop: boolean | 'ping-pong')
|
|
158
|
+
if (kinds.has('ZodBoolean') && kinds.has('ZodLiteral')) {
|
|
159
|
+
const literals = members
|
|
160
|
+
.filter((m) => typeName(m) === 'ZodLiteral')
|
|
161
|
+
.map((m) => String(defOf(unwrap(m)).value));
|
|
162
|
+
return { control: 'select', animatable: false, options: ['off', 'on', ...literals] };
|
|
163
|
+
}
|
|
164
|
+
// number | literal('auto'/'end') (duration)
|
|
165
|
+
if (kinds.has('ZodNumber') && kinds.has('ZodLiteral')) {
|
|
166
|
+
return { control: 'length', animatable: false, note: 'accepts keyword values' };
|
|
167
|
+
}
|
|
168
|
+
return { control: 'json', animatable: hasKeyframes, note: 'union fallback' };
|
|
169
|
+
}
|
|
170
|
+
if (name === 'ZodObject')
|
|
171
|
+
return { control: 'json', animatable: false, note: 'object fallback' };
|
|
172
|
+
if (name === 'ZodTuple')
|
|
173
|
+
return { control: 'json', animatable: false, note: 'tuple fallback' };
|
|
174
|
+
if (name === 'ZodDiscriminatedUnion') {
|
|
175
|
+
return { control: 'json', animatable: false, note: 'discriminated-union fallback' };
|
|
176
|
+
}
|
|
177
|
+
if (name === 'ZodRecord')
|
|
178
|
+
return { control: 'json', animatable: false, note: 'record fallback' };
|
|
179
|
+
if (name === 'ZodLiteral')
|
|
180
|
+
return { control: 'text', animatable: false, note: 'literal' };
|
|
181
|
+
return { control: 'json', animatable: false, note: `unrecognized (${name})` };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Derive the default knob set for one object schema. `skip` drops
|
|
185
|
+
* structural keys a scope handles elsewhere (e.g. element `type`).
|
|
186
|
+
*/
|
|
187
|
+
export function deriveScope(scope, schema, skip = new Set()) {
|
|
188
|
+
const shape = shapeOf(schema);
|
|
189
|
+
if (!shape)
|
|
190
|
+
return [];
|
|
191
|
+
const out = [];
|
|
192
|
+
for (const [path, fieldSchema] of Object.entries(shape)) {
|
|
193
|
+
if (skip.has(path))
|
|
194
|
+
continue;
|
|
195
|
+
const c = classify(path, fieldSchema);
|
|
196
|
+
out.push({
|
|
197
|
+
path,
|
|
198
|
+
control: c.control,
|
|
199
|
+
label: labelOf(path),
|
|
200
|
+
section: SECTION_BY_FIELD[path] ?? scope,
|
|
201
|
+
order: 1000,
|
|
202
|
+
min: c.min,
|
|
203
|
+
max: c.max,
|
|
204
|
+
options: c.options,
|
|
205
|
+
animatable: c.animatable,
|
|
206
|
+
origin: 'derived',
|
|
207
|
+
note: c.note,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=derive.js.map
|