shellfie-cli 2.0.1 → 2.0.2

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 (42) hide show
  1. package/README.md +1 -1
  2. package/dist/animator.d.ts +8 -0
  3. package/dist/animator.d.ts.map +1 -0
  4. package/dist/animator.js +210 -0
  5. package/dist/animator.js.map +1 -0
  6. package/dist/dvd-executor-v2.d.ts +76 -0
  7. package/dist/dvd-executor-v2.d.ts.map +1 -0
  8. package/dist/dvd-executor-v2.js +258 -0
  9. package/dist/dvd-executor-v2.js.map +1 -0
  10. package/dist/dvd-executor.d.ts +144 -0
  11. package/dist/dvd-executor.d.ts.map +1 -0
  12. package/dist/dvd-executor.js +669 -0
  13. package/dist/dvd-executor.js.map +1 -0
  14. package/dist/dvd-parser.d.ts +96 -0
  15. package/dist/dvd-parser.d.ts.map +1 -0
  16. package/dist/dvd-parser.js +279 -0
  17. package/dist/dvd-parser.js.map +1 -0
  18. package/dist/dvd.d.ts +31 -0
  19. package/dist/dvd.d.ts.map +1 -0
  20. package/dist/dvd.js +154 -0
  21. package/dist/dvd.js.map +1 -0
  22. package/dist/frame-animator.d.ts +21 -0
  23. package/dist/frame-animator.d.ts.map +1 -0
  24. package/dist/frame-animator.js +254 -0
  25. package/dist/frame-animator.js.map +1 -0
  26. package/dist/frame-capture.d.ts +16 -0
  27. package/dist/frame-capture.d.ts.map +1 -0
  28. package/dist/frame-capture.js +162 -0
  29. package/dist/frame-capture.js.map +1 -0
  30. package/dist/svg-animator-v2.d.ts +23 -0
  31. package/dist/svg-animator-v2.d.ts.map +1 -0
  32. package/dist/svg-animator-v2.js +134 -0
  33. package/dist/svg-animator-v2.js.map +1 -0
  34. package/dist/svg-animator.d.ts +23 -0
  35. package/dist/svg-animator.d.ts.map +1 -0
  36. package/dist/svg-animator.js +134 -0
  37. package/dist/svg-animator.js.map +1 -0
  38. package/dist/terminal-renderer.d.ts +34 -0
  39. package/dist/terminal-renderer.d.ts.map +1 -0
  40. package/dist/terminal-renderer.js +229 -0
  41. package/dist/terminal-renderer.js.map +1 -0
  42. package/package.json +2 -2
@@ -0,0 +1,21 @@
1
+ import type { Frame } from './frame-capture';
2
+ import { type shellfieOptions } from 'shellfie';
3
+ export interface FrameAnimationOptions {
4
+ fps: number;
5
+ loop: boolean;
6
+ maxDuration: number;
7
+ maxSizeKB: number;
8
+ shellfieOptions: shellfieOptions;
9
+ }
10
+ /**
11
+ * Generates an animated SVG from multiple frames.
12
+ * Uses SMIL visibility toggling for efficient frame switching.
13
+ * Aggressively deduplicates content at multiple levels:
14
+ * 1. Consecutive identical frames are merged
15
+ * 2. Individual text lines are stored once and referenced via <use>
16
+ * 3. Glyph patterns are shared across frames
17
+ *
18
+ * Enforces size and duration limits by progressively reducing frames.
19
+ */
20
+ export declare function generateFrameAnimatedSvg(frames: Frame[], options: FrameAnimationOptions): Promise<string>;
21
+ //# sourceMappingURL=frame-animator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-animator.d.ts","sourceRoot":"","sources":["../src/frame-animator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,eAAe,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CA8BjB"}
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFrameAnimatedSvg = generateFrameAnimatedSvg;
4
+ const shellfie_1 = require("shellfie");
5
+ /**
6
+ * Generates an animated SVG from multiple frames.
7
+ * Uses SMIL visibility toggling for efficient frame switching.
8
+ * Aggressively deduplicates content at multiple levels:
9
+ * 1. Consecutive identical frames are merged
10
+ * 2. Individual text lines are stored once and referenced via <use>
11
+ * 3. Glyph patterns are shared across frames
12
+ *
13
+ * Enforces size and duration limits by progressively reducing frames.
14
+ */
15
+ async function generateFrameAnimatedSvg(frames, options) {
16
+ if (frames.length === 0) {
17
+ throw new Error('No frames to animate');
18
+ }
19
+ if (frames.length === 1) {
20
+ return (0, shellfie_1.shellfieAsync)(frames[0].content, options.shellfieOptions);
21
+ }
22
+ // Apply duration limit first - cap frames based on max duration
23
+ const maxFramesByDuration = Math.floor(options.maxDuration * options.fps);
24
+ let cappedFrames = frames.slice(0, maxFramesByDuration);
25
+ // Try generating with current frame count, reduce if size exceeds limit
26
+ let result = await generateWithFrames(cappedFrames, options);
27
+ // If size exceeds limit, progressively reduce frames
28
+ const maxSizeBytes = options.maxSizeKB * 1024;
29
+ let attempts = 0;
30
+ const maxAttempts = 5;
31
+ while (result.length > maxSizeBytes && cappedFrames.length > 2 && attempts < maxAttempts) {
32
+ // Reduce frame count by sampling (keep every Nth frame)
33
+ const reductionFactor = Math.ceil(cappedFrames.length / Math.max(2, cappedFrames.length * 0.6));
34
+ cappedFrames = sampleFrames(cappedFrames, reductionFactor);
35
+ result = await generateWithFrames(cappedFrames, options);
36
+ attempts++;
37
+ }
38
+ return result;
39
+ }
40
+ /**
41
+ * Samples frames by keeping every Nth frame, always keeping first and last.
42
+ */
43
+ function sampleFrames(frames, step) {
44
+ if (frames.length <= 2 || step <= 1)
45
+ return frames;
46
+ const sampled = [frames[0]];
47
+ for (let i = step; i < frames.length - 1; i += step) {
48
+ sampled.push(frames[i]);
49
+ }
50
+ sampled.push(frames[frames.length - 1]);
51
+ // Re-index the frames
52
+ return sampled.map((frame, index) => ({
53
+ ...frame,
54
+ index,
55
+ timestamp: index * (1000 / 10), // Approximate timing
56
+ }));
57
+ }
58
+ /**
59
+ * Internal function to generate animated SVG from frames.
60
+ */
61
+ async function generateWithFrames(frames, options) {
62
+ // Deduplicate identical consecutive frames
63
+ const deduplicatedFrames = deduplicateConsecutiveFrames(frames);
64
+ // Generate SVG only for unique frames
65
+ const uniqueFrameSvgs = await Promise.all(deduplicatedFrames.uniqueFrames.map(frame => (0, shellfie_1.shellfieAsync)(frame.content, options.shellfieOptions)));
66
+ // Extract and deduplicate content at line level
67
+ const { lineLibrary, glyphLibrary, frameLineRefs, frameGlyphRefs } = buildContentLibrary(uniqueFrameSvgs);
68
+ // Build animated SVG with shared content library
69
+ const baseSvg = uniqueFrameSvgs[0];
70
+ return injectFrameAnimationsWithLibrary(baseSvg, lineLibrary, glyphLibrary, frameLineRefs, frameGlyphRefs, deduplicatedFrames.frameDurations, options.fps, options.loop);
71
+ }
72
+ /**
73
+ * Builds a shared library of unique lines and glyphs across all frames.
74
+ * Returns references for each frame to reconstruct content.
75
+ */
76
+ function buildContentLibrary(frameSvgs) {
77
+ const lineLibrary = new Map();
78
+ const glyphLibrary = new Map();
79
+ const frameLineRefs = [];
80
+ const frameGlyphRefs = [];
81
+ let lineCounter = 0;
82
+ let glyphCounter = 0;
83
+ for (const svg of frameSvgs) {
84
+ const textContent = extractTextContent(svg);
85
+ const glyphContent = extractGlyphsContent(svg);
86
+ // Parse individual text elements (lines)
87
+ const lineRefs = [];
88
+ const textElements = textContent.match(/<text[^>]*>[\s\S]*?<\/text>/g) || [];
89
+ for (const textEl of textElements) {
90
+ if (!lineLibrary.has(textEl)) {
91
+ const lineId = `L${lineCounter++}`;
92
+ lineLibrary.set(textEl, lineId);
93
+ }
94
+ lineRefs.push(lineLibrary.get(textEl));
95
+ }
96
+ frameLineRefs.push(lineRefs);
97
+ // Parse individual glyph elements
98
+ const glyphRefs = [];
99
+ // Match rect, path, or other glyph elements
100
+ const glyphElements = glyphContent.match(/<(?:rect|path|line|polygon)[^>]*\/?>/g) || [];
101
+ for (const glyphEl of glyphElements) {
102
+ if (!glyphLibrary.has(glyphEl)) {
103
+ const glyphId = `G${glyphCounter++}`;
104
+ glyphLibrary.set(glyphEl, glyphId);
105
+ }
106
+ glyphRefs.push(glyphLibrary.get(glyphEl));
107
+ }
108
+ frameGlyphRefs.push(glyphRefs);
109
+ }
110
+ return { lineLibrary, glyphLibrary, frameLineRefs, frameGlyphRefs };
111
+ }
112
+ /**
113
+ * Deduplicates identical consecutive frames.
114
+ * Returns unique frames and their durations.
115
+ */
116
+ function deduplicateConsecutiveFrames(frames) {
117
+ if (frames.length === 0) {
118
+ return { uniqueFrames: [], frameDurations: [] };
119
+ }
120
+ const uniqueFrames = [frames[0]];
121
+ const frameDurations = [1];
122
+ for (let i = 1; i < frames.length; i++) {
123
+ const current = frames[i];
124
+ const lastUnique = uniqueFrames[uniqueFrames.length - 1];
125
+ if (current.content === lastUnique.content) {
126
+ // Same content - extend duration of last unique frame
127
+ frameDurations[frameDurations.length - 1]++;
128
+ }
129
+ else {
130
+ // Different content - add new unique frame
131
+ uniqueFrames.push(current);
132
+ frameDurations.push(1);
133
+ }
134
+ }
135
+ return { uniqueFrames, frameDurations };
136
+ }
137
+ /**
138
+ * Extracts the text content group from an SVG.
139
+ * Sanitizes any remaining control characters that are invalid in XML.
140
+ */
141
+ function extractTextContent(svg) {
142
+ // Extract everything inside <g class="text">...</g>
143
+ const match = svg.match(/<g class="text">([\s\S]*?)<\/g>/);
144
+ if (!match)
145
+ return '';
146
+ // Remove any control characters (ASCII 0-31 except tab, newline, carriage return)
147
+ // These are invalid in XML and can cause parsing errors
148
+ const content = match[1];
149
+ const sanitized = content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
150
+ return sanitized;
151
+ }
152
+ /**
153
+ * Extracts the glyphs content group from an SVG.
154
+ */
155
+ function extractGlyphsContent(svg) {
156
+ // Extract everything inside <g class="glyphs">...</g>
157
+ const match = svg.match(/<g class="glyphs">([\s\S]*?)<\/g>/);
158
+ return match ? match[1] : '';
159
+ }
160
+ /**
161
+ * Injects frame animation using a shared content library.
162
+ * Content is defined once in <defs> and referenced via <use>.
163
+ * This dramatically reduces file size for animations with repeated content.
164
+ */
165
+ function injectFrameAnimationsWithLibrary(baseSvg, lineLibrary, glyphLibrary, frameLineRefs, frameGlyphRefs, frameDurations, fps, loop) {
166
+ const baseFrameDuration = 1 / fps;
167
+ const totalFrameUnits = frameDurations.reduce((sum, d) => sum + d, 0);
168
+ const totalDuration = totalFrameUnits * baseFrameDuration;
169
+ // Build the content library in <defs>
170
+ let defsContent = '\n <!-- Shared content library -->\n';
171
+ // Add all unique lines to defs
172
+ for (const [content, id] of lineLibrary) {
173
+ // Wrap in a group with id for referencing
174
+ defsContent += ` <g id="${id}">${content}</g>\n`;
175
+ }
176
+ // Add all unique glyphs to defs
177
+ for (const [content, id] of glyphLibrary) {
178
+ defsContent += ` <g id="${id}">${content}</g>\n`;
179
+ }
180
+ // Calculate start times for each frame
181
+ const startTimes = [];
182
+ let currentTime = 0;
183
+ for (const duration of frameDurations) {
184
+ startTimes.push(currentTime);
185
+ currentTime += duration * baseFrameDuration;
186
+ }
187
+ // Build frame groups that reference the shared content via <use>
188
+ // Use simpler SMIL: animate visibility with keyTimes for precise control
189
+ const frameGroups = frameLineRefs.map((lineRefs, index) => {
190
+ const glyphRefs = frameGlyphRefs[index] || [];
191
+ const startTime = startTimes[index];
192
+ const duration = frameDurations[index] * baseFrameDuration;
193
+ const isFirstFrame = index === 0;
194
+ const isLastFrame = index === frameLineRefs.length - 1;
195
+ // Build <use> references for glyphs and lines
196
+ const glyphUses = glyphRefs.map(id => `<use href="#${id}"/>`).join('');
197
+ const lineUses = lineRefs.map(id => `<use href="#${id}"/>`).join('');
198
+ // Simple SMIL: show at startTime, hide at startTime + duration
199
+ // First frame starts visible, others start hidden
200
+ const initialVisibility = isFirstFrame ? 'visible' : 'hidden';
201
+ let smilTags = '';
202
+ if (loop) {
203
+ // For looping: use animate with values and keyTimes
204
+ const showStart = startTime / totalDuration;
205
+ const showEnd = (startTime + duration) / totalDuration;
206
+ smilTags = `
207
+ <animate attributeName="visibility" values="hidden;visible;hidden" keyTimes="0;${showStart.toFixed(4)};${showEnd.toFixed(4)}" dur="${totalDuration.toFixed(3)}s" repeatCount="indefinite" calcMode="discrete"/>`;
208
+ }
209
+ else if (isLastFrame) {
210
+ // Last frame: show and keep visible
211
+ if (isFirstFrame) {
212
+ // Single frame - just stay visible
213
+ smilTags = '';
214
+ }
215
+ else {
216
+ smilTags = `
217
+ <set attributeName="visibility" to="visible" begin="${startTime.toFixed(3)}s" fill="freeze"/>`;
218
+ }
219
+ }
220
+ else if (isFirstFrame) {
221
+ // First frame: start visible, hide when next frame starts
222
+ smilTags = `
223
+ <set attributeName="visibility" to="hidden" begin="${(startTime + duration).toFixed(3)}s" fill="freeze"/>`;
224
+ }
225
+ else {
226
+ // Middle frames: show at start, hide at end
227
+ smilTags = `
228
+ <set attributeName="visibility" to="visible" begin="${startTime.toFixed(3)}s" fill="freeze"/>
229
+ <set attributeName="visibility" to="hidden" begin="${(startTime + duration).toFixed(3)}s" fill="freeze"/>`;
230
+ }
231
+ return `
232
+ <g class="frame" id="frame-${index}" visibility="${initialVisibility}">
233
+ ${glyphUses}${lineUses}${smilTags}
234
+ </g>`;
235
+ }).join('');
236
+ // Insert defs content and replace frame groups
237
+ let animatedSvg = baseSvg;
238
+ // Add library to defs
239
+ const defsMatch = animatedSvg.match(/<defs>/);
240
+ if (defsMatch) {
241
+ animatedSvg = animatedSvg.replace(/<defs>/, `<defs>${defsContent}`);
242
+ }
243
+ else {
244
+ // Create defs section
245
+ animatedSvg = animatedSvg.replace(/<svg([^>]*)>/, `<svg$1>\n<defs>${defsContent}</defs>`);
246
+ }
247
+ // Remove glyphs group (content is now in defs)
248
+ animatedSvg = animatedSvg.replace(/<g class="glyphs">[\s\S]*?<\/g>\s*/g, '');
249
+ // Replace the text content with frame groups
250
+ animatedSvg = animatedSvg.replace(/<g class="text">[\s\S]*?<\/g>/, `<g class="animation">${frameGroups}
251
+ </g>`);
252
+ return animatedSvg;
253
+ }
254
+ //# sourceMappingURL=frame-animator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-animator.js","sourceRoot":"","sources":["../src/frame-animator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface CaptureOptions {
2
+ mode: 'clear' | 'time';
3
+ fps: number;
4
+ maxFrames: number;
5
+ }
6
+ export interface Frame {
7
+ content: string;
8
+ timestamp: number;
9
+ index: number;
10
+ }
11
+ /**
12
+ * Captures frames from terminal animation input.
13
+ * Supports two modes: clear-screen detection and time-based buffering.
14
+ */
15
+ export declare function captureFrames(input: string, options: CaptureOptions): Frame[];
16
+ //# sourceMappingURL=frame-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-capture.d.ts","sourceRoot":"","sources":["../src/frame-capture.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,GACtB,KAAK,EAAE,CAMT"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.captureFrames = captureFrames;
4
+ /**
5
+ * Captures frames from terminal animation input.
6
+ * Supports two modes: clear-screen detection and time-based buffering.
7
+ */
8
+ function captureFrames(input, options) {
9
+ if (options.mode === 'clear') {
10
+ return captureByClearScreen(input, options);
11
+ }
12
+ else {
13
+ return captureByTime(input, options);
14
+ }
15
+ }
16
+ /**
17
+ * Strips common cursor control sequences that shouldn't be rendered.
18
+ */
19
+ function stripCursorControls(text) {
20
+ return text
21
+ .replace(/\x1B\[\?25l/g, '') // Hide cursor
22
+ .replace(/\x1B\[\?25h/g, '') // Show cursor
23
+ .replace(/\x1B\[H/g, '') // Move cursor to home
24
+ .replace(/\x1B\[\d+;\d+H/g, '') // Move cursor to position
25
+ .replace(/\x1B\[K/g, '') // Clear line from cursor
26
+ .replace(/\x1B\[J/g, ''); // Clear screen from cursor
27
+ }
28
+ /**
29
+ * Checks if content has any visible text (not just whitespace/control chars).
30
+ */
31
+ function hasVisibleContent(text) {
32
+ const stripped = stripCursorControls(text).trim();
33
+ return stripped.length > 0;
34
+ }
35
+ /**
36
+ * Captures frames by detecting clear screen commands.
37
+ * Each clear command marks the boundary between frames.
38
+ */
39
+ function captureByClearScreen(input, options) {
40
+ const CLEAR_PATTERNS = [
41
+ '\x1Bc', // Full reset (used by chartscii)
42
+ '\x1B[2J', // Clear entire screen (ESC[2J)
43
+ '\x1B[H\x1B[2J', // Move to home + clear
44
+ '\x1Bc', // Alternative reset
45
+ ];
46
+ const frames = [];
47
+ let remainingInput = input;
48
+ // Try to split by each clear pattern
49
+ for (const pattern of CLEAR_PATTERNS) {
50
+ if (remainingInput.includes(pattern)) {
51
+ const parts = remainingInput.split(pattern);
52
+ // First part is before any clear (skip it)
53
+ // Subsequent parts are frames
54
+ for (let i = 1; i < parts.length && frames.length < options.maxFrames; i++) {
55
+ const stripped = stripCursorControls(parts[i]);
56
+ const content = stripped.trim();
57
+ // Only include frames with actual visible content
58
+ if (content && hasVisibleContent(content)) {
59
+ frames.push({
60
+ content,
61
+ timestamp: frames.length * (1000 / options.fps),
62
+ index: frames.length,
63
+ });
64
+ }
65
+ }
66
+ // If we found frames with this pattern, stop trying other patterns
67
+ if (frames.length > 0) {
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ // If no frames were captured (no clear commands found), treat entire input as single frame
73
+ if (frames.length === 0) {
74
+ const stripped = stripCursorControls(input);
75
+ const content = stripped.trim();
76
+ if (content && hasVisibleContent(content)) {
77
+ frames.push({
78
+ content,
79
+ timestamp: 0,
80
+ index: 0,
81
+ });
82
+ }
83
+ }
84
+ return frames;
85
+ }
86
+ /**
87
+ * Captures frames based on time intervals.
88
+ * Simulates terminal state at regular intervals by replaying the input stream.
89
+ *
90
+ * This works by:
91
+ * 1. Parsing the input for timing information (escape sequences, delays, etc.)
92
+ * 2. Building a virtual terminal buffer
93
+ * 3. Snapshotting the buffer at regular intervals (based on fps)
94
+ *
95
+ * For inputs without timing info, it distributes content evenly across frames.
96
+ */
97
+ function captureByTime(input, options) {
98
+ const intervalMs = 1000 / options.fps; // e.g., 100ms for 10fps
99
+ // Try to detect if input has timing markers or clear sequences
100
+ const clearPattern = /\x1Bc|\x1B\[2J|\x1B\[H\x1B\[2J/g;
101
+ const hasClearSequences = clearPattern.test(input);
102
+ if (hasClearSequences) {
103
+ // If we have clear sequences, use them as frame boundaries with timestamps
104
+ return captureByTimeWithClearMarkers(input, options, intervalMs);
105
+ }
106
+ // No clear sequences - this is static content (like lolcat output)
107
+ // Return as a single frame instead of progressive reveal
108
+ const stripped = stripCursorControls(input);
109
+ const content = stripped.trim();
110
+ if (content && hasVisibleContent(content)) {
111
+ return [{
112
+ content,
113
+ timestamp: 0,
114
+ index: 0,
115
+ }];
116
+ }
117
+ return [];
118
+ }
119
+ /**
120
+ * Captures frames using clear sequences as boundaries, but assigns proper timestamps.
121
+ */
122
+ function captureByTimeWithClearMarkers(input, options, intervalMs) {
123
+ const CLEAR_PATTERNS = [
124
+ '\x1Bc', // Full reset
125
+ '\x1B[2J', // Clear entire screen
126
+ '\x1B\[H\x1B\[2J', // Move to home + clear
127
+ ];
128
+ const frames = [];
129
+ let remainingInput = input;
130
+ for (const pattern of CLEAR_PATTERNS) {
131
+ if (remainingInput.includes(pattern)) {
132
+ const parts = remainingInput.split(pattern);
133
+ for (let i = 1; i < parts.length && frames.length < options.maxFrames; i++) {
134
+ const stripped = stripCursorControls(parts[i]);
135
+ const content = stripped.trim();
136
+ if (content && hasVisibleContent(content)) {
137
+ frames.push({
138
+ content,
139
+ timestamp: frames.length * intervalMs,
140
+ index: frames.length,
141
+ });
142
+ }
143
+ }
144
+ if (frames.length > 0)
145
+ break;
146
+ }
147
+ }
148
+ // Fallback: treat entire input as single frame
149
+ if (frames.length === 0) {
150
+ const stripped = stripCursorControls(input);
151
+ const content = stripped.trim();
152
+ if (content && hasVisibleContent(content)) {
153
+ frames.push({
154
+ content,
155
+ timestamp: 0,
156
+ index: 0,
157
+ });
158
+ }
159
+ }
160
+ return frames;
161
+ }
162
+ //# sourceMappingURL=frame-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-capture.js","sourceRoot":"","sources":["../src/frame-capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * SVG Animator V2
3
+ * Combines pre-rendered terminal SVGs into animated SVG
4
+ */
5
+ import type { TerminalFrame } from './dvd-executor-v2';
6
+ export interface AnimationOptions {
7
+ fps?: number;
8
+ loop?: boolean;
9
+ pauseAtEnd?: number;
10
+ }
11
+ /**
12
+ * Create animated SVG from frames
13
+ */
14
+ export declare function createAnimatedSVG(frames: TerminalFrame[], options?: AnimationOptions): Promise<string>;
15
+ /**
16
+ * Get animation metadata
17
+ */
18
+ export declare function getAnimationMetadata(frames: TerminalFrame[]): {
19
+ duration: number;
20
+ frameCount: number;
21
+ fps: number;
22
+ };
23
+ //# sourceMappingURL=svg-animator-v2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-animator-v2.d.ts","sourceRoot":"","sources":["../src/svg-animator-v2.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAqDjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb,CAUA"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * SVG Animator V2
4
+ * Combines pre-rendered terminal SVGs into animated SVG
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createAnimatedSVG = createAnimatedSVG;
8
+ exports.getAnimationMetadata = getAnimationMetadata;
9
+ /**
10
+ * Extract SVG body content (everything inside <svg>)
11
+ */
12
+ function extractSVGBody(svg, frameId) {
13
+ const contentMatch = svg.match(/<svg[^>]*>([\s\S]*)<\/svg>/);
14
+ if (!contentMatch)
15
+ return '';
16
+ let content = contentMatch[1];
17
+ // Make IDs unique
18
+ content = content
19
+ .replace(/id="([^"]*)"/g, `id="$1-${frameId}"`)
20
+ .replace(/url\(#([^)]*)\)/g, `url(#$1-${frameId})`)
21
+ .replace(/class="([^"]*)"/g, `class="$1 ${frameId}"`);
22
+ return content;
23
+ }
24
+ /**
25
+ * Get SVG dimensions from first frame
26
+ */
27
+ function getSVGDimensions(svg) {
28
+ const widthMatch = svg.match(/width="(\d+)"/);
29
+ const heightMatch = svg.match(/height="(\d+)"/);
30
+ return {
31
+ width: widthMatch ? parseInt(widthMatch[1], 10) : 800,
32
+ height: heightMatch ? parseInt(heightMatch[1], 10) : 600,
33
+ };
34
+ }
35
+ /**
36
+ * Create CSS keyframes for animation
37
+ */
38
+ function createKeyframes(frames) {
39
+ if (frames.length === 0)
40
+ return '';
41
+ const totalDuration = frames[frames.length - 1].timestamp;
42
+ let css = '';
43
+ frames.forEach((frame, i) => {
44
+ const startPercent = i === 0 ? 0 : (frames[i - 1].timestamp / totalDuration) * 100;
45
+ const endPercent = (frame.timestamp / totalDuration) * 100;
46
+ const nextPercent = i < frames.length - 1
47
+ ? (frames[i + 1].timestamp / totalDuration) * 100
48
+ : 100;
49
+ css += `
50
+ @keyframes frame-${i}-anim {
51
+ 0% { opacity: 0; }
52
+ ${startPercent.toFixed(2)}% { opacity: 0; }
53
+ ${endPercent.toFixed(2)}% { opacity: 1; }
54
+ ${nextPercent.toFixed(2)}% { opacity: 1; }
55
+ ${(nextPercent + 0.01).toFixed(2)}% { opacity: 0; }
56
+ 100% { opacity: 0; }
57
+ }`;
58
+ });
59
+ // First frame special case
60
+ const firstEnd = (frames[1]?.timestamp / totalDuration) * 100 || 100;
61
+ css = `
62
+ @keyframes frame-0-anim {
63
+ 0% { opacity: 1; }
64
+ ${firstEnd.toFixed(2)}% { opacity: 1; }
65
+ ${(firstEnd + 0.01).toFixed(2)}% { opacity: 0; }
66
+ 100% { opacity: 0; }
67
+ }` + css.substring(css.indexOf('@keyframes frame-1-anim'));
68
+ return css;
69
+ }
70
+ /**
71
+ * Create animated SVG from frames
72
+ */
73
+ async function createAnimatedSVG(frames, options = {}) {
74
+ if (frames.length === 0) {
75
+ throw new Error('No frames to animate');
76
+ }
77
+ // Get dimensions from first frame
78
+ const { width, height } = getSVGDimensions(frames[0].svg);
79
+ // Calculate duration
80
+ const totalDuration = frames[frames.length - 1].timestamp;
81
+ const pauseAtEnd = options.pauseAtEnd || 1000;
82
+ const animationDuration = ((totalDuration + pauseAtEnd) / 1000).toFixed(2);
83
+ const animationIterationCount = options.loop !== false ? 'infinite' : '1';
84
+ // Create keyframes
85
+ const keyframes = createKeyframes(frames);
86
+ // Extract frame contents
87
+ const frameBodies = frames.map((frame, i) => ({
88
+ id: `frame-${i}`,
89
+ content: extractSVGBody(frame.svg, `f${i}`),
90
+ }));
91
+ // Build animated SVG
92
+ const svg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
93
+ <style>
94
+ ${keyframes}
95
+
96
+ .frame {
97
+ animation-duration: ${animationDuration}s;
98
+ animation-timing-function: step-end;
99
+ animation-iteration-count: ${animationIterationCount};
100
+ animation-fill-mode: forwards;
101
+ }
102
+
103
+ ${frames.map((_, i) => `
104
+ .frame-${i} {
105
+ animation-name: frame-${i}-anim;
106
+ }`).join('')}
107
+ </style>
108
+
109
+ <defs>
110
+ ${frameBodies.map((frame) => `
111
+ <g id="${frame.id}">
112
+ ${frame.content}
113
+ </g>`).join('')}
114
+ </defs>
115
+
116
+ ${frameBodies.map((frame, i) => `
117
+ <use href="#${frame.id}" class="frame frame-${i}" />`).join('')}
118
+ </svg>`;
119
+ return svg;
120
+ }
121
+ /**
122
+ * Get animation metadata
123
+ */
124
+ function getAnimationMetadata(frames) {
125
+ const duration = frames.length > 0 ? frames[frames.length - 1].timestamp : 0;
126
+ const frameCount = frames.length;
127
+ const fps = frameCount > 1 ? frameCount / (duration / 1000) : 0;
128
+ return {
129
+ duration,
130
+ frameCount,
131
+ fps: Math.round(fps * 10) / 10,
132
+ };
133
+ }
134
+ //# sourceMappingURL=svg-animator-v2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-animator-v2.js","sourceRoot":"","sources":["../src/svg-animator-v2.ts"],"names":[],"mappings":"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"}