@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.
Files changed (71) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +20 -0
  3. package/dist/asset-store.d.ts +40 -0
  4. package/dist/asset-store.d.ts.map +1 -0
  5. package/dist/asset-store.js +181 -0
  6. package/dist/asset-store.js.map +1 -0
  7. package/dist/context.d.ts +17 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +23 -0
  10. package/dist/context.js.map +1 -0
  11. package/dist/index.d.ts +20 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +17 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/node-graph.d.ts +42 -0
  16. package/dist/node-graph.d.ts.map +1 -0
  17. package/dist/node-graph.js +123 -0
  18. package/dist/node-graph.js.map +1 -0
  19. package/dist/registry/build.d.ts +3 -0
  20. package/dist/registry/build.d.ts.map +1 -0
  21. package/dist/registry/build.js +84 -0
  22. package/dist/registry/build.js.map +1 -0
  23. package/dist/registry/configuration.d.ts +51 -0
  24. package/dist/registry/configuration.d.ts.map +1 -0
  25. package/dist/registry/configuration.js +92 -0
  26. package/dist/registry/configuration.js.map +1 -0
  27. package/dist/registry/derive.d.ts +30 -0
  28. package/dist/registry/derive.d.ts.map +1 -0
  29. package/dist/registry/derive.js +212 -0
  30. package/dist/registry/derive.js.map +1 -0
  31. package/dist/registry/overrides.d.ts +7 -0
  32. package/dist/registry/overrides.d.ts.map +1 -0
  33. package/dist/registry/overrides.js +322 -0
  34. package/dist/registry/overrides.js.map +1 -0
  35. package/dist/registry/types.d.ts +58 -0
  36. package/dist/registry/types.d.ts.map +1 -0
  37. package/dist/registry/types.js +7 -0
  38. package/dist/registry/types.js.map +1 -0
  39. package/dist/source-diff.d.ts +8 -0
  40. package/dist/source-diff.d.ts.map +1 -0
  41. package/dist/source-diff.js +148 -0
  42. package/dist/source-diff.js.map +1 -0
  43. package/dist/stage-utils.d.ts +170 -0
  44. package/dist/stage-utils.d.ts.map +1 -0
  45. package/dist/stage-utils.js +476 -0
  46. package/dist/stage-utils.js.map +1 -0
  47. package/dist/store.d.ts +123 -0
  48. package/dist/store.d.ts.map +1 -0
  49. package/dist/store.js +234 -0
  50. package/dist/store.js.map +1 -0
  51. package/dist/timeline-utils.d.ts +69 -0
  52. package/dist/timeline-utils.d.ts.map +1 -0
  53. package/dist/timeline-utils.js +211 -0
  54. package/dist/timeline-utils.js.map +1 -0
  55. package/dist/types.d.ts +6 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +4 -0
  58. package/dist/types.js.map +1 -0
  59. package/dist/useEditor.d.ts +77 -0
  60. package/dist/useEditor.d.ts.map +1 -0
  61. package/dist/useEditor.js +74 -0
  62. package/dist/useEditor.js.map +1 -0
  63. package/dist/useEditorStore.d.ts +3 -0
  64. package/dist/useEditorStore.d.ts.map +1 -0
  65. package/dist/useEditorStore.js +15 -0
  66. package/dist/useEditorStore.js.map +1 -0
  67. package/dist/usePlaybackSession.d.ts +20 -0
  68. package/dist/usePlaybackSession.d.ts.map +1 -0
  69. package/dist/usePlaybackSession.js +120 -0
  70. package/dist/usePlaybackSession.js.map +1 -0
  71. 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,3 @@
1
+ import type { EditorRegistry } from './types.js';
2
+ export declare function buildEditorRegistry(): EditorRegistry;
3
+ //# sourceMappingURL=build.d.ts.map
@@ -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":"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"}
@@ -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