@hyperframes/sdk 0.6.113

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 (78) hide show
  1. package/LICENSE +190 -0
  2. package/dist/adapters/fs.d.ts +9 -0
  3. package/dist/adapters/fs.d.ts.map +1 -0
  4. package/dist/adapters/fs.js +122 -0
  5. package/dist/adapters/fs.js.map +1 -0
  6. package/dist/adapters/headless.d.ts +3 -0
  7. package/dist/adapters/headless.d.ts.map +1 -0
  8. package/dist/adapters/headless.js +17 -0
  9. package/dist/adapters/headless.js.map +1 -0
  10. package/dist/adapters/iframe.d.ts +102 -0
  11. package/dist/adapters/iframe.d.ts.map +1 -0
  12. package/dist/adapters/iframe.js +569 -0
  13. package/dist/adapters/iframe.js.map +1 -0
  14. package/dist/adapters/memory.d.ts +5 -0
  15. package/dist/adapters/memory.d.ts.map +1 -0
  16. package/dist/adapters/memory.js +54 -0
  17. package/dist/adapters/memory.js.map +1 -0
  18. package/dist/adapters/types.d.ts +65 -0
  19. package/dist/adapters/types.d.ts.map +1 -0
  20. package/dist/adapters/types.js +2 -0
  21. package/dist/adapters/types.js.map +1 -0
  22. package/dist/document.d.ts +25 -0
  23. package/dist/document.d.ts.map +1 -0
  24. package/dist/document.js +238 -0
  25. package/dist/document.js.map +1 -0
  26. package/dist/engine/apply-patches.d.ts +20 -0
  27. package/dist/engine/apply-patches.d.ts.map +1 -0
  28. package/dist/engine/apply-patches.js +284 -0
  29. package/dist/engine/apply-patches.js.map +1 -0
  30. package/dist/engine/cssWriter.d.ts +18 -0
  31. package/dist/engine/cssWriter.d.ts.map +1 -0
  32. package/dist/engine/cssWriter.js +139 -0
  33. package/dist/engine/cssWriter.js.map +1 -0
  34. package/dist/engine/keyframeBackfill.d.ts +17 -0
  35. package/dist/engine/keyframeBackfill.d.ts.map +1 -0
  36. package/dist/engine/keyframeBackfill.js +43 -0
  37. package/dist/engine/keyframeBackfill.js.map +1 -0
  38. package/dist/engine/model.d.ts +55 -0
  39. package/dist/engine/model.d.ts.map +1 -0
  40. package/dist/engine/model.js +256 -0
  41. package/dist/engine/model.js.map +1 -0
  42. package/dist/engine/mutate.d.ts +26 -0
  43. package/dist/engine/mutate.d.ts.map +1 -0
  44. package/dist/engine/mutate.js +1243 -0
  45. package/dist/engine/mutate.js.map +1 -0
  46. package/dist/engine/patches.d.ts +71 -0
  47. package/dist/engine/patches.d.ts.map +1 -0
  48. package/dist/engine/patches.js +197 -0
  49. package/dist/engine/patches.js.map +1 -0
  50. package/dist/engine/serialize.d.ts +15 -0
  51. package/dist/engine/serialize.d.ts.map +1 -0
  52. package/dist/engine/serialize.js +20 -0
  53. package/dist/engine/serialize.js.map +1 -0
  54. package/dist/engine/variableModel.d.ts +29 -0
  55. package/dist/engine/variableModel.d.ts.map +1 -0
  56. package/dist/engine/variableModel.js +81 -0
  57. package/dist/engine/variableModel.js.map +1 -0
  58. package/dist/history.d.ts +39 -0
  59. package/dist/history.d.ts.map +1 -0
  60. package/dist/history.js +116 -0
  61. package/dist/history.js.map +1 -0
  62. package/dist/index.d.ts +15 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +11 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/persist-queue.d.ts +24 -0
  67. package/dist/persist-queue.d.ts.map +1 -0
  68. package/dist/persist-queue.js +62 -0
  69. package/dist/persist-queue.js.map +1 -0
  70. package/dist/session.d.ts +38 -0
  71. package/dist/session.d.ts.map +1 -0
  72. package/dist/session.js +514 -0
  73. package/dist/session.js.map +1 -0
  74. package/dist/types.d.ts +521 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +16 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +55 -0
@@ -0,0 +1,65 @@
1
+ import type { PersistErrorEvent } from "../types.js";
2
+ export interface PersistVersionEntry {
3
+ /** Opaque key identifying this version (adapter-defined format) */
4
+ key: string;
5
+ /** Full HTML content — may be omitted by adapters that load content lazily via loadFrom() */
6
+ content?: string;
7
+ timestamp?: number;
8
+ }
9
+ /**
10
+ * Injectable storage adapter — decouples the SDK from the underlying persistence mechanism.
11
+ * Implementations: memory (tests/demos), fs (local dev), S3 (cloud), HTTP (Pacific).
12
+ *
13
+ * Contract:
14
+ * - read() returns undefined for a path never written
15
+ * - write() is idempotent (second write overwrites)
16
+ * - flush() resolves when any queued writes are committed
17
+ * - listVersions() returns entries newest-first
18
+ * - loadFrom() returns content for the given version key (undefined if not found)
19
+ * - on('persist:error') fires when a write fails; the error must not propagate as a thrown exception
20
+ */
21
+ export interface PersistAdapter {
22
+ read(path: string): Promise<string | undefined>;
23
+ write(path: string, content: string): Promise<void>;
24
+ /** Force all pending writes to commit before returning */
25
+ flush(): Promise<void>;
26
+ listVersions(path: string): Promise<PersistVersionEntry[]>;
27
+ loadFrom(path: string, versionKey: string): Promise<string | undefined>;
28
+ on(event: "persist:error", handler: (event: PersistErrorEvent) => void): () => void;
29
+ }
30
+ export interface ElementAtPointResult {
31
+ id: string;
32
+ tag: string;
33
+ }
34
+ export interface DraftProps {
35
+ dx?: number;
36
+ dy?: number;
37
+ width?: number;
38
+ height?: number;
39
+ }
40
+ /**
41
+ * Injectable preview adapter — decouples the SDK from the host preview surface.
42
+ * The null/headless adapter stubs all methods (no browser needed).
43
+ *
44
+ * The SDK is NOT in the 60fps draft loop — consumers call applyDraft() directly on
45
+ * the preview at 60fps; commitPreview() fires once on pointer-up to derive and
46
+ * dispatch the resulting op.
47
+ */
48
+ export interface PreviewAdapter {
49
+ /** Sync hit-test at composition coordinates. Requires same-origin iframe. */
50
+ elementAtPoint(x: number, y: number, opts?: {
51
+ atTime?: number;
52
+ }): ElementAtPointResult | null;
53
+ /** Apply draft CSS markers to the preview element (60fps, SDK not involved) */
54
+ applyDraft(id: string, props: DraftProps): void;
55
+ /** Derive op from draft markers, dispatch it, emit patch event, clear markers */
56
+ commitPreview(): void;
57
+ /** Revert draft markers without committing. Model never changed. */
58
+ cancelPreview(): void;
59
+ /** Set preview selection; fires selectionchange on the session */
60
+ select(ids: string[], opts?: {
61
+ additive?: boolean;
62
+ }): void;
63
+ on(event: "selection", handler: (ids: string[]) => void): () => void;
64
+ }
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,0DAA0D;IAC1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACxE,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACrF;AAID,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,oBAAoB,GAAG,IAAI,CAAC;IAE9F,+EAA+E;IAC/E,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAEhD,iFAAiF;IACjF,aAAa,IAAI,IAAI,CAAC;IAEtB,oEAAoE;IACpE,aAAa,IAAI,IAAI,CAAC;IAEtB,kEAAkE;IAClE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAI3D,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACtE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * SDK document model — adaptation layer on top of @hyperframes/core.
3
+ *
4
+ * F6 decision: SDK builds ON core, no parser duplication.
5
+ * - ensureHfIds (from core) is the parse entry point: all construction starts here.
6
+ * - DOMParser is NOT used (browser-only). linkedom is the node-safe primitive.
7
+ * - ParsedHtml (core) is the Studio timeline view (timed elements only).
8
+ * HyperFramesElement is the editing view (ALL editable elements, with raw attrs).
9
+ */
10
+ import type { HyperFramesElement, SdkDocument } from "./types.js";
11
+ /**
12
+ * Build the element tree from an already-parsed (hf-id-stamped) linkedom Document.
13
+ * Walks the live DOM directly — no serialize/re-parse round trip. This is what
14
+ * the session's query API uses against its mutable document.
15
+ */
16
+ export declare function buildRoots(document: Document): HyperFramesElement[];
17
+ /**
18
+ * Parse an HTML string into the SDK document model.
19
+ * Calls ensureHfIds first so every element has a stable data-hf-id.
20
+ * Uses linkedom — node-safe (works in agents, CI, server-side).
21
+ */
22
+ export declare function buildDocument(html: string): SdkDocument;
23
+ /** Flat walk of the element tree — returns every element in document order */
24
+ export declare function flatElements(roots: readonly HyperFramesElement[]): HyperFramesElement[];
25
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA0LlE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,kBAAkB,EAAE,CAWnE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAoBvD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAQvF"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * SDK document model — adaptation layer on top of @hyperframes/core.
3
+ *
4
+ * F6 decision: SDK builds ON core, no parser duplication.
5
+ * - ensureHfIds (from core) is the parse entry point: all construction starts here.
6
+ * - DOMParser is NOT used (browser-only). linkedom is the node-safe primitive.
7
+ * - ParsedHtml (core) is the Studio timeline view (timed elements only).
8
+ * HyperFramesElement is the editing view (ALL editable elements, with raw attrs).
9
+ */
10
+ import { parseHTML } from "linkedom";
11
+ import { ensureHfIds } from "@hyperframes/core/hf-ids";
12
+ import { parseGsapScriptAcornForWrite } from "@hyperframes/core/gsap-parser-acorn";
13
+ import { findRoot, getElementStyles, isNewHostBoundary } from "./engine/model.js";
14
+ // Tags that carry no editable content and must not enter the element tree.
15
+ const EXCLUDED_TAGS = new Set([
16
+ "script",
17
+ "style",
18
+ "template",
19
+ "meta",
20
+ "link",
21
+ "noscript",
22
+ "base",
23
+ "head",
24
+ ]);
25
+ // Snapshot text is TRIMMED for display (markup indentation produces noisy
26
+ // whitespace text nodes). setText writes verbatim — engine getOwnText/setOwnText
27
+ // operate on raw text. el.text is a display value, not a round-trip identity.
28
+ function ownText(el) {
29
+ let text = "";
30
+ el.childNodes.forEach((n) => {
31
+ if (n.nodeType === 3)
32
+ text += n.nodeValue ?? "";
33
+ });
34
+ const trimmed = text.trim();
35
+ return trimmed.length > 0 ? trimmed : null;
36
+ }
37
+ // Parsing the GSAP script (acorn AST walk) is the expensive part and depends
38
+ // only on the script text, so memoize the {tween id, selector} pairs by script.
39
+ // Selector→hf-id resolution still runs each call — it depends on the live DOM,
40
+ // which changes on dispatch. Single-entry cache covers the hot path (same comp,
41
+ // repeated getElements() rebuilds) and stays bounded.
42
+ let gsapLocatedCacheKey = null;
43
+ let gsapLocatedCacheVal = [];
44
+ function parseLocatedCached(script) {
45
+ if (gsapLocatedCacheKey === script)
46
+ return gsapLocatedCacheVal;
47
+ const parsed = parseGsapScriptAcornForWrite(script);
48
+ gsapLocatedCacheVal = parsed
49
+ ? parsed.located.map(({ id, animation }) => ({ id, selector: animation.targetSelector }))
50
+ : [];
51
+ gsapLocatedCacheKey = script;
52
+ return gsapLocatedCacheVal;
53
+ }
54
+ /**
55
+ * Map each element's data-hf-id → the GSAP tween ids targeting it. Tween ids
56
+ * come from the acorn parser's stable `targetSelector-method-position` scheme —
57
+ * the SAME id-space the studio-api read path and the SDK GSAP ops use, so these
58
+ * ids are dispatchable as-is via setGsapTween/removeGsapTween. Best-effort: a
59
+ * malformed selector or unparseable script yields no entries (animationIds: []).
60
+ */
61
+ function buildAnimationIdMap(document) {
62
+ const map = new Map();
63
+ const script = extractGsapScript(document);
64
+ if (!script)
65
+ return map;
66
+ for (const { id, selector } of parseLocatedCached(script)) {
67
+ if (!selector)
68
+ continue;
69
+ let matches = [];
70
+ try {
71
+ matches = Array.from(document.querySelectorAll(selector));
72
+ }
73
+ catch {
74
+ continue; // selector not valid for querySelectorAll — skip
75
+ }
76
+ for (const el of matches) {
77
+ const hfId = el.getAttribute("data-hf-id");
78
+ if (!hfId)
79
+ continue;
80
+ const list = map.get(hfId);
81
+ if (list)
82
+ list.push(id);
83
+ else
84
+ map.set(hfId, [id]);
85
+ }
86
+ }
87
+ return map;
88
+ }
89
+ // fallow-ignore-next-line complexity
90
+ function buildElement(el, scopePrefix, animationIdsByHfId) {
91
+ const tag = el.tagName.toLowerCase();
92
+ if (EXCLUDED_TAGS.has(tag))
93
+ return null;
94
+ const id = el.getAttribute("data-hf-id") ?? "";
95
+ if (!id)
96
+ return null; // should never happen after ensureHfIds, but guard defensively
97
+ // scopedId: if we're inside a sub-comp scope, prefix with "scopePrefix/".
98
+ // The host element itself is in the PARENT scope (no prefix change for its own id).
99
+ const scopedId = scopePrefix ? `${scopePrefix}/${id}` : id;
100
+ // Children inherit the scope prefix from their parent.
101
+ // If this element is a new host boundary (starts a new sub-comp scope), its
102
+ // children use THIS element's scopedId as their prefix.
103
+ // Otherwise, children inherit the same prefix that this element used.
104
+ const childPrefix = isNewHostBoundary(el) ? scopedId : scopePrefix;
105
+ const inlineStyles = getElementStyles(el);
106
+ const classAttr = el.getAttribute("class") ?? "";
107
+ const classNames = classAttr
108
+ .split(/\s+/)
109
+ .map((c) => c.trim())
110
+ .filter(Boolean);
111
+ const attributes = {};
112
+ for (const attr of Array.from(el.attributes)) {
113
+ if (attr.name === "style" || attr.name === "class" || attr.name.startsWith("data-hf-")) {
114
+ continue;
115
+ }
116
+ attributes[attr.name] = attr.value;
117
+ }
118
+ const startAttr = el.getAttribute("data-start");
119
+ const endAttr = el.getAttribute("data-end");
120
+ const trackAttr = el.getAttribute("data-track-index");
121
+ const start = startAttr !== null ? parseFloat(startAttr) : null;
122
+ const duration = start !== null && endAttr !== null ? Math.max(0, parseFloat(endAttr) - start) : null;
123
+ const trackIndex = trackAttr !== null ? parseInt(trackAttr, 10) : null;
124
+ const children = [];
125
+ for (const child of Array.from(el.children)) {
126
+ const built = buildElement(child, childPrefix, animationIdsByHfId);
127
+ if (built)
128
+ children.push(built);
129
+ }
130
+ return {
131
+ id,
132
+ scopedId,
133
+ tag,
134
+ children,
135
+ inlineStyles,
136
+ classNames,
137
+ attributes,
138
+ text: ownText(el),
139
+ start,
140
+ duration,
141
+ trackIndex,
142
+ animationIds: animationIdsByHfId.get(id) ?? [],
143
+ };
144
+ }
145
+ // fallow-ignore-next-line complexity
146
+ function extractGsapScript(doc) {
147
+ // GSAP script is the first <script> tag whose text references gsap. Marker
148
+ // set must match studio sdkShadow.ts isGsapScriptBody so both pick the same
149
+ // script from a given composition.
150
+ for (const script of Array.from(doc.querySelectorAll("script"))) {
151
+ const text = script.textContent ?? "";
152
+ if (text.includes("gsap") || text.includes("__timelines") || text.includes("ScrollTrigger")) {
153
+ return text;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+ function extractStyles(doc) {
159
+ const styleEl = doc.querySelector("style");
160
+ return styleEl ? styleEl.textContent : null;
161
+ }
162
+ // Root resolution delegates to the engine's findRoot so dimension extraction
163
+ // and mutations agree on which element is the composition root.
164
+ // fallow-ignore-next-line complexity
165
+ function extractDimensions(doc) {
166
+ const stage = findRoot(doc);
167
+ if (!stage)
168
+ return { width: null, height: null };
169
+ // data-width/data-height are the runtime's forced override — prefer them.
170
+ const wAttr = stage.getAttribute("data-width");
171
+ const hAttr = stage.getAttribute("data-height");
172
+ const style = stage.getAttribute?.("style") ?? "";
173
+ const wm = /width:\s*(\d+)px/.exec(style);
174
+ const hm = /height:\s*(\d+)px/.exec(style);
175
+ return {
176
+ width: wAttr !== null ? parseInt(wAttr, 10) : wm ? parseInt(wm[1] ?? "", 10) : null,
177
+ height: hAttr !== null ? parseInt(hAttr, 10) : hm ? parseInt(hm[1] ?? "", 10) : null,
178
+ };
179
+ }
180
+ function extractDuration(doc) {
181
+ const root = findRoot(doc) ?? doc.body;
182
+ const dur = root?.getAttribute("data-duration");
183
+ return dur ? parseFloat(dur) : null;
184
+ }
185
+ /**
186
+ * Build the element tree from an already-parsed (hf-id-stamped) linkedom Document.
187
+ * Walks the live DOM directly — no serialize/re-parse round trip. This is what
188
+ * the session's query API uses against its mutable document.
189
+ */
190
+ export function buildRoots(document) {
191
+ const body = document.body;
192
+ const roots = [];
193
+ const animationIdsByHfId = buildAnimationIdMap(document);
194
+ if (body) {
195
+ for (const child of Array.from(body.children)) {
196
+ const built = buildElement(child, "", animationIdsByHfId);
197
+ if (built)
198
+ roots.push(built);
199
+ }
200
+ }
201
+ return roots;
202
+ }
203
+ /**
204
+ * Parse an HTML string into the SDK document model.
205
+ * Calls ensureHfIds first so every element has a stable data-hf-id.
206
+ * Uses linkedom — node-safe (works in agents, CI, server-side).
207
+ */
208
+ export function buildDocument(html) {
209
+ const stamped = ensureHfIds(html);
210
+ const hasShell = /<!doctype|<html[\s>]/i.test(stamped);
211
+ const wrapped = !hasShell;
212
+ const { document } = wrapped
213
+ ? parseHTML(`<!DOCTYPE html><html><head></head><body>${stamped}</body></html>`)
214
+ : parseHTML(stamped);
215
+ const dims = extractDimensions(document);
216
+ return {
217
+ roots: buildRoots(document),
218
+ gsapScript: extractGsapScript(document),
219
+ styles: extractStyles(document),
220
+ width: dims.width,
221
+ height: dims.height,
222
+ compositionDuration: extractDuration(document),
223
+ html: stamped,
224
+ };
225
+ }
226
+ /** Flat walk of the element tree — returns every element in document order */
227
+ export function flatElements(roots) {
228
+ const result = [];
229
+ function walk(el) {
230
+ result.push(el);
231
+ for (const child of el.children)
232
+ walk(child);
233
+ }
234
+ for (const root of roots)
235
+ walk(root);
236
+ return result;
237
+ }
238
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Bounded RFC 6902 patch applier — handles only the path patterns emitted by mutate.ts.
3
+ *
4
+ * Not a general-purpose JSON Patch implementation. Translates the well-defined path
5
+ * grammar back into DOM mutations. Used by applyPatches() for host undo (T3 mode).
6
+ *
7
+ * Supports only the emit subset (add/remove/replace) — move/copy/test ops and
8
+ * unknown paths are silently ignored, matching the JsonPatchOp contract.
9
+ */
10
+ import type { JsonPatchOp, OverrideSet } from "../types.js";
11
+ import type { ParsedDocument } from "./model.js";
12
+ /**
13
+ * Replay a stored override-set onto a freshly-parsed base document (T3 init).
14
+ * A null value means the property was explicitly deleted — emit a remove patch
15
+ * so the base document matches the session state. (Removing a non-existent
16
+ * property is a no-op in applyOne, so this is safe against fresh-base misses.)
17
+ */
18
+ export declare function applyOverrideSet(parsed: ParsedDocument, overrides: OverrideSet): void;
19
+ export declare function applyPatchesToDocument(parsed: ParsedDocument, patches: readonly JsonPatchOp[]): void;
20
+ //# sourceMappingURL=apply-patches.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-patches.d.ts","sourceRoot":"","sources":["../../src/engine/apply-patches.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwFjD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAwBrF;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,WAAW,EAAE,GAC9B,IAAI,CAMN"}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Bounded RFC 6902 patch applier — handles only the path patterns emitted by mutate.ts.
3
+ *
4
+ * Not a general-purpose JSON Patch implementation. Translates the well-defined path
5
+ * grammar back into DOM mutations. Used by applyPatches() for host undo (T3 mode).
6
+ *
7
+ * Supports only the emit subset (add/remove/replace) — move/copy/test ops and
8
+ * unknown paths are silently ignored, matching the JsonPatchOp contract.
9
+ */
10
+ import { findById, findRoot, setElementStyles, setOwnText, setGsapScript, setStyleSheet, } from "./model.js";
11
+ import { keyToPath, stylePath } from "./patches.js";
12
+ import { writeVariableDefault, clearVariableDefault } from "./variableModel.js";
13
+ // fallow-ignore-next-line complexity
14
+ function parsePath(path) {
15
+ const styleM = /^\/elements\/([^/]+)\/inlineStyles\/(.+)$/.exec(path);
16
+ if (styleM)
17
+ return { type: "style", id: styleM[1], prop: styleM[2] };
18
+ const textM = /^\/elements\/([^/]+)\/text$/.exec(path);
19
+ if (textM)
20
+ return { type: "text", id: textM[1] };
21
+ const attrM = /^\/elements\/([^/]+)\/attributes\/(.+)$/.exec(path);
22
+ if (attrM)
23
+ return {
24
+ type: "attribute",
25
+ id: attrM[1],
26
+ prop: attrM[2]?.replace(/~1/g, "/").replace(/~0/g, "~"),
27
+ };
28
+ const timingM = /^\/elements\/([^/]+)\/timing\/(.+)$/.exec(path);
29
+ if (timingM)
30
+ return { type: "timing", id: timingM[1], field: timingM[2] };
31
+ const holdM = /^\/elements\/([^/]+)\/hold\/(.+)$/.exec(path);
32
+ if (holdM)
33
+ return { type: "hold", id: holdM[1], field: holdM[2] };
34
+ const elemM = /^\/elements\/([^/]+)$/.exec(path);
35
+ if (elemM)
36
+ return { type: "element", id: elemM[1] };
37
+ const varM = /^\/variables\/(.+)$/.exec(path);
38
+ if (varM)
39
+ return { type: "variable", id: varM[1] };
40
+ const metaM = /^\/metadata\/(.+)$/.exec(path);
41
+ if (metaM)
42
+ return { type: "metadata", field: metaM[1] };
43
+ if (path === "/script/gsap")
44
+ return { type: "script" };
45
+ if (path === "/style/css")
46
+ return { type: "stylesheet" };
47
+ return null;
48
+ }
49
+ // ─── Variable JSON model helper ───────────────────────────────────────────────
50
+ /**
51
+ * Apply a variable patch to `data-composition-variables`. A remove op (null)
52
+ * deletes the declaration's `default` key, restoring its "no authored default"
53
+ * state — the exact inverse of a first-set that added a default to a
54
+ * default-less variable, so undo of such a set round-trips. A value op upserts
55
+ * the matching declaration's `default`. No-ops when the attr/decl is absent.
56
+ * Shares the model logic with mutate.ts via ./variableModel.ts.
57
+ */
58
+ function applyVariableDefault(document, id, newDefault) {
59
+ if (newDefault === null) {
60
+ clearVariableDefault(document, id);
61
+ }
62
+ else {
63
+ writeVariableDefault(document, id, newDefault);
64
+ }
65
+ }
66
+ // ─── Patch application ───────────────────────────────────────────────────────
67
+ /**
68
+ * Replay a stored override-set onto a freshly-parsed base document (T3 init).
69
+ * A null value means the property was explicitly deleted — emit a remove patch
70
+ * so the base document matches the session state. (Removing a non-existent
71
+ * property is a no-op in applyOne, so this is safe against fresh-base misses.)
72
+ */
73
+ export function applyOverrideSet(parsed, overrides) {
74
+ const patches = [];
75
+ const rootId = findRoot(parsed.document)?.getAttribute("data-hf-id") ?? null;
76
+ for (const [key, value] of Object.entries(overrides)) {
77
+ const path = keyToPath(key);
78
+ if (!path)
79
+ continue;
80
+ if (value === null) {
81
+ patches.push({ op: "remove", path });
82
+ }
83
+ else {
84
+ patches.push({ op: "replace", path, value });
85
+ }
86
+ // A scalar `var.{id}` override must also restore the `--{id}` CSS custom
87
+ // prop on the root. Current sessions persist a paired style override, but
88
+ // sets written before the model/CSS split only carry `var.{id}`; derive the
89
+ // CSS here so `var(--{id})` bindings rehydrate. Object (font/image) values
90
+ // are never CSS, so they are skipped.
91
+ if (rootId && key.startsWith("var.") && value !== null && typeof value !== "object") {
92
+ const cssPath = stylePath(rootId, `--${key.slice("var.".length)}`);
93
+ patches.push({ op: "replace", path: cssPath, value: String(value) });
94
+ }
95
+ else if (rootId && key.startsWith("var.") && value === null) {
96
+ patches.push({ op: "remove", path: stylePath(rootId, `--${key.slice("var.".length)}`) });
97
+ }
98
+ }
99
+ applyPatchesToDocument(parsed, patches);
100
+ }
101
+ export function applyPatchesToDocument(parsed, patches) {
102
+ for (const patch of patches) {
103
+ const p = parsePath(patch.path);
104
+ if (!p)
105
+ continue;
106
+ applyOne(parsed, patch, p);
107
+ }
108
+ }
109
+ // fallow-ignore-next-line complexity
110
+ function applyOne(parsed, patch, p) {
111
+ switch (p.type) {
112
+ case "style": {
113
+ const el = p.id ? findById(parsed.document, p.id) : null;
114
+ if (!el || !p.prop)
115
+ return;
116
+ if (patch.op === "remove") {
117
+ setElementStyles(el, { [p.prop]: null });
118
+ }
119
+ else {
120
+ setElementStyles(el, { [p.prop]: String(patch.value) });
121
+ }
122
+ break;
123
+ }
124
+ case "text": {
125
+ const el = p.id ? findById(parsed.document, p.id) : null;
126
+ if (!el)
127
+ return;
128
+ if (patch.op === "remove") {
129
+ setOwnText(el, "");
130
+ }
131
+ else {
132
+ setOwnText(el, String(patch.value ?? ""));
133
+ }
134
+ break;
135
+ }
136
+ case "attribute": {
137
+ const el = p.id ? findById(parsed.document, p.id) : null;
138
+ if (!el || !p.prop)
139
+ return;
140
+ if (patch.op === "remove") {
141
+ el.removeAttribute(p.prop);
142
+ }
143
+ else {
144
+ el.setAttribute(p.prop, String(patch.value ?? ""));
145
+ }
146
+ break;
147
+ }
148
+ case "timing": {
149
+ const el = p.id ? findById(parsed.document, p.id) : null;
150
+ if (!el || !p.field)
151
+ return;
152
+ if (p.field === "start") {
153
+ if (patch.op === "remove")
154
+ el.removeAttribute("data-start");
155
+ else
156
+ el.setAttribute("data-start", String(patch.value));
157
+ }
158
+ else if (p.field === "duration") {
159
+ // Patch value is the data-duration value — set directly.
160
+ if (patch.op === "remove")
161
+ el.removeAttribute("data-duration");
162
+ else
163
+ el.setAttribute("data-duration", String(patch.value));
164
+ }
165
+ else if (p.field === "end") {
166
+ // Patch value is the absolute data-end time — set directly, no re-derivation.
167
+ if (patch.op === "remove")
168
+ el.removeAttribute("data-end");
169
+ else
170
+ el.setAttribute("data-end", String(patch.value));
171
+ }
172
+ else if (p.field === "trackIndex") {
173
+ if (patch.op === "remove")
174
+ el.removeAttribute("data-track-index");
175
+ else
176
+ el.setAttribute("data-track-index", String(patch.value));
177
+ }
178
+ break;
179
+ }
180
+ case "hold": {
181
+ const el = p.id ? findById(parsed.document, p.id) : null;
182
+ if (!el || !p.field)
183
+ return;
184
+ const attrName = `data-hold-${p.field}`;
185
+ if (patch.op === "remove")
186
+ el.removeAttribute(attrName);
187
+ else
188
+ el.setAttribute(attrName, String(patch.value));
189
+ break;
190
+ }
191
+ case "element": {
192
+ if (!p.id)
193
+ return;
194
+ if (patch.op === "remove") {
195
+ const el = findById(parsed.document, p.id);
196
+ el?.remove();
197
+ }
198
+ else if (patch.op === "add" && patch.value) {
199
+ const v = patch.value;
200
+ const parent = v.parentId
201
+ ? findById(parsed.document, v.parentId)
202
+ : parsed.document.body;
203
+ if (!parent)
204
+ return;
205
+ // Parse within the target document to avoid cross-document node issues.
206
+ const tmp = parsed.document.createElement("div");
207
+ tmp.innerHTML = v.html;
208
+ const node = tmp.firstElementChild;
209
+ if (!node)
210
+ return;
211
+ const children = Array.from(parent.children);
212
+ const ref = children[v.siblingIndex] ?? null;
213
+ parent.insertBefore(node, ref);
214
+ }
215
+ break;
216
+ }
217
+ case "variable": {
218
+ if (!p.id)
219
+ return;
220
+ // B1: update the JSON model (data-composition-variables) so
221
+ // getVariables() returns the correct value in both preview and render.
222
+ // CSS compat is handled by explicit style-path patches emitted by mutate.ts,
223
+ // so we do NOT write CSS here — the style case above handles those patches.
224
+ applyVariableDefault(parsed.document, p.id, patch.op === "remove" ? null : patch.value);
225
+ break;
226
+ }
227
+ case "script": {
228
+ if (patch.op === "remove") {
229
+ setGsapScript(parsed.document, "");
230
+ }
231
+ else {
232
+ setGsapScript(parsed.document, String(patch.value ?? ""));
233
+ }
234
+ break;
235
+ }
236
+ case "stylesheet": {
237
+ if (patch.op === "remove") {
238
+ setStyleSheet(parsed.document, "");
239
+ }
240
+ else {
241
+ setStyleSheet(parsed.document, String(patch.value ?? ""));
242
+ }
243
+ break;
244
+ }
245
+ case "metadata": {
246
+ const root = findRoot(parsed.document);
247
+ if (!root || !p.field)
248
+ return;
249
+ // Mirror mutate.ts: style always written; the data-* forced-override
250
+ // attribute is updated only when the composition already carries it.
251
+ if (p.field === "width") {
252
+ if (patch.op === "remove") {
253
+ setElementStyles(root, { width: null });
254
+ root.removeAttribute("data-width");
255
+ }
256
+ else {
257
+ setElementStyles(root, { width: `${patch.value}px` });
258
+ if (root.hasAttribute("data-width"))
259
+ root.setAttribute("data-width", String(patch.value));
260
+ }
261
+ }
262
+ else if (p.field === "height") {
263
+ if (patch.op === "remove") {
264
+ setElementStyles(root, { height: null });
265
+ root.removeAttribute("data-height");
266
+ }
267
+ else {
268
+ setElementStyles(root, { height: `${patch.value}px` });
269
+ if (root.hasAttribute("data-height")) {
270
+ root.setAttribute("data-height", String(patch.value));
271
+ }
272
+ }
273
+ }
274
+ else if (p.field === "duration") {
275
+ if (patch.op === "remove")
276
+ root.removeAttribute("data-duration");
277
+ else
278
+ root.setAttribute("data-duration", String(patch.value));
279
+ }
280
+ break;
281
+ }
282
+ }
283
+ }
284
+ //# sourceMappingURL=apply-patches.js.map