mulmocast 2.4.9 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,293 +1,40 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en" class="h-full">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <script src="https://cdn.tailwindcss.com"></script>
7
- <style>
8
- /* Disable all CSS animations/transitions for deterministic frame-based rendering */
9
- *, *::before, *::after {
10
- animation-play-state: paused !important;
11
- transition: none !important;
12
- }
13
- ${custom_style}
14
- </style>
15
- </head>
16
- <body class="bg-white text-gray-800 h-full flex flex-col">
17
- ${html_body}
18
-
19
- <script>
20
- // === MulmoCast Animation Helpers ===
21
-
22
- /**
23
- * Easing functions for non-linear interpolation.
24
- */
25
- const Easing = {
26
- linear: (t) => t,
27
- easeIn: (t) => t * t,
28
- easeOut: (t) => 1 - (1 - t) * (1 - t),
29
- easeInOut: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
30
- };
31
-
32
- /**
33
- * Interpolation with clamping and optional easing.
34
- *
35
- * @param {number} value - Current value (typically frame number)
36
- * @param {Object} opts - { input: { inMin, inMax }, output: { outMin, outMax }, easing?: string | function }
37
- * @returns {number} Interpolated and clamped value
38
- *
39
- * @example
40
- * interpolate(frame, { input: { inMin: 0, inMax: 30 }, output: { outMin: 0, outMax: 1 } })
41
- * interpolate(frame, { input: { inMin: 0, inMax: 30 }, output: { outMin: 0, outMax: 1 }, easing: 'easeOut' })
42
- */
43
- function interpolate(value, opts) {
44
- const { inMin, inMax } = opts.input;
45
- const { outMin, outMax } = opts.output;
46
- if (inMax === inMin) {
47
- return outMin;
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ <style>
8
+ /* Disable all CSS animations/transitions for deterministic frame-based rendering */
9
+ *, *::before, *::after {
10
+ animation-play-state: paused !important;
11
+ transition: none !important;
48
12
  }
49
- const easing = !opts.easing ? Easing.linear
50
- : typeof opts.easing === 'function' ? opts.easing
51
- : Easing[opts.easing] || Easing.linear;
52
- const progress = Math.max(0, Math.min(1, (value - inMin) / (inMax - inMin)));
53
- return outMin + easing(progress) * (outMax - outMin);
54
- }
55
-
56
- // === MulmoAnimation Helper Class ===
57
-
58
- const TRANSFORM_PROPS = { translateX: 'px', translateY: 'px', scale: '', rotate: 'deg', rotateX: 'deg', rotateY: 'deg', rotateZ: 'deg' };
59
- const SVG_PROPS = ['r', 'cx', 'cy', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'rx', 'ry',
60
- 'width', 'height', 'stroke-width', 'stroke-dashoffset', 'stroke-dasharray', 'opacity'];
61
-
62
- function MulmoAnimation() {
63
- this._entries = [];
64
- }
65
-
66
- /**
67
- * Register a property animation on a single element.
68
- * @param {string} selector - CSS selector (e.g. '#title')
69
- * @param {Object} props - { opacity: [0, 1], translateY: [30, 0], width: [0, 80, '%'] }
70
- * @param {Object} opts - { start, end, easing } (start/end in seconds)
71
- */
72
- MulmoAnimation.prototype.animate = function(selector, props, opts) {
73
- this._entries.push({ kind: 'animate', selector, props, opts: opts || {} });
74
- return this;
75
- };
76
-
77
- /**
78
- * Stagger animation across numbered elements.
79
- * Selector must contain {i} placeholder (e.g. '#item{i}').
80
- * @param {string} selector - e.g. '#item{i}'
81
- * @param {number} count - number of elements (0-indexed)
82
- * @param {Object} props - same as animate()
83
- * @param {Object} opts - { start, stagger, duration, easing }
84
- */
85
- MulmoAnimation.prototype.stagger = function(selector, count, props, opts) {
86
- this._entries.push({ kind: 'stagger', selector, count, props, opts: opts || {} });
87
- return this;
88
- };
89
-
90
- /**
91
- * Typewriter effect — reveal text character by character.
92
- * @param {string} selector - target element selector
93
- * @param {string} text - full text to reveal
94
- * @param {Object} opts - { start, end }
95
- */
96
- MulmoAnimation.prototype.typewriter = function(selector, text, opts) {
97
- this._entries.push({ kind: 'typewriter', selector, text, opts: opts || {} });
98
- return this;
99
- };
100
-
101
- /**
102
- * Animated counter — interpolate a number and display with optional prefix/suffix.
103
- * @param {string} selector - target element selector
104
- * @param {[number, number]} range - [from, to]
105
- * @param {Object} opts - { start, end, prefix, suffix, decimals }
106
- */
107
- MulmoAnimation.prototype.counter = function(selector, range, opts) {
108
- this._entries.push({ kind: 'counter', selector, range, opts: opts || {} });
109
- return this;
110
- };
111
-
112
- /**
113
- * Code reveal — show lines of code one by one (line-level typewriter).
114
- * @param {string} selector - target element selector
115
- * @param {string[]} lines - array of code lines
116
- * @param {Object} opts - { start, end }
117
- */
118
- MulmoAnimation.prototype.codeReveal = function(selector, lines, opts) {
119
- this._entries.push({ kind: 'codeReveal', selector, lines, opts: opts || {} });
120
- return this;
121
- };
122
-
123
- /**
124
- * Blink — periodic show/hide toggle (e.g. cursor blinking).
125
- * @param {string} selector - target element selector
126
- * @param {Object} opts - { interval } (half-cycle seconds, default 0.5)
127
- */
128
- MulmoAnimation.prototype.blink = function(selector, opts) {
129
- this._entries.push({ kind: 'blink', selector, opts: opts || {} });
130
- return this;
131
- };
132
-
133
- /** Resolve easing name string or function to an easing function */
134
- MulmoAnimation.prototype._resolveEasing = function(e) {
135
- if (!e) return Easing.linear;
136
- if (typeof e === 'function') return e;
137
- return Easing[e] || Easing.linear;
138
- };
139
-
140
- /** Apply props to element at a given progress (0-1) with easing */
141
- MulmoAnimation.prototype._applyProps = function(el, props, progress, easingFn) {
142
- if (!el) return;
143
- const transforms = [];
144
- Object.keys(props).forEach((prop) => {
145
- const spec = props[prop];
146
- const from = spec[0], to = spec[1];
147
- const unit = (spec.length > 2) ? spec[2] : null;
148
- const val = from + easingFn(progress) * (to - from);
149
-
150
- if (TRANSFORM_PROPS.hasOwnProperty(prop)) {
151
- const tUnit = unit || TRANSFORM_PROPS[prop];
152
- transforms.push(prop === 'scale' ? 'scale(' + val + ')' : prop + '(' + val + tUnit + ')');
153
- } else if (el instanceof SVGElement && SVG_PROPS.indexOf(prop) !== -1) {
154
- el.setAttribute(prop, val);
155
- } else if (prop === 'opacity') {
156
- el.style.opacity = val;
157
- } else {
158
- const cssUnit = unit || 'px';
159
- el.style[prop] = val + cssUnit;
160
- }
161
- });
162
- if (transforms.length > 0) {
163
- el.style.transform = transforms.join(' ');
164
- }
165
- };
166
-
167
- /**
168
- * Update all registered animations for the given frame.
169
- * @param {number} frame - current frame number
170
- * @param {number} fps - frames per second
171
- */
172
- MulmoAnimation.prototype.update = function(frame, fps) {
173
- this._entries.forEach((entry) => {
174
- const opts = entry.opts;
175
- const easingFn = this._resolveEasing(opts.easing);
176
-
177
- if (entry.kind === 'animate') {
178
- const startFrame = (opts.start || 0) * fps;
179
- const endFrame = (opts.end === 'auto' ? window.__MULMO.totalFrames / fps : (opts.end || 0)) * fps;
180
- const progress = Math.max(0, Math.min(1, endFrame === startFrame ? 1 : (frame - startFrame) / (endFrame - startFrame)));
181
- const el = document.querySelector(entry.selector);
182
- this._applyProps(el, entry.props, progress, easingFn);
183
-
184
- } else if (entry.kind === 'stagger') {
185
- const baseStart = (opts.start || 0) * fps;
186
- const staggerDelay = (opts.stagger || 0.2) * fps;
187
- const dur = (opts.duration || 0.5) * fps;
188
- for (let j = 0; j < entry.count; j++) {
189
- const sel = entry.selector.replace(/\{i\}/g, j);
190
- const sEl = document.querySelector(sel);
191
- const sStart = baseStart + j * staggerDelay;
192
- const sEnd = sStart + dur;
193
- const sProgress = Math.max(0, Math.min(1, sEnd === sStart ? 1 : (frame - sStart) / (sEnd - sStart)));
194
- this._applyProps(sEl, entry.props, sProgress, easingFn);
195
- }
196
-
197
- } else if (entry.kind === 'typewriter') {
198
- const twStart = (opts.start || 0) * fps;
199
- const twEnd = (opts.end === 'auto' ? window.__MULMO.totalFrames / fps : (opts.end || 0)) * fps;
200
- const twProgress = Math.max(0, Math.min(1, twEnd === twStart ? 1 : (frame - twStart) / (twEnd - twStart)));
201
- const charCount = Math.floor(twProgress * entry.text.length);
202
- const twEl = document.querySelector(entry.selector);
203
- if (twEl) twEl.textContent = entry.text.substring(0, charCount);
204
-
205
- } else if (entry.kind === 'counter') {
206
- const cStart = (opts.start || 0) * fps;
207
- const cEnd = (opts.end === 'auto' ? window.__MULMO.totalFrames / fps : (opts.end || 0)) * fps;
208
- const cProgress = Math.max(0, Math.min(1, cEnd === cStart ? 1 : (frame - cStart) / (cEnd - cStart)));
209
- const cVal = entry.range[0] + easingFn(cProgress) * (entry.range[1] - entry.range[0]);
210
- const decimals = opts.decimals || 0;
211
- const display = (opts.prefix || '') + cVal.toFixed(decimals) + (opts.suffix || '');
212
- const cEl = document.querySelector(entry.selector);
213
- if (cEl) cEl.textContent = display;
214
-
215
- } else if (entry.kind === 'codeReveal') {
216
- const crStart = (opts.start || 0) * fps;
217
- const crEnd = (opts.end === 'auto' ? window.__MULMO.totalFrames / fps : (opts.end || 0)) * fps;
218
- const crProgress = Math.max(0, Math.min(1, crEnd === crStart ? 1 : (frame - crStart) / (crEnd - crStart)));
219
- const lineCount = Math.floor(crProgress * entry.lines.length);
220
- const crEl = document.querySelector(entry.selector);
221
- if (crEl) crEl.textContent = entry.lines.slice(0, lineCount).join('\n');
222
-
223
- } else if (entry.kind === 'blink') {
224
- const interval_s = opts.interval || 0.5;
225
- const blinkEl = document.querySelector(entry.selector);
226
- if (blinkEl) {
227
- const cycle = (frame / fps) / interval_s;
228
- blinkEl.style.opacity = (Math.floor(cycle) % 2 === 0) ? 1 : 0;
229
- }
230
- }
231
- });
232
- };
233
-
234
- // === MulmoCast Frame State (updated by Puppeteer per frame) ===
235
- window.__MULMO = {
236
- frame: 0,
237
- totalFrames: ${totalFrames},
238
- fps: ${fps},
239
- };
240
- </script>
241
-
242
- ${user_script}
243
-
244
- <script>
245
- // Auto-render: if MulmoAnimation is used but render() is not defined, generate it
246
- if (typeof render !== 'function' && typeof animation !== 'undefined' && animation instanceof MulmoAnimation) {
247
- window.render = function(frame, totalFrames, fps) { animation.update(frame, fps); };
248
- }
249
-
250
- // Initial render (frame 0)
251
- if (typeof render === 'function') {
252
- const result = render(0, window.__MULMO.totalFrames, window.__MULMO.fps);
253
- if (result && typeof result.then === 'function') {
254
- result.catch(console.error);
255
- }
256
- }
257
-
258
- /**
259
- * Play animation in real-time using requestAnimationFrame.
260
- * Returns a Promise that resolves when all frames have been rendered.
261
- * Called by Puppeteer's page.evaluate() during screencast recording.
262
- */
263
- window.playAnimation = function() {
264
- return new Promise(function(resolve) {
265
- var mulmo = window.__MULMO;
266
- var fps = mulmo.fps;
267
- var totalFrames = mulmo.totalFrames;
268
- var frameDuration = 1000 / fps;
269
- var startTime = null;
270
-
271
- function tick(timestamp) {
272
- if (startTime === null) startTime = timestamp;
273
- var elapsed = timestamp - startTime;
274
- var frame = Math.min(Math.floor(elapsed / frameDuration), totalFrames - 1);
275
-
276
- mulmo.frame = frame;
277
- if (typeof render === 'function') {
278
- render(frame, totalFrames, fps);
279
- }
280
-
281
- if (frame < totalFrames - 1) {
282
- requestAnimationFrame(tick);
283
- } else {
284
- resolve();
285
- }
286
- }
287
-
288
- requestAnimationFrame(tick);
289
- });
290
- };
291
- </script>
292
- </body>
13
+ ${custom_style}
14
+ </style>
15
+ </head>
16
+ <body class="bg-white text-gray-800 h-full flex flex-col">
17
+ ${html_body}
18
+
19
+ <script>
20
+ ${animation_runtime}
21
+
22
+ // === MulmoCast Frame State (updated by Puppeteer per frame) ===
23
+ window.__MULMO = {
24
+ frame: 0,
25
+ totalFrames: ${totalFrames},
26
+ fps: ${fps},
27
+ };
28
+ </script>
29
+
30
+ ${user_script}
31
+
32
+ <script>
33
+ ${data_attribute_registration}
34
+ </script>
35
+
36
+ <script>
37
+ ${auto_render}
38
+ </script>
39
+ </body>
293
40
  </html>
@@ -33,7 +33,7 @@ export const generateReferenceImage = async (inputs) => {
33
33
  },
34
34
  params: {
35
35
  model: imageAgentInfo.imageParams.model,
36
- canvasSize: context.presentationStyle.canvasSize,
36
+ canvasSize: image.canvasSize ?? context.presentationStyle.canvasSize,
37
37
  },
38
38
  },
39
39
  },
@@ -372,14 +372,16 @@ export declare const mulmoMermaidMediaSchema: z.ZodObject<{
372
372
  opacity: z.ZodOptional<z.ZodNumber>;
373
373
  }, z.core.$strip>]>>>;
374
374
  }, z.core.$strict>;
375
+ export declare const swipeElementSchema: z.ZodType;
375
376
  export declare const htmlTailwindAnimationSchema: z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
376
377
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
377
378
  movie: z.ZodOptional<z.ZodBoolean>;
378
379
  }, z.core.$strip>]>;
379
380
  export declare const mulmoHtmlTailwindMediaSchema: z.ZodObject<{
380
381
  type: z.ZodLiteral<"html_tailwind">;
381
- html: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
382
+ html: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
382
383
  script: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
384
+ elements: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
383
385
  animation: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
384
386
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
385
387
  movie: z.ZodOptional<z.ZodBoolean>;
@@ -551,8 +553,9 @@ export declare const mulmoImageAssetSchema: z.ZodUnion<readonly [z.ZodObject<{
551
553
  }, z.core.$strip>]>>>;
552
554
  }, z.core.$strict>, z.ZodObject<{
553
555
  type: z.ZodLiteral<"html_tailwind">;
554
- html: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
556
+ html: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
555
557
  script: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
558
+ elements: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
556
559
  animation: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
557
560
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
558
561
  movie: z.ZodOptional<z.ZodBoolean>;
@@ -2957,6 +2960,10 @@ export declare const mulmoAudioAssetSchema: z.ZodUnion<readonly [z.ZodObject<{
2957
2960
  export declare const mulmoImagePromptMediaSchema: z.ZodObject<{
2958
2961
  type: z.ZodLiteral<"imagePrompt">;
2959
2962
  prompt: z.ZodString;
2963
+ canvasSize: z.ZodOptional<z.ZodObject<{
2964
+ width: z.ZodNumber;
2965
+ height: z.ZodNumber;
2966
+ }, z.core.$strict>>;
2960
2967
  }, z.core.$strict>;
2961
2968
  export declare const mulmoImageParamsImagesValueSchema: z.ZodUnion<readonly [z.ZodObject<{
2962
2969
  type: z.ZodLiteral<"image">;
@@ -2973,6 +2980,10 @@ export declare const mulmoImageParamsImagesValueSchema: z.ZodUnion<readonly [z.Z
2973
2980
  }, z.core.$strict>, z.ZodObject<{
2974
2981
  type: z.ZodLiteral<"imagePrompt">;
2975
2982
  prompt: z.ZodString;
2983
+ canvasSize: z.ZodOptional<z.ZodObject<{
2984
+ width: z.ZodNumber;
2985
+ height: z.ZodNumber;
2986
+ }, z.core.$strict>>;
2976
2987
  }, z.core.$strict>]>;
2977
2988
  export declare const mulmoImageParamsImagesSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
2978
2989
  type: z.ZodLiteral<"image">;
@@ -2989,6 +3000,10 @@ export declare const mulmoImageParamsImagesSchema: z.ZodRecord<z.ZodString, z.Zo
2989
3000
  }, z.core.$strict>, z.ZodObject<{
2990
3001
  type: z.ZodLiteral<"imagePrompt">;
2991
3002
  prompt: z.ZodString;
3003
+ canvasSize: z.ZodOptional<z.ZodObject<{
3004
+ width: z.ZodNumber;
3005
+ height: z.ZodNumber;
3006
+ }, z.core.$strict>>;
2992
3007
  }, z.core.$strict>]>>;
2993
3008
  export declare const mulmoFillOptionSchema: z.ZodObject<{
2994
3009
  style: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
@@ -3057,6 +3072,10 @@ export declare const mulmoImageParamsSchema: z.ZodObject<{
3057
3072
  }, z.core.$strict>, z.ZodObject<{
3058
3073
  type: z.ZodLiteral<"imagePrompt">;
3059
3074
  prompt: z.ZodString;
3075
+ canvasSize: z.ZodOptional<z.ZodObject<{
3076
+ width: z.ZodNumber;
3077
+ height: z.ZodNumber;
3078
+ }, z.core.$strict>>;
3060
3079
  }, z.core.$strict>]>>>;
3061
3080
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
3062
3081
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -3605,8 +3624,9 @@ export declare const mulmoBeatSchema: z.ZodObject<{
3605
3624
  }, z.core.$strip>]>>>;
3606
3625
  }, z.core.$strict>, z.ZodObject<{
3607
3626
  type: z.ZodLiteral<"html_tailwind">;
3608
- html: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
3627
+ html: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
3609
3628
  script: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
3629
+ elements: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
3610
3630
  animation: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
3611
3631
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
3612
3632
  movie: z.ZodOptional<z.ZodBoolean>;
@@ -6405,6 +6425,10 @@ export declare const mulmoPresentationStyleSchema: z.ZodObject<{
6405
6425
  }, z.core.$strict>, z.ZodObject<{
6406
6426
  type: z.ZodLiteral<"imagePrompt">;
6407
6427
  prompt: z.ZodString;
6428
+ canvasSize: z.ZodOptional<z.ZodObject<{
6429
+ width: z.ZodNumber;
6430
+ height: z.ZodNumber;
6431
+ }, z.core.$strict>>;
6408
6432
  }, z.core.$strict>]>>>;
6409
6433
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
6410
6434
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -6861,6 +6885,10 @@ export declare const mulmoScriptSchema: z.ZodObject<{
6861
6885
  }, z.core.$strict>, z.ZodObject<{
6862
6886
  type: z.ZodLiteral<"imagePrompt">;
6863
6887
  prompt: z.ZodString;
6888
+ canvasSize: z.ZodOptional<z.ZodObject<{
6889
+ width: z.ZodNumber;
6890
+ height: z.ZodNumber;
6891
+ }, z.core.$strict>>;
6864
6892
  }, z.core.$strict>]>>>;
6865
6893
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
6866
6894
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -7404,8 +7432,9 @@ export declare const mulmoScriptSchema: z.ZodObject<{
7404
7432
  }, z.core.$strip>]>>>;
7405
7433
  }, z.core.$strict>, z.ZodObject<{
7406
7434
  type: z.ZodLiteral<"html_tailwind">;
7407
- html: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
7435
+ html: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
7408
7436
  script: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
7437
+ elements: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
7409
7438
  animation: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
7410
7439
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
7411
7440
  movie: z.ZodOptional<z.ZodBoolean>;
@@ -10279,6 +10308,10 @@ export declare const mulmoStudioSchema: z.ZodObject<{
10279
10308
  }, z.core.$strict>, z.ZodObject<{
10280
10309
  type: z.ZodLiteral<"imagePrompt">;
10281
10310
  prompt: z.ZodString;
10311
+ canvasSize: z.ZodOptional<z.ZodObject<{
10312
+ width: z.ZodNumber;
10313
+ height: z.ZodNumber;
10314
+ }, z.core.$strict>>;
10282
10315
  }, z.core.$strict>]>>>;
10283
10316
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
10284
10317
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -10822,8 +10855,9 @@ export declare const mulmoStudioSchema: z.ZodObject<{
10822
10855
  }, z.core.$strip>]>>>;
10823
10856
  }, z.core.$strict>, z.ZodObject<{
10824
10857
  type: z.ZodLiteral<"html_tailwind">;
10825
- html: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
10858
+ html: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
10826
10859
  script: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
10860
+ elements: z.ZodOptional<z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
10827
10861
  animation: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
10828
10862
  fps: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10829
10863
  movie: z.ZodOptional<z.ZodBoolean>;
@@ -13633,6 +13667,10 @@ export declare const mulmoPromptTemplateSchema: z.ZodObject<{
13633
13667
  }, z.core.$strict>, z.ZodObject<{
13634
13668
  type: z.ZodLiteral<"imagePrompt">;
13635
13669
  prompt: z.ZodString;
13670
+ canvasSize: z.ZodOptional<z.ZodObject<{
13671
+ width: z.ZodNumber;
13672
+ height: z.ZodNumber;
13673
+ }, z.core.$strict>>;
13636
13674
  }, z.core.$strict>]>>>;
13637
13675
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
13638
13676
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -14083,6 +14121,10 @@ export declare const mulmoPromptTemplateFileSchema: z.ZodObject<{
14083
14121
  }, z.core.$strict>, z.ZodObject<{
14084
14122
  type: z.ZodLiteral<"imagePrompt">;
14085
14123
  prompt: z.ZodString;
14124
+ canvasSize: z.ZodOptional<z.ZodObject<{
14125
+ width: z.ZodNumber;
14126
+ height: z.ZodNumber;
14127
+ }, z.core.$strict>>;
14086
14128
  }, z.core.$strict>]>>>;
14087
14129
  backgroundImage: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
14088
14130
  source: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -194,6 +194,72 @@ export const mulmoMermaidMediaSchema = z
194
194
  backgroundImage: backgroundImageSchema,
195
195
  })
196
196
  .strict();
197
+ // Swipe-inspired element animation schemas
198
+ const swipePositionValue = z.union([z.number(), z.string()]);
199
+ const swipeTransitionSchema = z
200
+ .object({
201
+ opacity: z.number().min(0).max(1).optional(),
202
+ rotate: z.number().optional(),
203
+ scale: z.union([z.number(), z.tuple([z.number(), z.number()])]).optional(),
204
+ translate: z.tuple([z.number(), z.number()]).optional(),
205
+ bc: z.string().optional(),
206
+ timing: z.tuple([z.number(), z.number()]).default([0, 1]).optional().describe("Animation timing [start, end] as ratio 0.0-1.0 of beat duration"),
207
+ })
208
+ .strict();
209
+ const swipeLoopSchema = z
210
+ .object({
211
+ style: z.enum(["vibrate", "blink", "wiggle", "spin", "shift", "bounce", "pulse"]),
212
+ count: z.number().optional().describe("Number of loop iterations. 0 = infinite"),
213
+ delta: z.number().optional().describe("Distance for vibrate / angle for wiggle"),
214
+ duration: z.number().optional().describe("Duration of one cycle in seconds"),
215
+ direction: z.enum(["n", "s", "e", "w"]).optional().describe("Direction for shift animation"),
216
+ clockwise: z.boolean().optional().describe("Spin direction. Default: true"),
217
+ })
218
+ .strict();
219
+ const swipeShadowSchema = z
220
+ .object({
221
+ color: z.string().optional().default("black"),
222
+ offset: z.tuple([z.number(), z.number()]).optional().default([1, 1]),
223
+ opacity: z.number().optional().default(0.5),
224
+ radius: z.number().optional().default(1),
225
+ })
226
+ .strict();
227
+ export const swipeElementSchema = z.lazy(() => z
228
+ .object({
229
+ id: z.string().optional(),
230
+ // Position & size
231
+ x: swipePositionValue.optional(),
232
+ y: swipePositionValue.optional(),
233
+ w: swipePositionValue.optional(),
234
+ h: swipePositionValue.optional(),
235
+ pos: z.tuple([swipePositionValue, swipePositionValue]).optional().describe("Position by anchor point [x, y]"),
236
+ // Visual
237
+ bc: z.string().optional().describe("Background color"),
238
+ opacity: z.number().min(0).max(1).optional(),
239
+ rotate: z.number().optional(),
240
+ scale: z.union([z.number(), z.tuple([z.number(), z.number()])]).optional(),
241
+ translate: z.tuple([z.number(), z.number()]).optional(),
242
+ cornerRadius: z.number().optional(),
243
+ borderWidth: z.number().optional(),
244
+ borderColor: z.string().optional(),
245
+ shadow: swipeShadowSchema.optional(),
246
+ clip: z.boolean().optional(),
247
+ // Content
248
+ text: z.string().optional(),
249
+ fontSize: z.union([z.number(), z.string()]).optional(),
250
+ fontWeight: z.string().optional(),
251
+ textColor: z.string().optional(),
252
+ textAlign: z.enum(["center", "left", "right"]).optional(),
253
+ lineHeight: z.union([z.number(), z.string()]).optional(),
254
+ img: z.string().optional().describe("Image URL or image:ref"),
255
+ imgFit: z.enum(["contain", "cover", "fill"]).optional().default("contain"),
256
+ // Animation
257
+ to: swipeTransitionSchema.optional().describe("Transition animation"),
258
+ loop: swipeLoopSchema.optional().describe("Loop animation"),
259
+ // Children
260
+ elements: z.array(z.lazy(() => swipeElementSchema)).optional(),
261
+ })
262
+ .strict());
197
263
  export const htmlTailwindAnimationSchema = z.union([
198
264
  z.literal(true),
199
265
  z.object({
@@ -204,8 +270,12 @@ export const htmlTailwindAnimationSchema = z.union([
204
270
  export const mulmoHtmlTailwindMediaSchema = z
205
271
  .object({
206
272
  type: z.literal("html_tailwind"),
207
- html: stringOrStringArray,
273
+ html: stringOrStringArray.optional(),
208
274
  script: stringOrStringArray.optional().describe("JavaScript code for the beat. Injected as a <script> tag after html. Use for render() function etc."),
275
+ elements: z
276
+ .array(swipeElementSchema)
277
+ .optional()
278
+ .describe("Swipe-style declarative animation elements. Converted to HTML + render() automatically. Use this OR html, not both."),
209
279
  animation: htmlTailwindAnimationSchema
210
280
  .optional()
211
281
  .describe("Enable frame-based animation (Remotion-style). true for defaults (30fps), or { fps: N } for custom frame rate."),
@@ -264,6 +334,7 @@ export const mulmoImagePromptMediaSchema = z
264
334
  .object({
265
335
  type: z.literal("imagePrompt"),
266
336
  prompt: z.string().min(1),
337
+ canvasSize: z.object({ width: z.number(), height: z.number() }).strict().optional(),
267
338
  })
268
339
  .strict();
269
340
  export const mulmoImageParamsImagesValueSchema = z.union([mulmoImageMediaSchema, mulmoImagePromptMediaSchema]);
@@ -69,6 +69,10 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
69
69
  } | {
70
70
  type: "imagePrompt";
71
71
  prompt: string;
72
+ canvasSize?: {
73
+ width: number;
74
+ height: number;
75
+ } | undefined;
72
76
  }> | undefined;
73
77
  backgroundImage?: string | {
74
78
  source: {
@@ -1721,8 +1725,9 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
1721
1725
  } | null | undefined;
1722
1726
  } | {
1723
1727
  type: "html_tailwind";
1724
- html: string | string[];
1728
+ html?: string | string[] | undefined;
1725
1729
  script?: string | string[] | undefined;
1730
+ elements?: unknown[] | undefined;
1726
1731
  animation?: true | {
1727
1732
  fps: number;
1728
1733
  movie?: boolean | undefined;
@@ -2164,6 +2169,10 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
2164
2169
  } | {
2165
2170
  type: "imagePrompt";
2166
2171
  prompt: string;
2172
+ canvasSize?: {
2173
+ width: number;
2174
+ height: number;
2175
+ } | undefined;
2167
2176
  }> | undefined;
2168
2177
  backgroundImage?: string | {
2169
2178
  source: {
@@ -3816,8 +3825,9 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
3816
3825
  } | null | undefined;
3817
3826
  } | {
3818
3827
  type: "html_tailwind";
3819
- html: string | string[];
3828
+ html?: string | string[] | undefined;
3820
3829
  script?: string | string[] | undefined;
3830
+ elements?: unknown[] | undefined;
3821
3831
  animation?: true | {
3822
3832
  fps: number;
3823
3833
  movie?: boolean | undefined;
@@ -4266,6 +4276,10 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
4266
4276
  } | {
4267
4277
  type: "imagePrompt";
4268
4278
  prompt: string;
4279
+ canvasSize?: {
4280
+ width: number;
4281
+ height: number;
4282
+ } | undefined;
4269
4283
  }> | undefined;
4270
4284
  backgroundImage?: string | {
4271
4285
  source: {
@@ -49,6 +49,7 @@ export declare const blankImagePath: () => string;
49
49
  export declare const blankVerticalImagePath: () => string;
50
50
  export declare const blankSquareImagePath: () => string;
51
51
  export declare const getHTMLFile: (filename: string) => string;
52
+ export declare const getJSFile: (filename: string) => string;
52
53
  export declare const getBaseDirPath: (basedir?: string) => string;
53
54
  export declare const getFullPath: (baseDirPath: string | undefined, file: string) => string;
54
55
  export declare const readScriptTemplateFile: (scriptTemplateFileName: string) => MulmoScript;
package/lib/utils/file.js CHANGED
@@ -158,6 +158,10 @@ export const getHTMLFile = (filename) => {
158
158
  const htmlPath = resolveAssetFile(`./assets/html/${filename}.html`, npmRoot);
159
159
  return fs.readFileSync(htmlPath, "utf-8");
160
160
  };
161
+ export const getJSFile = (filename) => {
162
+ const jsPath = resolveAssetFile(`./assets/html/js/${filename}.js`, npmRoot);
163
+ return fs.readFileSync(jsPath, "utf-8");
164
+ };
161
165
  // for cli
162
166
  export const getBaseDirPath = (basedir) => {
163
167
  if (!basedir) {