@hyperframes/parsers 0.7.15 → 0.7.17

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.
@@ -0,0 +1,92 @@
1
+ /** Current manifest schema version. Stamped on persist so future schema
2
+ * changes can detect and migrate older islands. */
3
+ declare const SLIDESHOW_MANIFEST_VERSION = 1;
4
+ /** Raw author-facing shapes parsed from the JSON island. */
5
+ interface SlideshowManifest {
6
+ /** Schema version (absent on pre-versioning islands → treat as 1). */
7
+ version?: number;
8
+ slides: SlideRef[];
9
+ slideSequences?: SlideSequence[];
10
+ }
11
+ interface SlideRef {
12
+ sceneId: string;
13
+ startTime?: number;
14
+ endTime?: number;
15
+ notes?: string;
16
+ fragments?: number[];
17
+ hotspots?: SlideHotspot[];
18
+ /**
19
+ * When true, the slide's first `<video>` plays automatically on enter (the
20
+ * presenter lands on the slide and the clip plays). The slideshow still holds
21
+ * — it never auto-advances — so the presenter clicks Next when ready.
22
+ * Defaults to false. Use it when the video is the slide's primary content and
23
+ * its natural end is the cue to advance, not for background/ambient clips.
24
+ */
25
+ autoplay?: boolean;
26
+ ttsScript?: string;
27
+ ttsAudioUrl?: string;
28
+ ttsDurationMs?: number;
29
+ }
30
+ interface SlideHotspot {
31
+ id: string;
32
+ label: string;
33
+ target: string;
34
+ region?: {
35
+ x: number;
36
+ y: number;
37
+ w: number;
38
+ h: number;
39
+ };
40
+ }
41
+ interface SlideSequence {
42
+ id: string;
43
+ label: string;
44
+ slides: SlideRef[];
45
+ }
46
+ /** A slide with its time range resolved from the matching scene. */
47
+ interface ResolvedSlide extends SlideRef {
48
+ start: number;
49
+ end: number;
50
+ fragments: number[];
51
+ hotspots: SlideHotspot[];
52
+ }
53
+ interface ResolvedSlideSequence {
54
+ id: string;
55
+ label: string;
56
+ slides: ResolvedSlide[];
57
+ }
58
+ interface ResolvedSlideshow {
59
+ slides: ResolvedSlide[];
60
+ sequences: Record<string, ResolvedSlideSequence>;
61
+ }
62
+
63
+ declare const SLIDESHOW_ISLAND_TYPE = "application/hyperframes-slideshow+json";
64
+ /**
65
+ * Builds the island <script> matcher. Capture group 1 = inner JSON.
66
+ *
67
+ * Factory (fresh RegExp per call) on purpose: a RegExp with the `g` flag carries
68
+ * a mutable `lastIndex`, so callers that need `g` must call this each time rather
69
+ * than caching a shared instance.
70
+ */
71
+ declare function slideshowIslandRegex(flags?: string): RegExp;
72
+ interface SceneRange {
73
+ id: string;
74
+ start: number;
75
+ duration: number;
76
+ }
77
+ /** Extract the JSON island from composition HTML. Returns null if absent. */
78
+ declare function parseSlideshowManifest(html: string): SlideshowManifest | null;
79
+ declare function resolveSlideshow(manifest: SlideshowManifest, scenes: SceneRange[]): {
80
+ resolved: ResolvedSlideshow;
81
+ errors: string[];
82
+ };
83
+
84
+ /**
85
+ * Whether a composition id names a "scene-like" composition — i.e. a real slide
86
+ * scene, not the root timeline (`main`) or a non-scene overlay (captions, ambient
87
+ * layers). Shared by the runtime scene-window computation and the slideshow lint
88
+ * rule so the two can never drift.
89
+ */
90
+ declare function isSceneLikeCompositionId(compositionId: string): boolean;
91
+
92
+ export { type ResolvedSlide, type ResolvedSlideSequence, type ResolvedSlideshow, SLIDESHOW_ISLAND_TYPE, SLIDESHOW_MANIFEST_VERSION, type SlideHotspot, type SlideRef, type SlideSequence, type SlideshowManifest, isSceneLikeCompositionId, parseSlideshowManifest, resolveSlideshow, slideshowIslandRegex };
@@ -0,0 +1,149 @@
1
+ // src/slideshow/slideshow.types.ts
2
+ var SLIDESHOW_MANIFEST_VERSION = 1;
3
+
4
+ // src/slideshow/parseSlideshow.ts
5
+ var SLIDESHOW_ISLAND_TYPE = "application/hyperframes-slideshow+json";
6
+ function slideshowIslandRegex(flags = "i") {
7
+ const escaped = SLIDESHOW_ISLAND_TYPE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8
+ return new RegExp(`<script[^>]*type=["']${escaped}["'][^>]*>([\\s\\S]*?)<\\/script>`, flags);
9
+ }
10
+ function parseSlideshowManifest(html) {
11
+ const re = slideshowIslandRegex("i");
12
+ const match = re.exec(html);
13
+ if (!match || match[1] === void 0) return null;
14
+ const raw = match[1].trim();
15
+ if (raw.length === 0) return null;
16
+ const parsed = JSON.parse(raw);
17
+ if (!isManifest(parsed)) {
18
+ throw new Error("slideshow island is not a valid SlideshowManifest");
19
+ }
20
+ return parsed;
21
+ }
22
+ function isOptionalNumberArray(v) {
23
+ return v === void 0 || Array.isArray(v) && v.every((n) => typeof n === "number");
24
+ }
25
+ function isOptionalBoolean(v) {
26
+ return v === void 0 || typeof v === "boolean";
27
+ }
28
+ function isSlideRef(v) {
29
+ if (typeof v !== "object" || v === null) return false;
30
+ const r = v;
31
+ if (typeof r["sceneId"] !== "string") return false;
32
+ if (!isOptionalNumberArray(r["fragments"])) return false;
33
+ if (r["hotspots"] !== void 0 && !Array.isArray(r["hotspots"])) return false;
34
+ if (!isOptionalBoolean(r["autoplay"])) return false;
35
+ return true;
36
+ }
37
+ function isSlideSequence(v) {
38
+ if (typeof v !== "object" || v === null) return false;
39
+ const s = v;
40
+ return typeof s["id"] === "string" && typeof s["label"] === "string" && Array.isArray(s["slides"]) && s["slides"].every(isSlideRef);
41
+ }
42
+ function isManifest(v) {
43
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
44
+ const o = v;
45
+ if (!Array.isArray(o["slides"]) || !o["slides"].every(isSlideRef)) return false;
46
+ if (o["slideSequences"] !== void 0) {
47
+ if (!Array.isArray(o["slideSequences"]) || !o["slideSequences"].every(isSlideSequence))
48
+ return false;
49
+ }
50
+ return true;
51
+ }
52
+ function missingBoundError(sceneId, missing) {
53
+ const present = missing === "startTime" ? "endTime" : "startTime";
54
+ return `slide "${sceneId}" sets ${present} but ${missing} cannot be resolved (no scene "${sceneId}")`;
55
+ }
56
+ function resolveTimeRange(ref, scene, errors) {
57
+ const { startTime, endTime, sceneId } = ref;
58
+ if (startTime !== void 0 && endTime !== void 0) {
59
+ return { start: startTime, end: endTime };
60
+ }
61
+ if (startTime === void 0 && endTime === void 0) {
62
+ if (!scene) {
63
+ errors.push(`slide references unresolved sceneId "${sceneId}"`);
64
+ return { start: 0, end: 0 };
65
+ }
66
+ return { start: scene.start, end: scene.start + scene.duration };
67
+ }
68
+ if (!scene) {
69
+ const missing = startTime === void 0 ? "startTime" : "endTime";
70
+ errors.push(missingBoundError(sceneId, missing));
71
+ const bound = startTime ?? endTime ?? 0;
72
+ return { start: bound, end: bound };
73
+ }
74
+ return {
75
+ start: startTime ?? scene.start,
76
+ end: endTime ?? scene.start + scene.duration
77
+ };
78
+ }
79
+ function validateFragments(sceneId, fragments, start, end, errors) {
80
+ for (const f of fragments) {
81
+ if (f < start || f > end) {
82
+ errors.push(`slide "${sceneId}" fragment ${f} is outside range [${start}, ${end}]`);
83
+ }
84
+ }
85
+ }
86
+ function resolveSlide(ref, sceneById, errors) {
87
+ const scene = sceneById.get(ref.sceneId);
88
+ const { start, end } = resolveTimeRange(ref, scene, errors);
89
+ if (ref.startTime !== void 0 && ref.endTime !== void 0 && end <= start) {
90
+ errors.push(`slide "${ref.sceneId}" has endTime (${end}) <= startTime (${start})`);
91
+ }
92
+ const fragments = [...new Set(ref.fragments ?? [])].sort((a, b) => a - b);
93
+ validateFragments(ref.sceneId, fragments, start, end, errors);
94
+ return { ...ref, start, end, fragments, hotspots: ref.hotspots ?? [] };
95
+ }
96
+ function resolveSlideshow(manifest, scenes) {
97
+ const errors = [];
98
+ const sceneById = new Map(scenes.map((s) => [s.id, s]));
99
+ const sequences = {};
100
+ for (const seq of manifest.slideSequences ?? []) {
101
+ if (Object.prototype.hasOwnProperty.call(sequences, seq.id)) {
102
+ errors.push(`duplicate slideSequence id "${seq.id}" \u2014 only the last definition is kept`);
103
+ }
104
+ sequences[seq.id] = {
105
+ id: seq.id,
106
+ label: seq.label,
107
+ slides: seq.slides.map((s) => resolveSlide(s, sceneById, errors))
108
+ };
109
+ }
110
+ const slides = manifest.slides.map((s) => resolveSlide(s, sceneById, errors));
111
+ const allSlides = [...slides, ...Object.values(sequences).flatMap((s) => s.slides)];
112
+ for (const slide of allSlides) {
113
+ for (const h of slide.hotspots) {
114
+ const seq = sequences[h.target];
115
+ if (!seq) {
116
+ errors.push(`hotspot "${h.id}" targets unknown sequence "${h.target}"`);
117
+ } else if (seq.slides.length === 0) {
118
+ errors.push(`hotspot "${h.id}" targets empty sequence "${h.target}"`);
119
+ }
120
+ }
121
+ }
122
+ const ordered = [...slides].sort((a, b) => a.start - b.start);
123
+ for (let i = 1; i < ordered.length; i++) {
124
+ const prev = ordered[i - 1];
125
+ const curr = ordered[i];
126
+ if (prev !== void 0 && curr !== void 0 && curr.start < prev.end) {
127
+ errors.push(`main-line slides "${prev.sceneId}" and "${curr.sceneId}" overlap`);
128
+ }
129
+ }
130
+ return { resolved: { slides, sequences }, errors };
131
+ }
132
+
133
+ // src/slideshow/sceneId.ts
134
+ function isSceneLikeCompositionId(compositionId) {
135
+ const normalized = compositionId.trim().toLowerCase();
136
+ if (!normalized || normalized === "main") return false;
137
+ if (normalized.includes("caption")) return false;
138
+ if (normalized.includes("ambient")) return false;
139
+ return true;
140
+ }
141
+ export {
142
+ SLIDESHOW_ISLAND_TYPE,
143
+ SLIDESHOW_MANIFEST_VERSION,
144
+ isSceneLikeCompositionId,
145
+ parseSlideshowManifest,
146
+ resolveSlideshow,
147
+ slideshowIslandRegex
148
+ };
149
+ //# sourceMappingURL=slideshow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/slideshow/slideshow.types.ts","../src/slideshow/parseSlideshow.ts","../src/slideshow/sceneId.ts"],"sourcesContent":["// packages/core/src/slideshow/slideshow.types.ts\n\n/** Current manifest schema version. Stamped on persist so future schema\n * changes can detect and migrate older islands. */\nexport const SLIDESHOW_MANIFEST_VERSION = 1;\n\n/** Raw author-facing shapes parsed from the JSON island. */\nexport interface SlideshowManifest {\n /** Schema version (absent on pre-versioning islands → treat as 1). */\n version?: number;\n slides: SlideRef[];\n slideSequences?: SlideSequence[];\n}\n\nexport interface SlideRef {\n sceneId: string;\n startTime?: number;\n endTime?: number;\n notes?: string;\n fragments?: number[];\n hotspots?: SlideHotspot[];\n /**\n * When true, the slide's first `<video>` plays automatically on enter (the\n * presenter lands on the slide and the clip plays). The slideshow still holds\n * — it never auto-advances — so the presenter clicks Next when ready.\n * Defaults to false. Use it when the video is the slide's primary content and\n * its natural end is the cue to advance, not for background/ambient clips.\n */\n autoplay?: boolean;\n // Reserved — TTS deferred. Parsed and carried, never consumed.\n ttsScript?: string;\n ttsAudioUrl?: string;\n ttsDurationMs?: number;\n}\n\nexport interface SlideHotspot {\n id: string;\n label: string;\n target: string; // references a SlideSequence.id\n region?: { x: number; y: number; w: number; h: number }; // % of slide\n}\n\nexport interface SlideSequence {\n id: string;\n label: string;\n slides: SlideRef[];\n}\n\n/** A slide with its time range resolved from the matching scene. */\nexport interface ResolvedSlide extends SlideRef {\n start: number;\n end: number;\n fragments: number[]; // always present, sorted, defaulted to []\n hotspots: SlideHotspot[]; // always present, defaulted to []\n}\n\nexport interface ResolvedSlideSequence {\n id: string;\n label: string;\n slides: ResolvedSlide[];\n}\n\nexport interface ResolvedSlideshow {\n slides: ResolvedSlide[];\n sequences: Record<string, ResolvedSlideSequence>; // keyed by sequence id\n}\n","// packages/core/src/slideshow/parseSlideshow.ts\nimport type {\n SlideshowManifest,\n SlideRef,\n ResolvedSlide,\n ResolvedSlideshow,\n ResolvedSlideSequence,\n} from \"./slideshow.types\";\n\nexport const SLIDESHOW_ISLAND_TYPE = \"application/hyperframes-slideshow+json\";\n\n/**\n * Builds the island <script> matcher. Capture group 1 = inner JSON.\n *\n * Factory (fresh RegExp per call) on purpose: a RegExp with the `g` flag carries\n * a mutable `lastIndex`, so callers that need `g` must call this each time rather\n * than caching a shared instance.\n */\nexport function slideshowIslandRegex(flags = \"i\"): RegExp {\n // Escape ALL regex metacharacters in the constant (CodeQL flags the incomplete\n // `\\` escape). The constant has none today, but a complete escape is correct.\n const escaped = SLIDESHOW_ISLAND_TYPE.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`<script[^>]*type=[\"']${escaped}[\"'][^>]*>([\\\\s\\\\S]*?)<\\\\/script>`, flags);\n}\n\ninterface SceneRange {\n id: string;\n start: number;\n duration: number;\n}\n\n/** Extract the JSON island from composition HTML. Returns null if absent. */\nexport function parseSlideshowManifest(html: string): SlideshowManifest | null {\n // Match <script type=\"application/hyperframes-slideshow+json\"> ... </script>\n const re = slideshowIslandRegex(\"i\");\n const match = re.exec(html);\n if (!match || match[1] === undefined) return null;\n const raw = match[1].trim();\n if (raw.length === 0) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!isManifest(parsed)) {\n throw new Error(\"slideshow island is not a valid SlideshowManifest\");\n }\n return parsed;\n}\n\nfunction isOptionalNumberArray(v: unknown): boolean {\n return v === undefined || (Array.isArray(v) && v.every((n) => typeof n === \"number\"));\n}\n\nfunction isOptionalBoolean(v: unknown): v is boolean | undefined {\n return v === undefined || typeof v === \"boolean\";\n}\n\nfunction isSlideRef(v: unknown): v is SlideRef {\n if (typeof v !== \"object\" || v === null) return false;\n const r = v as Record<string, unknown>;\n if (typeof r[\"sceneId\"] !== \"string\") return false;\n if (!isOptionalNumberArray(r[\"fragments\"])) return false;\n if (r[\"hotspots\"] !== undefined && !Array.isArray(r[\"hotspots\"])) return false;\n if (!isOptionalBoolean(r[\"autoplay\"])) return false;\n return true;\n}\n\nfunction isSlideSequence(v: unknown): boolean {\n if (typeof v !== \"object\" || v === null) return false;\n const s = v as Record<string, unknown>;\n return (\n typeof s[\"id\"] === \"string\" &&\n typeof s[\"label\"] === \"string\" &&\n Array.isArray(s[\"slides\"]) &&\n s[\"slides\"].every(isSlideRef)\n );\n}\n\nfunction isManifest(v: unknown): v is SlideshowManifest {\n if (typeof v !== \"object\" || v === null || Array.isArray(v)) return false;\n const o = v as Record<string, unknown>;\n if (!Array.isArray(o[\"slides\"]) || !o[\"slides\"].every(isSlideRef)) return false;\n if (o[\"slideSequences\"] !== undefined) {\n if (!Array.isArray(o[\"slideSequences\"]) || !o[\"slideSequences\"].every(isSlideSequence))\n return false;\n }\n return true;\n}\n\nfunction missingBoundError(sceneId: string, missing: \"startTime\" | \"endTime\"): string {\n const present = missing === \"startTime\" ? \"endTime\" : \"startTime\";\n return `slide \"${sceneId}\" sets ${present} but ${missing} cannot be resolved (no scene \"${sceneId}\")`;\n}\n\n// fallow-ignore-next-line complexity\nfunction resolveTimeRange(\n ref: SlideRef,\n scene: SceneRange | undefined,\n errors: string[],\n): { start: number; end: number } {\n const { startTime, endTime, sceneId } = ref;\n\n // Both explicit — use them directly, no scene needed.\n if (startTime !== undefined && endTime !== undefined) {\n return { start: startTime, end: endTime };\n }\n\n // Neither explicit — resolve both from scene.\n if (startTime === undefined && endTime === undefined) {\n if (!scene) {\n errors.push(`slide references unresolved sceneId \"${sceneId}\"`);\n return { start: 0, end: 0 };\n }\n return { start: scene.start, end: scene.start + scene.duration };\n }\n\n // Exactly one bound explicit — fill from scene, or report a clear error.\n if (!scene) {\n const missing = startTime === undefined ? \"startTime\" : \"endTime\";\n errors.push(missingBoundError(sceneId, missing));\n const bound = startTime ?? endTime ?? 0;\n return { start: bound, end: bound };\n }\n\n return {\n start: startTime ?? scene.start,\n end: endTime ?? scene.start + scene.duration,\n };\n}\n\nfunction validateFragments(\n sceneId: string,\n fragments: number[],\n start: number,\n end: number,\n errors: string[],\n): void {\n for (const f of fragments) {\n if (f < start || f > end) {\n errors.push(`slide \"${sceneId}\" fragment ${f} is outside range [${start}, ${end}]`);\n }\n }\n}\n\nfunction resolveSlide(\n ref: SlideRef,\n sceneById: Map<string, SceneRange>,\n errors: string[],\n): ResolvedSlide {\n const scene = sceneById.get(ref.sceneId);\n const { start, end } = resolveTimeRange(ref, scene, errors);\n if (ref.startTime !== undefined && ref.endTime !== undefined && end <= start) {\n errors.push(`slide \"${ref.sceneId}\" has endTime (${end}) <= startTime (${start})`);\n }\n const fragments = [...new Set(ref.fragments ?? [])].sort((a, b) => a - b);\n validateFragments(ref.sceneId, fragments, start, end, errors);\n return { ...ref, start, end, fragments, hotspots: ref.hotspots ?? [] };\n}\n\nexport function resolveSlideshow(\n manifest: SlideshowManifest,\n scenes: SceneRange[],\n): { resolved: ResolvedSlideshow; errors: string[] } {\n const errors: string[] = [];\n const sceneById = new Map(scenes.map((s) => [s.id, s]));\n\n const sequences: Record<string, ResolvedSlideSequence> = {};\n for (const seq of manifest.slideSequences ?? []) {\n // Flag duplicate sequence ids rather than silently overwriting the earlier one.\n if (Object.prototype.hasOwnProperty.call(sequences, seq.id)) {\n errors.push(`duplicate slideSequence id \"${seq.id}\" — only the last definition is kept`);\n }\n sequences[seq.id] = {\n id: seq.id,\n label: seq.label,\n slides: seq.slides.map((s) => resolveSlide(s, sceneById, errors)),\n };\n }\n\n const slides = manifest.slides.map((s) => resolveSlide(s, sceneById, errors));\n\n // Validate hotspot targets.\n const allSlides = [...slides, ...Object.values(sequences).flatMap((s) => s.slides)];\n for (const slide of allSlides) {\n for (const h of slide.hotspots) {\n const seq = sequences[h.target];\n if (!seq) {\n errors.push(`hotspot \"${h.id}\" targets unknown sequence \"${h.target}\"`);\n } else if (seq.slides.length === 0) {\n errors.push(`hotspot \"${h.id}\" targets empty sequence \"${h.target}\"`);\n }\n }\n }\n\n // Validate no main-line overlap (sorted by start; adjacent compare).\n const ordered = [...slides].sort((a, b) => a.start - b.start);\n for (let i = 1; i < ordered.length; i++) {\n const prev = ordered[i - 1];\n const curr = ordered[i];\n if (prev !== undefined && curr !== undefined && curr.start < prev.end) {\n errors.push(`main-line slides \"${prev.sceneId}\" and \"${curr.sceneId}\" overlap`);\n }\n }\n\n return { resolved: { slides, sequences }, errors };\n}\n","// packages/core/src/slideshow/sceneId.ts\n\n/**\n * Whether a composition id names a \"scene-like\" composition — i.e. a real slide\n * scene, not the root timeline (`main`) or a non-scene overlay (captions, ambient\n * layers). Shared by the runtime scene-window computation and the slideshow lint\n * rule so the two can never drift.\n */\nexport function isSceneLikeCompositionId(compositionId: string): boolean {\n const normalized = compositionId.trim().toLowerCase();\n if (!normalized || normalized === \"main\") return false;\n if (normalized.includes(\"caption\")) return false;\n if (normalized.includes(\"ambient\")) return false;\n return true;\n}\n"],"mappings":";AAIO,IAAM,6BAA6B;;;ACKnC,IAAM,wBAAwB;AAS9B,SAAS,qBAAqB,QAAQ,KAAa;AAGxD,QAAM,UAAU,sBAAsB,QAAQ,uBAAuB,MAAM;AAC3E,SAAO,IAAI,OAAO,wBAAwB,OAAO,qCAAqC,KAAK;AAC7F;AASO,SAAS,uBAAuB,MAAwC;AAE7E,QAAM,KAAK,qBAAqB,GAAG;AACnC,QAAM,QAAQ,GAAG,KAAK,IAAI;AAC1B,MAAI,CAAC,SAAS,MAAM,CAAC,MAAM,OAAW,QAAO;AAC7C,QAAM,MAAM,MAAM,CAAC,EAAE,KAAK;AAC1B,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,GAAqB;AAClD,SAAO,MAAM,UAAc,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ;AACrF;AAEA,SAAS,kBAAkB,GAAsC;AAC/D,SAAO,MAAM,UAAa,OAAO,MAAM;AACzC;AAEA,SAAS,WAAW,GAA2B;AAC7C,MAAI,OAAO,MAAM,YAAY,MAAM,KAAM,QAAO;AAChD,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,SAAS,MAAM,SAAU,QAAO;AAC7C,MAAI,CAAC,sBAAsB,EAAE,WAAW,CAAC,EAAG,QAAO;AACnD,MAAI,EAAE,UAAU,MAAM,UAAa,CAAC,MAAM,QAAQ,EAAE,UAAU,CAAC,EAAG,QAAO;AACzE,MAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,EAAG,QAAO;AAC9C,SAAO;AACT;AAEA,SAAS,gBAAgB,GAAqB;AAC5C,MAAI,OAAO,MAAM,YAAY,MAAM,KAAM,QAAO;AAChD,QAAM,IAAI;AACV,SACE,OAAO,EAAE,IAAI,MAAM,YACnB,OAAO,EAAE,OAAO,MAAM,YACtB,MAAM,QAAQ,EAAE,QAAQ,CAAC,KACzB,EAAE,QAAQ,EAAE,MAAM,UAAU;AAEhC;AAEA,SAAS,WAAW,GAAoC;AACtD,MAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAG,QAAO;AACpE,QAAM,IAAI;AACV,MAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,MAAM,UAAU,EAAG,QAAO;AAC1E,MAAI,EAAE,gBAAgB,MAAM,QAAW;AACrC,QAAI,CAAC,MAAM,QAAQ,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAAE,gBAAgB,EAAE,MAAM,eAAe;AACnF,aAAO;AAAA,EACX;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,SAAiB,SAA0C;AACpF,QAAM,UAAU,YAAY,cAAc,YAAY;AACtD,SAAO,UAAU,OAAO,UAAU,OAAO,QAAQ,OAAO,kCAAkC,OAAO;AACnG;AAGA,SAAS,iBACP,KACA,OACA,QACgC;AAChC,QAAM,EAAE,WAAW,SAAS,QAAQ,IAAI;AAGxC,MAAI,cAAc,UAAa,YAAY,QAAW;AACpD,WAAO,EAAE,OAAO,WAAW,KAAK,QAAQ;AAAA,EAC1C;AAGA,MAAI,cAAc,UAAa,YAAY,QAAW;AACpD,QAAI,CAAC,OAAO;AACV,aAAO,KAAK,wCAAwC,OAAO,GAAG;AAC9D,aAAO,EAAE,OAAO,GAAG,KAAK,EAAE;AAAA,IAC5B;AACA,WAAO,EAAE,OAAO,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,SAAS;AAAA,EACjE;AAGA,MAAI,CAAC,OAAO;AACV,UAAM,UAAU,cAAc,SAAY,cAAc;AACxD,WAAO,KAAK,kBAAkB,SAAS,OAAO,CAAC;AAC/C,UAAM,QAAQ,aAAa,WAAW;AACtC,WAAO,EAAE,OAAO,OAAO,KAAK,MAAM;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,OAAO,aAAa,MAAM;AAAA,IAC1B,KAAK,WAAW,MAAM,QAAQ,MAAM;AAAA,EACtC;AACF;AAEA,SAAS,kBACP,SACA,WACA,OACA,KACA,QACM;AACN,aAAW,KAAK,WAAW;AACzB,QAAI,IAAI,SAAS,IAAI,KAAK;AACxB,aAAO,KAAK,UAAU,OAAO,cAAc,CAAC,sBAAsB,KAAK,KAAK,GAAG,GAAG;AAAA,IACpF;AAAA,EACF;AACF;AAEA,SAAS,aACP,KACA,WACA,QACe;AACf,QAAM,QAAQ,UAAU,IAAI,IAAI,OAAO;AACvC,QAAM,EAAE,OAAO,IAAI,IAAI,iBAAiB,KAAK,OAAO,MAAM;AAC1D,MAAI,IAAI,cAAc,UAAa,IAAI,YAAY,UAAa,OAAO,OAAO;AAC5E,WAAO,KAAK,UAAU,IAAI,OAAO,kBAAkB,GAAG,mBAAmB,KAAK,GAAG;AAAA,EACnF;AACA,QAAM,YAAY,CAAC,GAAG,IAAI,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACxE,oBAAkB,IAAI,SAAS,WAAW,OAAO,KAAK,MAAM;AAC5D,SAAO,EAAE,GAAG,KAAK,OAAO,KAAK,WAAW,UAAU,IAAI,YAAY,CAAC,EAAE;AACvE;AAEO,SAAS,iBACd,UACA,QACmD;AACnD,QAAM,SAAmB,CAAC;AAC1B,QAAM,YAAY,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAEtD,QAAM,YAAmD,CAAC;AAC1D,aAAW,OAAO,SAAS,kBAAkB,CAAC,GAAG;AAE/C,QAAI,OAAO,UAAU,eAAe,KAAK,WAAW,IAAI,EAAE,GAAG;AAC3D,aAAO,KAAK,+BAA+B,IAAI,EAAE,2CAAsC;AAAA,IACzF;AACA,cAAU,IAAI,EAAE,IAAI;AAAA,MAClB,IAAI,IAAI;AAAA,MACR,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI,OAAO,IAAI,CAAC,MAAM,aAAa,GAAG,WAAW,MAAM,CAAC;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,SAAS,SAAS,OAAO,IAAI,CAAC,MAAM,aAAa,GAAG,WAAW,MAAM,CAAC;AAG5E,QAAM,YAAY,CAAC,GAAG,QAAQ,GAAG,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;AAClF,aAAW,SAAS,WAAW;AAC7B,eAAW,KAAK,MAAM,UAAU;AAC9B,YAAM,MAAM,UAAU,EAAE,MAAM;AAC9B,UAAI,CAAC,KAAK;AACR,eAAO,KAAK,YAAY,EAAE,EAAE,+BAA+B,EAAE,MAAM,GAAG;AAAA,MACxE,WAAW,IAAI,OAAO,WAAW,GAAG;AAClC,eAAO,KAAK,YAAY,EAAE,EAAE,6BAA6B,EAAE,MAAM,GAAG;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UAAU,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC5D,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,OAAO,QAAQ,IAAI,CAAC;AAC1B,UAAM,OAAO,QAAQ,CAAC;AACtB,QAAI,SAAS,UAAa,SAAS,UAAa,KAAK,QAAQ,KAAK,KAAK;AACrE,aAAO,KAAK,qBAAqB,KAAK,OAAO,UAAU,KAAK,OAAO,WAAW;AAAA,IAChF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,EAAE,QAAQ,UAAU,GAAG,OAAO;AACnD;;;AClMO,SAAS,yBAAyB,eAAgC;AACvE,QAAM,aAAa,cAAc,KAAK,EAAE,YAAY;AACpD,MAAI,CAAC,cAAc,eAAe,OAAQ,QAAO;AACjD,MAAI,WAAW,SAAS,SAAS,EAAG,QAAO;AAC3C,MAAI,WAAW,SAAS,SAAS,EAAG,QAAO;AAC3C,SAAO;AACT;","names":[]}
@@ -1,5 +1,3 @@
1
- import { PropertyGroupName } from './gsapConstants.js';
2
-
3
1
  interface Asset {
4
2
  id: string;
5
3
  url: string;
@@ -370,166 +368,4 @@ interface StageZoomKeyframe {
370
368
  }
371
369
  declare function getDefaultStageZoom(resolution: CanvasResolution): StageZoom;
372
370
 
373
- /**
374
- * Recast-free GSAP helpers: serialization, keyframe<->animation conversion,
375
- * validation, and shared types.
376
- *
377
- * This module MUST NOT import recast / @babel/parser. It is part of the
378
- * isomorphic core layer that the barrel and browser code depend on. AST
379
- * parsing of GSAP source lives in the Node-only `./gsapParser` module.
380
- */
381
-
382
- type GsapMethod = "set" | "to" | "from" | "fromTo";
383
- /** How a tween was constructed in source — drives display classification and editability. */
384
- type GsapProvenanceKind = "literal" | "helper" | "loop" | "runtime-dynamic";
385
- /**
386
- * Origin of a parsed tween. `literal` tweens map 1:1 to a source call and edit
387
- * directly; `helper`/`loop` tweens are expanded from a reused construct (unroll
388
- * to edit); `runtime-dynamic` tweens come from live introspection (override to
389
- * edit). Absent provenance is treated as `literal`.
390
- */
391
- interface GsapProvenance {
392
- kind: GsapProvenanceKind;
393
- /** Helper function name (kind === "helper"). */
394
- fn?: string;
395
- /** 1-based ordinal of the originating call site / loop construct in source order. */
396
- callSite?: number;
397
- /** 0-based iteration index (kind === "loop"). */
398
- iteration?: number;
399
- /** Source offset [start, end] of the originating call/loop, when known. */
400
- sourceRange?: [number, number];
401
- }
402
- /** How a tween's keyframes can be edited, derived from its provenance. */
403
- type KeyframeEditability = "direct" | "unroll" | "source";
404
- /**
405
- * Map provenance to an editing strategy:
406
- * - `direct` — literal tween, maps 1:1 to source; edit in place.
407
- * - `unroll` — helper/loop expansion; unroll to literal tweens, then edit.
408
- * - `source` — runtime-dynamic value; not statically editable, edit the code.
409
- */
410
- declare function editabilityForProvenance(provenance?: GsapProvenance): KeyframeEditability;
411
- interface GsapAnimation {
412
- id: string;
413
- targetSelector: string;
414
- method: GsapMethod;
415
- position: number | string;
416
- properties: Record<string, number | string>;
417
- fromProperties?: Record<string, number | string>;
418
- duration?: number;
419
- ease?: string;
420
- /** Non-editable GSAP config (stagger, yoyo, repeat, etc.) preserved for round-trips. */
421
- extras?: Record<string, unknown>;
422
- /** Native GSAP keyframes data — present when the tween uses keyframes: { ... }. */
423
- keyframes?: GsapKeyframesData;
424
- /** Arc motion path config — present when the tween uses motionPath for curved position interpolation. */
425
- arcPath?: ArcPathConfig;
426
- /** True when the tween has a `keyframes` property that couldn't be statically resolved (dynamic). */
427
- hasUnresolvedKeyframes?: boolean;
428
- /** True when the tween's target selector couldn't be statically resolved (dynamic). */
429
- hasUnresolvedSelector?: boolean;
430
- /** Absolute start time computed by walking the timeline chain (handles +=, -=, <, >, labels). */
431
- resolvedStart?: number;
432
- /** True when no position arg was authored — the tween is sequentially placed by GSAP. */
433
- implicitPosition?: boolean;
434
- /** Which property group this tween belongs to (position, scale, size, rotation, visual, other).
435
- * Undefined for legacy mixed tweens that bundle multiple groups. */
436
- propertyGroup?: PropertyGroupName;
437
- /** True for a base `gsap.set(...)` (a static hold that runs immediately, OFF the
438
- * timeline) rather than `tl.set(...)`. Carries no timeline position and shows no
439
- * keyframe marker — used to persist a static value (e.g. a 3D transform) without
440
- * introducing a 0% keyframe. */
441
- global?: boolean;
442
- /** How this tween was constructed in source. Absent ⇒ literal. */
443
- provenance?: GsapProvenance;
444
- }
445
- interface GsapPercentageKeyframe {
446
- percentage: number;
447
- properties: Record<string, number | string>;
448
- ease?: string;
449
- }
450
- type GsapKeyframeFormat = "percentage" | "object-array" | "simple-array";
451
- interface GsapKeyframesData {
452
- format: GsapKeyframeFormat;
453
- keyframes: GsapPercentageKeyframe[];
454
- ease?: string;
455
- easeEach?: string;
456
- }
457
- interface ArcPathSegment {
458
- curviness: number;
459
- cp1?: {
460
- x: number;
461
- y: number;
462
- };
463
- cp2?: {
464
- x: number;
465
- y: number;
466
- };
467
- }
468
- interface ArcPathConfig {
469
- enabled: boolean;
470
- autoRotate: boolean | number;
471
- segments: ArcPathSegment[];
472
- }
473
- interface MotionPathShape {
474
- arcPath: ArcPathConfig;
475
- waypoints: Array<{
476
- x: number;
477
- y: number;
478
- }>;
479
- }
480
- /**
481
- * Build arcPath segments + waypoints from resolved path coordinates. Shared by
482
- * the AST parser (coords from literal nodes) and the runtime scanner (coords
483
- * from a live `vars.motionPath`), so both produce identical arc config.
484
- */
485
- declare function buildArcPath(coords: Array<{
486
- x: number;
487
- y: number;
488
- }>, curviness: number, autoRotate: boolean | number, isCubic: boolean): MotionPathShape | undefined;
489
- interface ParsedGsap {
490
- animations: GsapAnimation[];
491
- timelineVar: string;
492
- preamble: string;
493
- postamble: string;
494
- multipleTimelines?: boolean;
495
- unsupportedTimelinePattern?: boolean;
496
- }
497
-
498
- interface SplitAnimationsOptions {
499
- originalId: string;
500
- newId: string;
501
- splitTime: number;
502
- elementStart: number;
503
- elementDuration: number;
504
- }
505
- interface SplitAnimationsResult {
506
- script: string;
507
- /** Non-ID-selector animations that the engine cannot safely retarget. */
508
- skippedSelectors: string[];
509
- }
510
- declare function serializeGsapAnimations(animations: GsapAnimation[], timelineVar?: string, options?: {
511
- includeMediaSync?: boolean;
512
- preamble?: string;
513
- postamble?: string;
514
- }): string;
515
- /**
516
- * Filter animations to those targeting `#<elementId>` (id-only match). For the
517
- * studio panel's id-OR-selector matching, see `getAnimationsForElement` in
518
- * `useGsapTweenCache.ts` — distinct on purpose, hence the distinct name.
519
- */
520
- declare function getAnimationsForElementId(animations: GsapAnimation[], elementId: string): GsapAnimation[];
521
- declare function validateCompositionGsap(script: string): ValidationResult;
522
- declare function keyframesToGsapAnimations(elementId: string, keyframes: Keyframe[], elementStartTime: number, base?: {
523
- x?: number;
524
- y?: number;
525
- scale?: number;
526
- }): GsapAnimation[];
527
- declare function gsapAnimationsToKeyframes(animations: GsapAnimation[], elementStartTime: number, options?: {
528
- baseX?: number;
529
- baseY?: number;
530
- baseScale?: number;
531
- clampTimeToZero?: boolean;
532
- skipBaseSet?: boolean;
533
- }): Keyframe[];
534
-
535
- export { type TimelineElementType as $, type ArcPathConfig as A, type BooleanVariable as B, type CompositionVariable as C, type CompositionVariableBase as D, type CompositionVariableType as E, DEFAULT_DURATIONS as F, type GsapAnimation as G, type ElementKeyframes as H, type EnumVariable as I, type FontVariable as J, type KeyframeEditability as K, type ImageVariable as L, type MotionPathShape as M, type KeyframeProperties as N, type MediaElementType as O, type ParsedGsap as P, type MediaFile as Q, type NumberVariable as R, type SplitAnimationsOptions as S, type TimelineElement as T, type PlayerAPI as U, type ValidationResult as V, type StageZoom as W, type StringVariable as X, TIMELINE_COLORS as Y, type TimelineCompositionElement as Z, type TimelineElementBase as _, type ArcPathSegment as a, type TimelineMediaElement as a0, type TimelineTextElement as a1, VALID_CANVAS_RESOLUTIONS as a2, type WaveformData as a3, getDefaultStageZoom as a4, isCompositionElement as a5, isMediaElement as a6, isTextElement as a7, normalizeResolutionFlag as a8, type GsapKeyframesData as b, type GsapMethod as c, type GsapPercentageKeyframe as d, type GsapProvenance as e, type GsapProvenanceKind as f, type SplitAnimationsResult as g, editabilityForProvenance as h, getAnimationsForElementId as i, gsapAnimationsToKeyframes as j, keyframesToGsapAnimations as k, buildArcPath as l, type GsapKeyframeFormat as m, type CanvasResolution as n, type Keyframe as o, type StageZoomKeyframe as p, type AddElementData as q, type Asset as r, serializeGsapAnimations as s, CANVAS_DIMENSIONS as t, COMPOSITION_VARIABLE_TYPES as u, validateCompositionGsap as v, type ColorVariable as w, type CompositionAPI as x, type CompositionAsset as y, type CompositionSpec as z };
371
+ export { type AddElementData as A, type BooleanVariable as B, type CompositionVariable as C, DEFAULT_DURATIONS as D, type ElementKeyframes as E, type FontVariable as F, normalizeResolutionFlag as G, type ImageVariable as I, type Keyframe as K, type MediaElementType as M, type NumberVariable as N, type PlayerAPI as P, type StageZoomKeyframe as S, type TimelineElement as T, type ValidationResult as V, type WaveformData as W, type CanvasResolution as a, type Asset as b, CANVAS_DIMENSIONS as c, COMPOSITION_VARIABLE_TYPES as d, type ColorVariable as e, type CompositionAPI as f, type CompositionAsset as g, type CompositionSpec as h, type CompositionVariableBase as i, type CompositionVariableType as j, type EnumVariable as k, type KeyframeProperties as l, type MediaFile as m, type StageZoom as n, type StringVariable as o, TIMELINE_COLORS as p, type TimelineCompositionElement as q, type TimelineElementBase as r, type TimelineElementType as s, type TimelineMediaElement as t, type TimelineTextElement as u, VALID_CANVAS_RESOLUTIONS as v, getDefaultStageZoom as w, isCompositionElement as x, isMediaElement as y, isTextElement as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/parsers",
3
- "version": "0.7.15",
3
+ "version": "0.7.17",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -11,6 +11,7 @@
11
11
  "README.md"
12
12
  ],
13
13
  "type": "module",
14
+ "sideEffects": false,
14
15
  "main": "./dist/index.js",
15
16
  "types": "./dist/index.d.ts",
16
17
  "exports": {
@@ -46,6 +47,18 @@
46
47
  "./gsap-parser-recast": {
47
48
  "import": "./dist/gsapParser.js",
48
49
  "types": "./dist/gsapParser.d.ts"
50
+ },
51
+ "./slideshow": {
52
+ "import": "./dist/slideshow.js",
53
+ "types": "./dist/slideshow.d.ts"
54
+ },
55
+ "./asset-paths": {
56
+ "import": "./dist/assets.js",
57
+ "types": "./dist/assets.d.ts"
58
+ },
59
+ "./composition": {
60
+ "import": "./dist/composition.js",
61
+ "types": "./dist/composition.d.ts"
49
62
  }
50
63
  },
51
64
  "publishConfig": {
@@ -65,7 +78,7 @@
65
78
  "tsx": "^4.21.0",
66
79
  "typescript": "^5.0.0",
67
80
  "vitest": "^3.2.4",
68
- "@hyperframes/core": "0.7.15"
81
+ "@hyperframes/core": "0.7.17"
69
82
  },
70
83
  "scripts": {
71
84
  "build": "tsup",