lumina-slides 8.9.5 → 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 +13333 -12801
  4. package/dist/lumina-slides.umd.cjs +217 -217
  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,61 @@
1
+ <template>
2
+ <div class="flex flex-col w-full h-screen bg-black text-white font-sans overflow-hidden">
3
+ <StudioToolbar />
4
+ <div class="flex flex-1 overflow-hidden relative">
5
+ <StudioSidebar />
6
+ <EditorCanvas />
7
+ <div class="w-72 bg-[#111] border-l border-[#333] flex flex-col">
8
+ <StudioInspector />
9
+ </div>
10
+
11
+ <!-- Floating Slide Navigator -->
12
+ <SlideNavigator />
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { onMounted, provide } from 'vue';
19
+ import { useRoute } from 'vue-router';
20
+ import { createStore, StoreKey } from '../core/store';
21
+ import { createEditor, EditorKey } from '../composables/useEditor';
22
+ import { getDeck, initFirebase } from '../utils/firebase';
23
+
24
+ import StudioToolbar from '../components/studio/StudioToolbar.vue';
25
+ import StudioSidebar from '../components/studio/StudioSidebar.vue';
26
+ import StudioInspector from '../components/studio/StudioInspector.vue';
27
+ import EditorCanvas from '../components/studio/EditorCanvas.vue';
28
+ import SlideNavigator from '../components/studio/SlideNavigator.vue';
29
+
30
+ // Initialize isolated Store & provide it
31
+ const store = createStore({ studio: true });
32
+ provide(StoreKey, store);
33
+
34
+ // Initialize Editor State & Provide it
35
+ const editor = createEditor(store);
36
+ provide(EditorKey, editor);
37
+
38
+ const route = useRoute();
39
+
40
+ onMounted(async () => {
41
+ try {
42
+ initFirebase();
43
+ } catch (e) { }
44
+
45
+ const deckId = route.params.id as string;
46
+
47
+ if (deckId) {
48
+ try {
49
+ const deckData = await getDeck(deckId);
50
+ if (deckData) {
51
+ // Initialize editor with loaded deck via proper store method
52
+ store.loadDeck(deckData);
53
+ }
54
+ } catch (e) {
55
+ console.error("Failed to load deck for studio:", e);
56
+ }
57
+ } else {
58
+ // Handle new/empty deck case if needed, or initialized by Dashboard creation
59
+ }
60
+ });
61
+ </script>
@@ -0,0 +1,6 @@
1
+ /// <reference types="vite/client" />
2
+ declare module '*.vue' {
3
+ import type { DefineComponent } from 'vue'
4
+ const component: DefineComponent<{}, {}, any>
5
+ export default component
6
+ }
package/IMPLEMENTATION.md DELETED
@@ -1,418 +0,0 @@
1
- # Implementing lumina-slides
2
-
3
- Guide for integrating the `lumina-slides` library into your application. For developers and **code agents** (Cursor, Copilot, etc.) that write the code that consumes the library.
4
-
5
- **Vanilla JavaScript library:** use `import { Lumina } from "lumina-slides"`, `new Lumina(selector)`, and `engine.load(deck)`. The host app can be vanilla JS, React, or any framework; **Vue is not required**. All control (render, element control, animations, events) is done via the engine API and the deck JSON. The API is designed for vanilla JS.
6
-
7
- ---
8
-
9
- ## 1. Installation
10
-
11
- ```bash
12
- npm install lumina-slides
13
- # Optional, only if you use "chart" slides:
14
- npm install lumina-slides chart.js
15
- ```
16
-
17
- ---
18
-
19
- ## 2. Main imports
20
-
21
- ```ts
22
- import { Lumina, parsePartialJson, generateSystemPrompt, getLuminaJsonSchema } from "lumina-slides";
23
- import "lumina-slides/style.css";
24
- ```
25
-
26
- Other useful exports: `createDebouncedLoader`, `isSlideReady`, `generateThemePrompt`, types `Deck`, `LuminaOptions`, `LuminaEventMap`, etc.
27
-
28
- ---
29
-
30
- ## 3. Create the engine and load a deck
31
-
32
- ```ts
33
- const engine = new Lumina("#app", {
34
- theme: "ocean", // presets: default, ocean, midnight, forest, cyber, latte, sunset, monochrome
35
- loop: true,
36
- navigation: true,
37
- touch: true,
38
- });
39
-
40
- engine.load({
41
- meta: { title: "My presentation" },
42
- slides: [
43
- { type: "statement", title: "Title", subtitle: "Subtitle" },
44
- { type: "features", title: "Features", features: [
45
- { title: "A", desc: "Desc A", icon: "star" },
46
- { title: "B", desc: "Desc B", icon: "zap" },
47
- ]},
48
- ],
49
- });
50
- ```
51
-
52
- ---
53
-
54
- ## 4. Update the deck (patch)
55
-
56
- For incremental updates without reloading:
57
-
58
- ```ts
59
- engine.patch({ meta: { title: "New title" } });
60
- // slides are replaced entirely if provided:
61
- // engine.patch({ slides: [...newSlides] });
62
- ```
63
-
64
- ---
65
-
66
- ## 5. Events
67
-
68
- ```ts
69
- engine.on("ready", (deck) => { /* deck loaded */ });
70
- engine.on("slideChange", ({ index, previousIndex, slide }) => { /* slide changed */ });
71
- engine.on("action", (p) => {
72
- // p: { type, label?, value? } — e.g. button clicks
73
- });
74
- engine.on("error", (err) => { /* internal error */ });
75
- ```
76
-
77
- To unsubscribe: `engine.off("slideChange", handler)` with the same function reference.
78
-
79
- ---
80
-
81
- ## 6. Programmatic navigation
82
-
83
- ```ts
84
- engine.next();
85
- engine.prev();
86
- engine.goTo(3);
87
- const i = engine.currentSlideIndex;
88
- ```
89
-
90
- ---
91
-
92
- ## 7. State for the agent / LLM
93
-
94
- ```ts
95
- const state = engine.exportState();
96
- // { status, currentSlide: { index, id, type, title }, narrative, engagementLevel, history }
97
- // Use in LLM context: "User is on slide X, did Y".
98
- ```
99
-
100
- ---
101
-
102
- ## 8. Streaming from an LLM
103
-
104
- When the model streams JSON in chunks:
105
-
106
- ```ts
107
- import { Lumina, parsePartialJson } from "lumina-slides";
108
-
109
- const engine = new Lumina("#app", { theme: "midnight" });
110
- let buffer = "";
111
-
112
- function onStream(chunk) {
113
- buffer += chunk;
114
- const json = parsePartialJson(buffer);
115
- if (json) engine.load(json);
116
- }
117
- ```
118
-
119
- Or with debounce to reduce flicker:
120
-
121
- ```ts
122
- import { createDebouncedLoader } from "lumina-slides";
123
-
124
- const onChunk = createDebouncedLoader((deck) => engine.load(deck), 80);
125
- // In the stream loop: onChunk(buffer);
126
- ```
127
-
128
- ---
129
-
130
- ## 9. Generate the system prompt for an LLM that creates slides
131
-
132
- If an LLM will generate the deck JSON:
133
-
134
- ```ts
135
- const systemPrompt = generateSystemPrompt({
136
- mode: "fast", // 'reasoning' for CoT before JSON
137
- includeSchema: true,
138
- includeTheming: true,
139
- });
140
- // Use systemPrompt as the system message in OpenAI, Anthropic, etc.
141
- ```
142
-
143
- Theming only: `generateThemePrompt()`.
144
-
145
- ---
146
-
147
- ## 10. JSON Schema
148
-
149
- To validate or pass to the LLM:
150
-
151
- ```ts
152
- const schema = getLuminaJsonSchema();
153
- ```
154
-
155
- ---
156
-
157
- ## 11. Destroy the instance
158
-
159
- When unmounting or leaving the view:
160
-
161
- ```ts
162
- engine.destroy();
163
- ```
164
-
165
- ---
166
-
167
- ## Deck structure
168
-
169
- ```ts
170
- interface Deck {
171
- meta: {
172
- title: string;
173
- author?: string;
174
- theme?: string;
175
- themeConfig?: ThemeConfig;
176
- elementControl?: { defaultVisible?: boolean }; // false = all hidden by default
177
- initialElementState?: { [id: string]: { visible?: boolean; opacity?: number; transform?: string; class?: string; style?: Record<string, string | number> } };
178
- [k: string]: any;
179
- };
180
- slides: Array<
181
- | { type: "statement"; title: string; subtitle?: string; tag?: string; notes?: string }
182
- | { type: "features"; title: string; features: { title: string; desc: string; icon?: string }[]; description?: string; notes?: string }
183
- | { type: "timeline"; title: string; timeline: { date: string; title: string; description: string }[]; subtitle?: string; notes?: string }
184
- | { type: "steps"; title: string; steps: { step: string; title: string; description?: string }[]; notes?: string }
185
- | { type: "half"; imageSide: "left"|"right"; image: string; title: string; paragraphs: string[]; tag?: string; cta?: string; notes?: string }
186
- | { type: "flex"; direction?: "horizontal"|"vertical"; elements: FlexElement[]; gap?: SpacingToken; padding?: SpacingToken; notes?: string }
187
- | { type: "chart"; chartType: "bar"|"line"|"pie"|"doughnut"; data: { labels: string[]; datasets: { label: string; values: number[]; color?: string }[] }; title?: string; subtitle?: string; notes?: string }
188
- | { type: "custom"; html: string; css?: string; notes?: string }
189
- // + diagram, video, etc.
190
- >;
191
- }
192
- ```
193
-
194
- Detailed types in the package: `Deck`, `SlideStatement`, `SlideFeatures`, `LuminaOptions`, `LuminaEventMap`, etc.
195
-
196
- ---
197
-
198
- ## Element IDs and `data-lumina-id` (vanilla JS / JSON API)
199
-
200
- The library assigns a `data-lumina-id` attribute to each controllable element when **rendering the deck from JSON**. You use only `engine.load(deck)` and the engine API; no framework or component code is required.
201
-
202
- **ID resolution order:** (1) `slide.id` for the root; (2) if the object at that path has `id: string`, it is used; (3) `slide.ids[path]` if present; (4) fallback `s{N}-{path}` (e.g. `s0-tag`, `s1-features-2`).
203
-
204
- **Discovering IDs from vanilla JS:** `engine.elements(slideIndex)` returns the array of ids for that slide. To predict ids without mounting: `getElementIds(slide, slideIndex)` or `resolveId(slide, slideIndex, path)` / `elemId(slideIndex, ...path)` (exported for advanced use).
205
-
206
- **Paths per slide type** (default id is `s{N}-{path}`):
207
-
208
- | Type | Paths (e.g. `s0-tag`, `s1-features-0`) |
209
- |----------|----------------------------------------|
210
- | statement| `tag`, `title`, `subtitle` |
211
- | features | `header`, `features.0`, `features.1`… |
212
- | half | `media`, `tag`, `title`, `paragraphs`, `cta` |
213
- | timeline | `title`, `subtitle`, `timeline.0`… |
214
- | steps | `header`, `steps.0`, `steps.1`… |
215
- | flex | `elements.0`, `elements.0.elements.0`… |
216
- | chart | `title`, `subtitle`, `chart` |
217
- | video | `video`, `title` |
218
- | diagram | `nodes.0`, `nodes.1`…, `edges.0`… |
219
- | custom | (none by default) |
220
-
221
- **Control from JSON:**
222
-
223
- - `meta.initialElementState`: `{ [id]: { visible?: false, opacity?, transform?, class?, style? } }` — initial state when the deck loads.
224
- - `meta.elementControl`: `{ defaultVisible?: boolean }` — if `false`, all elements start hidden; override per id with `initialElementState`.
225
-
226
- **APIs (vanilla JS):** `engine.element(id)`, `engine.element(slideIndex, path)`, `engine.getElementById(id)`, `engine.elements(slideIndex)`.
227
-
228
- ---
229
-
230
- ## Element control and animations (vanilla JS)
231
-
232
- Use `engine.element(id)` or `engine.element(slideIndex, path)` to get an **ElementController**:
233
-
234
- - **Visibility:** `.show()`, `.hide()`, `.toggle(force?)`
235
- - **Style:** `.opacity(n)`, `.transform(s)`, `.css({ ... })`, `.addClass(name)`, `.removeClass(name)`
236
- - **Animations:** `.animate({ from?, to, duration?, ease?, onComplete? })`, `.animateAsync(...)`
237
-
238
- `engine.getElementById(id)` returns the DOM node with `data-lumina-id`, or `null` if the slide is not mounted. Animations (GSAP) are no-op when the node is null.
239
-
240
- **Avoiding null / no visible effect:** The element must be in the DOM when you call `.show()` or `.animate()`. Pattern in **vanilla JS**:
241
-
242
- 1. **Register `ready` and `slideChange` before `engine.load(deck)`** so callbacks run when the deck/slide is mounted.
243
-
244
- 2. **In `ready`:** use `setTimeout` (e.g. `(i + 1) * 700` ms per element) before each `engine.element(id).show()` for the first slide.
245
-
246
- 3. **In `slideChange`:** when `index` is the slide with controlled elements, repeat the same `setTimeout` + `.show()` for that slide’s ids.
247
-
248
- 4. **Order:** `new Lumina(...)` → `engine.on('ready', ...)` → `engine.on('slideChange', ...)` → `engine.load(deck)`.
249
-
250
- **Example (vanilla JS) — simple reveal with delay:**
251
-
252
- ```js
253
- const engine = new Lumina("#app", { theme: "midnight" });
254
-
255
- engine.on("ready", (deck) => {
256
- const ids = engine.elements(0);
257
- ids.forEach((id, i) => {
258
- setTimeout(() => engine.element(id).show(), (i + 1) * 500);
259
- });
260
- });
261
-
262
- engine.on("slideChange", ({ index }) => {
263
- const ids = engine.elements(index);
264
- ids.forEach((id, i) => {
265
- setTimeout(() => engine.element(id).show(), (i + 1) * 500);
266
- });
267
- });
268
-
269
- engine.load({
270
- meta: {
271
- title: "Reveal",
272
- elementControl: { defaultVisible: false },
273
- initialElementState: { "s0-tag": { visible: false }, "s0-title": { visible: false }, "s0-subtitle": { visible: false } },
274
- },
275
- slides: [{ type: "statement", title: "Hello", subtitle: "World", tag: "Intro" }],
276
- });
277
- ```
278
-
279
- ---
280
-
281
- ## Delay-controlled animations (vanilla JS, no Vue)
282
-
283
- Staggered reveals and per-element animations are driven from vanilla JS using `setTimeout`, `engine.element(id).show()`, and `engine.element(id).animate()`. Register `ready` and `slideChange` **before** `engine.load(deck)`.
284
-
285
- **Helper to reveal a slide’s elements with delay and optional animation:**
286
-
287
- ```js
288
- /**
289
- * Reveal all elements of a slide with staggered delay. Pure JavaScript, no Vue.
290
- * @param {import('lumina-slides').Lumina} engine
291
- * @param {number} slideIndex
292
- * @param {Object} [opts]
293
- * @param {number} [opts.delayMs=500] - Delay between each element (ms).
294
- * @param {boolean} [opts.animate=true] - If true, animate from { opacity: 0, y: 20 } to { opacity: 1, y: 0 }.
295
- * @param {number} [opts.animDuration=0.5] - Animation duration in seconds.
296
- */
297
- function revealSlideWithDelay(engine, slideIndex, opts = {}) {
298
- const { delayMs = 500, animate = true, animDuration = 0.5 } = opts;
299
- const ids = engine.elements(slideIndex);
300
- if (!ids || !ids.length) return;
301
-
302
- ids.forEach((id, i) => {
303
- const t = (i + 1) * delayMs;
304
- setTimeout(() => {
305
- const el = engine.element(id);
306
- el.show();
307
- if (animate) {
308
- el.animate({
309
- from: { opacity: 0, y: 20 },
310
- to: { opacity: 1, y: 0 },
311
- duration: animDuration,
312
- ease: "power2.out",
313
- });
314
- }
315
- }, t);
316
- });
317
- }
318
- ```
319
-
320
- **Full HTML + vanilla JS example (delay-controlled animations):**
321
-
322
- ```html
323
- <!DOCTYPE html>
324
- <html lang="en">
325
- <head>
326
- <meta charset="UTF-8" />
327
- <title>Lumina – delay-controlled animations (vanilla JS)</title>
328
- <link rel="stylesheet" href="node_modules/lumina-slides/dist/style.css" />
329
- </head>
330
- <body>
331
- <div id="app" style="width:100vw;height:100vh;"></div>
332
- <script type="module">
333
- import { Lumina } from "lumina-slides";
334
-
335
- const engine = new Lumina("#app", { theme: "midnight" });
336
-
337
- function revealSlideWithDelay(engine, slideIndex, opts = {}) {
338
- const { delayMs = 500, animate = true, animDuration = 0.5 } = opts;
339
- const ids = engine.elements(slideIndex);
340
- if (!ids || !ids.length) return;
341
- ids.forEach((id, i) => {
342
- setTimeout(() => {
343
- engine.element(id).show();
344
- if (animate) {
345
- engine.element(id).animate({
346
- from: { opacity: 0, y: 20 },
347
- to: { opacity: 1, y: 0 },
348
- duration: animDuration,
349
- ease: "power2.out",
350
- });
351
- }
352
- }, (i + 1) * delayMs);
353
- });
354
- }
355
-
356
- // Must register before load so callbacks run when DOM is ready
357
- engine.on("ready", () => revealSlideWithDelay(engine, 0, { delayMs: 600, animDuration: 0.5 }));
358
- engine.on("slideChange", ({ index }) => revealSlideWithDelay(engine, index, { delayMs: 600, animDuration: 0.5 }));
359
-
360
- engine.load({
361
- meta: {
362
- title: "Staggered reveal",
363
- elementControl: { defaultVisible: false },
364
- initialElementState: {
365
- "s0-tag": { visible: false },
366
- "s0-title": { visible: false },
367
- "s0-subtitle": { visible: false },
368
- "s1-header": { visible: false },
369
- "s1-features-0": { visible: false },
370
- "s1-features-1": { visible: false },
371
- },
372
- },
373
- slides: [
374
- { type: "statement", title: "Welcome", subtitle: "Each line appears with a delay", tag: "Intro" },
375
- { type: "features", title: "Highlights", features: [
376
- { title: "First", desc: "Revealed after 600ms", icon: "star" },
377
- { title: "Second", desc: "Revealed after 1200ms", icon: "zap" },
378
- ]},
379
- ],
380
- });
381
- </script>
382
- </body>
383
- </html>
384
- ```
385
-
386
- **Animation-only (no show/hide), with custom stagger:**
387
-
388
- ```js
389
- engine.on("ready", () => {
390
- const ids = engine.elements(0);
391
- ids.forEach((id, i) => {
392
- setTimeout(() => {
393
- engine.element(id).animate({
394
- from: { opacity: 0, scale: 0.95 },
395
- to: { opacity: 1, scale: 1 },
396
- duration: 0.6,
397
- ease: "back.out(1.2)",
398
- });
399
- }, i * 400);
400
- });
401
- });
402
- ```
403
-
404
- ---
405
-
406
- ## Animation options (engine and deck)
407
-
408
- - **Engine options:** `options.animation: { enabled?: boolean, type?: 'fade'|'cascade'|'zoom', durationIn?, durationOut? }` when creating `new Lumina(selector, options)`.
409
- - **Slide transitions** are applied by the engine; you listen to `slideChange` for timing.
410
- - **Per-element animations** are done with `engine.element(id).animate({ from: { opacity: 0, y: 20 }, to: { opacity: 1, y: 0 }, duration: 0.5 })` from vanilla JS.
411
-
412
- ---
413
-
414
- ## Where to go next
415
-
416
- - **API and types:** JSDoc in the code and generated `.d.ts`.
417
- - **Guide for agents that generate slides:** [AGENTS.md](./AGENTS.md).
418
- - **Docs and playground:** [lumina-slides](https://pailletjuanpablo.github.io/lumina-slides/).