lumina-slides 8.9.4 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LUMINA_LLM_EXAMPLES.json +234 -0
  2. package/README.md +18 -18
  3. package/dist/lumina-slides.js +13207 -12659
  4. package/dist/lumina-slides.umd.cjs +215 -215
  5. package/dist/style.css +1 -1
  6. package/package.json +5 -4
  7. package/src/App.vue +16 -0
  8. package/src/animation/index.ts +11 -0
  9. package/src/animation/registry.ts +126 -0
  10. package/src/animation/stagger.ts +95 -0
  11. package/src/animation/types.ts +53 -0
  12. package/src/components/LandingPage.vue +229 -0
  13. package/src/components/LuminaDeck.vue +224 -0
  14. package/src/components/LuminaSpeakerNotes.vue +701 -0
  15. package/src/components/base/BaseSlide.vue +122 -0
  16. package/src/components/base/LuminaElement.vue +67 -0
  17. package/src/components/base/VideoPlayer.vue +204 -0
  18. package/src/components/layouts/LayoutAuto.vue +71 -0
  19. package/src/components/layouts/LayoutChart.vue +287 -0
  20. package/src/components/layouts/LayoutCustom.vue +92 -0
  21. package/src/components/layouts/LayoutDiagram.vue +253 -0
  22. package/src/components/layouts/LayoutFeatures.vue +121 -0
  23. package/src/components/layouts/LayoutFlex.vue +172 -0
  24. package/src/components/layouts/LayoutFree.vue +62 -0
  25. package/src/components/layouts/LayoutHalf.vue +127 -0
  26. package/src/components/layouts/LayoutStatement.vue +74 -0
  27. package/src/components/layouts/LayoutSteps.vue +106 -0
  28. package/src/components/layouts/LayoutTimeline.vue +104 -0
  29. package/src/components/layouts/LayoutVideo.vue +41 -0
  30. package/src/components/parts/FlexBullets.vue +45 -0
  31. package/src/components/parts/FlexButton.vue +132 -0
  32. package/src/components/parts/FlexImage.vue +54 -0
  33. package/src/components/parts/FlexOrdered.vue +44 -0
  34. package/src/components/parts/FlexSpacer.vue +13 -0
  35. package/src/components/parts/FlexStepper.vue +59 -0
  36. package/src/components/parts/FlexText.vue +29 -0
  37. package/src/components/parts/FlexTimeline.vue +67 -0
  38. package/src/components/parts/FlexTitle.vue +39 -0
  39. package/src/components/parts/LuminaBackground.vue +100 -0
  40. package/src/components/site/LivePreview.vue +101 -0
  41. package/src/components/site/SiteApi.vue +301 -0
  42. package/src/components/site/SiteDashboard.vue +604 -0
  43. package/src/components/site/SiteDocs.vue +3267 -0
  44. package/src/components/site/SiteExamples.vue +65 -0
  45. package/src/components/site/SiteFooter.vue +6 -0
  46. package/src/components/site/SiteHome.vue +362 -0
  47. package/src/components/site/SiteNavBar.vue +122 -0
  48. package/src/components/site/SitePlayground.vue +389 -0
  49. package/src/components/site/SitePromptBuilder.vue +266 -0
  50. package/src/components/site/SiteUserMenu.vue +90 -0
  51. package/src/components/studio/ActionEditor.vue +108 -0
  52. package/src/components/studio/ArrayEditor.vue +124 -0
  53. package/src/components/studio/CollapsibleSection.vue +33 -0
  54. package/src/components/studio/ColorField.vue +22 -0
  55. package/src/components/studio/EditorCanvas.vue +326 -0
  56. package/src/components/studio/EditorLayoutFeatures.vue +18 -0
  57. package/src/components/studio/EditorLayoutFixed.vue +46 -0
  58. package/src/components/studio/EditorLayoutFlex.vue +133 -0
  59. package/src/components/studio/EditorLayoutHalf.vue +18 -0
  60. package/src/components/studio/EditorLayoutStatement.vue +18 -0
  61. package/src/components/studio/EditorLayoutSteps.vue +18 -0
  62. package/src/components/studio/EditorLayoutTimeline.vue +18 -0
  63. package/src/components/studio/EditorNode.vue +89 -0
  64. package/src/components/studio/FieldEditor.vue +133 -0
  65. package/src/components/studio/IconPicker.vue +109 -0
  66. package/src/components/studio/LayerItem.vue +117 -0
  67. package/src/components/studio/LuminaStudio.vue +30 -0
  68. package/src/components/studio/SaveSuccessModal.vue +138 -0
  69. package/src/components/studio/SlideNavigator.vue +373 -0
  70. package/src/components/studio/SliderField.vue +44 -0
  71. package/src/components/studio/StudioInspector.vue +595 -0
  72. package/src/components/studio/StudioJsonEditor.vue +191 -0
  73. package/src/components/studio/StudioLayers.vue +145 -0
  74. package/src/components/studio/StudioSettings.vue +514 -0
  75. package/src/components/studio/StudioSidebar.vue +29 -0
  76. package/src/components/studio/StudioToolbar.vue +222 -0
  77. package/src/components/studio/fieldLabels.ts +224 -0
  78. package/src/components/studio/inspectors/DiagramEdgeEditor.vue +77 -0
  79. package/src/components/studio/inspectors/DiagramNodeEditor.vue +117 -0
  80. package/src/components/studio/nodes/StudioDiagramNode.vue +138 -0
  81. package/src/composables/useAuth.ts +87 -0
  82. package/src/composables/useEditor.ts +224 -0
  83. package/src/composables/useElementState.ts +81 -0
  84. package/src/composables/useFlexLayout.ts +122 -0
  85. package/src/composables/useKeyboard.ts +45 -0
  86. package/src/composables/useLumina.ts +32 -0
  87. package/src/composables/useStudio.ts +87 -0
  88. package/src/composables/useSwipeNav.ts +53 -0
  89. package/src/composables/useTransition.ts +373 -0
  90. package/src/core/Lumina.ts +819 -0
  91. package/src/core/animationConfig.ts +251 -0
  92. package/src/core/compression.ts +34 -0
  93. package/src/core/elementController.ts +170 -0
  94. package/src/core/elementId.ts +27 -0
  95. package/src/core/elementResolver.ts +207 -0
  96. package/src/core/events.ts +53 -0
  97. package/src/core/fonts.ts +100 -0
  98. package/src/core/presets.ts +231 -0
  99. package/src/core/prompts.ts +272 -0
  100. package/src/core/schema.ts +478 -0
  101. package/src/core/speaker-channel.ts +250 -0
  102. package/src/core/store.ts +461 -0
  103. package/src/core/theme.ts +666 -0
  104. package/src/core/types.ts +1611 -0
  105. package/src/directives/vStudio.ts +45 -0
  106. package/src/index.ts +175 -0
  107. package/src/main.ts +17 -0
  108. package/src/router/index.ts +92 -0
  109. package/src/style/main.css +462 -0
  110. package/src/utils/deep.ts +127 -0
  111. package/src/utils/firebase.ts +184 -0
  112. package/src/utils/streaming.ts +134 -0
  113. package/src/views/DashboardView.vue +32 -0
  114. package/src/views/DeckView.vue +289 -0
  115. package/src/views/HomeView.vue +17 -0
  116. package/src/views/SiteLayout.vue +21 -0
  117. package/src/views/StudioView.vue +61 -0
  118. package/src/vite-env.d.ts +6 -0
  119. package/IMPLEMENTATION.md +0 -418
@@ -0,0 +1,1611 @@
1
+ /**
2
+ * LUMINA ENGINE TYPES
3
+ * Centralized type definitions for strict contract enforcement.
4
+ */
5
+
6
+ // --- Slide & Deck Definitions ---
7
+
8
+ /**
9
+ * Supported slide layout types.
10
+ * Each type corresponds to a registered Vue component (e.g. 'statement' -> LayoutStatement).
11
+ */
12
+ export type SlideType = 'statement' | 'half' | 'features' | 'timeline' | 'steps' | 'flex' | 'chart' | 'diagram' | 'free' | (string & {});
13
+
14
+ /**
15
+ * Structure for items in the 'timeline' layout.
16
+ */
17
+ export interface TimelineItem {
18
+ /** Optional id for element control (engine.element(id)). */
19
+ id?: string;
20
+ date: string;
21
+ title: string;
22
+ t?: string; // Alias for title
23
+ description: string;
24
+ desc?: string; // Alias for description
25
+ icon?: string;
26
+ }
27
+
28
+ /**
29
+ * Structure for items in the 'steps' layout.
30
+ */
31
+ export interface StepItem {
32
+ /** Optional id for element control (engine.element(id)). */
33
+ id?: string;
34
+ step: string; // e.g., "01"
35
+ title: string;
36
+ description?: string;
37
+ icon?: string;
38
+ }
39
+
40
+ // --- Chart Layout Types ---
41
+
42
+ /**
43
+ * Supported chart types.
44
+ */
45
+ export type ChartType = 'bar' | 'line' | 'pie' | 'doughnut';
46
+
47
+ /**
48
+ * A single dataset for chart visualization.
49
+ */
50
+ export interface ChartDataset {
51
+ /** Label for this dataset (appears in legend). */
52
+ label: string;
53
+ /** Numeric values corresponding to each label. */
54
+ values: number[];
55
+ /**
56
+ * Color for this dataset. Supports Lumina tokens:
57
+ * - 'c:p' → primary color
58
+ * - 'c:s' → secondary color
59
+ * - 'c:m' → muted color
60
+ * - Or any valid CSS color (hex, rgb, etc.)
61
+ */
62
+ color?: string;
63
+ }
64
+
65
+ /**
66
+ * Data structure for chart visualization.
67
+ */
68
+ export interface ChartData {
69
+ /** Labels for the x-axis or pie segments. */
70
+ labels: string[];
71
+ /** Array of datasets to display. */
72
+ datasets: ChartDataset[];
73
+ }
74
+
75
+ // --- Flex Layout Types ---
76
+
77
+ /**
78
+ * Size tokens for flex element sizing.
79
+ * Determines how much horizontal/vertical space an element occupies.
80
+ */
81
+ export type FlexSize = 'auto' | 'quarter' | 'third' | 'half' | 'two-thirds' | 'three-quarters' | 'full';
82
+
83
+ /**
84
+ * Spacing tokens used for gaps and padding.
85
+ */
86
+ export type SpacingToken = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
87
+
88
+ /**
89
+ * Vertical alignment options.
90
+ */
91
+ export type VAlign = 'top' | 'center' | 'bottom';
92
+
93
+ /**
94
+ * Horizontal alignment options.
95
+ */
96
+ export type HAlign = 'left' | 'center' | 'right';
97
+
98
+ /**
99
+ * Text alignment options.
100
+ */
101
+ export type TextAlign = 'left' | 'center' | 'right';
102
+
103
+ /**
104
+ * Title element - Large heading text.
105
+ */
106
+ export interface FlexElementTitle {
107
+ type: 'title';
108
+ /** Optional id for element control (engine.element(id)). */
109
+ id?: string;
110
+ text: string;
111
+ /** Heading size. Default: 'xl' */
112
+ size?: 'lg' | 'xl' | '2xl' | '3xl';
113
+ /** Text alignment. Default: 'left' */
114
+ align?: TextAlign;
115
+ }
116
+
117
+ /**
118
+ * Text element - Body paragraph text.
119
+ */
120
+ export interface FlexElementText {
121
+ type: 'text';
122
+ /** Optional id for element control (engine.element(id)). */
123
+ id?: string;
124
+ text: string;
125
+ /** Text alignment. Default: 'left' */
126
+ align?: TextAlign;
127
+ /** Muted/subtle appearance. Default: false */
128
+ muted?: boolean;
129
+ }
130
+
131
+ /**
132
+ * Bullets element - Unordered list.
133
+ */
134
+ export interface FlexElementBullets {
135
+ type: 'bullets';
136
+ /** Optional id for element control (engine.element(id)). */
137
+ id?: string;
138
+ items: string[];
139
+ }
140
+
141
+ /**
142
+ * Ordered element - Numbered list.
143
+ */
144
+ export interface FlexElementOrdered {
145
+ type: 'ordered';
146
+ /** Optional id for element control (engine.element(id)). */
147
+ id?: string;
148
+ items: string[];
149
+ }
150
+
151
+ /**
152
+ * Image element - Visual media.
153
+ */
154
+ export interface FlexElementImage {
155
+ type: 'image';
156
+ /** Optional id for element control (engine.element(id)). */
157
+ id?: string;
158
+ src: string;
159
+ alt?: string;
160
+ /** Fill entire container edge-to-edge. Default: true */
161
+ fill?: boolean;
162
+ /** Object-fit mode when fill is true. Default: 'cover' */
163
+ fit?: 'cover' | 'contain';
164
+ /** Border radius. Default: 'none' when fill, 'lg' otherwise */
165
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
166
+ /** Link URL when image is clicked. */
167
+ href?: string;
168
+ /** Link target. Default: '_blank' */
169
+ target?: '_blank' | '_self';
170
+ /** Custom CSS class */
171
+ class?: string;
172
+ }
173
+
174
+ /**
175
+ * Video element - Visual media.
176
+ */
177
+ export interface FlexElementVideo extends VideoProperties {
178
+ type: 'video';
179
+ /** Optional id for element control (engine.element(id)). */
180
+ id?: string;
181
+ /** Fill entire container edge-to-edge. Default: true */
182
+ fill?: boolean;
183
+ /** Object-fit mode when fill is true. Default: 'cover' */
184
+ fit?: 'cover' | 'contain';
185
+ /** Border radius. Default: 'none' when fill, 'lg' otherwise */
186
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
187
+ /** Custom CSS class */
188
+ class?: string;
189
+ }
190
+
191
+ /**
192
+ * Button element - Call-to-action button.
193
+ */
194
+ export interface FlexElementButton {
195
+ type: 'button';
196
+ /** Optional id for element control (engine.element(id)). */
197
+ id?: string;
198
+ label: string;
199
+ /** Action identifier emitted on click (for custom event handling). */
200
+ action?: string;
201
+ /** Type of action to perform. Default: 'event' */
202
+ actionType?: 'event' | 'url' | 'slide' | 'download';
203
+ /** URL to navigate to when actionType is 'url'. */
204
+ href?: string;
205
+ /** Slide index to navigate to when actionType is 'slide'. */
206
+ gotoSlide?: number;
207
+ /** Link target for URL actions. Default: '_blank' */
208
+ target?: '_blank' | '_self';
209
+ /** Visual variant. Default: 'primary' */
210
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
211
+ /** Full width button. Default: false */
212
+ fullWidth?: boolean;
213
+ }
214
+
215
+ /**
216
+ * Timeline element - Embedded timeline with events.
217
+ */
218
+ export interface FlexElementTimeline {
219
+ type: 'timeline';
220
+ /** Optional id for element control (engine.element(id)). */
221
+ id?: string;
222
+ items: TimelineItem[];
223
+ /** Compact display mode. Default: false */
224
+ compact?: boolean;
225
+ }
226
+
227
+ /**
228
+ * Stepper element - Embedded step-by-step process.
229
+ */
230
+ export interface FlexElementStepper {
231
+ type: 'stepper';
232
+ /** Optional id for element control (engine.element(id)). */
233
+ id?: string;
234
+ items: StepItem[];
235
+ /** Compact display mode. Default: false */
236
+ compact?: boolean;
237
+ }
238
+
239
+ /**
240
+ * Spacer element - Adds visual spacing.
241
+ */
242
+ export interface FlexElementSpacer {
243
+ type: 'spacer';
244
+ /** Optional id for element control (engine.element(id)). */
245
+ id?: string;
246
+ /** Size of the space. Default: 'md' */
247
+ size?: SpacingToken;
248
+ }
249
+
250
+ /**
251
+ * Content container - Groups child elements vertically with alignment control.
252
+ */
253
+ export interface FlexElementContent {
254
+ type: 'content';
255
+ /** Optional id for element control (engine.element(id)). */
256
+ id?: string;
257
+ elements: FlexChildElement[];
258
+ /** Vertical alignment of content. Default: 'center' */
259
+ valign?: VAlign;
260
+ /** Horizontal alignment of content. Default: 'left' */
261
+ halign?: HAlign;
262
+ /** Gap between child elements. Default: 'md' */
263
+ gap?: SpacingToken;
264
+ /** Internal padding. Default: 'lg' */
265
+ padding?: SpacingToken;
266
+ }
267
+
268
+ /**
269
+ * Child elements that can appear inside a content container.
270
+ */
271
+ export type FlexChildElement =
272
+ | FlexElementTitle
273
+ | FlexElementText
274
+ | FlexElementBullets
275
+ | FlexElementOrdered
276
+ | FlexElementButton
277
+ | FlexElementTimeline
278
+ | FlexElementStepper
279
+ | FlexElementSpacer;
280
+
281
+ /**
282
+ * Top-level flex elements that can have size.
283
+ */
284
+ export type FlexElement =
285
+ | (FlexElementImage & { size?: FlexSize })
286
+ | (FlexElementVideo & { size?: FlexSize })
287
+ | (FlexElementContent & { size?: FlexSize })
288
+ | (FlexElementTitle & { size?: FlexSize })
289
+ | (FlexElementText & { size?: FlexSize })
290
+ | (FlexElementBullets & { size?: FlexSize })
291
+ | (FlexElementOrdered & { size?: FlexSize })
292
+ | (FlexElementButton & { size?: FlexSize })
293
+ | (FlexElementTimeline & { size?: FlexSize })
294
+ | (FlexElementStepper & { size?: FlexSize });
295
+
296
+ /**
297
+ * Metadata for individual slides.
298
+ * Can contain arbitrary data used by custom layouts.
299
+ */
300
+ export interface SlideMeta {
301
+ orbColor?: string;
302
+ orbPos?: { top: string; left: string };
303
+ [key: string]: any;
304
+ }
305
+
306
+ /**
307
+ * State for a single controllable element (visibility, opacity, transform, etc.).
308
+ * Applied by LuminaElement / useElementState; written by `engine.element(id).show()`, `.hide()`, `.opacity()`, etc.
309
+ *
310
+ * Use `meta.initialElementState` in the deck to start with elements hidden and reveal them
311
+ * programmatically (e.g. presentation starts "empty", then `engine.element('s0-title').show()`).
312
+ *
313
+ * @example
314
+ * // In deck JSON: start slide 0 title and subtitle hidden; reveal via API.
315
+ * { "meta": { "title": "Demo", "initialElementState": { "s0-title": { "visible": false }, "s0-subtitle": { "visible": false } } }, "slides": [...] }
316
+ *
317
+ * @see ElementController
318
+ * @see DeckMeta.initialElementState
319
+ */
320
+ export interface ElementState {
321
+ /** If false, the element is hidden (v-show). Omit or true = visible. */
322
+ visible?: boolean;
323
+ /** Opacity from 0 to 1. Applied as inline style. */
324
+ opacity?: number;
325
+ /** CSS transform (e.g. "scale(1.2)", "translateX(10px)"). */
326
+ transform?: string;
327
+ /** Extra CSS class names (space-separated). Merged with component classes. */
328
+ class?: string;
329
+ /** Inline style overrides. Merged with opacity/transform when those are set. */
330
+ style?: Record<string, string | number>;
331
+ }
332
+
333
+ /**
334
+ * Options for `ElementController.animate()` and `animateAsync()`. GSAP-compatible property names and values.
335
+ *
336
+ * @description
337
+ * `to` is required; `from` optional (animates from current state if omitted). `duration` and `ease` default
338
+ * to options.animation.elementDuration and elementEase (or resolveTransitionConfig). Element must be mounted (on the active slide).
339
+ *
340
+ * @example
341
+ * engine.element('s0-title').animate({ to: { opacity: 1, y: 0 }, duration: 0.6, ease: 'power2.out' });
342
+ * engine.element('s1-features-0').animate({ from: { scale: 0 }, to: { scale: 1 }, duration: 0.5 });
343
+ * await engine.element('s0-tag').animateAsync({ to: { opacity: 1 }, onComplete: () => console.log('done') });
344
+ *
345
+ * @see ElementController.animate
346
+ * @see ElementController.animateAsync
347
+ */
348
+ export interface AnimateOptions {
349
+ /** Preset name (fadeUp, scaleIn, slideLeft, etc.). Expands to from/to/ease; explicit from/to/ease override. */
350
+ preset?: string;
351
+ /** Motion name from meta.motions (future). Ignored if preset is set. */
352
+ motion?: string;
353
+ /** Starting values (GSAP from). If omitted, animates from current state or from preset. */
354
+ from?: Record<string, string | number>;
355
+ /** Target values (GSAP to). Optional when preset is set; preset provides to. */
356
+ to?: Record<string, string | number>;
357
+ /** Duration in seconds. Default from config or 0.5. */
358
+ duration?: number;
359
+ /** GSAP easing. Default from config or 'power2.out'. Preset provides default. */
360
+ ease?: string;
361
+ /** Callback when the animation completes. Also used by animateAsync. */
362
+ onComplete?: () => void;
363
+ }
364
+
365
+ /**
366
+ * Options for `Lumina.revealInSequence`. Controls staggered reveal of slide elements.
367
+ *
368
+ * @description
369
+ * When a value is omitted, it falls back to resolveTransitionConfig (revealDelayMs, revealDuration, revealEase).
370
+ * hideFirst: true (default) hides all elements before the sequence. only/exclude filter which ids are revealed.
371
+ *
372
+ * @example
373
+ * engine.revealInSequence(0, { delayMs: 500, animate: true });
374
+ * await engine.revealInSequence(undefined, { only: ['s1-title', 's1-subtitle'], hideFirst: true });
375
+ * engine.on('slideChange', e => engine.revealInSequence(e.index, { delayMs: 400, duration: 0.5 }));
376
+ *
377
+ * @see Lumina.revealInSequence
378
+ * @see resolveTransitionConfig
379
+ */
380
+ export interface RevealInSequenceOptions {
381
+ /** Delay in ms between each element. Default: 400 */
382
+ delayMs?: number;
383
+ /** Stagger mode: sequential, center-out, ends-in, wave, random. Default: sequential. */
384
+ staggerMode?: 'sequential' | 'center-out' | 'ends-in' | 'wave' | 'random';
385
+ /** For staggerMode 'random', seed for deterministic order. Omit for arbitrary. */
386
+ randomSeed?: number;
387
+ /** Preset for each element's animation (fadeUp, scaleIn, etc.). Overridden by from/to/ease. */
388
+ preset?: string;
389
+ /** Motion from meta.motions (future). */
390
+ motion?: string;
391
+ /** Whether to animate each element on reveal. Default: true */
392
+ animate?: boolean;
393
+ /** GSAP from vars. Default: `{ opacity: 0, y: 16 }` or from preset. */
394
+ from?: Record<string, string | number>;
395
+ /** GSAP to vars. Default: `{ opacity: 1, y: 0 }` or from preset. */
396
+ to?: Record<string, string | number>;
397
+ /** Animation duration in seconds. Default: 0.45 */
398
+ duration?: number;
399
+ /** GSAP ease. Default: `'power2.out'` */
400
+ ease?: string;
401
+ /** If true, hide all elements before starting the sequence. Default: true */
402
+ hideFirst?: boolean;
403
+ /** Only reveal these element ids (subset). Order follows slide elements. */
404
+ only?: string[];
405
+ /** Exclude these element ids from the sequence. */
406
+ exclude?: string[];
407
+ }
408
+
409
+ /**
410
+ * State at a keyframe for timeline-driven animation. Used in `slide.timelineTracks[id]` and
411
+ * `element(id).animateToProgress(progress, keyframes)`. Keys are progress strings 0–1 (e.g. "0", "0.25", "1").
412
+ *
413
+ * @description
414
+ * Remotion-style: progress 0–1 drives element state. Interpolation is linear between keyframes.
415
+ * `x`, `y`, `scale` are mapped to a CSS transform. `visible: false` hides the element.
416
+ *
417
+ * @example
418
+ * { "0": { opacity: 0, y: 20 }, "0.3": { opacity: 1, y: 0 }, "1": { opacity: 1 } }
419
+ *
420
+ * @see TimelineTracks
421
+ * @see Lumina.seekTo
422
+ * @see ElementController.animateToProgress
423
+ */
424
+ export type TimelineKeyframeState = Partial<{
425
+ opacity: number;
426
+ x: number;
427
+ y: number;
428
+ scale: number;
429
+ rotate: number;
430
+ visible: boolean;
431
+ }>;
432
+
433
+ /** Keyframes for one element: progress string -> state. */
434
+ export type TimelineKeyframes = Record<string, TimelineKeyframeState>;
435
+
436
+ /** Per-element timeline tracks. Keys = element ids. Use in `slide.timelineTracks`. */
437
+ export type TimelineTracks = Record<string, TimelineKeyframes>;
438
+
439
+ /**
440
+ * Fluent API to control one slide element in real time. Returned by `engine.element(id)`
441
+ * and `engine.element(slideIndex, path)`. All methods return `this` for chaining.
442
+ *
443
+ * Use for: starting with an empty slide and revealing items in order, hiding highlights,
444
+ * animating entrances on demand, or syncing with voice/agent.
445
+ *
446
+ * @example
447
+ * // Reveal sequence (after loading a deck with initialElementState hiding s0-tag, s0-title, s0-subtitle)
448
+ * engine.element(0, 'tag').show();
449
+ * await delay(500);
450
+ * engine.element(0, 'title').show().animate({ from: { y: 20, opacity: 0 }, to: { y: 0, opacity: 1 }, duration: 0.5 });
451
+ * engine.element(0, 'subtitle').show();
452
+ *
453
+ * @see Lumina.element
454
+ * @see Lumina.elements
455
+ * @see ElementState
456
+ * @see AnimateOptions
457
+ */
458
+ export interface ElementController {
459
+ /** Set visible = true. Use after starting with `initialElementState` { visible: false }. */
460
+ show(): ElementController;
461
+ /** Set visible = false. Element stays in DOM but is not displayed. */
462
+ hide(): ElementController;
463
+ /** Toggle visible. If `force` is given (true/false), set to that value. */
464
+ toggle(force?: boolean): ElementController;
465
+ /** Set opacity 0–1. Updates store; LuminaElement applies it. */
466
+ opacity(value: number): ElementController;
467
+ /** Set CSS transform string. */
468
+ transform(value: string): ElementController;
469
+ /** Merge props into element's style. Merged with existing style in the store. */
470
+ css(style: Record<string, string | number>): ElementController;
471
+ /** Append a CSS class to the element's state. */
472
+ addClass(className: string): ElementController;
473
+ /** Remove a CSS class from the element's state. */
474
+ removeClass(className: string): ElementController;
475
+ /** Run a GSAP animation on the DOM node. Element must be mounted (e.g. on that slide). */
476
+ animate(options: AnimateOptions): ElementController;
477
+ /** Like animate but returns a Promise that resolves when the animation completes. */
478
+ animateAsync(options: AnimateOptions): Promise<void>;
479
+ /**
480
+ * Set element state from a progress 0–1 by interpolating between keyframes. Remotion-style scrub.
481
+ * Use with `engine.seekTo(progress)` when the slide has `slide.timeline`.
482
+ *
483
+ * @param progress - 0–1. Interpolates between keyframes; keys are parseable numbers (e.g. "0", "0.3", "1").
484
+ * @param keyframes - Record of "progress" -> { opacity?, x?, y?, scale?, visible? }.
485
+ */
486
+ animateToProgress(progress: number, keyframes: TimelineKeyframes): ElementController;
487
+ }
488
+
489
+ /**
490
+ * Base properties shared by all slides.
491
+ */
492
+ interface SlideBase {
493
+ /** Unique ID for keying loop optimization (optional). */
494
+ id?: string;
495
+ /**
496
+ * Map of path keys to element ids. Use dot-notation for nested paths.
497
+ * Examples: { tag: "intro-tag" }, { "features.0": "hero-feature" }, { "elements.0.elements.1": "cta-btn" }.
498
+ * Overrides the default s{N}-{path} id when the target object has no explicit `id`.
499
+ */
500
+ ids?: Record<string, string>;
501
+ /** Slide-specific metadata. */
502
+ meta?: SlideMeta;
503
+ /** Sizing mode: 'viewport' (100vh) or 'container' (100%). Default: 'viewport' */
504
+ sizing?: 'viewport' | 'container';
505
+ /**
506
+ * Timeline tracks per element (Remotion-style): id -> { "0": { opacity, y, ... }, "0.3": { ... }, "1": { ... } }.
507
+ * Use with `engine.seekTo(progress)` and `engine.playTimeline()`. Do not confuse with the "timeline" layout's `timeline: TimelineItem[]`.
508
+ */
509
+ timelineTracks?: TimelineTracks;
510
+ /** Speaker notes for this slide (supports basic markdown). */
511
+ notes?: string;
512
+ /** Background image or video. */
513
+ background?: string | VideoProperties;
514
+ /** Opacity of the background (0-1). Default: 1 (or 0.2 for default dark overlay) */
515
+ backgroundOpacity?: number;
516
+ /** Optional custom CSS class for the slide content container. */
517
+ class?: string;
518
+ }
519
+
520
+ /**
521
+ * Common properties for video elements.
522
+ */
523
+ export interface VideoProperties {
524
+ type: 'video';
525
+ src: string;
526
+ poster?: string;
527
+ /** Default: true for backgrounds, false for elements */
528
+ autoplay?: boolean;
529
+ /** Default: true for backgrounds, false for elements */
530
+ loop?: boolean;
531
+ /** Default: true for backgrounds, false for elements */
532
+ muted?: boolean;
533
+ /** Default: false */
534
+ controls?: boolean;
535
+ /** Custom CSS class */
536
+ className?: string;
537
+ }
538
+
539
+ /**
540
+ * Slide: Statement Layout
541
+ * A simple, high-impact text layout.
542
+ * @example
543
+ * ```json
544
+ * {
545
+ * "type": "statement",
546
+ * "meta": { "orbColor": "#3b82f6" },
547
+ * "tag": "Declarative Engine",
548
+ * "title": "Lumina V8",
549
+ * "subtitle": "A highly modular, performance-first presentation engine."
550
+ * }
551
+ * ```
552
+ */
553
+ export interface SlideStatement extends SlideBase {
554
+ type: 'statement';
555
+ tag?: string;
556
+ title: string;
557
+ subtitle?: string;
558
+ }
559
+
560
+ /**
561
+ * Feature Item structure for Features Layout.
562
+ */
563
+ export interface FeatureItem {
564
+ /** Optional id for element control (engine.element(id)). */
565
+ id?: string;
566
+ class: string;
567
+ title: string;
568
+ desc: string;
569
+ description?: string; // Alias for desc (backwards compatibility)
570
+ icon: string;
571
+ }
572
+
573
+ /**
574
+ * Slide: Features Layout
575
+ * A grid of feature cards.
576
+ * @example
577
+ * ```json
578
+ * {
579
+ * "type": "features",
580
+ * "title": "Core Capabilities",
581
+ * "description": "Built to scale.",
582
+ * "features": [
583
+ * { "title": "Reactive", "desc": "State management", "icon": "fa-database" },
584
+ * { "title": "Modular", "desc": "Tree-shakable", "icon": "fa-cube" }
585
+ * ]
586
+ * }
587
+ * ```
588
+ */
589
+ export interface SlideFeatures extends SlideBase {
590
+ type: 'features';
591
+ title: string;
592
+ description?: string;
593
+ /** List of feature cards to display. */
594
+ features: FeatureItem[];
595
+ }
596
+
597
+ /**
598
+ * Slide: Split/Half Layout
599
+ * Text on one side, image on the other.
600
+ * @example
601
+ * ```json
602
+ * {
603
+ * "type": "half",
604
+ * "imageSide": "left",
605
+ * "image": "https://example.com/photo.jpg",
606
+ * "title": "Smart Split Layouts",
607
+ * "paragraphs": ["Lumina adapts to viewport size.", "Content is King."],
608
+ * "cta": "Read Documentation"
609
+ * }
610
+ * ```
611
+ */
612
+ export interface SlideHalf extends SlideBase {
613
+ imageClass: string;
614
+ type: 'half';
615
+ /** Which side the image appears on. */
616
+ imageSide: 'left' | 'right';
617
+ /** Image URL (optional if video is provided). */
618
+ image?: string;
619
+ /** Video properties (optional). */
620
+ video?: VideoProperties;
621
+ tag?: string;
622
+ title: string;
623
+ paragraphs: string[];
624
+ cta?: string;
625
+ }
626
+
627
+ /**
628
+ * Slide: Timeline Layout
629
+ * Chronological list of events.
630
+ */
631
+ export interface SlideTimeline extends SlideBase {
632
+ type: 'timeline';
633
+ title: string;
634
+ subtitle?: string;
635
+ timeline: TimelineItem[];
636
+ }
637
+
638
+ /**
639
+ * Slide: Steps / Process Layout
640
+ * Numbered steps with descriptions.
641
+ */
642
+ export interface SlideSteps extends SlideBase {
643
+ type: 'steps';
644
+ title: string;
645
+ subtitle?: string;
646
+ steps: StepItem[];
647
+ }
648
+
649
+ /**
650
+ * Slide: Flex Layout
651
+ * Flow-based layout for composing slides using semantic sizing.
652
+ * Elements flow in order with size tokens (half, third, etc.) instead of coordinates.
653
+ * @example
654
+ * ```json
655
+ * {
656
+ * "type": "flex",
657
+ * "direction": "horizontal",
658
+ * "elements": [
659
+ * { "type": "image", "src": "photo.jpg", "size": "half", "fill": true },
660
+ * { "type": "content", "size": "half", "valign": "center", "elements": [
661
+ * { "type": "title", "text": "Hello World" },
662
+ * { "type": "bullets", "items": ["Point A", "Point B"] }
663
+ * ]}
664
+ * ]
665
+ * }
666
+ * ```
667
+ */
668
+ export interface SlideFlex extends SlideBase {
669
+ type: 'flex';
670
+ /** Main flow direction. Default: 'horizontal' */
671
+ direction?: 'horizontal' | 'vertical';
672
+ /** Gap between top-level elements. Default: 'none' */
673
+ gap?: SpacingToken;
674
+ /** Padding around the entire container. Default: 'none' */
675
+ padding?: SpacingToken;
676
+ /** Horizontal alignment of the flex group within the slide. Default: 'left' */
677
+ halign?: HAlign;
678
+ /** Vertical alignment of the flex group within the slide. Default: 'top' */
679
+ valign?: VAlign;
680
+ /** Array of flex elements to render. */
681
+ elements: FlexElement[];
682
+ }
683
+
684
+ /**
685
+ * Slide: Custom HTML
686
+ * Renders arbitrary HTML content fullscreen.
687
+ * Use with caution - HTML is injected directly.
688
+ * @example
689
+ * ```json
690
+ * {
691
+ * "type": "custom",
692
+ * "html": "<div class='my-custom-slide'>Custom Content</div>",
693
+ * "css": ".my-custom-slide { color: white; }"
694
+ * }
695
+ * ```
696
+ */
697
+ export interface SlideCustom extends SlideBase {
698
+ type: 'custom';
699
+ /** Raw HTML content to render */
700
+ html: string;
701
+ /** Optional scoped CSS styles */
702
+ css?: string;
703
+ }
704
+
705
+ /**
706
+ * Slide: Chart Layout
707
+ * Renders data visualizations using Chart.js.
708
+ * @example
709
+ * ```json
710
+ * {
711
+ * "type": "chart",
712
+ * "chartType": "bar",
713
+ * "title": "Revenue by Quarter",
714
+ * "data": {
715
+ * "labels": ["Q1", "Q2", "Q3", "Q4"],
716
+ * "datasets": [{ "label": "2024", "values": [120, 150, 180, 220], "color": "c:p" }]
717
+ * }
718
+ * }
719
+ * ```
720
+ */
721
+ export interface SlideChart extends SlideBase {
722
+ chartClass: string;
723
+ type: 'chart';
724
+ /** The type of chart to render. */
725
+ chartType: ChartType;
726
+ /** Chart title displayed above the chart. */
727
+ title?: string;
728
+ /** Subtitle or description. */
729
+ subtitle?: string;
730
+ /** The chart data configuration. */
731
+ data: ChartData;
732
+ }
733
+
734
+ /**
735
+ * Slide: Diagram Layout
736
+ * Renders an interactive node-based diagram.
737
+ */
738
+ export interface SlideDiagram extends SlideBase {
739
+ type: 'diagram';
740
+ title?: string;
741
+ /** Diagram nodes (compatible with Vue Flow) */
742
+ nodes: any[];
743
+ /** Diagram edges (compatible with Vue Flow) */
744
+ edges: any[];
745
+ /** Optional configuration for the diagram engine */
746
+ config?: {
747
+ fitView?: boolean;
748
+ [key: string]: any;
749
+ };
750
+ }
751
+
752
+ /**
753
+ * Element in a free/composition layout. Absolutely positioned; position is driven by
754
+ * slide.timelineTracks (x, y as translate). Use for Remotion-style storytelling.
755
+ */
756
+ export interface FreeElement {
757
+ id?: string;
758
+ type: 'text' | 'image' | 'box';
759
+ text?: string;
760
+ src?: string;
761
+ width?: number | string;
762
+ height?: number | string;
763
+ fontSize?: string | number;
764
+ color?: string;
765
+ fontWeight?: string;
766
+ backgroundColor?: string;
767
+ }
768
+
769
+ /**
770
+ * Slide: Free / Composition (Remotion-style)
771
+ * Absolutely positioned elements (text, image, box) animated via timelineTracks.
772
+ * x, y in keyframes = translate; use for storytelling, motion, and layout freedom.
773
+ */
774
+ export interface SlideFree extends SlideBase {
775
+ type: 'free';
776
+ elements: FreeElement[];
777
+ }
778
+
779
+ /**
780
+ * Slide: Generic / Custom
781
+ * Fallback for custom layouts or unknown types.
782
+ */
783
+ export interface SlideGeneric extends SlideBase {
784
+ type: string & {};
785
+ [key: string]: any;
786
+ }
787
+
788
+ /**
789
+ * Slide: Video Layout
790
+ * Full screen video player.
791
+ */
792
+ export interface SlideVideo extends SlideBase {
793
+ type: 'video';
794
+ video: VideoProperties;
795
+ /** Optional caption or title overlay */
796
+ title?: string;
797
+ }
798
+
799
+ /**
800
+ * The main Slide Data structure.
801
+ * A discriminated union of all supported slide types.
802
+ */
803
+ export type BaseSlideData =
804
+ | SlideStatement
805
+ | SlideFeatures
806
+ | SlideHalf
807
+ | SlideTimeline
808
+ | SlideSteps
809
+ | SlideFlex
810
+ | SlideChart
811
+ | SlideDiagram
812
+ | SlideCustom
813
+ | SlideVideo
814
+ | SlideFree
815
+ | SlideGeneric;
816
+
817
+ /**
818
+ * Map of element id → partial ElementState. Used in `DeckMeta.initialElementState`
819
+ * to define initial visibility/opacity/transform when the deck loads. Enables
820
+ * "start empty, reveal on demand" presentations.
821
+ */
822
+ export type InitialElementState = Record<string, Partial<ElementState>>;
823
+
824
+ /**
825
+ * Global metadata for the entire presentation deck.
826
+ */
827
+ export interface DeckMeta {
828
+ id?: string; // Firestore document ID
829
+ title: string;
830
+ author?: string;
831
+ authorId?: string; // ID of the user who owns this deck
832
+ authorName?: string; // Display name of the author
833
+ isPublic?: boolean; // Visibility flag
834
+ date?: string;
835
+ deletedAt?: string; // Soft delete timestamp
836
+ /**
837
+ * Initial state for controllable elements when the deck is loaded. Keys are element ids
838
+ * (e.g. "s0-tag", "s0-title", "s1-features-0" or custom ids). Use { visible: false } to
839
+ * start hidden; then call engine.element(id).show() to reveal. Also supports opacity,
840
+ * transform, class, style. Overwritten on load; patchDeck does not reset it.
841
+ *
842
+ * @example
843
+ * "initialElementState": { "s0-tag": { "visible": false }, "s0-title": { "visible": false }, "s0-subtitle": { "visible": false } }
844
+ *
845
+ * @see ElementState
846
+ * @see Lumina.element
847
+ * @see getElementIds
848
+ */
849
+ initialElementState?: InitialElementState;
850
+ /**
851
+ * Element control defaults. Can be set in deck JSON so the deck is self-contained.
852
+ * defaultVisible: false = start all elements hidden; override per-id via initialElementState.
853
+ */
854
+ elementControl?: { defaultVisible?: boolean };
855
+ [key: string]: any;
856
+ }
857
+
858
+ /**
859
+ * Complete deck object. Primary input to `engine.load(deck)`.
860
+ *
861
+ * @description
862
+ * Must have `meta.title` and `slides` (array of slide objects). meta.initialElementState and
863
+ * meta.elementControl enable reveal-on-demand. meta.effects overrides animation (see LuminaAnimationOptions).
864
+ *
865
+ * @example
866
+ * { meta: { title: "Demo", initialElementState: { "s0-title": { visible: false } } }, slides: [{ type: "statement", title: "Hi" }] }
867
+ *
868
+ * @see Lumina.load
869
+ * @see DeckMeta
870
+ * @see BaseSlideData
871
+ */
872
+ export interface Deck {
873
+ meta: DeckMeta;
874
+ slides: BaseSlideData[];
875
+ /** Theme preset name or custom ThemeConfig object. */
876
+ theme?: string | ThemeConfig;
877
+ }
878
+
879
+ // --- Configuration ---
880
+
881
+ // ============================================================================
882
+ // THEME CONFIGURATION TYPES
883
+ // Comprehensive design token system for full customization
884
+ // ============================================================================
885
+
886
+ /**
887
+ * Color palette configuration.
888
+ * All values should be Hex codes (e.g. '#EF4444') or valid CSS colors.
889
+ */
890
+ export interface ThemeColors {
891
+ // --- Core Colors ---
892
+ /** Main brand/accent color. Used for highlights, buttons, links. */
893
+ primary?: string;
894
+ /** Secondary brand color. Used for complementary elements. */
895
+ secondary?: string;
896
+ /** Tertiary/accent color. Used for special highlights. */
897
+ accent?: string;
898
+ /** Main background color. */
899
+ background?: string;
900
+ /** Elevated surface color (cards, panels, modals). */
901
+ surface?: string;
902
+ /** Primary text color. */
903
+ text?: string;
904
+ /** Secondary/subdued text color. */
905
+ textSecondary?: string;
906
+ /** Muted/disabled text color. */
907
+ muted?: string;
908
+
909
+ // --- Semantic Colors ---
910
+ /** Success state color (green tones). */
911
+ success?: string;
912
+ /** Warning state color (yellow/amber tones). */
913
+ warning?: string;
914
+ /** Danger/error state color (red tones). */
915
+ danger?: string;
916
+ /** Informational state color (blue tones). */
917
+ info?: string;
918
+
919
+ // --- UI Element Colors ---
920
+ /** Border color for containers, cards, inputs. */
921
+ border?: string;
922
+ /** Subtle border color for dividers. */
923
+ borderSubtle?: string;
924
+ /** Shadow color (used with rgba). */
925
+ shadow?: string;
926
+ /** Overlay color for modals, backdrops. */
927
+ overlay?: string;
928
+ /** Highlight/selection color. */
929
+ highlight?: string;
930
+
931
+ // --- Interactive Element Colors ---
932
+ /** Primary button background. */
933
+ buttonPrimary?: string;
934
+ /** Primary button text. */
935
+ buttonPrimaryText?: string;
936
+ /** Secondary button background. */
937
+ buttonSecondary?: string;
938
+ /** Secondary button text. */
939
+ buttonSecondaryText?: string;
940
+ /** Link color. */
941
+ link?: string;
942
+ /** Link hover color. */
943
+ linkHover?: string;
944
+
945
+ // --- Gradient Colors ---
946
+ /** Gradient start color. */
947
+ gradientFrom?: string;
948
+ /** Gradient middle color (optional). */
949
+ gradientVia?: string;
950
+ /** Gradient end color. */
951
+ gradientTo?: string;
952
+ }
953
+
954
+ /**
955
+ * Typography configuration.
956
+ */
957
+ export interface ThemeTypography {
958
+ /** Font family definitions. */
959
+ fontFamily?: {
960
+ /** Font for headings (h1-h6, titles). */
961
+ heading?: string;
962
+ /** Font for body text, paragraphs. */
963
+ body?: string;
964
+ /** Font for code, monospace text. */
965
+ mono?: string;
966
+ };
967
+ /** Font size scale. Values should be CSS size values (rem, px, etc.). */
968
+ fontSize?: {
969
+ /** Extra small: 0.75rem / 12px */
970
+ xs?: string;
971
+ /** Small: 0.875rem / 14px */
972
+ sm?: string;
973
+ /** Base: 1rem / 16px */
974
+ base?: string;
975
+ /** Large: 1.125rem / 18px */
976
+ lg?: string;
977
+ /** Extra large: 1.25rem / 20px */
978
+ xl?: string;
979
+ /** 2XL: 1.5rem / 24px */
980
+ '2xl'?: string;
981
+ /** 3XL: 1.875rem / 30px */
982
+ '3xl'?: string;
983
+ /** 4XL: 2.25rem / 36px */
984
+ '4xl'?: string;
985
+ /** 5XL: 3rem / 48px */
986
+ '5xl'?: string;
987
+ /** 6XL: 3.75rem / 60px */
988
+ '6xl'?: string;
989
+ /** 7XL: 4.5rem / 72px */
990
+ '7xl'?: string;
991
+ };
992
+ /** Font weight scale. Values should be numeric (100-900). */
993
+ fontWeight?: {
994
+ /** Light weight: 300 */
995
+ light?: number;
996
+ /** Normal weight: 400 */
997
+ normal?: number;
998
+ /** Medium weight: 500 */
999
+ medium?: number;
1000
+ /** Semibold weight: 600 */
1001
+ semibold?: number;
1002
+ /** Bold weight: 700 */
1003
+ bold?: number;
1004
+ /** Extra bold weight: 800 */
1005
+ extrabold?: number;
1006
+ };
1007
+ /** Line height scale. Values should be unitless or CSS values. */
1008
+ lineHeight?: {
1009
+ /** Tight: 1.1 */
1010
+ tight?: string;
1011
+ /** Snug: 1.25 */
1012
+ snug?: string;
1013
+ /** Normal: 1.5 */
1014
+ normal?: string;
1015
+ /** Relaxed: 1.625 */
1016
+ relaxed?: string;
1017
+ /** Loose: 2 */
1018
+ loose?: string;
1019
+ };
1020
+ /** Letter spacing scale. Values should be CSS values (em, px). */
1021
+ letterSpacing?: {
1022
+ /** Tighter: -0.05em */
1023
+ tighter?: string;
1024
+ /** Tight: -0.025em */
1025
+ tight?: string;
1026
+ /** Normal: 0 */
1027
+ normal?: string;
1028
+ /** Wide: 0.025em */
1029
+ wide?: string;
1030
+ /** Wider: 0.05em */
1031
+ wider?: string;
1032
+ /** Widest: 0.1em (tracking-widest for labels) */
1033
+ widest?: string;
1034
+ };
1035
+ }
1036
+
1037
+ /**
1038
+ * Spacing scale configuration.
1039
+ * Values should be CSS size values (rem, px, etc.).
1040
+ */
1041
+ export interface ThemeSpacing {
1042
+ /** 0 */
1043
+ none?: string;
1044
+ /** 0.25rem / 4px */
1045
+ xs?: string;
1046
+ /** 0.5rem / 8px */
1047
+ sm?: string;
1048
+ /** 1rem / 16px */
1049
+ md?: string;
1050
+ /** 1.5rem / 24px */
1051
+ lg?: string;
1052
+ /** 2rem / 32px */
1053
+ xl?: string;
1054
+ /** 3rem / 48px */
1055
+ '2xl'?: string;
1056
+ /** 4rem / 64px */
1057
+ '3xl'?: string;
1058
+ /** 6rem / 96px */
1059
+ '4xl'?: string;
1060
+ }
1061
+
1062
+ /**
1063
+ * Border radius scale configuration.
1064
+ * Values should be CSS size values (rem, px, etc.).
1065
+ */
1066
+ export interface ThemeBorderRadius {
1067
+ /** No rounding: 0 */
1068
+ none?: string;
1069
+ /** Small: 0.25rem / 4px */
1070
+ sm?: string;
1071
+ /** Medium: 0.5rem / 8px */
1072
+ md?: string;
1073
+ /** Large: 0.75rem / 12px */
1074
+ lg?: string;
1075
+ /** Extra large: 1rem / 16px */
1076
+ xl?: string;
1077
+ /** 2XL: 1.5rem / 24px */
1078
+ '2xl'?: string;
1079
+ /** 3XL: 2rem / 32px */
1080
+ '3xl'?: string;
1081
+ /** Full circle/pill: 9999px */
1082
+ full?: string;
1083
+ }
1084
+
1085
+ /**
1086
+ * Visual effects configuration.
1087
+ */
1088
+ export interface ThemeEffects {
1089
+ // --- Gradients ---
1090
+ /** Enable/disable gradient effects globally. */
1091
+ useGradients?: boolean;
1092
+ /** Gradient direction. */
1093
+ gradientDirection?: 'to-t' | 'to-b' | 'to-l' | 'to-r' | 'to-tl' | 'to-tr' | 'to-bl' | 'to-br';
1094
+ /** Override gradient start color (uses colors.gradientFrom if not set). */
1095
+ gradientFrom?: string;
1096
+ /** Override gradient via/middle color. */
1097
+ gradientVia?: string;
1098
+ /** Override gradient end color (uses colors.gradientTo if not set). */
1099
+ gradientTo?: string;
1100
+
1101
+ // --- Shadows ---
1102
+ /** Enable/disable shadow effects globally. */
1103
+ useShadows?: boolean;
1104
+ /** Shadow intensity level. */
1105
+ shadowIntensity?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
1106
+ /** Shadow color (supports CSS color). */
1107
+ shadowColor?: string;
1108
+
1109
+ // --- Glass/Blur Effect ---
1110
+ /** Enable/disable glassmorphism effect on panels. */
1111
+ useGlass?: boolean;
1112
+ /** Glass panel opacity (0-1). */
1113
+ glassOpacity?: number;
1114
+ /** Glass blur amount (e.g., "20px"). */
1115
+ glassBlur?: string;
1116
+ /** Glass border opacity (0-1). */
1117
+ glassBorderOpacity?: number;
1118
+
1119
+ // --- Ambient Orb/Glow ---
1120
+ /** Enable/disable ambient orb effect. */
1121
+ useOrb?: boolean;
1122
+ /** Orb opacity (0-1). */
1123
+ orbOpacity?: number;
1124
+ /** Orb blur amount (e.g., "120px"). */
1125
+ orbBlur?: string;
1126
+ /** Orb size (e.g., "60vw"). */
1127
+ orbSize?: string;
1128
+
1129
+ // --- Animations ---
1130
+ /** Enable/disable animations globally. */
1131
+ animationsEnabled?: boolean;
1132
+ /** Default transition duration (e.g., "0.3s"). */
1133
+ transitionDuration?: string;
1134
+ /** Default transition easing (e.g., "ease-out", CSS cubic-bezier). */
1135
+ transitionEasing?: string;
1136
+ /** Hover scale factor (e.g., 1.05 for 5% scale up). */
1137
+ hoverScale?: number;
1138
+ }
1139
+
1140
+ /**
1141
+ * Component-specific theming configuration.
1142
+ */
1143
+ export interface ThemeComponents {
1144
+ // --- Buttons ---
1145
+ /** Button border radius token or CSS value. */
1146
+ buttonRadius?: string;
1147
+ /** Button padding (e.g., "0.75rem 1.5rem"). */
1148
+ buttonPadding?: string;
1149
+ /** Button font weight. */
1150
+ buttonFontWeight?: number;
1151
+ /** Button text transform (uppercase, capitalize, none). */
1152
+ buttonTextTransform?: 'none' | 'uppercase' | 'capitalize';
1153
+
1154
+ // --- Cards/Panels ---
1155
+ /** Card border radius token or CSS value. */
1156
+ cardRadius?: string;
1157
+ /** Card padding. */
1158
+ cardPadding?: string;
1159
+ /** Card border width (e.g., "1px"). */
1160
+ cardBorderWidth?: string;
1161
+ /** Card background (can override surface color). */
1162
+ cardBackground?: string;
1163
+
1164
+ // --- Timeline ---
1165
+ /** Timeline node/dot size (e.g., "1rem"). */
1166
+ timelineNodeSize?: string;
1167
+ /** Timeline connector line width (e.g., "2px"). */
1168
+ timelineLineWidth?: string;
1169
+ /** Timeline node color (uses primary if not set). */
1170
+ timelineNodeColor?: string;
1171
+ /** Timeline line color (uses border if not set). */
1172
+ timelineLineColor?: string;
1173
+
1174
+ // --- Steps ---
1175
+ /** Step number badge size. */
1176
+ stepBadgeSize?: string;
1177
+ /** Step number font size. */
1178
+ stepFontSize?: string;
1179
+
1180
+ // --- Progress Bar ---
1181
+ /** Progress bar height. */
1182
+ progressHeight?: string;
1183
+ /** Progress bar border radius. */
1184
+ progressRadius?: string;
1185
+ /** Progress bar background (track). */
1186
+ progressBackground?: string;
1187
+ /** Progress bar fill color. */
1188
+ progressFill?: string;
1189
+
1190
+ // --- Tags/Badges ---
1191
+ /** Tag padding. */
1192
+ tagPadding?: string;
1193
+ /** Tag border radius. */
1194
+ tagRadius?: string;
1195
+ /** Tag font size. */
1196
+ tagFontSize?: string;
1197
+
1198
+ // --- Input/Form Elements ---
1199
+ /** Input border radius. */
1200
+ inputRadius?: string;
1201
+ /** Input padding. */
1202
+ inputPadding?: string;
1203
+ /** Input border color. */
1204
+ inputBorder?: string;
1205
+ /** Input focus border color. */
1206
+ inputFocusBorder?: string;
1207
+ }
1208
+
1209
+ /**
1210
+ * Configuration for the visual theme.
1211
+ * Comprehensive design token system for full slide customization.
1212
+ *
1213
+ * @example
1214
+ * ```typescript
1215
+ * const theme: ThemeConfig = {
1216
+ * colors: {
1217
+ * primary: '#8b5cf6',
1218
+ * background: '#0a0a0a',
1219
+ * text: '#ffffff'
1220
+ * },
1221
+ * effects: {
1222
+ * useGradients: true,
1223
+ * useGlass: true,
1224
+ * glassBlur: '20px'
1225
+ * }
1226
+ * };
1227
+ * ```
1228
+ */
1229
+ export interface ThemeConfig {
1230
+ /**
1231
+ * Color palette configuration.
1232
+ * Defines all colors used throughout the presentation.
1233
+ */
1234
+ colors?: ThemeColors;
1235
+
1236
+ /**
1237
+ * Typography configuration.
1238
+ * Controls fonts, sizes, weights, and text styling.
1239
+ */
1240
+ typography?: ThemeTypography;
1241
+
1242
+ /**
1243
+ * Spacing scale configuration.
1244
+ * Defines consistent spacing values for gaps, padding, margins.
1245
+ */
1246
+ spacing?: ThemeSpacing;
1247
+
1248
+ /**
1249
+ * Border radius configuration.
1250
+ * Defines rounded corner values for UI elements.
1251
+ */
1252
+ borderRadius?: ThemeBorderRadius;
1253
+
1254
+ /**
1255
+ * Visual effects configuration.
1256
+ * Controls gradients, shadows, glass effects, animations.
1257
+ */
1258
+ effects?: ThemeEffects;
1259
+
1260
+ /**
1261
+ * Component-specific theming.
1262
+ * Fine-grained control over individual component styles.
1263
+ */
1264
+ components?: ThemeComponents;
1265
+
1266
+ // Legacy support (mapped to new structure internally)
1267
+ /** @deprecated Use typography.fontFamily instead */
1268
+ fonts?: {
1269
+ heading?: string;
1270
+ body?: string;
1271
+ mono?: string;
1272
+ };
1273
+ }
1274
+
1275
+ /**
1276
+ * Custom keyboard shortcuts.
1277
+ */
1278
+ export interface LuminaKeyBindings {
1279
+ next: string[];
1280
+ prev: string[];
1281
+ }
1282
+
1283
+ /**
1284
+ * UI Visibility toggles.
1285
+ */
1286
+ export interface LuminaUIOptions {
1287
+ visible?: boolean; // Global UI visibility
1288
+ showProgressBar?: boolean;
1289
+ showSlideCount?: boolean;
1290
+ showControls?: boolean; // Next/Prev buttons
1291
+ }
1292
+
1293
+ /**
1294
+ * Animation configuration. All properties optional. Overrides apply in: options.animation, deck.meta.effects, slide.meta.effects.
1295
+ * Same keys are supported in meta.effects; legacy names (animationDuration→durationIn, etc.) are mapped. See resolveTransitionConfig.
1296
+ *
1297
+ * @description
1298
+ * Use when creating the engine: `new Lumina("#app", { animation: { durationIn: 1.2, revealDelayMs: 500 } })`.
1299
+ * Or in deck/slide JSON: `meta: { effects: { durationIn: 1, revealDelayMs: 400 } }`.
1300
+ *
1301
+ * @example
1302
+ * new Lumina("#app", { animation: { enabled: true, type: "cascade", durationIn: 1, revealDelayMs: 500, elementDuration: 0.6 } });
1303
+ *
1304
+ * @see resolveTransitionConfig
1305
+ * @see ANIMATION_DEFAULTS
1306
+ * @see ResolvedTransitionConfig
1307
+ */
1308
+ export interface LuminaAnimationOptions {
1309
+ /** Global toggle for animations. */
1310
+ enabled?: boolean;
1311
+ /** The style of entry animation for slide elements. */
1312
+ type?: 'fade' | 'slide' | 'zoom' | 'cascade'; // Default: 'cascade'
1313
+ /** Duration of entry animations in seconds. */
1314
+ durationIn?: number;
1315
+ /** Duration of exit animations in seconds. */
1316
+ durationOut?: number;
1317
+ /** Stagger delay between sequential elements in seconds. */
1318
+ stagger?: number;
1319
+ /** GSAP easing string (e.g. 'power3.out'). */
1320
+ ease?: string;
1321
+ /**
1322
+ * If true, run the built-in GSAP reveal entrance on elements with .reveal-* classes.
1323
+ * Default: false. Also settable via meta.effects.entranceReveal.
1324
+ */
1325
+ entranceReveal?: boolean;
1326
+
1327
+ // --- revealInSequence (engine.revealInSequence) ---
1328
+ /** Delay in ms between each element. Default: 400. */
1329
+ revealDelayMs?: number;
1330
+ /** Duration in seconds for each element's reveal. Default: 0.45. */
1331
+ revealDuration?: number;
1332
+ /** GSAP ease for reveal. Default: 'power2.out'. */
1333
+ revealEase?: string;
1334
+
1335
+ // --- element().animate() ---
1336
+ /** Default duration in seconds. Default: 0.5. */
1337
+ elementDuration?: number;
1338
+ /** Default GSAP ease. Default: 'power2.out'. */
1339
+ elementEase?: string;
1340
+
1341
+ // --- Exit / deck leave ---
1342
+ /** Ease for exit and slide leave. Default: 'power2.in'. */
1343
+ easeOut?: string;
1344
+
1345
+ // --- useTransition: entrance (granular) ---
1346
+ /** Image duration = durationIn * this. Default: 1.5. */
1347
+ imageDurationMultiplier?: number;
1348
+ /** Image reveal ease. Default: 'expo.out'. */
1349
+ imageEase?: string;
1350
+ /** Stagger between character spans (split text). Default: 0.015. */
1351
+ characterStagger?: number;
1352
+ /** Character duration = durationIn * this. Default: 0.8. */
1353
+ characterDurationFactor?: number;
1354
+ /** Character ease. Default: 'back.out(2)'. */
1355
+ characterEase?: string;
1356
+ /** Stagger between .reveal-child. Default: 0.05. */
1357
+ childRevealStagger?: number;
1358
+ /** Child duration = durationIn * this. Default: 0.6. */
1359
+ childRevealDurationFactor?: number;
1360
+ /** Timeline start offset for child reveal. Default: 0.2. */
1361
+ childRevealStart?: number;
1362
+ /** Child reveal ease. Default: 'power2.out'. */
1363
+ childRevealEase?: string;
1364
+ /** Fallback duration when type is not elastic. Default: 0.7. */
1365
+ durationInStandard?: number;
1366
+ /** Duration for elastic-up type. Default: 1.2. */
1367
+ durationInElastic?: number;
1368
+ /** Ease for elastic-up. Default: 'elastic.out(1, 0.5)'. */
1369
+ easeElastic?: string;
1370
+ /** Ease for spring type. Default: 'back.out(1.5)'. */
1371
+ easeSpring?: string;
1372
+
1373
+ // --- useTransition: exit ---
1374
+ /** Stagger between chars on exit. Default: 0.005. */
1375
+ exitCharStagger?: number;
1376
+ /** Exit element stagger = stagger * this. Default: 0.5. */
1377
+ exitStaggerFactor?: number;
1378
+ /** Exit image duration = durationOut * this. Default: 1.2. */
1379
+ exitImageDurationFactor?: number;
1380
+ /** Exit child duration = durationOut * this. Default: 0.8. */
1381
+ exitChildDurationFactor?: number;
1382
+
1383
+ // --- LuminaElement opacity transition ---
1384
+ /** CSS transition value for opacity (e.g. '0.35s ease-out'). Used via --lumina-element-opacity-transition. */
1385
+ elementOpacityTransition?: string;
1386
+ }
1387
+
1388
+ /**
1389
+ * Options for `new Lumina(selector, options)`. All properties optional.
1390
+ *
1391
+ * @description
1392
+ * Controls theme (preset or ThemeConfig), loop, navigation, keyboard, touch, UI toggles, animation,
1393
+ * and elementControl (defaultVisible for reveal-on-demand). For animation overrides see LuminaAnimationOptions.
1394
+ *
1395
+ * @example
1396
+ * new Lumina("#app", { theme: "midnight", loop: true, animation: { durationIn: 1.2 } });
1397
+ * new Lumina("#app", { elementControl: { defaultVisible: false } });
1398
+ *
1399
+ * @see Lumina
1400
+ * @see LuminaAnimationOptions
1401
+ */
1402
+ export interface LuminaOptions {
1403
+ /** CSS Selector for the container to mount into (default: '#app' or internal). */
1404
+ selector?: string;
1405
+ /** Whether to loop back to the start after the last slide. */
1406
+ loop?: boolean;
1407
+ /** Enable/Disable keyboard navigation. */
1408
+ navigation?: boolean;
1409
+ /** Enable/Disable keyboard shortcuts. */
1410
+ keyboard?: boolean;
1411
+ /** Enable/Disable touch swipe gestures. */
1412
+ touch?: boolean;
1413
+ /** Enable debug logs. */
1414
+ debug?: boolean;
1415
+ /** Theme configuration object or preset name string. */
1416
+ theme?: ThemeConfig | string;
1417
+ /** UI element toggles. */
1418
+ ui?: LuminaUIOptions;
1419
+ /** Custom key bindings. */
1420
+ keys?: LuminaKeyBindings;
1421
+ /** Animation settings. */
1422
+ animation?: LuminaAnimationOptions;
1423
+ /**
1424
+ * Element control defaults. Use for reveal-on-demand: set defaultVisible to false so all
1425
+ * elements start hidden; override per-id via meta.initialElementState. Then reveal with
1426
+ * engine.element(id).show().
1427
+ */
1428
+ elementControl?: {
1429
+ /** If false, all elements start hidden unless listed in meta.initialElementState. Default: true. */
1430
+ defaultVisible?: boolean;
1431
+ };
1432
+ /** Enable Studio (Page Builder) mode. */
1433
+ studio?: boolean;
1434
+ }
1435
+
1436
+ // --- Events ---
1437
+
1438
+ /**
1439
+ * Supported event names emitted by the engine.
1440
+ */
1441
+ export type LuminaEventType =
1442
+ | 'ready'
1443
+ | 'slideChange'
1444
+ | 'action'
1445
+ | 'error'
1446
+ | 'patch'
1447
+ | 'destroy'
1448
+ | 'navigate'
1449
+ | 'themeChange'
1450
+ | 'speakerNotesOpen'
1451
+ | 'speakerNotesClose'
1452
+ | 'timelineSeek'
1453
+ | 'timelineComplete'
1454
+ | 'revealStart'
1455
+ | 'revealComplete'
1456
+ | 'revealElement'
1457
+ | 'diagram-node-update'
1458
+ | 'studio:update';
1459
+
1460
+ /**
1461
+ * Payload for the 'slideChange' event.
1462
+ */
1463
+ export interface SlideChangePayload {
1464
+ index: number;
1465
+ previousIndex: number;
1466
+ slide: BaseSlideData;
1467
+ }
1468
+
1469
+ /**
1470
+ * Payload for the 'action' event (e.g. button clicks).
1471
+ */
1472
+ export interface ActionPayload {
1473
+ type: string;
1474
+ label?: string;
1475
+ value?: any;
1476
+ origin?: string; // Component ID
1477
+ /** Slide index for slide-update actions */
1478
+ slideIndex?: number;
1479
+ /** Patch object for slide-update actions (e.g., { nodes, edges }) */
1480
+ patch?: Record<string, any>;
1481
+ }
1482
+
1483
+ /** Payload for the 'patch' event. Fired when engine.patch(diff) is called. */
1484
+ export interface PatchPayload {
1485
+ diff: any;
1486
+ }
1487
+
1488
+ /** Payload for the 'navigate' event. Fired when next(), prev(), or goTo() changes the slide. */
1489
+ export interface NavigatePayload {
1490
+ direction: 'next' | 'prev' | 'goto';
1491
+ fromIndex: number;
1492
+ toIndex: number;
1493
+ }
1494
+
1495
+ /** Payload for the 'themeChange' event. Fired when theme or meta (colors, etc.) is applied. */
1496
+ export interface ThemeChangePayload {
1497
+ theme: string;
1498
+ }
1499
+
1500
+ /** Payload for 'timelineSeek'. Fired when seekTo(progress) is called. */
1501
+ export interface TimelineSeekPayload {
1502
+ progress: number;
1503
+ slideIndex: number;
1504
+ }
1505
+
1506
+ /** Payload for 'timelineComplete'. Fired when playTimeline finishes. */
1507
+ export interface TimelineCompletePayload {
1508
+ slideIndex: number;
1509
+ }
1510
+
1511
+ /** Payload for 'revealStart'. Fired when revealInSequence starts. */
1512
+ export interface RevealStartPayload {
1513
+ slideIndex: number;
1514
+ elementIds: string[];
1515
+ }
1516
+
1517
+ /** Payload for 'revealComplete'. Fired when revealInSequence finishes. */
1518
+ export interface RevealCompletePayload {
1519
+ slideIndex: number;
1520
+ }
1521
+
1522
+ /** Payload for 'revealElement'. Fired for each element as it is revealed in a sequence. */
1523
+ export interface RevealElementPayload {
1524
+ slideIndex: number;
1525
+ elementId: string;
1526
+ index: number;
1527
+ }
1528
+
1529
+ /**
1530
+ * Event name → payload type map. Use with `engine.on(k, (payload) => ...)` for type-safe handlers.
1531
+ * - `ready`: deck loaded. - `slideChange`: active slide changed. - `action`: user action (e.g. button). - `error`: engine error.
1532
+ * - `patch`: deck patched (streaming). - `destroy`: engine destroyed. - `navigate`: next/prev/goTo changed slide.
1533
+ * - `themeChange`: theme or meta applied. - `speakerNotesOpen` / `speakerNotesClose`: speaker window.
1534
+ * - `timelineSeek` / `timelineComplete`: Remotion-style timeline. - `revealStart` / `revealComplete` / `revealElement`: revealInSequence.
1535
+ */
1536
+ export interface LuminaEventMap {
1537
+ /** Fired when a deck is successfully loaded. */
1538
+ ready: Deck;
1539
+ /** Fired when the active slide changes. */
1540
+ slideChange: SlideChangePayload;
1541
+ /** Fired when a user interaction occurs. */
1542
+ action: ActionPayload;
1543
+ /** Fired when an internal or forwarded error occurs. Use engine.emitError(err) to forward. */
1544
+ error: Error;
1545
+ /** Fired when engine.patch(diff) is called. Useful for streaming or analytics. */
1546
+ patch: PatchPayload;
1547
+ /** Fired when engine.destroy() is called. Use for cleanup. */
1548
+ destroy: Record<string, never>;
1549
+ /** Fired when next(), prev(), or goTo() changes the slide. */
1550
+ navigate: NavigatePayload;
1551
+ /** Fired when theme or meta (colors, typography, etc.) is applied. */
1552
+ themeChange: ThemeChangePayload;
1553
+ /** Fired when the speaker notes window is opened. */
1554
+ speakerNotesOpen: Record<string, never>;
1555
+ /** Fired when the speaker notes window is closed. */
1556
+ speakerNotesClose: Record<string, never>;
1557
+ /** Fired when seekTo(progress) is called. */
1558
+ timelineSeek: TimelineSeekPayload;
1559
+ /** Fired when playTimeline(duration) finishes. */
1560
+ timelineComplete: TimelineCompletePayload;
1561
+ /** Fired when revealInSequence starts. */
1562
+ revealStart: RevealStartPayload;
1563
+ /** Fired when revealInSequence finishes. */
1564
+ revealComplete: RevealCompletePayload;
1565
+ /** Fired for each element as it is revealed in revealInSequence. */
1566
+ revealElement: RevealElementPayload;
1567
+ /** Fired to update a diagram node directly (bypasses store -> watch cycle). Internal. */
1568
+ 'diagram-node-update': { slideIndex: number; nodeId: string; key: string; value: any };
1569
+ /** Fired when Studio updates a node (path, value). Internal. */
1570
+ 'studio:update': { path: string; value: any };
1571
+ }
1572
+
1573
+ /**
1574
+ * Key-value store exposed as `engine.data`. Persists across deck loads; not serialized.
1575
+ * Use for preferences, flags, or any app-specific state accessible from the engine or
1576
+ * from components via the injected store (getUserData/setUserData).
1577
+ */
1578
+ export interface LuminaDataStore {
1579
+ get(key: string): unknown;
1580
+ set(key: string, value: unknown): LuminaDataStore;
1581
+ has(key: string): boolean;
1582
+ delete(key: string): boolean;
1583
+ clear(): void;
1584
+ keys(): string[];
1585
+ }
1586
+
1587
+ // --- Speaker Notes ---
1588
+
1589
+ /**
1590
+ * Message payload for Speaker Notes cross-window synchronization.
1591
+ * Used by BroadcastChannel for bidirectional communication.
1592
+ */
1593
+ export interface SpeakerSyncPayload {
1594
+ /** Action type for the sync message. */
1595
+ action: 'goto' | 'next' | 'prev' | 'state' | 'ping' | 'pong' | 'close';
1596
+ /** Current slide index (for 'goto' and 'state'). */
1597
+ index?: number;
1598
+ /** Total number of slides in the deck. */
1599
+ totalSlides?: number;
1600
+ /** Notes content for current slide. */
1601
+ currentNotes?: string;
1602
+ /** Preview info for the next slide. */
1603
+ nextSlidePreview?: {
1604
+ title?: string;
1605
+ type?: string;
1606
+ };
1607
+ /** Timestamp to prevent echo loops. */
1608
+ timestamp?: number;
1609
+ /** Channel ID for multi-instance support. */
1610
+ channelId?: string;
1611
+ }