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.
- package/LUMINA_LLM_EXAMPLES.json +234 -0
- package/README.md +18 -18
- package/dist/lumina-slides.js +13207 -12659
- package/dist/lumina-slides.umd.cjs +215 -215
- package/dist/style.css +1 -1
- package/package.json +5 -4
- package/src/App.vue +16 -0
- package/src/animation/index.ts +11 -0
- package/src/animation/registry.ts +126 -0
- package/src/animation/stagger.ts +95 -0
- package/src/animation/types.ts +53 -0
- package/src/components/LandingPage.vue +229 -0
- package/src/components/LuminaDeck.vue +224 -0
- package/src/components/LuminaSpeakerNotes.vue +701 -0
- package/src/components/base/BaseSlide.vue +122 -0
- package/src/components/base/LuminaElement.vue +67 -0
- package/src/components/base/VideoPlayer.vue +204 -0
- package/src/components/layouts/LayoutAuto.vue +71 -0
- package/src/components/layouts/LayoutChart.vue +287 -0
- package/src/components/layouts/LayoutCustom.vue +92 -0
- package/src/components/layouts/LayoutDiagram.vue +253 -0
- package/src/components/layouts/LayoutFeatures.vue +121 -0
- package/src/components/layouts/LayoutFlex.vue +172 -0
- package/src/components/layouts/LayoutFree.vue +62 -0
- package/src/components/layouts/LayoutHalf.vue +127 -0
- package/src/components/layouts/LayoutStatement.vue +74 -0
- package/src/components/layouts/LayoutSteps.vue +106 -0
- package/src/components/layouts/LayoutTimeline.vue +104 -0
- package/src/components/layouts/LayoutVideo.vue +41 -0
- package/src/components/parts/FlexBullets.vue +45 -0
- package/src/components/parts/FlexButton.vue +132 -0
- package/src/components/parts/FlexImage.vue +54 -0
- package/src/components/parts/FlexOrdered.vue +44 -0
- package/src/components/parts/FlexSpacer.vue +13 -0
- package/src/components/parts/FlexStepper.vue +59 -0
- package/src/components/parts/FlexText.vue +29 -0
- package/src/components/parts/FlexTimeline.vue +67 -0
- package/src/components/parts/FlexTitle.vue +39 -0
- package/src/components/parts/LuminaBackground.vue +100 -0
- package/src/components/site/LivePreview.vue +101 -0
- package/src/components/site/SiteApi.vue +301 -0
- package/src/components/site/SiteDashboard.vue +604 -0
- package/src/components/site/SiteDocs.vue +3267 -0
- package/src/components/site/SiteExamples.vue +65 -0
- package/src/components/site/SiteFooter.vue +6 -0
- package/src/components/site/SiteHome.vue +362 -0
- package/src/components/site/SiteNavBar.vue +122 -0
- package/src/components/site/SitePlayground.vue +389 -0
- package/src/components/site/SitePromptBuilder.vue +266 -0
- package/src/components/site/SiteUserMenu.vue +90 -0
- package/src/components/studio/ActionEditor.vue +108 -0
- package/src/components/studio/ArrayEditor.vue +124 -0
- package/src/components/studio/CollapsibleSection.vue +33 -0
- package/src/components/studio/ColorField.vue +22 -0
- package/src/components/studio/EditorCanvas.vue +326 -0
- package/src/components/studio/EditorLayoutFeatures.vue +18 -0
- package/src/components/studio/EditorLayoutFixed.vue +46 -0
- package/src/components/studio/EditorLayoutFlex.vue +133 -0
- package/src/components/studio/EditorLayoutHalf.vue +18 -0
- package/src/components/studio/EditorLayoutStatement.vue +18 -0
- package/src/components/studio/EditorLayoutSteps.vue +18 -0
- package/src/components/studio/EditorLayoutTimeline.vue +18 -0
- package/src/components/studio/EditorNode.vue +89 -0
- package/src/components/studio/FieldEditor.vue +133 -0
- package/src/components/studio/IconPicker.vue +109 -0
- package/src/components/studio/LayerItem.vue +117 -0
- package/src/components/studio/LuminaStudio.vue +30 -0
- package/src/components/studio/SaveSuccessModal.vue +138 -0
- package/src/components/studio/SlideNavigator.vue +373 -0
- package/src/components/studio/SliderField.vue +44 -0
- package/src/components/studio/StudioInspector.vue +595 -0
- package/src/components/studio/StudioJsonEditor.vue +191 -0
- package/src/components/studio/StudioLayers.vue +145 -0
- package/src/components/studio/StudioSettings.vue +514 -0
- package/src/components/studio/StudioSidebar.vue +29 -0
- package/src/components/studio/StudioToolbar.vue +222 -0
- package/src/components/studio/fieldLabels.ts +224 -0
- package/src/components/studio/inspectors/DiagramEdgeEditor.vue +77 -0
- package/src/components/studio/inspectors/DiagramNodeEditor.vue +117 -0
- package/src/components/studio/nodes/StudioDiagramNode.vue +138 -0
- package/src/composables/useAuth.ts +87 -0
- package/src/composables/useEditor.ts +224 -0
- package/src/composables/useElementState.ts +81 -0
- package/src/composables/useFlexLayout.ts +122 -0
- package/src/composables/useKeyboard.ts +45 -0
- package/src/composables/useLumina.ts +32 -0
- package/src/composables/useStudio.ts +87 -0
- package/src/composables/useSwipeNav.ts +53 -0
- package/src/composables/useTransition.ts +373 -0
- package/src/core/Lumina.ts +819 -0
- package/src/core/animationConfig.ts +251 -0
- package/src/core/compression.ts +34 -0
- package/src/core/elementController.ts +170 -0
- package/src/core/elementId.ts +27 -0
- package/src/core/elementResolver.ts +207 -0
- package/src/core/events.ts +53 -0
- package/src/core/fonts.ts +100 -0
- package/src/core/presets.ts +231 -0
- package/src/core/prompts.ts +272 -0
- package/src/core/schema.ts +478 -0
- package/src/core/speaker-channel.ts +250 -0
- package/src/core/store.ts +461 -0
- package/src/core/theme.ts +666 -0
- package/src/core/types.ts +1611 -0
- package/src/directives/vStudio.ts +45 -0
- package/src/index.ts +175 -0
- package/src/main.ts +17 -0
- package/src/router/index.ts +92 -0
- package/src/style/main.css +462 -0
- package/src/utils/deep.ts +127 -0
- package/src/utils/firebase.ts +184 -0
- package/src/utils/streaming.ts +134 -0
- package/src/views/DashboardView.vue +32 -0
- package/src/views/DeckView.vue +289 -0
- package/src/views/HomeView.vue +17 -0
- package/src/views/SiteLayout.vue +21 -0
- package/src/views/StudioView.vue +61 -0
- package/src/vite-env.d.ts +6 -0
- 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>
|
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/).
|