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,3267 @@
1
+ <template>
2
+ <div class="min-h-screen pt-32 px-6 md:px-12 max-w-8xl mx-auto flex flex-col md:flex-row gap-16">
3
+ <!-- Sidebar Navigation -->
4
+ <aside class="w-full md:w-64 flex-shrink-0 hidden md:block">
5
+ <div class="sticky top-32 space-y-10">
6
+ <div v-for="(group, i) in navigation" :key="i">
7
+ <!-- Group Title -->
8
+ <h3 class="text-xs font-bold uppercase tracking-widest text-white/30 mb-4 pl-3">
9
+ {{ group.title }}
10
+ </h3>
11
+
12
+ <!-- Links -->
13
+ <ul class="space-y-1 relative border-l border-white/5">
14
+ <li v-for="item in group.items" :key="item.id">
15
+ <button @click="activeSection = item.id; scrollToTop()" :class="[
16
+ 'group w-full text-left px-4 py-2 text-sm transition-all duration-300 border-l-2 -ml-[1px]',
17
+ activeSection === item.id
18
+ ? 'border-blue-500 text-white font-medium bg-blue-500/5'
19
+ : 'border-transparent text-white/50 hover:text-white hover:border-white/20'
20
+ ]">
21
+ {{ item.label }}
22
+ </button>
23
+ </li>
24
+ </ul>
25
+ </div>
26
+ </div>
27
+ </aside>
28
+
29
+ <!-- Main Content Area -->
30
+ <main class="flex-1 w-full min-w-0 pb-32">
31
+ <Transition name="fade" mode="out-in">
32
+ <div :key="activeSection" class="doc-content max-w-6xl">
33
+
34
+ <!-- MEDIA & VIDEO -->
35
+ <div v-if="activeSection === 'media'">
36
+ <h1>Media & Video</h1>
37
+ <p class="lead">Lumina supports video backgrounds on any slide, and video elements in Half and Flex layouts.</p>
38
+
39
+ <h2>Video object (VideoProperties)</h2>
40
+ <p>Use <code>{ type: "video", src, poster?, autoplay?, loop?, muted?, controls?, className? }</code>. For <strong>backgrounds</strong>, wrap in <code>background: { type: "video", src, ... }</code> and optionally set <code>opacity</code> on the slide. For <strong>Half</strong>, use <code>video: { type: "video", src, autoplay?, muted?, loop? }</code> instead of <code>image</code>. For <strong>Flex</strong>, use an element <code>{ type: "video", src, controls?, size?, fill?, fit? }</code>.</p>
41
+
42
+ <h2>Video Backgrounds</h2>
43
+ <p>Set <code>background</code> on any slide to a video object. Works on <strong>all layouts</strong>. You can add <code>opacity</code> for an overlay effect.</p>
44
+
45
+ <div class="my-8">
46
+ <LivePreview :initial-code="EXAMPLES.video" />
47
+ </div>
48
+
49
+ <h2>Video Elements</h2>
50
+ <p>In Half and Flex layouts, you can use <code>video</code> instead of <code>image</code> (Half) or a <code>type: "video"</code> element (Flex).</p>
51
+
52
+ <h3>In Half Layout</h3>
53
+ <div class="my-6">
54
+ <LivePreview :initial-code="EXAMPLES.video_half" />
55
+ </div>
56
+
57
+ <h3>In Flex Layout</h3>
58
+ <div class="my-6">
59
+ <LivePreview :initial-code="EXAMPLES.video_flex" />
60
+ </div>
61
+ </div>
62
+
63
+ <!-- INTRODUCTION -->
64
+ <div v-else-if="activeSection === 'intro'">
65
+ <h1>Introduction</h1>
66
+ <p class="lead text-2xl text-white font-light">
67
+ Lumina is a high-performance, Universal Presentation Engine.
68
+ </p>
69
+ <p>
70
+ Unlike traditional tools that trap your content in proprietary file formats, Lumina defines
71
+ presentations as <strong>pure JSON data</strong>. This approach enables programmatic
72
+ generation, easy version control, and dynamic storytelling experiences.
73
+ </p>
74
+
75
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-12 not-prose">
76
+ <div
77
+ class="group p-8 bg-white/[0.02] hover:bg-white/[0.04] rounded-2xl border border-white/5 hover:border-white/10 transition-all duration-500 hover:-translate-y-1 shadow-lg hover:shadow-2xl hover:shadow-blue-500/5">
78
+ <div
79
+ class="w-12 h-12 rounded-full bg-yellow-500/10 flex items-center justify-center mb-6">
80
+ <i class="ph-thin ph-lightning text-xl text-yellow-400"></i>
81
+ </div>
82
+ <h3
83
+ class="text-xl font-bold mb-2 text-white group-hover:text-yellow-400 transition-colors">
84
+ Fast by default</h3>
85
+ <p class="text-sm text-white/50 leading-relaxed">Bundles Vue 3 and GSAP internally to
86
+ ensure instant load times and buttery smooth 60fps animations.</p>
87
+ </div>
88
+
89
+ <div
90
+ class="group p-8 bg-white/[0.02] hover:bg-white/[0.04] rounded-2xl border border-white/5 hover:border-white/10 transition-all duration-500 hover:-translate-y-1 shadow-lg hover:shadow-2xl hover:shadow-blue-500/5">
91
+ <div
92
+ class="w-12 h-12 rounded-full bg-blue-500/10 flex items-center justify-center mb-6">
93
+ <i class="ph-thin ph-code text-xl text-blue-400"></i>
94
+ </div>
95
+ <h3
96
+ class="text-xl font-bold mb-2 text-white group-hover:text-blue-400 transition-colors">
97
+ Declarative JSON</h3>
98
+ <p class="text-sm text-white/50 leading-relaxed">Manage your content as code. Git-diff
99
+ your presentations. Generate decks from APIs.</p>
100
+ </div>
101
+ </div>
102
+ </div>
103
+
104
+ <!-- INSTALLATION -->
105
+ <div v-else-if="activeSection === 'install'">
106
+ <h1>Installation</h1>
107
+ <p>Lumina is available as a Universal NPM package. It works in any JavaScript environment
108
+ (Vanilla, React, Vue, Svelte, etc.). Vue and GSAP are bundled; Chart.js is an optional peer only if you use <code>type: "chart"</code> slides.</p>
109
+
110
+ <div class="not-prose my-8">
111
+ <div class="bg-[#0f0f0f] border border-white/10 rounded-xl overflow-hidden shadow-2xl">
112
+ <div class="flex items-center px-4 py-2 border-b border-white/5 bg-white/[0.02]">
113
+ <div class="flex gap-1.5">
114
+ <div class="w-2.5 h-2.5 rounded-full bg-red-500/50"></div>
115
+ <div class="w-2.5 h-2.5 rounded-full bg-yellow-500/50"></div>
116
+ <div class="w-2.5 h-2.5 rounded-full bg-green-500/50"></div>
117
+ </div>
118
+ <div class="ml-4 text-xs text-white/30 font-mono">Terminal</div>
119
+ </div>
120
+ <div class="p-6 overflow-x-auto space-y-2">
121
+ <code class="font-mono text-sm block">
122
+ <span class="text-purple-400">npm</span> install lumina-slides
123
+ </code>
124
+ <code class="font-mono text-sm text-white/50 block">
125
+ <span class="text-gray-500"># Optional, only for chart slides:</span> npm install chart.js
126
+ </code>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ <h3>Requirements</h3>
132
+ <ul>
133
+ <li><strong>Node.js 18+</strong> for development.</li>
134
+ <li><strong>Vue 3 and GSAP</strong> are bundled; no separate install.</li>
135
+ <li><strong>Chart.js ^4</strong> (optional): install only if your deck uses <code>type: "chart"</code>.</li>
136
+ </ul>
137
+ </div>
138
+
139
+ <!-- SETUP -->
140
+ <div v-else-if="activeSection === 'setup'">
141
+ <h1>Quick Start</h1>
142
+ <p>Import the stylesheet and initialize the engine in your main entry file.
143
+ </p>
144
+
145
+ <pre class="language-js"><code><span class="text-purple-400">import</span> { Lumina } <span class="text-purple-400">from</span> 'lumina-slides';
146
+ <span class="text-purple-400">import</span> 'lumina-slides/style.css';
147
+
148
+ <span class="text-gray-500">// Initialize (theme, loop, navigation, touch, etc.)</span>
149
+ <span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app', {
150
+ theme: <span class="text-green-400">'ocean'</span>,
151
+ loop: <span class="text-yellow-400">true</span>,
152
+ navigation: <span class="text-yellow-400">true</span>,
153
+ touch: <span class="text-yellow-400">true</span>
154
+ });
155
+
156
+ <span class="text-gray-500">// Load your deck</span>
157
+ engine.load(myDeckData);</code></pre>
158
+ </div>
159
+
160
+ <!-- DECK STRUCTURE -->
161
+ <div v-else-if="activeSection === 'deck'">
162
+ <h1>Deck Structure</h1>
163
+ <p>A deck is a JSON object with <code>meta</code> and <code>slides</code>. Both are required.</p>
164
+
165
+ <pre><code>{
166
+ <span class="text-blue-400">"meta"</span>: {
167
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"My Presentation"</span>,
168
+ <span class="text-blue-400">"author"</span>: <span class="text-green-400">"Dev Team"</span>,
169
+ <span class="text-gray-500">// Optional: theme preset or ThemeConfig</span>
170
+ <span class="text-blue-400">"theme"</span>: <span class="text-green-400">"ocean"</span>,
171
+ <span class="text-gray-500">// Optional: start elements hidden (see Element Control)</span>
172
+ <span class="text-blue-400">"initialElementState"</span>: { <span class="text-green-400">"s0-tag"</span>: { <span class="text-blue-400">"visible"</span>: <span class="text-yellow-400">false</span> } },
173
+ <span class="text-blue-400">"elementControl"</span>: { <span class="text-blue-400">"defaultVisible"</span>: <span class="text-yellow-400">false</span> },
174
+ <span class="text-gray-500">// Optional: animation overrides (durationIn, revealDelayMs, etc.)</span>
175
+ <span class="text-blue-400">"effects"</span>: { <span class="text-blue-400">"durationIn"</span>: <span class="text-cyan-400">1</span> }
176
+ },
177
+ <span class="text-blue-400">"slides"</span>: [
178
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"statement"</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Hello"</span>, <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"World"</span> }
179
+ <span class="text-gray-500">// Each slide: type, and props per layout (title, image, timeline, ...)</span>
180
+ ]
181
+ }</code></pre>
182
+
183
+ <h3>Common slide-level properties</h3>
184
+ <p>Any slide can include: <code>sizing</code> (<code>"viewport"</code> | <code>"container"</code>), <code>background</code> (image URL or <code>{ type: "video", src, ... }</code>), <code>notes</code> (speaker notes), <code>class</code>, <code>timelineTracks</code> (for keyframe animations; see <em>Timeline (keyframes)</em>). Layout-specific props (e.g. <code>image</code>, <code>timeline</code>, <code>features</code>) are described in each layout reference.</p>
185
+ </div>
186
+
187
+ <!-- SLIDE LAYOUTS -->
188
+ <div v-else-if="activeSection === 'slides'">
189
+ <h1>Slide Layouts</h1>
190
+ <p>Lumina includes responsive layouts for common scenarios. Each layout has a reference section with a full property table.</p>
191
+
192
+ <div class="grid grid-cols-1 gap-12 mt-12 not-prose">
193
+ <!-- Statement -->
194
+ <div class="p-8 rounded-2xl bg-white/[0.02] border border-white/5 shadow-2xl">
195
+ <div class="flex items-center gap-4 mb-6">
196
+ <div
197
+ class="w-10 h-10 rounded-lg bg-blue-500/20 text-blue-400 flex items-center justify-center font-bold text-lg">
198
+ 1</div>
199
+ <div>
200
+ <h2 class="text-2xl font-bold text-white mb-1">Statement</h2>
201
+ <p class="text-sm text-white/50">High impact text for big ideas. Ideal for
202
+ section headers or punchlines.</p>
203
+ </div>
204
+ </div>
205
+ <div class="prose prose-invert max-w-none prose-pre:bg-black/50 prose-pre:border-none">
206
+ <pre><code>{
207
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"statement"</span>,
208
+ <span class="text-blue-400">"tag"</span>: <span class="text-green-400">"Optional label"</span>,
209
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Hello World"</span>,
210
+ <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"This is a statement slide."</span>
211
+ }</code></pre>
212
+ </div>
213
+ <p class="text-sm text-white/50 mt-4">Properties: <code>title</code> (required), <code>tag</code>, <code>subtitle</code> (optional).</p>
214
+ </div>
215
+
216
+ <!-- Half -->
217
+ <div class="p-8 rounded-2xl bg-white/[0.02] border border-white/5 shadow-2xl">
218
+ <div class="flex items-center gap-4 mb-6">
219
+ <div
220
+ class="w-10 h-10 rounded-lg bg-purple-500/20 text-purple-400 flex items-center justify-center font-bold text-lg">
221
+ 2</div>
222
+ <div>
223
+ <h2 class="text-2xl font-bold text-white mb-1">Half / Split</h2>
224
+ <p class="text-sm text-white/50">Image or video on one side, content on the other. <code>imageSide</code>: <code>left</code> | <code>right</code>. Use <code>image</code> or <code>video</code> (<code>{ type: "video", src, autoplay?, muted?, loop?, controls? }</code>). Optional: <code>tag</code>, <code>cta</code> (button label).</p>
225
+ </div>
226
+ </div>
227
+ <div class="prose prose-invert max-w-none prose-pre:bg-black/50 prose-pre:border-none">
228
+ <pre><code>{
229
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"half"</span>,
230
+ <span class="text-blue-400">"imageSide"</span>: <span class="text-green-400">"right"</span>,
231
+ <span class="text-blue-400">"image"</span>: <span class="text-green-400">"/assets/demo.jpg"</span>,
232
+ <span class="text-blue-400">"tag"</span>: <span class="text-green-400">"Optional"</span>,
233
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Split Layout"</span>,
234
+ <span class="text-blue-400">"paragraphs"</span>: [<span class="text-green-400">"Content goes here."</span>],
235
+ <span class="text-blue-400">"cta"</span>: <span class="text-green-400">"Learn More"</span>
236
+ }</code></pre>
237
+ </div>
238
+ </div>
239
+
240
+ <!-- Features -->
241
+ <div class="p-8 rounded-2xl bg-white/[0.02] border border-white/5 shadow-2xl">
242
+ <div class="flex items-center gap-4 mb-6">
243
+ <div
244
+ class="w-10 h-10 rounded-lg bg-yellow-500/20 text-yellow-400 flex items-center justify-center font-bold text-lg">
245
+ 3</div>
246
+ <div>
247
+ <h2 class="text-2xl font-bold text-white mb-1">Features</h2>
248
+ <p class="text-sm text-white/50">A responsive grid of feature cards. Each item: <code>title</code>, <code>desc</code> (or <code>description</code>), <code>icon</code> (e.g. <code>fa-bolt</code>, <code>star</code>, <code>zap</code>); optional <code>class</code>, <code>id</code>. Optional section <code>description</code>.</p>
249
+ </div>
250
+ </div>
251
+ <div class="prose prose-invert max-w-none prose-pre:bg-black/50 prose-pre:border-none">
252
+ <pre><code>{
253
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"features"</span>,
254
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Core Features"</span>,
255
+ <span class="text-blue-400">"description"</span>: <span class="text-green-400">"Optional"</span>,
256
+ <span class="text-blue-400">"features"</span>: [
257
+ { <span class="text-blue-400">"class"</span>: <span class="text-green-400">""</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Fast"</span>, <span class="text-blue-400">"desc"</span>: <span class="text-green-400">"60fps"</span>, <span class="text-blue-400">"icon"</span>: <span class="text-green-400">"fa-bolt"</span> }
258
+ ]
259
+ }</code></pre>
260
+ </div>
261
+ </div>
262
+
263
+ <!-- Timeline -->
264
+ <div class="p-8 rounded-2xl bg-white/[0.02] border border-white/5 shadow-2xl">
265
+ <div class="flex items-center gap-4 mb-6">
266
+ <div
267
+ class="w-10 h-10 rounded-lg bg-green-500/20 text-green-400 flex items-center justify-center font-bold text-lg">
268
+ 4</div>
269
+ <div>
270
+ <h2 class="text-2xl font-bold text-white mb-1">Timeline</h2>
271
+ <p class="text-sm text-white/50">Chronological events in a vertical layout. Each item: <code>date</code>, <code>title</code>, <code>description</code>; optional <code>icon</code>, <code>id</code>.</p>
272
+ </div>
273
+ </div>
274
+ <div class="prose prose-invert max-w-none prose-pre:bg-black/50 prose-pre:border-none">
275
+ <pre><code>{
276
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"timeline"</span>,
277
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"History"</span>,
278
+ <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"Optional"</span>,
279
+ <span class="text-blue-400">"timeline"</span>: [
280
+ { <span class="text-blue-400">"date"</span>: <span class="text-green-400">"2024"</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Launch"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"V1 Released"</span> }
281
+ ]
282
+ }</code></pre>
283
+ </div>
284
+ </div>
285
+
286
+ <!-- Flex -->
287
+ <div class="p-8 rounded-2xl bg-white/[0.02] border border-white/5 shadow-2xl">
288
+ <div class="flex items-center gap-4 mb-6">
289
+ <div
290
+ class="w-10 h-10 rounded-lg bg-cyan-500/20 text-cyan-400 flex items-center justify-center font-bold text-lg">
291
+ 5</div>
292
+ <div>
293
+ <h2 class="text-2xl font-bold text-white mb-1">Flex</h2>
294
+ <p class="text-sm text-white/50">Flow-based layout with size tokens (<code>half</code>, <code>third</code>, …). Elements: <code>title</code>, <code>text</code>, <code>bullets</code>, <code>image</code>, <code>video</code>, <code>button</code>, <code>content</code>, <code>timeline</code>, <code>stepper</code>, <code>spacer</code>. See <em>Flex</em> in Layouts.</p>
295
+ </div>
296
+ </div>
297
+ <div class="prose prose-invert max-w-none prose-pre:bg-black/50 prose-pre:border-none">
298
+ <pre><code>{
299
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"flex"</span>,
300
+ <span class="text-blue-400">"direction"</span>: <span class="text-green-400">"horizontal"</span>,
301
+ <span class="text-blue-400">"elements"</span>: [
302
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"image"</span>, <span class="text-blue-400">"src"</span>: <span class="text-green-400">"/img.jpg"</span>, <span class="text-blue-400">"size"</span>: <span class="text-green-400">"half"</span>, <span class="text-blue-400">"fill"</span>: <span class="text-yellow-400">true</span> },
303
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"content"</span>, <span class="text-blue-400">"size"</span>: <span class="text-green-400">"half"</span>, <span class="text-blue-400">"elements"</span>: [
304
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"title"</span>, <span class="text-blue-400">"text"</span>: <span class="text-green-400">"Hello"</span> },
305
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"bullets"</span>, <span class="text-blue-400">"items"</span>: [<span class="text-green-400">"A"</span>, <span class="text-green-400">"B"</span>] }
306
+ ]}
307
+ ]
308
+ }</code></pre>
309
+ </div>
310
+ </div>
311
+
312
+ <p class="text-white/60 mt-8">Also: <strong>Chart</strong> (<code>bar</code>, <code>line</code>, <code>pie</code>, <code>doughnut</code>), <strong>Custom HTML</strong>, <strong>Video</strong> (full-screen), <strong>Diagram</strong> (Vue Flow nodes/edges), <strong>Free</strong> (absolutely positioned text/image/box with <code>timelineTracks</code>). See the Layouts section for property tables.</p>
313
+ </div>
314
+ </div>
315
+
316
+
317
+ <!-- SIZING / EMBEDDING -->
318
+ <div v-else-if="activeSection === 'sizing'">
319
+ <h1>Embedding & Sizing</h1>
320
+ <p>By default, Lumina slides take up the full viewport height (<code>100vh</code>) to create an
321
+ immersive experience.</p>
322
+ <p>However, if you want to embed a slide inside a smaller container (e.g., a dashboard widget or
323
+ a blog post card), you can change the sizing mode.</p>
324
+
325
+ <h3>Container Mode</h3>
326
+ <p>Set the <code>sizing</code> property to <code>'container'</code> in your slide data. This
327
+ tells the engine to respect the parent container's height instead of forcing full screen.
328
+ </p>
329
+
330
+ <pre><code>{
331
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"timeline"</span>,
332
+ <span class="text-blue-400">"sizing"</span>: <span class="text-green-400">"container"</span>, <span class="text-gray-500">// Default is "viewport"</span>
333
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Project History"</span>,
334
+ <span class="text-blue-400">"timeline"</span>: [...]
335
+ }</code></pre>
336
+
337
+ <div class="mt-8 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
338
+ <h4 class="text-yellow-400 font-bold mb-2"><i class="ph-thin ph-lightbulb mr-2"></i>Pro Tip
339
+ </h4>
340
+ <p class="text-sm text-yellow-200/80 m-0">When using 'container' mode, ensure the parent
341
+ element has a defined height (e.g., <code>h-96</code> or fixed pixels), otherwise the
342
+ slide content might collapse or expand unexpectedly.</p>
343
+ </div>
344
+ </div>
345
+
346
+ <!-- CONFIGURATION -->
347
+ <div v-else-if="activeSection === 'config'">
348
+ <h1>Configuration</h1>
349
+ <p>Pass options to <code>new Lumina(selector, options)</code>. All properties are optional.</p>
350
+
351
+ <h3>Core</h3>
352
+ <ul>
353
+ <li><code>theme</code> — Preset name (<code>default</code>, <code>ocean</code>, <code>midnight</code>, <code>forest</code>, <code>cyber</code>, <code>latte</code>, <code>sunset</code>, <code>monochrome</code>) or a <code>ThemeConfig</code> object.</li>
354
+ <li><code>loop</code> — Loop back to the first slide after the last. Default: <code>false</code>.</li>
355
+ <li><code>navigation</code>, <code>keyboard</code>, <code>touch</code> — Enable/disable navigation. Defaults: <code>true</code>.</li>
356
+ <li><code>selector</code> — Override the mount selector (default from constructor).</li>
357
+ <li><code>debug</code> — Enable debug logs. Default: <code>false</code>.</li>
358
+ <li><code>studio</code> — Enable Studio (page builder) mode. Default: <code>false</code>.</li>
359
+ </ul>
360
+
361
+ <h3>elementControl</h3>
362
+ <p>For reveal-on-demand: <code>elementControl: { defaultVisible: false }</code> makes all elements start hidden. Override per id with <code>meta.initialElementState</code>. See <em>Element Control</em>.</p>
363
+
364
+ <h3>animation</h3>
365
+ <p>Override timings and eases. Same keys can be set in <code>deck.meta.effects</code> or <code>slide.meta.effects</code>; precedence: slide &gt; meta &gt; options &gt; defaults.</p>
366
+ <ul>
367
+ <li><code>enabled</code>, <code>type</code> (<code>fade</code> | <code>slide</code> | <code>zoom</code> | <code>cascade</code>), <code>durationIn</code>, <code>durationOut</code>, <code>stagger</code>, <code>ease</code>, <code>easeOut</code>, <code>entranceReveal</code></li>
368
+ <li><code>revealDelayMs</code>, <code>revealDuration</code>, <code>revealEase</code> — for <code>revealInSequence</code></li>
369
+ <li><code>elementDuration</code>, <code>elementEase</code> — for <code>element(id).animate()</code></li>
370
+ </ul>
371
+
372
+ <h3>ui</h3>
373
+ <pre><code>ui: {
374
+ <span class="text-blue-400">visible</span>: <span class="text-yellow-400">true</span>,
375
+ <span class="text-blue-400">showProgressBar</span>: <span class="text-yellow-400">true</span>,
376
+ <span class="text-blue-400">showSlideCount</span>: <span class="text-yellow-400">true</span>,
377
+ <span class="text-blue-400">showControls</span>: <span class="text-yellow-400">true</span> <span class="text-gray-500">// Next/Prev arrows</span>
378
+ }</code></pre>
379
+
380
+ <h3>keys</h3>
381
+ <p>Custom key bindings: <code>keys: { next: ['ArrowRight',' '], prev: ['ArrowLeft'] }</code>.</p>
382
+ </div>
383
+
384
+ <!-- EVENTS -->
385
+ <div v-else-if="activeSection === 'events'">
386
+ <h1>Events & API</h1>
387
+ <p>Subscribe with <code>engine.on(event, handler)</code>; unsubscribe with <code>engine.off(event, handler)</code> using the same function reference.</p>
388
+
389
+ <h3>ready</h3>
390
+ <p>Fired when a deck is successfully loaded. Payload: the <code>Deck</code> object. Register <code>ready</code> (and <code>slideChange</code>) <strong>before</strong> <code>engine.load(deck)</code> when using element control or <code>revealInSequence</code>.</p>
391
+
392
+ <h3>slideChange</h3>
393
+ <p>Fired when the active slide changes. Payload: <code>{ index, previousIndex, slide }</code>.</p>
394
+
395
+ <h3>action</h3>
396
+ <p>Fired on user interactions (e.g. button clicks). Payload: <code>{ type, label?, value?, origin? }</code>. For buttons, <code>type</code> is often <code>"button"</code>; <code>label</code> and <code>value</code> (or <code>action</code>) come from the element. For <code>slide-update</code> (e.g. diagram): <code>slideIndex</code>, <code>patch</code> (e.g. <code>{ nodes, edges }</code>).</p>
397
+
398
+ <h3>error</h3>
399
+ <p>Fired when an internal error occurs. Payload: <code>Error</code>. Use <code>engine.emitError(err)</code> to forward errors from your app.</p>
400
+
401
+ <h3>Additional events</h3>
402
+ <p>Lifecycle and features: <code>patch</code> (<code>{ diff }</code> when <code>engine.patch()</code>), <code>destroy</code> (on <code>engine.destroy()</code>), <code>navigate</code> (<code>{ direction, fromIndex, toIndex }</code> when <code>next</code>/<code>prev</code>/<code>goTo</code> change the slide), <code>themeChange</code> (<code>{ theme }</code>), <code>speakerNotesOpen</code>, <code>speakerNotesClose</code>, <code>timelineSeek</code> (<code>{ progress, slideIndex }</code>), <code>timelineComplete</code> (<code>{ slideIndex }</code>), <code>revealStart</code> (<code>{ slideIndex, elementIds }</code>), <code>revealComplete</code> (<code>{ slideIndex }</code>), <code>revealElement</code> (<code>{ slideIndex, elementId, index }</code>).</p>
403
+
404
+ <pre><code>engine.<span class="text-yellow-400">on</span>('ready', (deck) => { <span class="text-gray-500">/* deck loaded */</span> });
405
+ engine.<span class="text-yellow-400">on</span>('slideChange', ({ index, slide }) => {
406
+ console.log(<span class="text-green-400">`Slide ${index}: ${slide.type}`</span>);
407
+ });
408
+ engine.<span class="text-yellow-400">on</span>('action', (p) => {
409
+ if (p.type === <span class="text-green-400">'button'</span> && p.value) window.open(p.value, <span class="text-green-400">'_blank'</span>);
410
+ });
411
+ engine.<span class="text-yellow-400">on</span>('error', (err) => console.error(err));
412
+ engine.<span class="text-yellow-400">on</span>('patch', ({ diff }) => console.log(<span class="text-green-400">'Patched'</span>, diff));
413
+ engine.<span class="text-yellow-400">on</span>('navigate', ({ direction, toIndex }) => console.log(direction, toIndex));</code></pre>
414
+
415
+ <h3>Navigation & state</h3>
416
+ <p><code>engine.next()</code>, <code>engine.prev()</code>, <code>engine.goTo(index)</code>, <code>engine.currentSlideIndex</code>, <code>engine.exportState()</code> (for LLM context: <code>{ status, currentSlide: { index, type, title }, narrative, engagementLevel, history }</code>), <code>engine.destroy()</code>.</p>
417
+ </div>
418
+
419
+ <!-- ELEMENT CONTROL -->
420
+ <div v-else-if="activeSection === 'element-control'">
421
+ <h1>Element Control (Reveal on Demand)</h1>
422
+ <p class="lead">Start with elements hidden and reveal them in sequence: ideal for blank slides that
423
+ fill in as you speak, or for effects driven by <code>engine.element(id)</code>.</p>
424
+
425
+ <h2>How element ids are assigned</h2>
426
+ <p>Each controllable element has a <strong>stable id</strong> used in <code>engine.element(id)</code>, <code>meta.initialElementState</code>, and <code>engine.getElementById(id)</code>. Layouts wrap content in <code>LuminaElement</code> with <code>:id="resolveId(data, slideIndex, path)"</code>, which is written as <code>data-lumina-id</code> in the DOM.</p>
427
+
428
+ <p><strong>Resolution order</strong> (in <code>resolveId</code>):</p>
429
+ <ol class="list-decimal list-inside mb-6 space-y-1 text-white/70">
430
+ <li><strong>Slide root</strong> (path <code>[]</code> or <code>['slide']</code>): <code>slide.id</code> if present, otherwise <code>s{N}-slide</code>.</li>
431
+ <li><strong>Object with <code>id</code></strong>: if the value at that path is an object with <code>id: string</code> (e.g. <code>features[0].id</code>, a diagram node), that <code>id</code> is used.</li>
432
+ <li><strong><code>slide.ids</code></strong>: if the path has a single segment (e.g. <code>tag</code>) and <code>slide.ids.tag</code> exists, that value is used.</li>
433
+ <li><strong>Fallback</strong>: <code>elemId(slideIndex, ...path)</code> → format <code>s{N}-{path0}-{path1}-...</code> (e.g. <code>s0-tag</code>, <code>s1-features-2</code>, <code>s2-elements-0-elements-1</code>).</li>
434
+ </ol>
435
+
436
+ <p><strong>Ids by slide type</strong> (when there is no <code>id</code> or <code>slide.ids</code>):</p>
437
+ <div class="overflow-x-auto mb-6">
438
+ <table class="w-full text-sm border border-white/10 rounded-lg">
439
+ <thead><tr class="border-b border-white/10"><th class="text-left py-2 px-3 text-white/90">Type</th><th class="text-left py-2 px-3 text-white/90">Paths (path → id fallback)</th></tr></thead>
440
+ <tbody class="text-white/70">
441
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">statement</td><td class="py-2 px-3"><code>tag</code>→s{N}-tag, <code>title</code>→s{N}-title, <code>subtitle</code>→s{N}-subtitle</td></tr>
442
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">features</td><td class="py-2 px-3"><code>header</code>→s{N}-header, <code>features.i</code>→s{N}-features-{i}</td></tr>
443
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">half</td><td class="py-2 px-3"><code>media</code>, <code>tag</code>, <code>title</code>, <code>paragraphs</code>, <code>cta</code> → s{N}-media, s{N}-tag, etc.</td></tr>
444
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">timeline</td><td class="py-2 px-3"><code>title</code>, <code>subtitle</code>, <code>timeline.i</code> → s{N}-title, s{N}-subtitle, s{N}-timeline-{i}</td></tr>
445
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">steps</td><td class="py-2 px-3"><code>header</code>, <code>steps.i</code> → s{N}-header, s{N}-steps-{i}</td></tr>
446
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">flex</td><td class="py-2 px-3"><code>elements.i</code> → s{N}-elements-{i}; nested <code>elements.i.elements.j</code> → s{N}-elements-{i}-elements-{j}</td></tr>
447
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">chart</td><td class="py-2 px-3"><code>title</code>, <code>subtitle</code>, <code>chart</code> → s{N}-title, s{N}-subtitle, s{N}-chart</td></tr>
448
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">video</td><td class="py-2 px-3"><code>video</code>, <code>title</code> → s{N}-video, s{N}-title</td></tr>
449
+ <tr class="border-b border-white/5"><td class="py-2 px-3 font-mono">diagram</td><td class="py-2 px-3"><code>nodes.i</code>, <code>edges.i</code>; if the node has <code>id</code>, it is used</td></tr>
450
+ <tr><td class="py-2 px-3 font-mono">free</td><td class="py-2 px-3"><code>elements.i</code> → s{N}-elements-0, s{N}-elements-1, …</td></tr>
451
+ </tbody>
452
+ </table>
453
+ </div>
454
+
455
+ <p>To see a slide's ids at runtime: <code>engine.elements(slideIndex)</code>. To use by path: <code>engine.element(slideIndex, "tag")</code> or <code>engine.element(slideIndex, "features.0")</code>; the id is resolved with the same rules.</p>
456
+
457
+ <h2>Option 1: meta.initialElementState</h2>
458
+ <p>Define in the deck which elements start hidden. Ids follow the pattern
459
+ <code>s{index}-{path}</code> (e.g. <code>s0-tag</code>, <code>s0-title</code>,
460
+ <code>s1-features-0</code>). <code>engine.elements(slideIndex)</code> lists all ids for a slide.
461
+ </p>
462
+
463
+ <pre><code>{
464
+ <span class="text-blue-400">"meta"</span>: {
465
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Reveal on Demand"</span>,
466
+ <span class="text-blue-400">"initialElementState"</span>: {
467
+ <span class="text-green-400">"s0-tag"</span>: { <span class="text-blue-400">"visible"</span>: <span class="text-yellow-400">false</span> },
468
+ <span class="text-green-400">"s0-title"</span>: { <span class="text-blue-400">"visible"</span>: <span class="text-yellow-400">false</span> },
469
+ <span class="text-green-400">"s0-subtitle"</span>: { <span class="text-blue-400">"visible"</span>: <span class="text-yellow-400">false</span> }
470
+ }
471
+ },
472
+ <span class="text-blue-400">"slides"</span>: [
473
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"statement"</span>, <span class="text-blue-400">"tag"</span>: <span class="text-green-400">"…"</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"…"</span>, <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"…"</span> }
474
+ ]
475
+ }</code></pre>
476
+
477
+ <h2>Option 2: elementControl.defaultVisible</h2>
478
+ <p>To hide <strong>all</strong> elements on all slides: pass
479
+ <code>elementControl: { defaultVisible: false }</code> when creating Lumina. Those you set in
480
+ <code>meta.initialElementState</code> override this (e.g. to keep some visible).
481
+ </p>
482
+
483
+ <pre><code>const engine = new Lumina('#app', {
484
+ <span class="text-blue-400">elementControl</span>: { <span class="text-blue-400">defaultVisible</span>: <span class="text-yellow-400">false</span> }
485
+ });
486
+ engine.load(deck);</code></pre>
487
+
488
+ <h2>Automatic reveal in sequence</h2>
489
+ <p>After loading the deck, use <code>engine.element(id).show()</code> (and optionally
490
+ <code>.animate()</code>) to reveal. With <code>ready</code> and <code>setTimeout</code> you can
491
+ chain the sequence:
492
+ </p>
493
+
494
+ <pre><code>engine.<span class="text-yellow-400">on</span>('ready', () => {
495
+ const ids = [<span class="text-green-400">'s0-tag'</span>, <span class="text-green-400">'s0-title'</span>, <span class="text-green-400">'s0-subtitle'</span>];
496
+ ids.forEach((id, i) => {
497
+ setTimeout(() => engine.element(id).show(), (i + 1) * 700);
498
+ });
499
+ });
500
+ engine.load(deck);</code></pre>
501
+
502
+ <h2>Step-by-step: using the controller correctly (avoiding null elements)</h2>
503
+ <p><code>engine.getElementById(id)</code> returns <code>null</code> if the element is not mounted (e.g. its slide is not the current one). If you call <code>engine.element(id).show()</code> or <code>.animate()</code> when the node is null, the library does nothing with GSAP (no-op) and no error is thrown; but the effect is not visible. For reveal to work, the element must exist in the DOM when you call <code>.show()</code>. This is the pattern that works.</p>
504
+
505
+ <p><strong>1. Register <code>ready</code> and <code>slideChange</code> before <code>engine.load(deck)</code>.</strong> That way, when the deck finishes loading or you change slides, your callbacks are already registered.</p>
506
+
507
+ <p><strong>2. In <code>ready</code>:</strong> the first slide is already mounted. Use <code>setTimeout</code> before each <code>engine.element(id).show()</code> to allow time for render and transitions (e.g. 700 ms per element).</p>
508
+
509
+ <p><strong>3. In <code>slideChange</code>:</strong> when entering a slide with controlled elements, that slide's DOM mounts on change. Use <code>setTimeout</code> again before each <code>.show()</code> for that slide.</p>
510
+
511
+ <p><strong>4. Order in your code:</strong> <code>new Lumina(...)</code> → <code>engine.on('ready', ...)</code> → <code>engine.on('slideChange', ...)</code> → <code>engine.load(deck)</code>.</p>
512
+
513
+ <p>Full example (used by the <code>layout-element-control</code> demo):</p>
514
+
515
+ <pre><code>const engine = new Lumina(<span class="text-green-400">'#app'</span>, { <span class="text-blue-400">loop</span>: <span class="text-yellow-400">true</span>, <span class="text-blue-400">navigation</span>: <span class="text-yellow-400">true</span> });
516
+
517
+ <span class="text-gray-500">// 1. ready: first slide mounted; setTimeout so the DOM is ready</span>
518
+ engine.<span class="text-yellow-400">on</span>(<span class="text-green-400">'ready'</span>, () => {
519
+ [<span class="text-green-400">'s0-tag'</span>, <span class="text-green-400">'s0-title'</span>, <span class="text-green-400">'s0-subtitle'</span>].forEach((id, i) => {
520
+ setTimeout(() => engine.element(id).show(), (i + 1) * 700);
521
+ });
522
+ });
523
+
524
+ <span class="text-gray-500">// 2. slideChange: on slide 1, reveal its elements with the same delay</span>
525
+ engine.<span class="text-yellow-400">on</span>(<span class="text-green-400">'slideChange'</span>, ({ index }) => {
526
+ if (index === 1) {
527
+ [<span class="text-green-400">'s1-tag'</span>, <span class="text-green-400">'s1-title'</span>, <span class="text-green-400">'s1-subtitle'</span>].forEach((id, i) => {
528
+ setTimeout(() => engine.element(id).show(), (i + 1) * 700);
529
+ });
530
+ }
531
+ });
532
+
533
+ <span class="text-gray-500">// 3. Load always after the listeners</span>
534
+ engine.load(deck);</code></pre>
535
+
536
+ <p>With this order and the <code>setTimeout</code>s, elements exist when <code>.show()</code> is called and you avoid null references in animations.</p>
537
+
538
+ <p class="not-prose mt-8">
539
+ <router-link to="/deck/layout-element-control"
540
+ class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-blue-500/20 text-blue-400 border border-blue-500/40 hover:bg-blue-500/30 transition font-medium">
541
+ <i class="ph-thin ph-play-circle"></i> View demo: Element Control
542
+ </router-link>
543
+ </p>
544
+ </div>
545
+
546
+ <!-- ANIMATIONS: revealInSequence, presets, stagger -->
547
+ <div v-else-if="activeSection === 'animations'">
548
+ <h1>Animations</h1>
549
+ <p class="lead">Control entrances with <code>revealInSequence</code>, built-in presets, and stagger modes. Use with <code>meta.initialElementState</code> or <code>elementControl.defaultVisible: false</code> to start hidden.</p>
550
+
551
+ <h2>revealInSequence</h2>
552
+ <p>Reveals slide elements one by one with optional stagger and animation. Register <code>ready</code> and <code>slideChange</code> before <code>load</code>; then call <code>engine.revealInSequence(slideIndex?, options)</code>.</p>
553
+
554
+ <pre><code>engine.<span class="text-yellow-400">on</span>(<span class="text-green-400">'ready'</span>, () => engine.revealInSequence(<span class="text-yellow-400">0</span>, { <span class="text-blue-400">delayMs</span>: <span class="text-cyan-400">500</span>, <span class="text-blue-400">preset</span>: <span class="text-green-400">'fadeUp'</span> }));
555
+ engine.<span class="text-yellow-400">on</span>(<span class="text-green-400">'slideChange'</span>, (e) => engine.revealInSequence(e.index, { <span class="text-blue-400">delayMs</span>: <span class="text-cyan-400">400</span> }));
556
+ engine.load(deck);</code></pre>
557
+
558
+ <p><strong>Options:</strong> <code>delayMs</code>, <code>staggerMode</code> (<code>sequential</code> | <code>center-out</code> | <code>ends-in</code> | <code>wave</code> | <code>random</code>), <code>preset</code>, <code>from</code>/<code>to</code>, <code>duration</code>, <code>ease</code>, <code>hideFirst</code>, <code>only</code>, <code>exclude</code>.</p>
559
+
560
+ <h2>Presets</h2>
561
+ <p>Use <code>preset</code> in <code>revealInSequence</code> or <code>element(id).animate({ preset: 'fadeUp' })</code>. Built-in: <code>fadeUp</code>, <code>fadeIn</code>, <code>scaleIn</code>, <code>slideLeft</code>, <code>slideRight</code>, <code>zoomIn</code>, <code>blurIn</code>, <code>spring</code>, <code>drop</code>, <code>fadeOut</code>.</p>
562
+
563
+ <h2>Stagger modes</h2>
564
+ <p><code>staggerMode</code> controls the order in which elements are revealed: <code>sequential</code> (default), <code>center-out</code>, <code>ends-in</code>, <code>wave</code>, <code>random</code>. For <code>random</code>, use <code>randomSeed</code> for deterministic order.</p>
565
+
566
+ <p class="not-prose mt-8 flex flex-wrap gap-4">
567
+ <router-link to="/deck/animation-presets"
568
+ class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-blue-500/20 text-blue-400 border border-blue-500/40 hover:bg-blue-500/30 transition font-medium">
569
+ <i class="ph-thin ph-play-circle"></i> Animation: Presets
570
+ </router-link>
571
+ <router-link to="/deck/animation-stagger"
572
+ class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-blue-500/20 text-blue-400 border border-blue-500/40 hover:bg-blue-500/30 transition font-medium">
573
+ <i class="ph-thin ph-play-circle"></i> Animation: Stagger
574
+ </router-link>
575
+ </p>
576
+ </div>
577
+
578
+ <!-- TIMELINE (Remotion-style keyframes) -->
579
+ <div v-else-if="activeSection === 'timeline-keyframes'">
580
+ <h1>Timeline (Remotion-style keyframes)</h1>
581
+ <p class="lead">Drive element state from a progress 0–1 with <code>slide.timelineTracks</code>. Use <code>engine.seekTo(progress)</code> to scrub or <code>engine.playTimeline(duration)</code> to play. Works with any layout; the <code>free</code> layout is for absolutely positioned storytelling.</p>
582
+
583
+ <h2>timelineTracks</h2>
584
+ <p>Per-slide, define <code>timelineTracks: { [elementId]: { "0": { opacity, x, y, scale, rotate?, visible? }, "0.3": {...}, "1": {...} } }</code>. Keys are progress strings 0–1; values are interpolated linearly. Supported: <code>opacity</code>, <code>x</code>, <code>y</code>, <code>scale</code>, <code>rotate</code>, <code>visible</code>.</p>
585
+
586
+ <pre><code>{
587
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"statement"</span>,
588
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Keyframes"</span>,
589
+ <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"Scrub or play."</span>,
590
+ <span class="text-blue-400">"timelineTracks"</span>: {
591
+ <span class="text-green-400">"s0-tag"</span>: { <span class="text-green-400">"0"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">0</span>, <span class="text-blue-400">y</span>: <span class="text-cyan-400">20</span> }, <span class="text-green-400">"0.2"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">1</span>, <span class="text-blue-400">y</span>: <span class="text-cyan-400">0</span> }, <span class="text-green-400">"1"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">1</span> } },
592
+ <span class="text-green-400">"s0-title"</span>: { <span class="text-green-400">"0"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">0</span>, <span class="text-blue-400">x</span>: <span class="text-cyan-400">-30</span> }, <span class="text-green-400">"0.4"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">1</span>, <span class="text-blue-400">x</span>: <span class="text-cyan-400">0</span> }, <span class="text-green-400">"1"</span>: { <span class="text-blue-400">opacity</span>: <span class="text-cyan-400">1</span> } }
593
+ }
594
+ }</code></pre>
595
+
596
+ <h2>API</h2>
597
+ <ul>
598
+ <li><code>engine.seekTo(progress, slideIndex?)</code> — scrub to 0–1. Updates <code>engine.timelineProgress</code>.</li>
599
+ <li><code>engine.playTimeline(durationSeconds?, slideIndex?)</code> — returns <code>[Promise, cancel]</code>. Plays from 0 to 1 over the given seconds (default 5).</li>
600
+ <li><code>element(id).animateToProgress(progress, keyframes)</code> — apply keyframes for one element manually.</li>
601
+ </ul>
602
+
603
+ <h2>Free layout</h2>
604
+ <p><code>type: "free"</code> with <code>elements: [{ type: "text"|"image"|"box", text?, src?, width?, height?, fontSize?, color?, backgroundColor?, ... }]</code>. Elements are absolutely positioned; <code>x</code>, <code>y</code> in <code>timelineTracks</code> act as translate. Ids: <code>s{N}-elements-0</code>, <code>s{N}-elements-1</code>, etc.</p>
605
+
606
+ <p class="not-prose mt-8">
607
+ <router-link to="/deck/animation-timeline"
608
+ class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-blue-500/20 text-blue-400 border border-blue-500/40 hover:bg-blue-500/30 transition font-medium">
609
+ <i class="ph-thin ph-play-circle"></i> Animation: Timeline (keyframes)
610
+ </router-link>
611
+ </p>
612
+ </div>
613
+
614
+ <!-- THEMING -->
615
+ <div v-else-if="activeSection === 'theming'">
616
+ <h1>Theming</h1>
617
+ <p class="lead">Customize colors, fonts, and visual style with built-in presets or create your
618
+ own theme.</p>
619
+
620
+ <!-- Theme Presets Section -->
621
+ <div class="my-12">
622
+ <h2 class="text-2xl font-bold text-white mb-6">Built-in Presets</h2>
623
+ <p class="text-white/60 mb-8">Lumina comes with 8 beautiful theme presets. Pass the preset
624
+ name as a string to instantly apply a complete look.</p>
625
+
626
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 not-prose">
627
+ <!-- Default Theme -->
628
+ <div
629
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-blue-500/50 transition-all duration-300">
630
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #030303;">
631
+ <span class="text-2xl font-bold" style="color: #3b82f6;">Lumina</span>
632
+ </div>
633
+ <div class="p-4 bg-black/50">
634
+ <div class="flex items-center justify-between mb-2">
635
+ <h3 class="font-bold text-white">default</h3>
636
+ <div class="flex gap-1">
637
+ <span class="w-4 h-4 rounded-full" style="background: #3b82f6;"></span>
638
+ <span class="w-4 h-4 rounded-full" style="background: #030303;"></span>
639
+ </div>
640
+ </div>
641
+ <p class="text-xs text-white/50">Clean blue on dark. Professional and modern.
642
+ </p>
643
+ </div>
644
+ </div>
645
+
646
+ <!-- Ocean Theme -->
647
+ <div
648
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-cyan-500/50 transition-all duration-300">
649
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #0f172a;">
650
+ <span class="text-2xl font-bold" style="color: #06b6d4;">Lumina</span>
651
+ </div>
652
+ <div class="p-4 bg-black/50">
653
+ <div class="flex items-center justify-between mb-2">
654
+ <h3 class="font-bold text-white">ocean</h3>
655
+ <div class="flex gap-1">
656
+ <span class="w-4 h-4 rounded-full" style="background: #06b6d4;"></span>
657
+ <span class="w-4 h-4 rounded-full" style="background: #0f172a;"></span>
658
+ </div>
659
+ </div>
660
+ <p class="text-xs text-white/50">Cyan accents on slate. Calm and focused.</p>
661
+ </div>
662
+ </div>
663
+
664
+ <!-- Midnight Theme -->
665
+ <div
666
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-indigo-500/50 transition-all duration-300">
667
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #000000;">
668
+ <span class="text-2xl font-bold" style="color: #6366f1;">Lumina</span>
669
+ </div>
670
+ <div class="p-4 bg-black/50">
671
+ <div class="flex items-center justify-between mb-2">
672
+ <h3 class="font-bold text-white">midnight</h3>
673
+ <div class="flex gap-1">
674
+ <span class="w-4 h-4 rounded-full" style="background: #6366f1;"></span>
675
+ <span class="w-4 h-4 rounded-full" style="background: #000000;"></span>
676
+ </div>
677
+ </div>
678
+ <p class="text-xs text-white/50">Indigo on pure black. Bold and dramatic.</p>
679
+ </div>
680
+ </div>
681
+
682
+ <!-- Forest Theme -->
683
+ <div
684
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-emerald-500/50 transition-all duration-300">
685
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #022c22;">
686
+ <span class="text-2xl font-bold" style="color: #10b981;">Lumina</span>
687
+ </div>
688
+ <div class="p-4 bg-black/50">
689
+ <div class="flex items-center justify-between mb-2">
690
+ <h3 class="font-bold text-white">forest</h3>
691
+ <div class="flex gap-1">
692
+ <span class="w-4 h-4 rounded-full" style="background: #10b981;"></span>
693
+ <span class="w-4 h-4 rounded-full" style="background: #022c22;"></span>
694
+ </div>
695
+ </div>
696
+ <p class="text-xs text-white/50">Emerald green tones. Natural and fresh.</p>
697
+ </div>
698
+ </div>
699
+
700
+ <!-- Cyber Theme -->
701
+ <div
702
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-pink-500/50 transition-all duration-300">
703
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #18181b;">
704
+ <span class="text-2xl font-bold"
705
+ style="color: #f472b6; font-family: 'Orbitron', sans-serif;">Lumina</span>
706
+ </div>
707
+ <div class="p-4 bg-black/50">
708
+ <div class="flex items-center justify-between mb-2">
709
+ <h3 class="font-bold text-white">cyber</h3>
710
+ <div class="flex gap-1">
711
+ <span class="w-4 h-4 rounded-full" style="background: #f472b6;"></span>
712
+ <span class="w-4 h-4 rounded-full" style="background: #18181b;"></span>
713
+ </div>
714
+ </div>
715
+ <p class="text-xs text-white/50">Neon pink futuristic. Sci-fi vibes.</p>
716
+ </div>
717
+ </div>
718
+
719
+ <!-- Latte Theme -->
720
+ <div
721
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-amber-500/50 transition-all duration-300">
722
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #fffbeb;">
723
+ <span class="text-2xl font-bold"
724
+ style="color: #d97706; font-family: 'Playfair Display', serif;">Lumina</span>
725
+ </div>
726
+ <div class="p-4 bg-black/50">
727
+ <div class="flex items-center justify-between mb-2">
728
+ <h3 class="font-bold text-white">latte</h3>
729
+ <div class="flex gap-1">
730
+ <span class="w-4 h-4 rounded-full" style="background: #d97706;"></span>
731
+ <span class="w-4 h-4 rounded-full" style="background: #fffbeb;"></span>
732
+ </div>
733
+ </div>
734
+ <p class="text-xs text-white/50">Light mode with warm amber. Elegant.</p>
735
+ </div>
736
+ </div>
737
+
738
+ <!-- Sunset Theme -->
739
+ <div
740
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-orange-500/50 transition-all duration-300">
741
+ <div class="h-32 p-6 flex items-center justify-center"
742
+ style="background: linear-gradient(135deg, #1c1917 0%, #292524 100%);">
743
+ <span class="text-2xl font-bold"
744
+ style="background: linear-gradient(to right, #f97316, #f43f5e); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">Lumina</span>
745
+ </div>
746
+ <div class="p-4 bg-black/50">
747
+ <div class="flex items-center justify-between mb-2">
748
+ <h3 class="font-bold text-white">sunset</h3>
749
+ <div class="flex gap-1">
750
+ <span class="w-4 h-4 rounded-full" style="background: #f97316;"></span>
751
+ <span class="w-4 h-4 rounded-full" style="background: #f43f5e;"></span>
752
+ </div>
753
+ </div>
754
+ <p class="text-xs text-white/50">Orange to rose gradient. Warm and vibrant.</p>
755
+ </div>
756
+ </div>
757
+
758
+ <!-- Monochrome Theme -->
759
+ <div
760
+ class="rounded-xl overflow-hidden border border-white/10 group hover:border-white/50 transition-all duration-300">
761
+ <div class="h-32 p-6 flex items-center justify-center" style="background: #000000;">
762
+ <span class="text-2xl font-bold" style="color: #ffffff;">Lumina</span>
763
+ </div>
764
+ <div class="p-4 bg-black/50">
765
+ <div class="flex items-center justify-between mb-2">
766
+ <h3 class="font-bold text-white">monochrome</h3>
767
+ <div class="flex gap-1">
768
+ <span class="w-4 h-4 rounded-full" style="background: #ffffff;"></span>
769
+ <span class="w-4 h-4 rounded-full" style="background: #000000;"></span>
770
+ </div>
771
+ </div>
772
+ <p class="text-xs text-white/50">Pure black and white. Timeless elegance.</p>
773
+ </div>
774
+ </div>
775
+ </div>
776
+ </div>
777
+
778
+ <!-- Using Presets -->
779
+ <div class="my-12">
780
+ <h2 class="text-2xl font-bold text-white mb-6">Using a Preset</h2>
781
+ <p class="text-white/60 mb-4">Pass the preset name as a string to the <code>theme</code>
782
+ option:</p>
783
+
784
+ <pre><code><span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app', {
785
+ theme: <span class="text-green-400">'ocean'</span> <span class="text-gray-500">// 'midnight', 'forest', 'cyber', 'latte', 'sunset', 'monochrome'</span>
786
+ });
787
+
788
+ engine.load(myDeck);</code></pre>
789
+ </div>
790
+
791
+ <!-- CSS Variables -->
792
+ <div class="my-12">
793
+ <h2 class="text-2xl font-bold text-white mb-6">Complete Theme Configuration</h2>
794
+ <p class="text-white/60 mb-8">Lumina v9 introduces 80+ CSS custom properties for full visual
795
+ customization.
796
+ Configure via <code>themeConfig</code> in your deck JSON or engine options.</p>
797
+
798
+ <!-- COLORS SECTION -->
799
+ <div class="mb-12">
800
+ <h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
801
+ <span
802
+ class="w-8 h-8 rounded-lg bg-blue-500/20 flex items-center justify-center text-blue-400">🎨</span>
803
+ Colors (25+ tokens)
804
+ </h3>
805
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
806
+ <table class="w-full text-left text-sm text-gray-400">
807
+ <thead class="bg-white/5 text-white font-bold">
808
+ <tr>
809
+ <th class="p-3 border-b border-white/10">Property</th>
810
+ <th class="p-3 border-b border-white/10">CSS Variable</th>
811
+ <th class="p-3 border-b border-white/10">Description</th>
812
+ </tr>
813
+ </thead>
814
+ <tbody class="divide-y divide-white/5">
815
+ <tr>
816
+ <td colspan="3"
817
+ class="p-2 text-xs text-blue-400 bg-blue-500/5 font-bold">Core
818
+ Colors</td>
819
+ </tr>
820
+ <tr>
821
+ <td class="p-3 font-mono text-green-400">primary</td>
822
+ <td class="p-3 font-mono text-white/50">--lumina-color-primary</td>
823
+ <td class="p-3">Main brand/accent color</td>
824
+ </tr>
825
+ <tr>
826
+ <td class="p-3 font-mono text-green-400">secondary</td>
827
+ <td class="p-3 font-mono text-white/50">--lumina-color-secondary</td>
828
+ <td class="p-3">Complementary brand color</td>
829
+ </tr>
830
+ <tr>
831
+ <td class="p-3 font-mono text-green-400">accent</td>
832
+ <td class="p-3 font-mono text-white/50">--lumina-color-accent</td>
833
+ <td class="p-3">Tertiary/accent highlights</td>
834
+ </tr>
835
+ <tr>
836
+ <td class="p-3 font-mono text-green-400">background</td>
837
+ <td class="p-3 font-mono text-white/50">--lumina-color-background</td>
838
+ <td class="p-3">Main slide background</td>
839
+ </tr>
840
+ <tr>
841
+ <td class="p-3 font-mono text-green-400">surface</td>
842
+ <td class="p-3 font-mono text-white/50">--lumina-color-surface</td>
843
+ <td class="p-3">Cards, panels, elevated surfaces</td>
844
+ </tr>
845
+ <tr>
846
+ <td class="p-3 font-mono text-green-400">text</td>
847
+ <td class="p-3 font-mono text-white/50">--lumina-color-text</td>
848
+ <td class="p-3">Primary text color</td>
849
+ </tr>
850
+ <tr>
851
+ <td class="p-3 font-mono text-green-400">textSecondary</td>
852
+ <td class="p-3 font-mono text-white/50">--lumina-color-text-secondary
853
+ </td>
854
+ <td class="p-3">Secondary body text</td>
855
+ </tr>
856
+ <tr>
857
+ <td class="p-3 font-mono text-green-400">muted</td>
858
+ <td class="p-3 font-mono text-white/50">--lumina-color-muted</td>
859
+ <td class="p-3">Subdued text, captions</td>
860
+ </tr>
861
+ <tr>
862
+ <td colspan="3"
863
+ class="p-2 text-xs text-yellow-400 bg-yellow-500/5 font-bold">
864
+ Semantic Colors</td>
865
+ </tr>
866
+ <tr>
867
+ <td class="p-3 font-mono text-green-400">success</td>
868
+ <td class="p-3 font-mono text-white/50">--lumina-color-success</td>
869
+ <td class="p-3">Positive actions (#22c55e)</td>
870
+ </tr>
871
+ <tr>
872
+ <td class="p-3 font-mono text-green-400">warning</td>
873
+ <td class="p-3 font-mono text-white/50">--lumina-color-warning</td>
874
+ <td class="p-3">Caution states (#eab308)</td>
875
+ </tr>
876
+ <tr>
877
+ <td class="p-3 font-mono text-green-400">danger</td>
878
+ <td class="p-3 font-mono text-white/50">--lumina-color-danger</td>
879
+ <td class="p-3">Errors, destructive (#ef4444)</td>
880
+ </tr>
881
+ <tr>
882
+ <td class="p-3 font-mono text-green-400">info</td>
883
+ <td class="p-3 font-mono text-white/50">--lumina-color-info</td>
884
+ <td class="p-3">Informational highlights</td>
885
+ </tr>
886
+ <tr>
887
+ <td colspan="3"
888
+ class="p-2 text-xs text-purple-400 bg-purple-500/5 font-bold">UI
889
+ Colors</td>
890
+ </tr>
891
+ <tr>
892
+ <td class="p-3 font-mono text-green-400">border</td>
893
+ <td class="p-3 font-mono text-white/50">--lumina-color-border</td>
894
+ <td class="p-3">Default border color</td>
895
+ </tr>
896
+ <tr>
897
+ <td class="p-3 font-mono text-green-400">borderSubtle</td>
898
+ <td class="p-3 font-mono text-white/50">--lumina-color-border-subtle
899
+ </td>
900
+ <td class="p-3">Subtle/light borders</td>
901
+ </tr>
902
+ <tr>
903
+ <td class="p-3 font-mono text-green-400">shadow</td>
904
+ <td class="p-3 font-mono text-white/50">--lumina-shadow-color</td>
905
+ <td class="p-3">Shadow color for elevation</td>
906
+ </tr>
907
+ <tr>
908
+ <td colspan="3"
909
+ class="p-2 text-xs text-pink-400 bg-pink-500/5 font-bold">Gradient
910
+ Colors</td>
911
+ </tr>
912
+ <tr>
913
+ <td class="p-3 font-mono text-green-400">gradientFrom</td>
914
+ <td class="p-3 font-mono text-white/50">--lumina-gradient-from</td>
915
+ <td class="p-3">Gradient start color</td>
916
+ </tr>
917
+ <tr>
918
+ <td class="p-3 font-mono text-green-400">gradientVia</td>
919
+ <td class="p-3 font-mono text-white/50">--lumina-gradient-via</td>
920
+ <td class="p-3">Gradient middle (optional)</td>
921
+ </tr>
922
+ <tr>
923
+ <td class="p-3 font-mono text-green-400">gradientTo</td>
924
+ <td class="p-3 font-mono text-white/50">--lumina-gradient-to</td>
925
+ <td class="p-3">Gradient end color</td>
926
+ </tr>
927
+ </tbody>
928
+ </table>
929
+ </div>
930
+ </div>
931
+
932
+ <!-- EFFECTS SECTION -->
933
+ <div class="mb-12">
934
+ <h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
935
+ <span
936
+ class="w-8 h-8 rounded-lg bg-purple-500/20 flex items-center justify-center text-purple-400">✨</span>
937
+ Effects
938
+ </h3>
939
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
940
+ <table class="w-full text-left text-sm text-gray-400">
941
+ <thead class="bg-white/5 text-white font-bold">
942
+ <tr>
943
+ <th class="p-3 border-b border-white/10">Property</th>
944
+ <th class="p-3 border-b border-white/10">Type</th>
945
+ <th class="p-3 border-b border-white/10">Values</th>
946
+ <th class="p-3 border-b border-white/10">Default</th>
947
+ </tr>
948
+ </thead>
949
+ <tbody class="divide-y divide-white/5">
950
+ <tr>
951
+ <td colspan="4"
952
+ class="p-2 text-xs text-pink-400 bg-pink-500/5 font-bold">Gradients
953
+ </td>
954
+ </tr>
955
+ <tr>
956
+ <td class="p-3 font-mono text-green-400">useGradients</td>
957
+ <td class="p-3 text-purple-400">boolean</td>
958
+ <td class="p-3">true / false</td>
959
+ <td class="p-3">true</td>
960
+ </tr>
961
+ <tr>
962
+ <td class="p-3 font-mono text-green-400">gradientDirection</td>
963
+ <td class="p-3 text-purple-400">enum</td>
964
+ <td class="p-3 font-mono text-xs">'to-t' | 'to-b' | 'to-l' | 'to-r' |
965
+ 'to-tl' | 'to-tr' | 'to-bl' | 'to-br'</td>
966
+ <td class="p-3">'to-br'</td>
967
+ </tr>
968
+ <tr>
969
+ <td colspan="4"
970
+ class="p-2 text-xs text-blue-400 bg-blue-500/5 font-bold">Shadows
971
+ </td>
972
+ </tr>
973
+ <tr>
974
+ <td class="p-3 font-mono text-green-400">useShadows</td>
975
+ <td class="p-3 text-purple-400">boolean</td>
976
+ <td class="p-3">true / false</td>
977
+ <td class="p-3">true</td>
978
+ </tr>
979
+ <tr>
980
+ <td class="p-3 font-mono text-green-400">shadowIntensity</td>
981
+ <td class="p-3 text-purple-400">enum</td>
982
+ <td class="p-3 font-mono text-xs">'none' | 'sm' | 'md' | 'lg' | 'xl' |
983
+ '2xl'</td>
984
+ <td class="p-3">'md'</td>
985
+ </tr>
986
+ <tr>
987
+ <td class="p-3 font-mono text-green-400">shadowColor</td>
988
+ <td class="p-3 text-purple-400">string</td>
989
+ <td class="p-3">Any CSS color (rgba works best)</td>
990
+ <td class="p-3">rgba(0,0,0,0.25)</td>
991
+ </tr>
992
+ <tr>
993
+ <td colspan="4"
994
+ class="p-2 text-xs text-cyan-400 bg-cyan-500/5 font-bold">
995
+ Glassmorphism</td>
996
+ </tr>
997
+ <tr>
998
+ <td class="p-3 font-mono text-green-400">useGlass</td>
999
+ <td class="p-3 text-purple-400">boolean</td>
1000
+ <td class="p-3">true / false (frosted glass panels)</td>
1001
+ <td class="p-3">true</td>
1002
+ </tr>
1003
+ <tr>
1004
+ <td class="p-3 font-mono text-green-400">glassOpacity</td>
1005
+ <td class="p-3 text-purple-400">number</td>
1006
+ <td class="p-3">0 to 1 (panel background opacity)</td>
1007
+ <td class="p-3">0.03</td>
1008
+ </tr>
1009
+ <tr>
1010
+ <td class="p-3 font-mono text-green-400">glassBlur</td>
1011
+ <td class="p-3 text-purple-400">string</td>
1012
+ <td class="p-3">CSS blur value (e.g., '20px')</td>
1013
+ <td class="p-3">'20px'</td>
1014
+ </tr>
1015
+ <tr>
1016
+ <td class="p-3 font-mono text-green-400">glassBorderOpacity</td>
1017
+ <td class="p-3 text-purple-400">number</td>
1018
+ <td class="p-3">0 to 1 (subtle border opacity)</td>
1019
+ <td class="p-3">0.05</td>
1020
+ </tr>
1021
+ <tr>
1022
+ <td colspan="4"
1023
+ class="p-2 text-xs text-orange-400 bg-orange-500/5 font-bold">
1024
+ Ambient Orb</td>
1025
+ </tr>
1026
+ <tr>
1027
+ <td class="p-3 font-mono text-green-400">useOrb</td>
1028
+ <td class="p-3 text-purple-400">boolean</td>
1029
+ <td class="p-3">true / false (background glow)</td>
1030
+ <td class="p-3">true</td>
1031
+ </tr>
1032
+ <tr>
1033
+ <td class="p-3 font-mono text-green-400">orbOpacity</td>
1034
+ <td class="p-3 text-purple-400">number</td>
1035
+ <td class="p-3">0 to 1</td>
1036
+ <td class="p-3">0.4</td>
1037
+ </tr>
1038
+ <tr>
1039
+ <td class="p-3 font-mono text-green-400">orbBlur</td>
1040
+ <td class="p-3 text-purple-400">string</td>
1041
+ <td class="p-3">CSS blur value</td>
1042
+ <td class="p-3">'120px'</td>
1043
+ </tr>
1044
+ <tr>
1045
+ <td class="p-3 font-mono text-green-400">orbSize</td>
1046
+ <td class="p-3 text-purple-400">string</td>
1047
+ <td class="p-3">CSS size (vw, vh, px, %)</td>
1048
+ <td class="p-3">'60vw'</td>
1049
+ </tr>
1050
+ <tr>
1051
+ <td colspan="4"
1052
+ class="p-2 text-xs text-green-400 bg-green-500/5 font-bold">
1053
+ Animations</td>
1054
+ </tr>
1055
+ <tr>
1056
+ <td class="p-3 font-mono text-green-400">animationsEnabled</td>
1057
+ <td class="p-3 text-purple-400">boolean</td>
1058
+ <td class="p-3">Master toggle for all animations</td>
1059
+ <td class="p-3">true</td>
1060
+ </tr>
1061
+ <tr>
1062
+ <td class="p-3 font-mono text-green-400">transitionDuration</td>
1063
+ <td class="p-3 text-purple-400">string</td>
1064
+ <td class="p-3">CSS duration (e.g., '0.3s')</td>
1065
+ <td class="p-3">'0.3s'</td>
1066
+ </tr>
1067
+ <tr>
1068
+ <td class="p-3 font-mono text-green-400">transitionEasing</td>
1069
+ <td class="p-3 text-purple-400">string</td>
1070
+ <td class="p-3">CSS easing function</td>
1071
+ <td class="p-3">cubic-bezier(0.16,1,0.3,1)</td>
1072
+ </tr>
1073
+ <tr>
1074
+ <td class="p-3 font-mono text-green-400">hoverScale</td>
1075
+ <td class="p-3 text-purple-400">number</td>
1076
+ <td class="p-3">Scale on hover (1.0 = no scale)</td>
1077
+ <td class="p-3">1.05</td>
1078
+ </tr>
1079
+ </tbody>
1080
+ </table>
1081
+ </div>
1082
+ </div>
1083
+
1084
+ <!-- COMPONENTS SECTION -->
1085
+ <div class="mb-12">
1086
+ <h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
1087
+ <span
1088
+ class="w-8 h-8 rounded-lg bg-green-500/20 flex items-center justify-center text-green-400">🧩</span>
1089
+ Component Tokens
1090
+ </h3>
1091
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1092
+ <table class="w-full text-left text-sm text-gray-400">
1093
+ <thead class="bg-white/5 text-white font-bold">
1094
+ <tr>
1095
+ <th class="p-3 border-b border-white/10">Property</th>
1096
+ <th class="p-3 border-b border-white/10">CSS Variable</th>
1097
+ <th class="p-3 border-b border-white/10">Default</th>
1098
+ </tr>
1099
+ </thead>
1100
+ <tbody class="divide-y divide-white/5">
1101
+ <tr>
1102
+ <td colspan="3"
1103
+ class="p-2 text-xs text-blue-400 bg-blue-500/5 font-bold">Buttons
1104
+ </td>
1105
+ </tr>
1106
+ <tr>
1107
+ <td class="p-3 font-mono text-green-400">buttonRadius</td>
1108
+ <td class="p-3 font-mono text-white/50">--lumina-button-radius</td>
1109
+ <td class="p-3">9999px (pill)</td>
1110
+ </tr>
1111
+ <tr>
1112
+ <td class="p-3 font-mono text-green-400">buttonPadding</td>
1113
+ <td class="p-3 font-mono text-white/50">--lumina-button-padding</td>
1114
+ <td class="p-3">0.75rem 1.5rem</td>
1115
+ </tr>
1116
+ <tr>
1117
+ <td class="p-3 font-mono text-green-400">buttonFontWeight</td>
1118
+ <td class="p-3 font-mono text-white/50">--lumina-button-font-weight</td>
1119
+ <td class="p-3">700</td>
1120
+ </tr>
1121
+ <tr>
1122
+ <td class="p-3 font-mono text-green-400">buttonTextTransform</td>
1123
+ <td class="p-3 font-mono text-white/50">--lumina-button-text-transform
1124
+ </td>
1125
+ <td class="p-3">none</td>
1126
+ </tr>
1127
+ <tr>
1128
+ <td colspan="3"
1129
+ class="p-2 text-xs text-purple-400 bg-purple-500/5 font-bold">Cards
1130
+ </td>
1131
+ </tr>
1132
+ <tr>
1133
+ <td class="p-3 font-mono text-green-400">cardRadius</td>
1134
+ <td class="p-3 font-mono text-white/50">--lumina-card-radius</td>
1135
+ <td class="p-3">1.5rem</td>
1136
+ </tr>
1137
+ <tr>
1138
+ <td class="p-3 font-mono text-green-400">cardPadding</td>
1139
+ <td class="p-3 font-mono text-white/50">--lumina-card-padding</td>
1140
+ <td class="p-3">2rem</td>
1141
+ </tr>
1142
+ <tr>
1143
+ <td class="p-3 font-mono text-green-400">cardBorderWidth</td>
1144
+ <td class="p-3 font-mono text-white/50">--lumina-card-border-width</td>
1145
+ <td class="p-3">1px</td>
1146
+ </tr>
1147
+ <tr>
1148
+ <td colspan="3"
1149
+ class="p-2 text-xs text-cyan-400 bg-cyan-500/5 font-bold">Timeline
1150
+ </td>
1151
+ </tr>
1152
+ <tr>
1153
+ <td class="p-3 font-mono text-green-400">timelineNodeSize</td>
1154
+ <td class="p-3 font-mono text-white/50">--lumina-timeline-node-size</td>
1155
+ <td class="p-3">1rem</td>
1156
+ </tr>
1157
+ <tr>
1158
+ <td class="p-3 font-mono text-green-400">timelineLineWidth</td>
1159
+ <td class="p-3 font-mono text-white/50">--lumina-timeline-line-width
1160
+ </td>
1161
+ <td class="p-3">2px</td>
1162
+ </tr>
1163
+ <tr>
1164
+ <td class="p-3 font-mono text-green-400">timelineNodeColor</td>
1165
+ <td class="p-3 font-mono text-white/50">--lumina-timeline-node-color
1166
+ </td>
1167
+ <td class="p-3">(uses primary)</td>
1168
+ </tr>
1169
+ <tr>
1170
+ <td colspan="3"
1171
+ class="p-2 text-xs text-pink-400 bg-pink-500/5 font-bold">
1172
+ Tags/Badges</td>
1173
+ </tr>
1174
+ <tr>
1175
+ <td class="p-3 font-mono text-green-400">tagPadding</td>
1176
+ <td class="p-3 font-mono text-white/50">--lumina-tag-padding</td>
1177
+ <td class="p-3">0.25rem 1rem</td>
1178
+ </tr>
1179
+ <tr>
1180
+ <td class="p-3 font-mono text-green-400">tagRadius</td>
1181
+ <td class="p-3 font-mono text-white/50">--lumina-tag-radius</td>
1182
+ <td class="p-3">9999px</td>
1183
+ </tr>
1184
+ <tr>
1185
+ <td class="p-3 font-mono text-green-400">tagFontSize</td>
1186
+ <td class="p-3 font-mono text-white/50">--lumina-tag-font-size</td>
1187
+ <td class="p-3">0.75rem</td>
1188
+ </tr>
1189
+ </tbody>
1190
+ </table>
1191
+ </div>
1192
+ </div>
1193
+
1194
+ <!-- TYPOGRAPHY SECTION -->
1195
+ <div class="mb-12">
1196
+ <h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
1197
+ <span
1198
+ class="w-8 h-8 rounded-lg bg-yellow-500/20 flex items-center justify-center text-yellow-400">🔤</span>
1199
+ Typography
1200
+ </h3>
1201
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1202
+ <div class="border border-white/10 rounded-lg p-4">
1203
+ <h4 class="text-white font-bold mb-3">Font Families</h4>
1204
+ <div class="space-y-2 text-sm">
1205
+ <div><code class="text-green-400">heading</code>: <span
1206
+ class="text-white/60">Headings (default: Outfit)</span></div>
1207
+ <div><code class="text-green-400">body</code>: <span
1208
+ class="text-white/60">Body text (default: Outfit)</span></div>
1209
+ <div><code class="text-green-400">mono</code>: <span
1210
+ class="text-white/60">Code (default: monospace)</span></div>
1211
+ </div>
1212
+ </div>
1213
+ <div class="border border-white/10 rounded-lg p-4">
1214
+ <h4 class="text-white font-bold mb-3">Font Sizes (--lumina-text-*)</h4>
1215
+ <div class="space-y-1 text-sm text-white/60">
1216
+ <div>xs: 0.75rem | sm: 0.875rem | base: 1rem</div>
1217
+ <div>lg: 1.125rem | xl: 1.25rem | 2xl: 1.5rem</div>
1218
+ <div>3xl: 1.875rem | 4xl: 2.25rem | 5xl: 3rem</div>
1219
+ <div>6xl: 3.75rem | 7xl: 4.5rem</div>
1220
+ </div>
1221
+ </div>
1222
+ <div class="border border-white/10 rounded-lg p-4">
1223
+ <h4 class="text-white font-bold mb-3">Font Weights (--lumina-font-weight-*)</h4>
1224
+ <div class="space-y-1 text-sm text-white/60">
1225
+ <div>light: 300 | normal: 400 | medium: 500</div>
1226
+ <div>semibold: 600 | bold: 800 | extrabold: 900</div>
1227
+ </div>
1228
+ </div>
1229
+ <div class="border border-white/10 rounded-lg p-4">
1230
+ <h4 class="text-white font-bold mb-3">Line Heights (--lumina-leading-*)</h4>
1231
+ <div class="space-y-1 text-sm text-white/60">
1232
+ <div>tight: 1.1 | snug: 1.25 | normal: 1.5</div>
1233
+ <div>relaxed: 1.625 | loose: 2</div>
1234
+ </div>
1235
+ </div>
1236
+ </div>
1237
+ </div>
1238
+
1239
+ <!-- SPACING SECTION -->
1240
+ <div class="mb-12">
1241
+ <h3 class="text-xl font-bold text-white mb-4 flex items-center gap-2">
1242
+ <span
1243
+ class="w-8 h-8 rounded-lg bg-cyan-500/20 flex items-center justify-center text-cyan-400">📏</span>
1244
+ Spacing & Border Radius
1245
+ </h3>
1246
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1247
+ <div class="border border-white/10 rounded-lg p-4">
1248
+ <h4 class="text-white font-bold mb-3">Spacing (--lumina-space-*)</h4>
1249
+ <div class="space-y-1 text-sm text-white/60">
1250
+ <div>none: 0 | xs: 0.25rem | sm: 0.5rem</div>
1251
+ <div>md: 1rem | lg: 1.5rem | xl: 2rem</div>
1252
+ <div>2xl: 3rem | 3xl: 4rem | 4xl: 6rem</div>
1253
+ </div>
1254
+ </div>
1255
+ <div class="border border-white/10 rounded-lg p-4">
1256
+ <h4 class="text-white font-bold mb-3">Border Radius (--lumina-radius-*)</h4>
1257
+ <div class="space-y-1 text-sm text-white/60">
1258
+ <div>none: 0 | sm: 0.25rem | md: 0.5rem</div>
1259
+ <div>lg: 0.75rem | xl: 1rem | 2xl: 1.5rem</div>
1260
+ <div>3xl: 2rem | full: 9999px</div>
1261
+ </div>
1262
+ </div>
1263
+ </div>
1264
+ </div>
1265
+
1266
+ <!-- JSON Example -->
1267
+ <div class="mt-8">
1268
+ <h3 class="text-xl font-bold text-white mb-4">Full themeConfig Example</h3>
1269
+ <pre><code><span class="text-gray-500">// In your deck JSON</span>
1270
+ {
1271
+ <span class="text-blue-400">"meta"</span>: {
1272
+ <span class="text-blue-400">"theme"</span>: <span class="text-green-400">"default"</span>,
1273
+ <span class="text-blue-400">"themeConfig"</span>: {
1274
+ <span class="text-blue-400">"colors"</span>: {
1275
+ <span class="text-blue-400">"primary"</span>: <span class="text-green-400">"#8b5cf6"</span>,
1276
+ <span class="text-blue-400">"secondary"</span>: <span class="text-green-400">"#ec4899"</span>,
1277
+ <span class="text-blue-400">"gradientFrom"</span>: <span class="text-green-400">"#8b5cf6"</span>,
1278
+ <span class="text-blue-400">"gradientTo"</span>: <span class="text-green-400">"#ec4899"</span>
1279
+ },
1280
+ <span class="text-blue-400">"effects"</span>: {
1281
+ <span class="text-blue-400">"useGradients"</span>: <span class="text-yellow-400">true</span>,
1282
+ <span class="text-blue-400">"useGlass"</span>: <span class="text-yellow-400">true</span>,
1283
+ <span class="text-blue-400">"glassOpacity"</span>: <span class="text-yellow-400">0.05</span>,
1284
+ <span class="text-blue-400">"useOrb"</span>: <span class="text-yellow-400">true</span>,
1285
+ <span class="text-blue-400">"orbOpacity"</span>: <span class="text-yellow-400">0.4</span>
1286
+ },
1287
+ <span class="text-blue-400">"components"</span>: {
1288
+ <span class="text-blue-400">"cardRadius"</span>: <span class="text-green-400">"1.5rem"</span>,
1289
+ <span class="text-blue-400">"buttonRadius"</span>: <span class="text-green-400">"9999px"</span>
1290
+ }
1291
+ }
1292
+ }
1293
+ }</code></pre>
1294
+ </div>
1295
+ </div>
1296
+
1297
+ <!-- Custom Theme Object -->
1298
+ <div class="my-12">
1299
+ <h2 class="text-2xl font-bold text-white mb-6">Custom Theme Configuration</h2>
1300
+ <p class="text-white/60 mb-4">Pass a configuration object for full control over colors and
1301
+ fonts:</p>
1302
+
1303
+ <pre><code><span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app', {
1304
+ theme: {
1305
+ colors: {
1306
+ primary: <span class="text-green-400">'#ec4899'</span>, <span class="text-gray-500">// Pink accent</span>
1307
+ background: <span class="text-green-400">'#1a1a2e'</span>, <span class="text-gray-500">// Dark purple bg</span>
1308
+ text: <span class="text-green-400">'#eaeaea'</span>, <span class="text-gray-500">// Light text</span>
1309
+ muted: <span class="text-green-400">'#888888'</span> <span class="text-gray-500">// Subdued text</span>
1310
+ },
1311
+ fonts: {
1312
+ heading: <span class="text-green-400">'Outfit, sans-serif'</span>,
1313
+ body: <span class="text-green-400">'Inter, sans-serif'</span>,
1314
+ mono: <span class="text-green-400">'Fira Code, monospace'</span>
1315
+ }
1316
+ }
1317
+ });</code></pre>
1318
+ </div>
1319
+
1320
+ <!-- Partial Override -->
1321
+ <div class="my-12">
1322
+ <h2 class="text-2xl font-bold text-white mb-6">Partial Overrides</h2>
1323
+ <p class="text-white/60 mb-4">You don't need to specify every property. Lumina merges your
1324
+ values with the default theme:</p>
1325
+
1326
+ <pre><code><span class="text-gray-500">// Only change the primary color</span>
1327
+ <span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app', {
1328
+ theme: {
1329
+ colors: {
1330
+ primary: <span class="text-green-400">'#f59e0b'</span> <span class="text-gray-500">// Amber accent</span>
1331
+ }
1332
+ }
1333
+ });
1334
+
1335
+ <span class="text-gray-500">// Everything else uses defaults!</span></code></pre>
1336
+ </div>
1337
+
1338
+ <!-- Font Loading -->
1339
+ <div class="my-12">
1340
+ <h2 class="text-2xl font-bold text-white mb-6">Custom Fonts</h2>
1341
+ <p class="text-white/60 mb-4">For custom fonts, load them via Google Fonts or your own
1342
+ stylesheet first:</p>
1343
+
1344
+ <pre
1345
+ class="language-html"><code><span class="text-gray-500">&lt;!-- In your HTML head --&gt;</span>
1346
+ <span class="text-blue-400">&lt;link</span> rel=<span class="text-green-400">"preconnect"</span> href=<span class="text-green-400">"https://fonts.googleapis.com"</span><span class="text-blue-400">&gt;</span>
1347
+ <span class="text-blue-400">&lt;link</span> href=<span class="text-green-400">"https://fonts.googleapis.com/css2?family=Outfit:wght@400;700&family=Rajdhani&display=swap"</span> rel=<span class="text-green-400">"stylesheet"</span><span class="text-blue-400">&gt;</span></code></pre>
1348
+
1349
+ <p class="text-white/60 mt-4 mb-4">Then reference them in your theme config:</p>
1350
+
1351
+ <pre><code><span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app', {
1352
+ theme: {
1353
+ fonts: {
1354
+ heading: <span class="text-green-400">'Outfit, sans-serif'</span>,
1355
+ body: <span class="text-green-400">'Rajdhani, sans-serif'</span>
1356
+ }
1357
+ }
1358
+ });</code></pre>
1359
+ </div>
1360
+
1361
+ <!-- Tips -->
1362
+ <div class="mt-8 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
1363
+ <h4 class="text-yellow-400 font-bold mb-2"><i class="fa-solid fa-lightbulb mr-2"></i>Pro
1364
+ Tips</h4>
1365
+ <ul class="text-sm text-yellow-200/80 space-y-2 m-0 list-none">
1366
+ <li>🎨 <strong>Brand colors:</strong> Set <code>primary</code> to your brand color for
1367
+ instant recognition</li>
1368
+ <li>🌙 <strong>Dark vs Light:</strong> Use <code>latte</code> preset for light mode
1369
+ presentations</li>
1370
+ <li>📱 <strong>Accessibility:</strong> Ensure sufficient contrast between
1371
+ <code>text</code> and <code>background</code>
1372
+ </li>
1373
+ <li>🔤 <strong>Performance:</strong> Only load font weights you actually use (400, 700)
1374
+ </li>
1375
+ </ul>
1376
+ </div>
1377
+ </div>
1378
+
1379
+ <!-- SPEAKER NOTES -->
1380
+ <div v-else-if="activeSection === 'speaker-notes'">
1381
+ <h1>Speaker Notes</h1>
1382
+ <p class="lead">Open a separate presenter view with notes, timer, and bidirectional navigation
1383
+ controls.</p>
1384
+
1385
+ <h2>Adding Notes to Slides</h2>
1386
+ <p>Add the <code>notes</code> property to any slide. Notes are only visible in the presenter
1387
+ view, not on the main display.</p>
1388
+
1389
+ <pre><code>{
1390
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"statement"</span>,
1391
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Welcome"</span>,
1392
+ <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"An introduction to our platform"</span>,
1393
+ <span class="text-blue-400">"notes"</span>: <span class="text-green-400">"Greet the audience. Mention key stakeholders. Time: 2 minutes."</span>
1394
+ }</code></pre>
1395
+
1396
+ <h2>Opening the Presenter View</h2>
1397
+ <p>Call <code>openSpeakerNotes()</code> on your Lumina instance to open the presenter window.
1398
+ </p>
1399
+
1400
+ <pre><code><span class="text-blue-400">const</span> engine = <span class="text-blue-400">new</span> Lumina('#app');
1401
+ engine.load(deckData);
1402
+
1403
+ <span class="text-gray-500">// Open presenter view</span>
1404
+ engine.<span class="text-yellow-400">openSpeakerNotes</span>();
1405
+
1406
+ <span class="text-gray-500">// Check if open</span>
1407
+ <span class="text-blue-400">if</span> (engine.isSpeakerNotesOpen) {
1408
+ console.log('Presenter view active');
1409
+ }
1410
+
1411
+ <span class="text-gray-500">// Close programmatically</span>
1412
+ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1413
+
1414
+ <h3>Presenter View Features</h3>
1415
+ <ul>
1416
+ <li><strong>Current Notes</strong> — Formatted notes for the current slide</li>
1417
+ <li><strong>Next Slide Preview</strong> — Title and type of the upcoming slide</li>
1418
+ <li><strong>Timer</strong> — Start/pause/reset presentation timer</li>
1419
+ <li><strong>Navigation</strong> — Synced controls (changes reflect in main window)</li>
1420
+ <li><strong>Connection Status</strong> — Visual indicator of sync state</li>
1421
+ </ul>
1422
+
1423
+ <h3>Keyboard Shortcuts (Presenter View)</h3>
1424
+ <div class="overflow-x-auto border border-white/10 rounded-lg my-6">
1425
+ <table class="w-full text-left text-sm text-gray-400">
1426
+ <thead class="bg-white/5 text-white font-bold">
1427
+ <tr>
1428
+ <th class="p-3 border-b border-white/10">Key</th>
1429
+ <th class="p-3 border-b border-white/10">Action</th>
1430
+ </tr>
1431
+ </thead>
1432
+ <tbody class="divide-y divide-white/5">
1433
+ <tr>
1434
+ <td class="p-3 font-mono">→ / Space / Enter</td>
1435
+ <td class="p-3">Next slide</td>
1436
+ </tr>
1437
+ <tr>
1438
+ <td class="p-3 font-mono">← / Backspace</td>
1439
+ <td class="p-3">Previous slide</td>
1440
+ </tr>
1441
+ <tr>
1442
+ <td class="p-3 font-mono">T</td>
1443
+ <td class="p-3">Toggle timer</td>
1444
+ </tr>
1445
+ <tr>
1446
+ <td class="p-3 font-mono">R</td>
1447
+ <td class="p-3">Reset timer</td>
1448
+ </tr>
1449
+ </tbody>
1450
+ </table>
1451
+ </div>
1452
+
1453
+ <div class="mt-8 p-4 bg-blue-500/10 border border-blue-500/20 rounded-lg">
1454
+ <h4 class="text-blue-400 font-bold mb-2"><i class="fa-solid fa-lightbulb mr-2"></i>Pro Tip
1455
+ </h4>
1456
+ <p class="text-sm text-blue-200/80 m-0">Position the presenter window on your secondary
1457
+ monitor while the presentation runs on the main display. Navigation from either window
1458
+ stays synchronized.</p>
1459
+ </div>
1460
+ </div>
1461
+
1462
+
1463
+ <!-- AGENTS: INTRO -->
1464
+ <div v-else-if="activeSection === 'agents-intro'">
1465
+ <h1>The Agent Protocol</h1>
1466
+ <p class="lead">Lumina is the first presentation engine designed for <strong>Agentic
1467
+ AI</strong>.</p>
1468
+ <p>Large Language Models (LLMs) are great at "What" (Content) but struggle with "How"
1469
+ (Pixel-perfect CSS).
1470
+ Lumina solves this by acting as a <strong>Semantic Interface Layer</strong>.</p>
1471
+
1472
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-8 my-12 not-prose">
1473
+ <div
1474
+ class="bg-gray-900 border border-purple-500/20 rounded-xl p-8 relative overflow-hidden">
1475
+ <div class="absolute top-0 right-0 p-4 opacity-20">
1476
+ <i class="fa-solid fa-robot text-6xl text-purple-500"></i>
1477
+ </div>
1478
+ <h3 class="text-purple-400 font-bold mb-4">The Agent's Job</h3>
1479
+ <ul class="space-y-3 text-sm text-gray-400">
1480
+ <li class="flex gap-2"><i class="fa-solid fa-check text-green-500"></i> Generate
1481
+ Text Content</li>
1482
+ <li class="flex gap-2"><i class="fa-solid fa-check text-green-500"></i> Determine
1483
+ Structure</li>
1484
+ <li class="flex gap-2"><i class="fa-solid fa-check text-green-500"></i> Choose
1485
+ Semantic Layouts</li>
1486
+ </ul>
1487
+ </div>
1488
+ <div class="bg-gray-900 border border-blue-500/20 rounded-xl p-8 relative overflow-hidden">
1489
+ <div class="absolute top-0 right-0 p-4 opacity-20">
1490
+ <i class="fa-solid fa-layer-group text-6xl text-blue-500"></i>
1491
+ </div>
1492
+ <h3 class="text-blue-400 font-bold mb-4">Lumina's Job</h3>
1493
+ <ul class="space-y-3 text-sm text-gray-400">
1494
+ <li class="flex gap-2"><i class="fa-solid fa-check text-blue-500"></i> 60fps
1495
+ Animations</li>
1496
+ <li class="flex gap-2"><i class="fa-solid fa-check text-blue-500"></i> Responsive
1497
+ Typography</li>
1498
+ <li class="flex gap-2"><i class="fa-solid fa-check text-blue-500"></i> Interactive
1499
+ State</li>
1500
+ </ul>
1501
+ </div>
1502
+ </div>
1503
+
1504
+ <h2>The Protocol</h2>
1505
+ <p>Agents communicate with Lumina via a JSON format that is strict enough for deterministic
1506
+ rendering but loose enough for LLMs.</p>
1507
+ </div>
1508
+
1509
+ <!-- AGENTS: TOKENS -->
1510
+ <div v-else-if="activeSection === 'agents-tokens'">
1511
+ <h1>Token Optimization</h1>
1512
+ <p class="lead">Reduce output size with short property aliases. Less characters = faster
1513
+ rendering and lower costs.</p>
1514
+
1515
+ <div class="not-prose grid gap-6 mt-8">
1516
+ <div class="bg-[#0A0A0A] border border-white/10 rounded-xl p-6">
1517
+ <h3 class="text-sm font-bold text-gray-500 uppercase tracking-widest mb-4">Standard JSON
1518
+ <span class="text-white/30 font-normal normal-case ml-2">~95 characters</span>
1519
+ </h3>
1520
+ <pre class="!mb-0 !p-0 !bg-transparent !border-none"><code>{
1521
+ "type": "statement",
1522
+ "title": "Quarterly Review",
1523
+ "subtitle": "Growth is exceeding expectations."
1524
+ }</code></pre>
1525
+ </div>
1526
+
1527
+ <div class="bg-[#0A0A0A] border border-green-500/20 rounded-xl p-6 relative">
1528
+ <span
1529
+ class="absolute top-4 right-4 text-xs font-bold text-green-400 bg-green-500/10 px-2 py-1 rounded">
1530
+ ~25% Shorter
1531
+ </span>
1532
+ <h3 class="text-sm font-bold text-gray-500 uppercase tracking-widest mb-4">Optimized
1533
+ JSON <span class="text-white/30 font-normal normal-case ml-2">~70 characters</span>
1534
+ </h3>
1535
+ <pre class="!mb-0 !p-0 !bg-transparent !border-none"><code>{
1536
+ "type": "statement",
1537
+ "t": "Quarterly Review",
1538
+ "s": "Growth is exceeding expectations."
1539
+ }</code></pre>
1540
+ </div>
1541
+ </div>
1542
+
1543
+ <div class="my-8">
1544
+ <h2 class="text-xl font-bold text-white mb-4">Supported Aliases</h2>
1545
+ <p class="text-white/60 mb-4">Both full property names and aliases work interchangeably. Mix
1546
+ and match as needed.</p>
1547
+
1548
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1549
+ <table class="w-full text-left text-sm text-gray-400">
1550
+ <thead class="bg-white/5 text-white font-bold">
1551
+ <tr>
1552
+ <th class="p-3 border-b border-white/10">Full Key</th>
1553
+ <th class="p-3 border-b border-white/10">Alias</th>
1554
+ <th class="p-3 border-b border-white/10">Used In</th>
1555
+ </tr>
1556
+ </thead>
1557
+ <tbody class="divide-y divide-white/5">
1558
+ <tr>
1559
+ <td class="p-3 font-mono">title</td>
1560
+ <td class="p-3 font-mono text-green-400">t</td>
1561
+ <td class="p-3 text-white/50">All layouts</td>
1562
+ </tr>
1563
+ <tr>
1564
+ <td class="p-3 font-mono">subtitle</td>
1565
+ <td class="p-3 font-mono text-green-400">s</td>
1566
+ <td class="p-3 text-white/50">Statement, Timeline, Steps</td>
1567
+ </tr>
1568
+ <tr>
1569
+ <td class="p-3 font-mono">description</td>
1570
+ <td class="p-3 font-mono text-green-400">d</td>
1571
+ <td class="p-3 text-white/50">Features, Timeline items, Steps items</td>
1572
+ </tr>
1573
+ <tr>
1574
+ <td class="p-3 font-mono">features</td>
1575
+ <td class="p-3 font-mono text-green-400">fs</td>
1576
+ <td class="p-3 text-white/50">Features layout</td>
1577
+ </tr>
1578
+ <tr>
1579
+ <td class="p-3 font-mono">timeline</td>
1580
+ <td class="p-3 font-mono text-green-400">tl</td>
1581
+ <td class="p-3 text-white/50">Timeline layout</td>
1582
+ </tr>
1583
+ <tr>
1584
+ <td class="p-3 font-mono">steps</td>
1585
+ <td class="p-3 font-mono text-green-400">st</td>
1586
+ <td class="p-3 text-white/50">Steps layout</td>
1587
+ </tr>
1588
+ <tr>
1589
+ <td class="p-3 font-mono">paragraphs</td>
1590
+ <td class="p-3 font-mono text-green-400">p</td>
1591
+ <td class="p-3 text-white/50">Half layout</td>
1592
+ </tr>
1593
+ <tr>
1594
+ <td class="p-3 font-mono">image</td>
1595
+ <td class="p-3 font-mono text-green-400">img</td>
1596
+ <td class="p-3 text-white/50">Half layout</td>
1597
+ </tr>
1598
+ <tr>
1599
+ <td class="p-3 font-mono">imageSide</td>
1600
+ <td class="p-3 font-mono text-green-400">side</td>
1601
+ <td class="p-3 text-white/50">Half layout</td>
1602
+ </tr>
1603
+ </tbody>
1604
+ </table>
1605
+ </div>
1606
+ </div>
1607
+
1608
+ <div class="mt-8 p-4 bg-blue-500/10 border border-blue-500/20 rounded-lg">
1609
+ <h4 class="text-blue-400 font-bold mb-2"><i class="fa-solid fa-info-circle mr-2"></i>How It
1610
+ Works</h4>
1611
+ <p class="text-sm text-blue-200/80 m-0">Lumina's normalizer automatically expands aliases to
1612
+ full property names before rendering. You can use standard JSON for readability or
1613
+ aliases for efficiency—both produce identical results.</p>
1614
+ </div>
1615
+ </div>
1616
+
1617
+ <!-- AGENTS: STREAMING -->
1618
+ <div v-else-if="activeSection === 'agents-streaming'">
1619
+ <h1>Streaming & Realtime</h1>
1620
+ <p>Lumina enables a <strong>"Type & See"</strong> experience. Using our partial parser, you can
1621
+ feed raw LLM streams directly into the engine.</p>
1622
+
1623
+ <div class="not-prose my-12">
1624
+ <div
1625
+ class="bg-[#050505] border border-white/10 rounded-2xl overflow-hidden shadow-2xl flex flex-col h-[800px] relative group">
1626
+
1627
+ <!-- CTA Overlay (Top Right) -->
1628
+ <div class="absolute top-4 right-4 z-20">
1629
+ <button @click="runDemo"
1630
+ class="px-5 py-1.5 rounded-full bg-blue-500 hover:bg-blue-400 text-white font-bold text-xs shadow-lg shadow-blue-500/20 transition-all transform hover:scale-105 flex items-center gap-2">
1631
+ <i class="fa-solid fa-play"></i> Simulate AI Stream
1632
+ </button>
1633
+ </div>
1634
+
1635
+ <!-- Editor (Top, Fixed Height) -->
1636
+ <div
1637
+ class="w-full h-48 flex flex-col bg-gray-900 border-b border-white/10 relative z-10">
1638
+ <div
1639
+ class="bg-white/5 px-4 py-2 text-xs font-bold text-white/50 flex justify-between items-center">
1640
+ <span>LLM JSON OUTPUT</span>
1641
+ <span class="text-xs text-white/20 mr-32" v-if="demoStarted">Receiving
1642
+ Tokens...</span>
1643
+ </div>
1644
+ <textarea v-model="demoInput"
1645
+ class="flex-1 bg-transparent p-6 font-mono text-sm text-green-400 resize-none focus:outline-none"
1646
+ placeholder='Waiting for tokens...'></textarea>
1647
+ </div>
1648
+
1649
+ <!-- Preview (Bottom, Flex) -->
1650
+ <div class="w-full flex-1 relative bg-black min-h-[300px]">
1651
+ <div id="streaming-demo-container" class="w-full h-full"></div>
1652
+ <div v-if="!demoStarted"
1653
+ class="absolute inset-0 flex items-center justify-center text-white/20">
1654
+ <div class="text-center">
1655
+ <i class="fa-solid fa-tv text-4xl mb-4"></i>
1656
+ <p class="text-sm">Preview UI Render</p>
1657
+ </div>
1658
+ </div>
1659
+ </div>
1660
+
1661
+ </div>
1662
+ </div>
1663
+ </div>
1664
+
1665
+ <!-- AGENTS: LAYOUT GALLERY -->
1666
+ <div v-else-if="activeSection === 'agents-layouts'">
1667
+ <h1>Layout Gallery</h1>
1668
+ <p>Lumina provides 5 core layouts optimized for different types of information. Click <strong><i
1669
+ class="fa-solid fa-play text-blue-400"></i> Visualize</strong> to load them into the
1670
+ <a @click.prevent="activeSection = 'agents-streaming'; scroll('streaming-demo-container')"
1671
+ href="#" class="text-blue-400 hover:underline">Streaming Demo</a>.
1672
+ </p>
1673
+
1674
+ <div class="space-y-12 mt-12">
1675
+
1676
+ <!-- Statement -->
1677
+ <div class="bg-[#0A0A0A] border border-white/10 rounded-xl overflow-hidden p-6">
1678
+ <div class="flex justify-between items-center mb-4">
1679
+ <h3 class="text-xl font-bold text-white flex items-center gap-3">
1680
+ <span
1681
+ class="px-2 py-0.5 rounded bg-blue-500/10 text-blue-400 text-xs uppercase font-bold">Statement</span>
1682
+ </h3>
1683
+ <button @click="loadIntoDemo(EXAMPLES.statement)"
1684
+ class="px-3 py-1.5 rounded-lg bg-white/5 hover:bg-white/10 text-xs font-bold text-white/50 hover:text-white transition flex items-center gap-2 border border-white/5">
1685
+ <i class="fa-solid fa-play text-blue-400"></i> Visualize
1686
+ </button>
1687
+ </div>
1688
+ <p class="text-sm text-gray-400 mb-4">Best for: Titles, Quotes, Big Ideas.</p>
1689
+ <pre class="!mb-0 !bg-black/50"><code>{{ EXAMPLES.statement }}</code></pre>
1690
+ </div>
1691
+
1692
+ <!-- Features -->
1693
+ <div class="bg-[#0A0A0A] border border-white/10 rounded-xl overflow-hidden p-6">
1694
+ <div class="flex justify-between items-center mb-4">
1695
+ <h3 class="text-xl font-bold text-white flex items-center gap-3">
1696
+ <span
1697
+ class="px-2 py-0.5 rounded bg-yellow-500/10 text-yellow-400 text-xs uppercase font-bold">Features</span>
1698
+ </h3>
1699
+ <button @click="loadIntoDemo(EXAMPLES.features)"
1700
+ class="px-3 py-1.5 rounded-lg bg-white/5 hover:bg-white/10 text-xs font-bold text-white/50 hover:text-white transition flex items-center gap-2 border border-white/5">
1701
+ <i class="fa-solid fa-play text-yellow-400"></i> Visualize
1702
+ </button>
1703
+ </div>
1704
+ <p class="text-sm text-gray-400 mb-4">Best for: Lists, Grids, Stats, Benefits.</p>
1705
+ <pre class="!mb-0 !bg-black/50"><code>{{ EXAMPLES.features }}</code></pre>
1706
+ </div>
1707
+
1708
+ <!-- Timeline -->
1709
+ <div class="bg-[#0A0A0A] border border-white/10 rounded-xl overflow-hidden p-6">
1710
+ <div class="flex justify-between items-center mb-4">
1711
+ <h3 class="text-xl font-bold text-white flex items-center gap-3">
1712
+ <span
1713
+ class="px-2 py-0.5 rounded bg-green-500/10 text-green-400 text-xs uppercase font-bold">Timeline</span>
1714
+ </h3>
1715
+ <button @click="loadIntoDemo(EXAMPLES.timeline)"
1716
+ class="px-3 py-1.5 rounded-lg bg-white/5 hover:bg-white/10 text-xs font-bold text-white/50 hover:text-white transition flex items-center gap-2 border border-white/5">
1717
+ <i class="fa-solid fa-play text-green-400"></i> Visualize
1718
+ </button>
1719
+ </div>
1720
+ <p class="text-sm text-gray-400 mb-4">Best for: Roadmaps, History, Changelogs.</p>
1721
+ <pre class="!mb-0 !bg-black/50"><code>{{ EXAMPLES.timeline }}</code></pre>
1722
+ </div>
1723
+
1724
+ </div>
1725
+ </div>
1726
+
1727
+ <div v-else-if="activeSection === 'agents-auto'">
1728
+ <h1>Auto-Layouts</h1>
1729
+ <p>Sometimes the Agent doesn't know which layout is best. Just use <code>type: 'auto'</code>.
1730
+ </p>
1731
+ <p>Lumina analyzes the content structure to pick the best view automatically.</p>
1732
+
1733
+ <div class="p-6 bg-blue-500/10 border border-blue-500/20 rounded-xl my-8">
1734
+ <h3 class="text-blue-400 font-bold mb-2">Detection Priority</h3>
1735
+ <ul class="list-disc list-inside text-sm text-blue-200/80 space-y-1">
1736
+ <li>If <code>timeline</code> array exists -> <strong>Timeline Layout</strong></li>
1737
+ <li>If <code>steps</code> array exists -> <strong>Steps Layout</strong></li>
1738
+ <li>If <code>features</code> array exists -> <strong>Features Layout</strong></li>
1739
+ <li>If <code>image</code> exists -> <strong>Half Layout</strong></li>
1740
+ <li>Otherwise -> <strong>Statement Layout</strong></li>
1741
+ </ul>
1742
+ </div>
1743
+
1744
+ <h3>Example: Multi-Slide Auto Detection</h3>
1745
+ <p class="text-sm text-gray-400 mb-4">Each slide uses <code>type: "auto"</code> but renders
1746
+ differently based on its content structure.</p>
1747
+
1748
+ <pre><code>{
1749
+ <span class="text-blue-400">"slides"</span>: [
1750
+ <span class="text-gray-500">// Renders as Statement (no special arrays detected)</span>
1751
+ {
1752
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"auto"</span>,
1753
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Welcome to Our Platform"</span>,
1754
+ <span class="text-blue-400">"subtitle"</span>: <span class="text-green-400">"The future of presentations"</span>
1755
+ },
1756
+ <span class="text-gray-500">// Renders as Features (features array detected)</span>
1757
+ {
1758
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"auto"</span>,
1759
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Why Choose Us"</span>,
1760
+ <span class="text-blue-400">"features"</span>: [
1761
+ { <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Fast"</span>, <span class="text-blue-400">"desc"</span>: <span class="text-green-400">"60fps animations"</span>, <span class="text-blue-400">"icon"</span>: <span class="text-green-400">"fa-bolt"</span> },
1762
+ { <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Simple"</span>, <span class="text-blue-400">"desc"</span>: <span class="text-green-400">"JSON-first"</span>, <span class="text-blue-400">"icon"</span>: <span class="text-green-400">"fa-code"</span> },
1763
+ { <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Smart"</span>, <span class="text-blue-400">"desc"</span>: <span class="text-green-400">"AI-ready"</span>, <span class="text-blue-400">"icon"</span>: <span class="text-green-400">"fa-brain"</span> }
1764
+ ]
1765
+ },
1766
+ <span class="text-gray-500">// Renders as Timeline (timeline array detected)</span>
1767
+ {
1768
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"auto"</span>,
1769
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Our Journey"</span>,
1770
+ <span class="text-blue-400">"timeline"</span>: [
1771
+ { <span class="text-blue-400">"date"</span>: <span class="text-green-400">"2023"</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Concept"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"The idea was born"</span> },
1772
+ { <span class="text-blue-400">"date"</span>: <span class="text-green-400">"2024"</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Launch"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"V1.0 released"</span> }
1773
+ ]
1774
+ },
1775
+ <span class="text-gray-500">// Renders as Steps (steps array detected)</span>
1776
+ {
1777
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"auto"</span>,
1778
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Getting Started"</span>,
1779
+ <span class="text-blue-400">"steps"</span>: [
1780
+ { <span class="text-blue-400">"step"</span>: <span class="text-yellow-400">1</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Install"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"npm install lumina-slides"</span> },
1781
+ { <span class="text-blue-400">"step"</span>: <span class="text-yellow-400">2</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Import"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"Import Lumina class"</span> },
1782
+ { <span class="text-blue-400">"step"</span>: <span class="text-yellow-400">3</span>, <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Create"</span>, <span class="text-blue-400">"description"</span>: <span class="text-green-400">"Define your slides"</span> }
1783
+ ]
1784
+ },
1785
+ <span class="text-gray-500">// Renders as Half (image property detected)</span>
1786
+ {
1787
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"auto"</span>,
1788
+ <span class="text-blue-400">"title"</span>: <span class="text-green-400">"Visual Storytelling"</span>,
1789
+ <span class="text-blue-400">"image"</span>: <span class="text-green-400">"https://images.unsplash.com/..."</span>,
1790
+ <span class="text-blue-400">"paragraphs"</span>: [<span class="text-green-400">"Combine text and visuals seamlessly."</span>]
1791
+ }
1792
+ ]
1793
+ }</code></pre>
1794
+
1795
+ <div class="mt-8 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
1796
+ <h4 class="text-yellow-400 font-bold mb-2"><i class="fa-solid fa-lightbulb mr-2"></i>Pro Tip
1797
+ </h4>
1798
+ <p class="text-sm text-yellow-200/80 m-0">Using <code>type: "auto"</code> simplifies prompt
1799
+ engineering for AI agents. The LLM only needs to provide the data structure, and Lumina
1800
+ will handle the visual representation automatically.</p>
1801
+ </div>
1802
+ </div>
1803
+
1804
+ <!-- AGENTS: STATE -->
1805
+ <div v-else-if="activeSection === 'agents-state'">
1806
+ <h1>State & Feedback Loop</h1>
1807
+ <p>Use <code>engine.exportState()</code> to get the current state for your LLM or agent. It returns a frozen object with <code>status</code>, <code>currentSlide</code> (<code>{ index, id, type, title }</code>), <code>narrative</code> (e.g. "User is on slide 3. Session: clicked 'Learn More' → next."), <code>engagementLevel</code> ("High" or "Low"), and <code>history</code> (recorded actions). Add this to your prompt for context-aware replies.</p>
1808
+ <pre><code><span class="text-blue-400">const</span> state = engine.exportState();
1809
+ <span class="text-gray-500">// "User is on slide ${state.currentSlide.index + 1} (${state.currentSlide.type}). ${state.narrative}"</span></code></pre>
1810
+ </div>
1811
+
1812
+ <!-- REF: STATEMENT -->
1813
+ <div v-else-if="activeSection === 'ref-statement'">
1814
+ <h1>Statement Slide</h1>
1815
+ <p class="lead">Used for high-impact titles, opening covers, or emphatic quotes. It is designed
1816
+ to be punchy and minimal.</p>
1817
+
1818
+ <div class="my-8">
1819
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1820
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1821
+ <table class="w-full text-left text-sm text-gray-400">
1822
+ <thead class="bg-white/5 text-white font-bold">
1823
+ <tr>
1824
+ <th class="p-3 border-b border-white/10">Property</th>
1825
+ <th class="p-3 border-b border-white/10">Type</th>
1826
+ <th class="p-3 border-b border-white/10">Description</th>
1827
+ </tr>
1828
+ </thead>
1829
+ <tbody class="divide-y divide-white/5">
1830
+ <tr>
1831
+ <td class="p-3 font-mono text-purple-400">type</td>
1832
+ <td class="p-3 font-mono text-xs">"statement"</td>
1833
+ <td class="p-3">Required. Identifies the layout.</td>
1834
+ </tr>
1835
+ <tr>
1836
+ <td class="p-3 font-mono text-blue-400">title</td>
1837
+ <td class="p-3 font-mono text-xs">string</td>
1838
+ <td class="p-3">Main headline. Keep it short (3-6 words).</td>
1839
+ </tr>
1840
+ <tr>
1841
+ <td class="p-3 font-mono text-blue-400">subtitle</td>
1842
+ <td class="p-3 font-mono text-xs">string?</td>
1843
+ <td class="p-3">Optional supporting text.</td>
1844
+ </tr>
1845
+ <tr>
1846
+ <td class="p-3 font-mono text-blue-400">tag</td>
1847
+ <td class="p-3 font-mono text-xs">string?</td>
1848
+ <td class="p-3">Small eyebrow tag above the title.</td>
1849
+ </tr>
1850
+ <tr>
1851
+ <td class="p-3 font-mono text-cyan-400">notes</td>
1852
+ <td class="p-3 font-mono text-xs">string?</td>
1853
+ <td class="p-3">Speaker notes (available on any slide via <code>SlideBase</code>).</td>
1854
+ </tr>
1855
+ </tbody>
1856
+ </table>
1857
+ </div>
1858
+ </div>
1859
+
1860
+ <div class="my-8">
1861
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1862
+ <LivePreview :initial-code="EXAMPLES.statement" />
1863
+ </div>
1864
+ </div>
1865
+
1866
+ <!-- REF: HALF -->
1867
+ <div v-else-if="activeSection === 'ref-half'">
1868
+ <h1>Half / Split Slide</h1>
1869
+ <p class="lead">Image or video on one side, text on the other. Ideal for product showcases or "about me" sections. <strong>One of <code>image</code> or <code>video</code> is required.</strong></p>
1870
+
1871
+ <div class="my-8">
1872
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1873
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1874
+ <table class="w-full text-left text-sm text-gray-400">
1875
+ <thead class="bg-white/5 text-white font-bold">
1876
+ <tr>
1877
+ <th class="p-3 border-b border-white/10">Property</th>
1878
+ <th class="p-3 border-b border-white/10">Type</th>
1879
+ <th class="p-3 border-b border-white/10">Description</th>
1880
+ </tr>
1881
+ </thead>
1882
+ <tbody class="divide-y divide-white/5">
1883
+ <tr>
1884
+ <td class="p-3 font-mono text-purple-400">type</td>
1885
+ <td class="p-3 font-mono text-xs">"half"</td>
1886
+ <td class="p-3">Required.</td>
1887
+ </tr>
1888
+ <tr>
1889
+ <td class="p-3 font-mono text-blue-400">image</td>
1890
+ <td class="p-3 font-mono text-xs">string?</td>
1891
+ <td class="p-3">Image URL. Omit if using <code>video</code>.</td>
1892
+ </tr>
1893
+ <tr>
1894
+ <td class="p-3 font-mono text-blue-400">video</td>
1895
+ <td class="p-3 font-mono text-xs">VideoProperties?</td>
1896
+ <td class="p-3"><code>{ type: "video", src, autoplay?, muted?, loop? }</code>. Use instead of <code>image</code>.</td>
1897
+ </tr>
1898
+ <tr>
1899
+ <td class="p-3 font-mono text-blue-400">imageSide</td>
1900
+ <td class="p-3 font-mono text-xs">"left" | "right"</td>
1901
+ <td class="p-3">Which side the media is on. Default: "left".</td>
1902
+ </tr>
1903
+ <tr>
1904
+ <td class="p-3 font-mono text-blue-400">tag</td>
1905
+ <td class="p-3 font-mono text-xs">string?</td>
1906
+ <td class="p-3">Optional eyebrow label above the title.</td>
1907
+ </tr>
1908
+ <tr>
1909
+ <td class="p-3 font-mono text-blue-400">title</td>
1910
+ <td class="p-3 font-mono text-xs">string</td>
1911
+ <td class="p-3">Headline text.</td>
1912
+ </tr>
1913
+ <tr>
1914
+ <td class="p-3 font-mono text-blue-400">paragraphs</td>
1915
+ <td class="p-3 font-mono text-xs">string[]</td>
1916
+ <td class="p-3">Array of text blocks.</td>
1917
+ </tr>
1918
+ <tr>
1919
+ <td class="p-3 font-mono text-blue-400">cta</td>
1920
+ <td class="p-3 font-mono text-xs">string?</td>
1921
+ <td class="p-3">Optional button label.</td>
1922
+ </tr>
1923
+ </tbody>
1924
+ </table>
1925
+ </div>
1926
+ </div>
1927
+
1928
+ <div class="my-8">
1929
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1930
+ <LivePreview :initial-code="EXAMPLES.half" />
1931
+ </div>
1932
+ </div>
1933
+
1934
+ <!-- REF: FEATURES -->
1935
+ <div v-else-if="activeSection === 'ref-features'">
1936
+ <h1>Features Slide</h1>
1937
+ <p class="lead">Displays a grid of cards, perfect for listing benefits, stats, or services.</p>
1938
+
1939
+ <div class="my-8">
1940
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1941
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1942
+ <table class="w-full text-left text-sm text-gray-400">
1943
+ <thead class="bg-white/5 text-white font-bold">
1944
+ <tr>
1945
+ <th class="p-3 border-b border-white/10">Property</th>
1946
+ <th class="p-3 border-b border-white/10">Type</th>
1947
+ <th class="p-3 border-b border-white/10">Description</th>
1948
+ </tr>
1949
+ </thead>
1950
+ <tbody class="divide-y divide-white/5">
1951
+ <tr>
1952
+ <td class="p-3 font-mono text-purple-400">type</td>
1953
+ <td class="p-3 font-mono text-xs">"features"</td>
1954
+ <td class="p-3">Required.</td>
1955
+ </tr>
1956
+ <tr>
1957
+ <td class="p-3 font-mono text-blue-400">title</td>
1958
+ <td class="p-3 font-mono text-xs">string</td>
1959
+ <td class="p-3">Section header.</td>
1960
+ </tr>
1961
+ <tr>
1962
+ <td class="p-3 font-mono text-blue-400">description</td>
1963
+ <td class="p-3 font-mono text-xs">string?</td>
1964
+ <td class="p-3">Optional context.</td>
1965
+ </tr>
1966
+ <tr>
1967
+ <td class="p-3 font-mono text-blue-400">features</td>
1968
+ <td class="p-3 font-mono text-xs">FeatureItem[]</td>
1969
+ <td class="p-3">Array: <code>{ class?, title, desc, icon?, id? }</code>. <code>class</code> can be <code>""</code>; <code>desc</code> or <code>description</code>.</td>
1970
+ </tr>
1971
+ </tbody>
1972
+ </table>
1973
+ </div>
1974
+ </div>
1975
+
1976
+ <div class="my-8">
1977
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1978
+ <LivePreview :initial-code="EXAMPLES.features" />
1979
+ </div>
1980
+ </div>
1981
+
1982
+ <!-- REF: TIMELINE -->
1983
+ <div v-else-if="activeSection === 'ref-timeline'">
1984
+ <h1>Timeline Slide</h1>
1985
+ <p class="lead">Vertical chronological list for events, roadmaps, or history. Do not confuse with <em>Timeline (keyframes)</em> for <code>slide.timelineTracks</code>.</p>
1986
+
1987
+ <div class="my-8">
1988
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1989
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1990
+ <table class="w-full text-left text-sm text-gray-400">
1991
+ <thead class="bg-white/5 text-white font-bold">
1992
+ <tr>
1993
+ <th class="p-3 border-b border-white/10">Property</th>
1994
+ <th class="p-3 border-b border-white/10">Type</th>
1995
+ <th class="p-3 border-b border-white/10">Description</th>
1996
+ </tr>
1997
+ </thead>
1998
+ <tbody class="divide-y divide-white/5">
1999
+ <tr>
2000
+ <td class="p-3 font-mono text-purple-400">type</td>
2001
+ <td class="p-3 font-mono text-xs">"timeline"</td>
2002
+ <td class="p-3">Required.</td>
2003
+ </tr>
2004
+ <tr>
2005
+ <td class="p-3 font-mono text-blue-400">title</td>
2006
+ <td class="p-3 font-mono text-xs">string</td>
2007
+ <td class="p-3">Header.</td>
2008
+ </tr>
2009
+ <tr>
2010
+ <td class="p-3 font-mono text-blue-400">subtitle</td>
2011
+ <td class="p-3 font-mono text-xs">string?</td>
2012
+ <td class="p-3">Optional.</td>
2013
+ </tr>
2014
+ <tr>
2015
+ <td class="p-3 font-mono text-blue-400">timeline</td>
2016
+ <td class="p-3 font-mono text-xs">TimelineItem[]</td>
2017
+ <td class="p-3">Array: <code>{ date, title, description, icon?, id? }</code>. Aliases: <code>t</code>→title, <code>desc</code>→description.</td>
2018
+ </tr>
2019
+ </tbody>
2020
+ </table>
2021
+ </div>
2022
+ </div>
2023
+
2024
+ <div class="my-8">
2025
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2026
+ <LivePreview :initial-code="EXAMPLES.timeline" />
2027
+ </div>
2028
+ </div>
2029
+
2030
+ <!-- REF: STEPS -->
2031
+ <div v-else-if="activeSection === 'ref-steps'">
2032
+ <h1>Steps Slide</h1>
2033
+ <p class="lead">Used for tutorials, "how-to" guides, or process flows.</p>
2034
+
2035
+ <div class="my-8">
2036
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2037
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2038
+ <table class="w-full text-left text-sm text-gray-400">
2039
+ <thead class="bg-white/5 text-white font-bold">
2040
+ <tr>
2041
+ <th class="p-3 border-b border-white/10">Property</th>
2042
+ <th class="p-3 border-b border-white/10">Type</th>
2043
+ <th class="p-3 border-b border-white/10">Description</th>
2044
+ </tr>
2045
+ </thead>
2046
+ <tbody class="divide-y divide-white/5">
2047
+ <tr>
2048
+ <td class="p-3 font-mono text-purple-400">type</td>
2049
+ <td class="p-3 font-mono text-xs">"steps"</td>
2050
+ <td class="p-3">Required.</td>
2051
+ </tr>
2052
+ <tr>
2053
+ <td class="p-3 font-mono text-blue-400">title</td>
2054
+ <td class="p-3 font-mono text-xs">string</td>
2055
+ <td class="p-3">Process name.</td>
2056
+ </tr>
2057
+ <tr>
2058
+ <td class="p-3 font-mono text-blue-400">subtitle</td>
2059
+ <td class="p-3 font-mono text-xs">string?</td>
2060
+ <td class="p-3">Optional.</td>
2061
+ </tr>
2062
+ <tr>
2063
+ <td class="p-3 font-mono text-blue-400">steps</td>
2064
+ <td class="p-3 font-mono text-xs">StepItem[]</td>
2065
+ <td class="p-3">Array: <code>{ step, title, description?, icon?, id? }</code>. <code>step</code> is e.g. <code>"01"</code>.</td>
2066
+ </tr>
2067
+ </tbody>
2068
+ </table>
2069
+ </div>
2070
+ </div>
2071
+
2072
+ <div class="my-8">
2073
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2074
+ <LivePreview :initial-code="EXAMPLES.steps" />
2075
+ </div>
2076
+ </div>
2077
+
2078
+ <!-- REF: FLEX -->
2079
+ <div v-else-if="activeSection === 'ref-flex'">
2080
+ <h1>Flex Layout</h1>
2081
+ <p class="lead">Flow-based layout for composing slides using semantic sizing. No coordinates
2082
+ needed—perfect for LLM generation.</p>
2083
+
2084
+ <div class="my-8">
2085
+ <h2 class="text-xl font-bold text-white mb-4">Slide Options</h2>
2086
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2087
+ <table class="w-full text-left text-sm text-gray-400">
2088
+ <thead class="bg-white/5 text-white font-bold">
2089
+ <tr>
2090
+ <th class="p-3 border-b border-white/10">Property</th>
2091
+ <th class="p-3 border-b border-white/10">Type</th>
2092
+ <th class="p-3 border-b border-white/10">Description</th>
2093
+ </tr>
2094
+ </thead>
2095
+ <tbody class="divide-y divide-white/5">
2096
+ <tr>
2097
+ <td class="p-3 font-mono text-purple-400">type</td>
2098
+ <td class="p-3 font-mono text-xs">"flex"</td>
2099
+ <td class="p-3">Required.</td>
2100
+ </tr>
2101
+ <tr>
2102
+ <td class="p-3 font-mono text-blue-400">direction</td>
2103
+ <td class="p-3 font-mono text-xs">"horizontal" | "vertical"</td>
2104
+ <td class="p-3">Main flow direction. Default: "horizontal".</td>
2105
+ </tr>
2106
+ <tr>
2107
+ <td class="p-3 font-mono text-blue-400">gap</td>
2108
+ <td class="p-3 font-mono text-xs">SpacingToken</td>
2109
+ <td class="p-3">Gap between elements. Default: "none".</td>
2110
+ </tr>
2111
+ <tr>
2112
+ <td class="p-3 font-mono text-blue-400">padding</td>
2113
+ <td class="p-3 font-mono text-xs">SpacingToken</td>
2114
+ <td class="p-3">Container padding. Default: "none".</td>
2115
+ </tr>
2116
+ <tr>
2117
+ <td class="p-3 font-mono text-blue-400">halign</td>
2118
+ <td class="p-3 font-mono text-xs">"left" | "center" | "right"</td>
2119
+ <td class="p-3">Horizontal alignment of the flex group. Default: "left".</td>
2120
+ </tr>
2121
+ <tr>
2122
+ <td class="p-3 font-mono text-blue-400">valign</td>
2123
+ <td class="p-3 font-mono text-xs">"top" | "center" | "bottom"</td>
2124
+ <td class="p-3">Vertical alignment of the flex group. Default: "top".</td>
2125
+ </tr>
2126
+ <tr>
2127
+ <td class="p-3 font-mono text-blue-400">elements</td>
2128
+ <td class="p-3 font-mono text-xs">FlexElement[]</td>
2129
+ <td class="p-3">Array of flex elements in flow order. Each can have <code>size</code> (FlexSize).</td>
2130
+ </tr>
2131
+ </tbody>
2132
+ </table>
2133
+ </div>
2134
+ </div>
2135
+
2136
+ <div class="my-8">
2137
+ <h2 class="text-xl font-bold text-white mb-4">Size Tokens</h2>
2138
+ <p>Each element can have a <code>size</code> property to control how much space it occupies:
2139
+ </p>
2140
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 my-6 not-prose">
2141
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>auto</code><br><span
2142
+ class="text-xs text-white/40">flexible</span></div>
2143
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>quarter</code><br><span
2144
+ class="text-xs text-white/40">25%</span></div>
2145
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>third</code><br><span
2146
+ class="text-xs text-white/40">33%</span></div>
2147
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>half</code><br><span
2148
+ class="text-xs text-white/40">50%</span></div>
2149
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>two-thirds</code><br><span
2150
+ class="text-xs text-white/40">66%</span></div>
2151
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>three-quarters</code><br><span
2152
+ class="text-xs text-white/40">75%</span></div>
2153
+ <div class="p-4 bg-white/5 rounded-lg text-center"><code>full</code><br><span
2154
+ class="text-xs text-white/40">100%</span></div>
2155
+ </div>
2156
+ </div>
2157
+
2158
+ <div class="my-8">
2159
+ <h2 class="text-xl font-bold text-white mb-4">Element Types</h2>
2160
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2161
+ <table class="w-full text-left text-sm text-gray-400">
2162
+ <thead class="bg-white/5 text-white font-bold">
2163
+ <tr>
2164
+ <th class="p-3 border-b border-white/10">Type</th>
2165
+ <th class="p-3 border-b border-white/10">Properties</th>
2166
+ <th class="p-3 border-b border-white/10">Description</th>
2167
+ </tr>
2168
+ </thead>
2169
+ <tbody class="divide-y divide-white/5">
2170
+ <tr>
2171
+ <td class="p-3 font-mono text-cyan-400">content</td>
2172
+ <td class="p-3 font-mono text-xs">elements, valign, halign, gap, padding
2173
+ </td>
2174
+ <td class="p-3">Container that groups child elements vertically.</td>
2175
+ </tr>
2176
+ <tr>
2177
+ <td class="p-3 font-mono text-cyan-400">image</td>
2178
+ <td class="p-3 font-mono text-xs">src, alt?, fill?, fit?, rounded?</td>
2179
+ <td class="p-3">Visual media element.</td>
2180
+ </tr>
2181
+ <tr>
2182
+ <td class="p-3 font-mono text-cyan-400">title</td>
2183
+ <td class="p-3 font-mono text-xs">text, size?, align?</td>
2184
+ <td class="p-3">Large heading (lg | xl | 2xl | 3xl).</td>
2185
+ </tr>
2186
+ <tr>
2187
+ <td class="p-3 font-mono text-cyan-400">text</td>
2188
+ <td class="p-3 font-mono text-xs">text, align?, muted?</td>
2189
+ <td class="p-3">Body paragraph text.</td>
2190
+ </tr>
2191
+ <tr>
2192
+ <td class="p-3 font-mono text-cyan-400">bullets</td>
2193
+ <td class="p-3 font-mono text-xs">items: string[]</td>
2194
+ <td class="p-3">Unordered bullet list.</td>
2195
+ </tr>
2196
+ <tr>
2197
+ <td class="p-3 font-mono text-cyan-400">ordered</td>
2198
+ <td class="p-3 font-mono text-xs">items: string[]</td>
2199
+ <td class="p-3">Numbered list.</td>
2200
+ </tr>
2201
+ <tr>
2202
+ <td class="p-3 font-mono text-cyan-400">button</td>
2203
+ <td class="p-3 font-mono text-xs">label, action?, variant?, fullWidth?</td>
2204
+ <td class="p-3">CTA button (primary | secondary | outline | ghost).</td>
2205
+ </tr>
2206
+ <tr>
2207
+ <td class="p-3 font-mono text-cyan-400">timeline</td>
2208
+ <td class="p-3 font-mono text-xs">items: TimelineItem[], compact?</td>
2209
+ <td class="p-3">Embedded timeline.</td>
2210
+ </tr>
2211
+ <tr>
2212
+ <td class="p-3 font-mono text-cyan-400">stepper</td>
2213
+ <td class="p-3 font-mono text-xs">items: StepItem[], compact?</td>
2214
+ <td class="p-3">Embedded step process.</td>
2215
+ </tr>
2216
+ <tr>
2217
+ <td class="p-3 font-mono text-cyan-400">spacer</td>
2218
+ <td class="p-3 font-mono text-xs">size?: SpacingToken</td>
2219
+ <td class="p-3">Adds visual spacing.</td>
2220
+ </tr>
2221
+ </tbody>
2222
+ </table>
2223
+ </div>
2224
+ </div>
2225
+
2226
+ <div class="my-8">
2227
+ <h2 class="text-xl font-bold text-white mb-4">Content Container</h2>
2228
+ <p>Use <code>content</code> to group related elements with alignment control:</p>
2229
+ <div class="overflow-x-auto border border-white/10 rounded-lg mt-6">
2230
+ <table class="w-full text-left text-sm text-gray-400">
2231
+ <thead class="bg-white/5 text-white font-bold">
2232
+ <tr>
2233
+ <th class="p-3 border-b border-white/10">Property</th>
2234
+ <th class="p-3 border-b border-white/10">Values</th>
2235
+ <th class="p-3 border-b border-white/10">Default</th>
2236
+ </tr>
2237
+ </thead>
2238
+ <tbody class="divide-y divide-white/5">
2239
+ <tr>
2240
+ <td class="p-3 font-mono text-blue-400">valign</td>
2241
+ <td class="p-3 font-mono text-xs">top | center | bottom</td>
2242
+ <td class="p-3">center</td>
2243
+ </tr>
2244
+ <tr>
2245
+ <td class="p-3 font-mono text-blue-400">halign</td>
2246
+ <td class="p-3 font-mono text-xs">left | center | right</td>
2247
+ <td class="p-3">left</td>
2248
+ </tr>
2249
+ <tr>
2250
+ <td class="p-3 font-mono text-blue-400">gap</td>
2251
+ <td class="p-3 font-mono text-xs">none | xs | sm | md | lg | xl | 2xl</td>
2252
+ <td class="p-3">md</td>
2253
+ </tr>
2254
+ <tr>
2255
+ <td class="p-3 font-mono text-blue-400">padding</td>
2256
+ <td class="p-3 font-mono text-xs">none | xs | sm | md | lg | xl | 2xl</td>
2257
+ <td class="p-3">lg</td>
2258
+ </tr>
2259
+ </tbody>
2260
+ </table>
2261
+ </div>
2262
+ </div>
2263
+
2264
+ <div class="my-8">
2265
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2266
+ <LivePreview :initial-code="EXAMPLES.flex" />
2267
+ </div>
2268
+ </div>
2269
+
2270
+ <!-- REF: CHART -->
2271
+ <div v-else-if="activeSection === 'ref-chart'">
2272
+ <h1>Chart Slide</h1>
2273
+ <p class="lead">Visualize data extracted from CSV/Excel files. Perfect for LLM-generated reports
2274
+ and dashboards.</p>
2275
+
2276
+ <div class="my-8">
2277
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2278
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2279
+ <table class="w-full text-left text-sm text-gray-400">
2280
+ <thead class="bg-white/5 text-white font-bold">
2281
+ <tr>
2282
+ <th class="p-3 border-b border-white/10">Property</th>
2283
+ <th class="p-3 border-b border-white/10">Type</th>
2284
+ <th class="p-3 border-b border-white/10">Description</th>
2285
+ </tr>
2286
+ </thead>
2287
+ <tbody class="divide-y divide-white/5">
2288
+ <tr>
2289
+ <td class="p-3 font-mono text-purple-400">type</td>
2290
+ <td class="p-3 font-mono text-xs">"chart"</td>
2291
+ <td class="p-3">Required.</td>
2292
+ </tr>
2293
+ <tr>
2294
+ <td class="p-3 font-mono text-blue-400">chartType</td>
2295
+ <td class="p-3 font-mono text-xs">"bar" | "line" | "pie" | "doughnut"</td>
2296
+ <td class="p-3">Type of chart to render.</td>
2297
+ </tr>
2298
+ <tr>
2299
+ <td class="p-3 font-mono text-blue-400">title</td>
2300
+ <td class="p-3 font-mono text-xs">string?</td>
2301
+ <td class="p-3">Chart title (optional).</td>
2302
+ </tr>
2303
+ <tr>
2304
+ <td class="p-3 font-mono text-blue-400">subtitle</td>
2305
+ <td class="p-3 font-mono text-xs">string?</td>
2306
+ <td class="p-3">Subtitle text (optional).</td>
2307
+ </tr>
2308
+ <tr>
2309
+ <td class="p-3 font-mono text-blue-400">data</td>
2310
+ <td class="p-3 font-mono text-xs">ChartData</td>
2311
+ <td class="p-3">Object with <code>labels</code> and <code>datasets</code>.
2312
+ </td>
2313
+ </tr>
2314
+ </tbody>
2315
+ </table>
2316
+ </div>
2317
+ </div>
2318
+
2319
+ <div class="my-8">
2320
+ <h2 class="text-xl font-bold text-white mb-4">Data Structure</h2>
2321
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2322
+ <table class="w-full text-left text-sm text-gray-400">
2323
+ <thead class="bg-white/5 text-white font-bold">
2324
+ <tr>
2325
+ <th class="p-3 border-b border-white/10">Property</th>
2326
+ <th class="p-3 border-b border-white/10">Type</th>
2327
+ <th class="p-3 border-b border-white/10">Description</th>
2328
+ </tr>
2329
+ </thead>
2330
+ <tbody class="divide-y divide-white/5">
2331
+ <tr>
2332
+ <td class="p-3 font-mono text-cyan-400">data.labels</td>
2333
+ <td class="p-3 font-mono text-xs">string[]</td>
2334
+ <td class="p-3">X-axis labels or pie segment names.</td>
2335
+ </tr>
2336
+ <tr>
2337
+ <td class="p-3 font-mono text-cyan-400">data.datasets</td>
2338
+ <td class="p-3 font-mono text-xs">Dataset[]</td>
2339
+ <td class="p-3">Each with <code>label</code>, <code>values</code>, and
2340
+ optional <code>color</code>.</td>
2341
+ </tr>
2342
+ <tr>
2343
+ <td class="p-3 font-mono text-cyan-400">dataset.color</td>
2344
+ <td class="p-3 font-mono text-xs">string?</td>
2345
+ <td class="p-3"><code>c:p</code> (primary), <code>c:s</code> (secondary),
2346
+ <code>c:m</code> (muted), or hex.
2347
+ </td>
2348
+ </tr>
2349
+ </tbody>
2350
+ </table>
2351
+ </div>
2352
+ </div>
2353
+
2354
+ <!-- Chart Types Section -->
2355
+ <div class="my-12">
2356
+ <h2 class="text-2xl font-bold text-white mb-6">Chart Types</h2>
2357
+
2358
+ <div class="space-y-12">
2359
+ <!-- Bar Chart -->
2360
+ <div class="p-6 rounded-2xl bg-white/[0.02] border border-white/5">
2361
+ <div class="flex items-center gap-3 mb-4">
2362
+ <div
2363
+ class="w-10 h-10 rounded-lg bg-blue-500/20 text-blue-400 flex items-center justify-center">
2364
+ <i class="fa-solid fa-chart-column"></i>
2365
+ </div>
2366
+ <div>
2367
+ <h3 class="text-xl font-bold text-white">Bar Chart</h3>
2368
+ <p class="text-sm text-white/50">Best for comparing discrete categories</p>
2369
+ </div>
2370
+ </div>
2371
+ <p class="text-white/60 text-sm mb-4">Use bar charts to compare values across
2372
+ categories. Ideal for revenue by quarter, sales by region, or any categorical
2373
+ comparison.</p>
2374
+ <LivePreview initial-code='{
2375
+ "type": "chart",
2376
+ "chartType": "bar",
2377
+ "title": "Sales by Region",
2378
+ "subtitle": "Q4 2024 Performance",
2379
+ "data": {
2380
+ "labels": ["North", "South", "East", "West"],
2381
+ "datasets": [{
2382
+ "label": "Sales ($K)",
2383
+ "values": [420, 380, 510, 290],
2384
+ "color": "c:p"
2385
+ }]
2386
+ }
2387
+ }' />
2388
+ </div>
2389
+
2390
+ <!-- Line Chart -->
2391
+ <div class="p-6 rounded-2xl bg-white/[0.02] border border-white/5">
2392
+ <div class="flex items-center gap-3 mb-4">
2393
+ <div
2394
+ class="w-10 h-10 rounded-lg bg-green-500/20 text-green-400 flex items-center justify-center">
2395
+ <i class="fa-solid fa-chart-line"></i>
2396
+ </div>
2397
+ <div>
2398
+ <h3 class="text-xl font-bold text-white">Line Chart</h3>
2399
+ <p class="text-sm text-white/50">Best for trends over time</p>
2400
+ </div>
2401
+ </div>
2402
+ <p class="text-white/60 text-sm mb-4">Use line charts to show trends and changes
2403
+ over time. Perfect for growth metrics, stock prices, or any time-series data.
2404
+ </p>
2405
+ <LivePreview initial-code='{
2406
+ "type": "chart",
2407
+ "chartType": "line",
2408
+ "title": "Monthly Active Users",
2409
+ "subtitle": "2024 Growth Trend",
2410
+ "data": {
2411
+ "labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
2412
+ "datasets": [{
2413
+ "label": "Users (K)",
2414
+ "values": [45, 52, 61, 78, 95, 120],
2415
+ "color": "#10b981"
2416
+ }]
2417
+ }
2418
+ }' />
2419
+ </div>
2420
+
2421
+ <!-- Pie Chart -->
2422
+ <div class="p-6 rounded-2xl bg-white/[0.02] border border-white/5">
2423
+ <div class="flex items-center gap-3 mb-4">
2424
+ <div
2425
+ class="w-10 h-10 rounded-lg bg-purple-500/20 text-purple-400 flex items-center justify-center">
2426
+ <i class="fa-solid fa-chart-pie"></i>
2427
+ </div>
2428
+ <div>
2429
+ <h3 class="text-xl font-bold text-white">Pie Chart</h3>
2430
+ <p class="text-sm text-white/50">Best for showing proportions</p>
2431
+ </div>
2432
+ </div>
2433
+ <p class="text-white/60 text-sm mb-4">Use pie charts to show how parts make up a
2434
+ whole. Ideal for market share, budget allocation, or percentage distributions.
2435
+ </p>
2436
+ <LivePreview initial-code='{
2437
+ "type": "chart",
2438
+ "chartType": "pie",
2439
+ "title": "Market Share",
2440
+ "subtitle": "Industry Distribution Q4 2024",
2441
+ "data": {
2442
+ "labels": ["Product A", "Product B", "Product C", "Others"],
2443
+ "datasets": [{
2444
+ "label": "Share %",
2445
+ "values": [35, 28, 22, 15]
2446
+ }]
2447
+ }
2448
+ }' />
2449
+ </div>
2450
+
2451
+ <!-- Doughnut Chart -->
2452
+ <div class="p-6 rounded-2xl bg-white/[0.02] border border-white/5">
2453
+ <div class="flex items-center gap-3 mb-4">
2454
+ <div
2455
+ class="w-10 h-10 rounded-lg bg-amber-500/20 text-amber-400 flex items-center justify-center">
2456
+ <i class="fa-solid fa-circle-notch"></i>
2457
+ </div>
2458
+ <div>
2459
+ <h3 class="text-xl font-bold text-white">Doughnut Chart</h3>
2460
+ <p class="text-sm text-white/50">Modern alternative to pie charts</p>
2461
+ </div>
2462
+ </div>
2463
+ <p class="text-white/60 text-sm mb-4">Similar to pie charts but with a hollow
2464
+ center. The modern look works great for dashboards and can display a metric in
2465
+ the center.</p>
2466
+ <LivePreview initial-code='{
2467
+ "type": "chart",
2468
+ "chartType": "doughnut",
2469
+ "title": "Budget Allocation",
2470
+ "subtitle": "2025 Planned Spend",
2471
+ "data": {
2472
+ "labels": ["Engineering", "Marketing", "Sales", "Operations"],
2473
+ "datasets": [{
2474
+ "label": "Budget",
2475
+ "values": [40, 25, 20, 15]
2476
+ }]
2477
+ }
2478
+ }' />
2479
+ </div>
2480
+ </div>
2481
+ </div>
2482
+
2483
+ <!-- Multiple Datasets Example -->
2484
+ <div class="my-12">
2485
+ <h2 class="text-2xl font-bold text-white mb-6">Advanced: Multiple Datasets</h2>
2486
+ <p class="text-white/60 mb-6">Bar and line charts support multiple datasets for comparisons.
2487
+ Each dataset gets its own color and legend entry.</p>
2488
+ <LivePreview initial-code='{
2489
+ "type": "chart",
2490
+ "chartType": "bar",
2491
+ "title": "Year-over-Year Comparison",
2492
+ "subtitle": "Revenue by Quarter",
2493
+ "data": {
2494
+ "labels": ["Q1", "Q2", "Q3", "Q4"],
2495
+ "datasets": [
2496
+ {
2497
+ "label": "2023",
2498
+ "values": [100, 120, 140, 160],
2499
+ "color": "#6b7280"
2500
+ },
2501
+ {
2502
+ "label": "2024",
2503
+ "values": [120, 150, 180, 220],
2504
+ "color": "c:p"
2505
+ }
2506
+ ]
2507
+ },
2508
+ video: `{
2509
+ "type": "statement",
2510
+ "sizing": "container",
2511
+ "meta": { "orbColor": "#ec4899" },
2512
+ "background": {
2513
+ "type": "video",
2514
+ "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
2515
+ "opacity": 0.4
2516
+ },
2517
+ "tag": "Cinematic",
2518
+ "title": "Video Backgrounds",
2519
+ "subtitle": "Immersive support."
2520
+ }`
2521
+ }' />
2522
+ </div>
2523
+
2524
+ <!-- Color Tokens -->
2525
+ <div class="my-8">
2526
+ <h2 class="text-xl font-bold text-white mb-4">Color Tokens</h2>
2527
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2528
+ <table class="w-full text-left text-sm text-gray-400">
2529
+ <thead class="bg-white/5 text-white font-bold">
2530
+ <tr>
2531
+ <th class="p-3 border-b border-white/10">Token</th>
2532
+ <th class="p-3 border-b border-white/10">Maps To</th>
2533
+ <th class="p-3 border-b border-white/10">Default</th>
2534
+ </tr>
2535
+ </thead>
2536
+ <tbody class="divide-y divide-white/5">
2537
+ <tr>
2538
+ <td class="p-3 font-mono text-blue-400">c:p</td>
2539
+ <td class="p-3">--lumina-colors-primary</td>
2540
+ <td class="p-3"><span
2541
+ class="inline-block w-4 h-4 rounded bg-blue-500 mr-2"></span>#3b82f6
2542
+ </td>
2543
+ </tr>
2544
+ <tr>
2545
+ <td class="p-3 font-mono text-green-400">c:s</td>
2546
+ <td class="p-3">--lumina-colors-secondary</td>
2547
+ <td class="p-3"><span
2548
+ class="inline-block w-4 h-4 rounded bg-emerald-500 mr-2"></span>#10b981
2549
+ </td>
2550
+ </tr>
2551
+ <tr>
2552
+ <td class="p-3 font-mono text-gray-400">c:m</td>
2553
+ <td class="p-3">--lumina-colors-muted</td>
2554
+ <td class="p-3"><span
2555
+ class="inline-block w-4 h-4 rounded bg-gray-400 mr-2"></span>#9ca3af
2556
+ </td>
2557
+ </tr>
2558
+ <tr>
2559
+ <td class="p-3 font-mono text-pink-400">#ec4899</td>
2560
+ <td class="p-3">Direct hex color</td>
2561
+ <td class="p-3"><span
2562
+ class="inline-block w-4 h-4 rounded bg-pink-500 mr-2"></span>Custom
2563
+ </td>
2564
+ </tr>
2565
+ </tbody>
2566
+ </table>
2567
+ </div>
2568
+ </div>
2569
+
2570
+ <div class="mt-8 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
2571
+ <h4 class="text-yellow-400 font-bold mb-2"><i class="fa-solid fa-lightbulb mr-2"></i>Pro
2572
+ Tips
2573
+ </h4>
2574
+ <ul class="text-sm text-yellow-200/80 space-y-2 m-0 list-none">
2575
+ <li>📊 <strong>Bar charts</strong> work best with 4-8 categories</li>
2576
+ <li>📈 <strong>Line charts</strong> need at least 3 data points for smooth curves</li>
2577
+ <li>🥧 <strong>Pie/Doughnut</strong> should have 3-6 segments max for clarity</li>
2578
+ <li>🤖 <strong>LLM Tip:</strong> Have agents extract tabular data from CSVs and generate
2579
+ charts automatically</li>
2580
+ </ul>
2581
+ </div>
2582
+ </div>
2583
+
2584
+ <!-- REF: VIDEO -->
2585
+ <div v-else-if="activeSection === 'ref-video'">
2586
+ <h1>Video Slide</h1>
2587
+ <p class="lead">Full-screen video player with optional title overlay (shown on hover).</p>
2588
+
2589
+ <div class="my-8">
2590
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2591
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2592
+ <table class="w-full text-left text-sm text-gray-400">
2593
+ <thead class="bg-white/5 text-white font-bold">
2594
+ <tr>
2595
+ <th class="p-3 border-b border-white/10">Property</th>
2596
+ <th class="p-3 border-b border-white/10">Type</th>
2597
+ <th class="p-3 border-b border-white/10">Description</th>
2598
+ </tr>
2599
+ </thead>
2600
+ <tbody class="divide-y divide-white/5">
2601
+ <tr>
2602
+ <td class="p-3 font-mono text-purple-400">type</td>
2603
+ <td class="p-3 font-mono text-xs">"video"</td>
2604
+ <td class="p-3">Required.</td>
2605
+ </tr>
2606
+ <tr>
2607
+ <td class="p-3 font-mono text-blue-400">video</td>
2608
+ <td class="p-3 font-mono text-xs">VideoProperties</td>
2609
+ <td class="p-3">Required. <code>src</code>, and optionally <code>poster</code>, <code>autoplay</code>, <code>loop</code>, <code>muted</code>, <code>controls</code>, <code>className</code>.</td>
2610
+ </tr>
2611
+ <tr>
2612
+ <td class="p-3 font-mono text-blue-400">title</td>
2613
+ <td class="p-3 font-mono text-xs">string?</td>
2614
+ <td class="p-3">Caption overlay at bottom-left, visible on hover.</td>
2615
+ </tr>
2616
+ </tbody>
2617
+ </table>
2618
+ </div>
2619
+ </div>
2620
+
2621
+ <div class="my-8">
2622
+ <h2 class="text-xl font-bold text-white mb-4">VideoProperties</h2>
2623
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2624
+ <table class="w-full text-left text-sm text-gray-400">
2625
+ <thead class="bg-white/5 text-white font-bold">
2626
+ <tr>
2627
+ <th class="p-3 border-b border-white/10">Property</th>
2628
+ <th class="p-3 border-b border-white/10">Type</th>
2629
+ <th class="p-3 border-b border-white/10">Description</th>
2630
+ </tr>
2631
+ </thead>
2632
+ <tbody class="divide-y divide-white/5">
2633
+ <tr><td class="p-3 font-mono text-cyan-400">src</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Required. Video URL.</td></tr>
2634
+ <tr><td class="p-3 font-mono text-cyan-400">poster</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Poster image URL.</td></tr>
2635
+ <tr><td class="p-3 font-mono text-cyan-400">autoplay</td><td class="p-3 font-mono text-xs">boolean?</td><td class="p-3">Default: false.</td></tr>
2636
+ <tr><td class="p-3 font-mono text-cyan-400">loop</td><td class="p-3 font-mono text-xs">boolean?</td><td class="p-3">Default: false.</td></tr>
2637
+ <tr><td class="p-3 font-mono text-cyan-400">muted</td><td class="p-3 font-mono text-xs">boolean?</td><td class="p-3">Default: false. Often true with autoplay for autoplay to work.</td></tr>
2638
+ <tr><td class="p-3 font-mono text-cyan-400">controls</td><td class="p-3 font-mono text-xs">boolean?</td><td class="p-3">Default: false. Show play/seek/volume.</td></tr>
2639
+ <tr><td class="p-3 font-mono text-cyan-400">className</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">CSS class for the title overlay container.</td></tr>
2640
+ </tbody>
2641
+ </table>
2642
+ </div>
2643
+ </div>
2644
+
2645
+ <div class="my-8">
2646
+ <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2647
+ <LivePreview :initial-code="EXAMPLES.video_slide" />
2648
+ </div>
2649
+
2650
+ <p class="text-white/60 text-sm">Element control ids: <code>s{N}-video</code>, <code>s{N}-title</code>.</p>
2651
+ </div>
2652
+
2653
+ <!-- REF: CUSTOM HTML -->
2654
+ <div v-else-if="activeSection === 'ref-custom'">
2655
+ <h1>Custom HTML Slide</h1>
2656
+ <p class="lead">Full control with raw HTML content. Inject any HTML structure with custom
2657
+ styling.</p>
2658
+
2659
+ <div class="my-8">
2660
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2661
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2662
+ <table class="w-full text-left text-sm text-gray-400">
2663
+ <thead class="bg-white/5 text-white font-bold">
2664
+ <tr>
2665
+ <th class="p-3 border-b border-white/10">Property</th>
2666
+ <th class="p-3 border-b border-white/10">Type</th>
2667
+ <th class="p-3 border-b border-white/10">Description</th>
2668
+ </tr>
2669
+ </thead>
2670
+ <tbody class="divide-y divide-white/5">
2671
+ <tr>
2672
+ <td class="p-3 font-mono text-purple-400">type</td>
2673
+ <td class="p-3 font-mono text-xs">"custom"</td>
2674
+ <td class="p-3">Required.</td>
2675
+ </tr>
2676
+ <tr>
2677
+ <td class="p-3 font-mono text-blue-400">html</td>
2678
+ <td class="p-3 font-mono text-xs">string</td>
2679
+ <td class="p-3">Raw HTML content to render.</td>
2680
+ </tr>
2681
+ <tr>
2682
+ <td class="p-3 font-mono text-blue-400">css</td>
2683
+ <td class="p-3 font-mono text-xs">string?</td>
2684
+ <td class="p-3">Optional scoped CSS styles.</td>
2685
+ </tr>
2686
+ </tbody>
2687
+ </table>
2688
+ </div>
2689
+ </div>
2690
+
2691
+ <div class="my-8">
2692
+ <h2 class="text-xl font-bold text-white mb-4">Basic Example</h2>
2693
+ <p class="text-white/60 mb-6">Render any HTML structure with full styling control.</p>
2694
+ <LivePreview initial-code='{
2695
+ "type": "custom",
2696
+ "html": "<div class=\"custom-slide\"><h1 class=\"custom-title\">Welcome!</h1><p class=\"custom-text\">This is fully custom HTML content.</p><div class=\"custom-grid\"><div class=\"custom-card\">Card 1</div><div class=\"custom-card\">Card 2</div><div class=\"custom-card\">Card 3</div></div></div>",
2697
+ "css": ".custom-slide { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100%; padding: 2rem; } .custom-title { font-size: 3rem; font-weight: bold; margin-bottom: 1rem; background: linear-gradient(90deg, #3b82f6, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .custom-text { font-size: 1.25rem; opacity: 0.7; margin-bottom: 2rem; } .custom-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; } .custom-card { padding: 2rem; background: rgba(255,255,255,0.05); border-radius: 1rem; border: 1px solid rgba(255,255,255,0.1); text-align: center; transition: transform 0.2s; } .custom-card:hover { transform: translateY(-4px); }"
2698
+ }' />
2699
+ </div>
2700
+
2701
+ <div class="my-8">
2702
+ <h2 class="text-xl font-bold text-white mb-4">Use Cases</h2>
2703
+ <ul class="space-y-3 text-white/70">
2704
+ <li><strong>Embedding iframes:</strong> YouTube videos, maps, or external widgets</li>
2705
+ <li><strong>Custom visualizations:</strong> D3.js charts, SVG animations</li>
2706
+ <li><strong>Brand-specific layouts:</strong> Complex designs not covered by built-in
2707
+ layouts</li>
2708
+ <li><strong>Interactive components:</strong> Forms, quizzes, or custom buttons</li>
2709
+ </ul>
2710
+ </div>
2711
+
2712
+ <div class="my-8">
2713
+ <h2 class="text-xl font-bold text-white mb-4">Sanitization</h2>
2714
+ <p class="text-white/70 mb-2">Before rendering, the following are removed for security: <code>&lt;script&gt;</code> tags, <code>on*</code> attribute handlers (e.g. <code>onclick=</code>), and <code>javascript:</code> URLs. Sanitize or escape user-generated HTML for other XSS vectors if needed.</p>
2715
+ <p class="text-white/60 text-sm">The <code>css</code> string is injected into <code>document.head</code> as a <code>&lt;style&gt;</code> and removed when the slide unmounts.</p>
2716
+ </div>
2717
+
2718
+ <div class="mt-8 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
2719
+ <h4 class="text-yellow-400 font-bold mb-2"><i class="fa-solid fa-lightbulb mr-2"></i>Pro
2720
+ Tips</h4>
2721
+ <ul class="text-sm text-yellow-200/80 space-y-2 m-0 list-none">
2722
+ <li>🎨 <strong>Scoped CSS:</strong> Styles are injected in <code>&lt;head&gt;</code>; use unique class names to avoid clashes with Lumina.</li>
2723
+ <li>📱 <strong>Responsive:</strong> Use CSS Grid or Flexbox with relative units for
2724
+ mobile support</li>
2725
+ <li>🤖 <strong>LLM Usage:</strong> Custom slides are great when the AI needs complete
2726
+ creative freedom</li>
2727
+ </ul>
2728
+ </div>
2729
+ </div>
2730
+
2731
+ <!-- REF: DIAGRAM -->
2732
+ <div v-else-if="activeSection === 'ref-diagram'">
2733
+ <h1>Diagram Slide</h1>
2734
+ <p class="lead">Interactive node-based diagram using Vue Flow. Supports nodes, edges, and (in Studio) drag-and-drop and inline editing.</p>
2735
+
2736
+ <div class="my-8">
2737
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2738
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2739
+ <table class="w-full text-left text-sm text-gray-400">
2740
+ <thead class="bg-white/5 text-white font-bold">
2741
+ <tr>
2742
+ <th class="p-3 border-b border-white/10">Property</th>
2743
+ <th class="p-3 border-b border-white/10">Type</th>
2744
+ <th class="p-3 border-b border-white/10">Description</th>
2745
+ </tr>
2746
+ </thead>
2747
+ <tbody class="divide-y divide-white/5">
2748
+ <tr>
2749
+ <td class="p-3 font-mono text-purple-400">type</td>
2750
+ <td class="p-3 font-mono text-xs">"diagram"</td>
2751
+ <td class="p-3">Required.</td>
2752
+ </tr>
2753
+ <tr>
2754
+ <td class="p-3 font-mono text-blue-400">nodes</td>
2755
+ <td class="p-3 font-mono text-xs">object[]</td>
2756
+ <td class="p-3">Vue Flow nodes: <code>id</code>, <code>type</code> (default/input), <code>position</code> <code>{x,y}</code>, <code>label</code>, <code>data</code> (e.g. shape, style).</td>
2757
+ </tr>
2758
+ <tr>
2759
+ <td class="p-3 font-mono text-blue-400">edges</td>
2760
+ <td class="p-3 font-mono text-xs">object[]</td>
2761
+ <td class="p-3">Vue Flow edges: <code>id</code>, <code>source</code>, <code>target</code>, <code>sourceHandle</code>, <code>targetHandle</code> (optional).</td>
2762
+ </tr>
2763
+ <tr>
2764
+ <td class="p-3 font-mono text-blue-400">config</td>
2765
+ <td class="p-3 font-mono text-xs">object?</td>
2766
+ <td class="p-3"><code>fitView</code> and other Vue Flow options.</td>
2767
+ </tr>
2768
+ </tbody>
2769
+ </table>
2770
+ </div>
2771
+ </div>
2772
+ <p class="text-white/60 text-sm">Element control: <code>s{N}-nodes-{i}</code>, <code>s{N}-edges-{i}</code>. With <code>studio: true</code>, diagrams are editable (add nodes via palette, drag, connect, resize).</p>
2773
+ </div>
2774
+
2775
+ <!-- REF: FREE -->
2776
+ <div v-else-if="activeSection === 'ref-free'">
2777
+ <h1>Free / Composition Slide</h1>
2778
+ <p class="lead">Absolutely positioned elements (text, image, box) for Remotion-style storytelling. Use <code>timelineTracks</code> to animate <code>x</code>, <code>y</code> (as translate), <code>opacity</code>, etc.</p>
2779
+
2780
+ <div class="my-8">
2781
+ <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2782
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2783
+ <table class="w-full text-left text-sm text-gray-400">
2784
+ <thead class="bg-white/5 text-white font-bold">
2785
+ <tr>
2786
+ <th class="p-3 border-b border-white/10">Property</th>
2787
+ <th class="p-3 border-b border-white/10">Type</th>
2788
+ <th class="p-3 border-b border-white/10">Description</th>
2789
+ </tr>
2790
+ </thead>
2791
+ <tbody class="divide-y divide-white/5">
2792
+ <tr>
2793
+ <td class="p-3 font-mono text-purple-400">type</td>
2794
+ <td class="p-3 font-mono text-xs">"free"</td>
2795
+ <td class="p-3">Required.</td>
2796
+ </tr>
2797
+ <tr>
2798
+ <td class="p-3 font-mono text-blue-400">elements</td>
2799
+ <td class="p-3 font-mono text-xs">FreeElement[]</td>
2800
+ <td class="p-3">Each: <code>type</code> (<code>"text"</code>|<code>"image"</code>|<code>"box"</code>), <code>text?</code>, <code>src?</code>, <code>width?</code>, <code>height?</code>, <code>fontSize?</code>, <code>color?</code>, <code>fontWeight?</code>, <code>backgroundColor?</code> (box), <code>id?</code>.</td>
2801
+ </tr>
2802
+ </tbody>
2803
+ </table>
2804
+ </div>
2805
+ </div>
2806
+ <p class="text-white/60 text-sm">Element ids: <code>s{N}-elements-0</code>, <code>s{N}-elements-1</code>, … (or custom <code>id</code> on each element). Position is driven by <code>slide.timelineTracks</code>; see <em>Timeline (keyframes)</em>.</p>
2807
+ </div>
2808
+
2809
+ <!-- REF: AUTO -->
2810
+ <div v-else-if="activeSection === 'ref-auto'">
2811
+ <h1>Auto Strategy</h1>
2812
+ <p class="lead">Smartly chooses the best layout based on the data structure provided. Useful for
2813
+ AI agents or rapid prototyping.</p>
2814
+
2815
+ <div class="p-6 bg-blue-500/10 border border-blue-500/20 rounded-xl my-8">
2816
+ <h3 class="text-blue-400 font-bold mb-2">How it works</h3>
2817
+ <ul class="list-disc list-inside text-sm text-blue-200/80 space-y-1">
2818
+ <li>If <code>chartType</code> + <code>data.datasets</code> exist ->
2819
+ <strong>Chart</strong>
2820
+ </li>
2821
+ <li>If <code>timeline</code> array exists -> <strong>Timeline</strong></li>
2822
+ <li>If <code>steps</code> array exists -> <strong>Steps</strong></li>
2823
+ <li>If <code>features</code> array exists -> <strong>Features</strong></li>
2824
+ <li>If <code>image</code> exists -> <strong>Half</strong></li>
2825
+ <li>Otherwise -> <strong>Statement</strong></li>
2826
+ </ul>
2827
+ </div>
2828
+
2829
+ <div class="my-8">
2830
+ <h2 class="text-xl font-bold text-white mb-4">Live Examples</h2>
2831
+ <p class="text-sm text-gray-400 mb-6">Use the navigation arrows to see how each slide
2832
+ renders with the same <code>type: "auto"</code> but different data structures.</p>
2833
+ <LivePreview initial-code='{
2834
+ "slides": [
2835
+ {
2836
+ "type": "auto",
2837
+ "title": "Welcome to Lumina",
2838
+ "subtitle": "The AI-Native Presentation Engine"
2839
+ },
2840
+ {
2841
+ "type": "auto",
2842
+ "title": "Core Features",
2843
+ "features": [
2844
+ { "title": "Fast", "desc": "60fps animations", "icon": "fa-bolt" },
2845
+ { "title": "Simple", "desc": "JSON-first API", "icon": "fa-code" },
2846
+ { "title": "Smart", "desc": "AI-ready engine", "icon": "fa-brain" }
2847
+ ]
2848
+ },
2849
+ {
2850
+ "type": "auto",
2851
+ "title": "Our Roadmap",
2852
+ "timeline": [
2853
+ { "date": "Q1", "title": "Alpha", "description": "Core Engine" },
2854
+ { "date": "Q2", "title": "Beta", "description": "Public Testing" },
2855
+ { "date": "Q4", "title": "Launch", "description": "V1.0 Release" }
2856
+ ]
2857
+ },
2858
+ {
2859
+ "type": "auto",
2860
+ "title": "Getting Started",
2861
+ "steps": [
2862
+ { "step": "1", "title": "Install", "description": "npm install lumina-slides" },
2863
+ { "step": "2", "title": "Import", "description": "Import the Lumina class" },
2864
+ { "step": "3", "title": "Create", "description": "Define your JSON slides" }
2865
+ ]
2866
+ },
2867
+ {
2868
+ "type": "auto",
2869
+ "title": "Visual Content",
2870
+ "image": "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=800",
2871
+ "paragraphs": ["Auto-detect image slides for visual storytelling."]
2872
+ }
2873
+ ]
2874
+ }' />
2875
+ </div>
2876
+ </div>
2877
+
2878
+ </div>
2879
+ </Transition>
2880
+ </main>
2881
+ </div>
2882
+ </template>
2883
+
2884
+ <script setup lang="ts">
2885
+ import { ref, watch, onUnmounted, nextTick } from 'vue';
2886
+ import { Lumina } from '../../core/Lumina';
2887
+ import { parsePartialJson } from '../../utils/streaming';
2888
+ import LivePreview from './LivePreview.vue';
2889
+
2890
+ const activeSection = ref('intro');
2891
+
2892
+ // --- DEMO LOGIC ---
2893
+ const demoInput = ref('');
2894
+ const demoStarted = ref(false);
2895
+ let demoEngine: Lumina | null = null;
2896
+
2897
+ const TARGET_JSON = `{
2898
+ "meta": { "title": "Lumina Demo" },
2899
+ "slides": [
2900
+ {
2901
+ "type": "half",
2902
+ "sizing": "container",
2903
+ "meta": {
2904
+ "orbColor": "#8b5cf6"
2905
+ },
2906
+ "imageSide": "left",
2907
+ "image": "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2000",
2908
+ "tag": "Left Aligned",
2909
+ "title": "Image on Left",
2910
+ "paragraphs": [
2911
+ "The classic split screen. Image on the left, content on the right.",
2912
+ "Perfect for introducing a product or feature where the visual context leads the narrative."
2913
+ ],
2914
+ "cta": "Explore Left"
2915
+ },
2916
+ {
2917
+ "type": "statement",
2918
+ "sizing": "container",
2919
+ "meta": {
2920
+ "orbColor": "#ec4899"
2921
+ },
2922
+ "tag": "Minimal",
2923
+ "title": "Impactful Headlines",
2924
+ "subtitle": "The standard statement slide allows for massive typography to drive a point home."
2925
+ },
2926
+ {
2927
+ "type": "features",
2928
+ "sizing": "container",
2929
+ "title": "Grid System",
2930
+ "description": "The features layout automatically arranges cards into a responsive grid.",
2931
+ "features": [
2932
+ { "title": "Card One", "desc": "Standard card with an icon.", "icon": "fa-bolt" },
2933
+ { "title": "Card Two", "desc": "Cards scale on hover.", "icon": "fa-star" },
2934
+ { "title": "Card Three", "desc": "Fully responsive on mobile.", "icon": "fa-mobile" }
2935
+ ]
2936
+ },
2937
+ {
2938
+ "type": "timeline",
2939
+ "sizing": "container",
2940
+ "title": "Project History",
2941
+ "subtitle": "A visual journey through our milestones.",
2942
+ "timeline": [
2943
+ { "date": "Q1 2023", "title": "Inception", "description": "The initial concept was drafted." },
2944
+ { "date": "Q3 2023", "title": "Prototype", "description": "First functional MVP released." },
2945
+ { "date": "Q1 2024", "title": "Beta Launch", "description": "Public beta opened to users." }
2946
+ ]
2947
+ }
2948
+ ]
2949
+ }`;
2950
+
2951
+ async function runDemo() {
2952
+ demoInput.value = '';
2953
+ demoStarted.value = true;
2954
+
2955
+ // Initialize Engine if needed
2956
+ if (!demoEngine) {
2957
+ // Ensure container is cleared to avoid duplicates
2958
+ const container = document.getElementById('streaming-demo-container');
2959
+ if (container) container.innerHTML = '';
2960
+
2961
+ nextTick(() => {
2962
+ demoEngine = new Lumina('#streaming-demo-container', {
2963
+ ui: { visible: true, showControls: true },
2964
+ keyboard: false,
2965
+ animation: { enabled: true, durationIn: 0.5 }
2966
+ });
2967
+ });
2968
+ }
2969
+
2970
+ let i = 0;
2971
+ // Faster typing for examples
2972
+ const interval = setInterval(() => {
2973
+ demoInput.value += TARGET_JSON[i];
2974
+ i++;
2975
+ if (i >= TARGET_JSON.length) clearInterval(interval);
2976
+ }, 10);
2977
+ }
2978
+
2979
+ function loadIntoDemo(json: string) {
2980
+ activeSection.value = 'agents-streaming';
2981
+ window.scrollTo({ top: 0, behavior: 'smooth' });
2982
+ demoInput.value = '';
2983
+ demoStarted.value = true;
2984
+
2985
+ setTimeout(() => {
2986
+ if (!demoEngine) {
2987
+ const container = document.getElementById('streaming-demo-container');
2988
+ if (container) container.innerHTML = '';
2989
+ demoEngine = new Lumina('#streaming-demo-container', {
2990
+ ui: { visible: true },
2991
+ keyboard: false,
2992
+ animation: { enabled: true, durationIn: 0.5 }
2993
+ });
2994
+ }
2995
+
2996
+ let i = 0;
2997
+ const interval = setInterval(() => {
2998
+ demoInput.value += json[i];
2999
+ i++;
3000
+ if (i >= json.length) clearInterval(interval);
3001
+ }, 5);
3002
+ }, 100);
3003
+ }
3004
+
3005
+ const EXAMPLES = {
3006
+ statement: `{
3007
+ "type": "statement",
3008
+ "sizing": "container",
3009
+ "t": "Big Impact",
3010
+ "s": "Created instantly from JSON.",
3011
+ "meta": { "variant": "v:g" }
3012
+ }`,
3013
+ features: `{
3014
+ "type": "features",
3015
+ "sizing": "container",
3016
+ "t": "Core Capabilities",
3017
+ "fs": [
3018
+ { "t": "Fast", "desc": "60fps", "icon": "bolt" },
3019
+ { "t": "AI", "desc": "Native", "icon": "robot" },
3020
+ { "t": "Typed", "desc": "TS", "icon": "code" }
3021
+ ]
3022
+ }`,
3023
+ timeline: `{
3024
+ "type": "timeline",
3025
+ "sizing": "container",
3026
+ "t": "Product Roadmap",
3027
+ "tl": [
3028
+ { "date": "Q1", "t": "Alpha", "desc": "Core Engine" },
3029
+ { "date": "Q2", "t": "Beta", "desc": "Public API" },
3030
+ { "date": "Q3", "t": "Launch", "desc": "Global Scale" }
3031
+ ]
3032
+ }`,
3033
+ half: `{
3034
+ "type": "half",
3035
+ "sizing": "container",
3036
+ "imageSide": "left",
3037
+ "image": "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2000",
3038
+ "t": "Split Layout",
3039
+ "tag": "Visual",
3040
+ "paragraphs": ["Image on left, text on right."]
3041
+ }`,
3042
+ steps: `{
3043
+ "type": "steps",
3044
+ "sizing": "container",
3045
+ "t": "Process Flow",
3046
+ "steps": [
3047
+ { "step": "01", "t": "Install", "desc": "npm install" },
3048
+ { "step": "02", "t": "Import", "desc": "import { Lumina }..." },
3049
+ { "step": "03", "t": "Launch", "desc": "new Lumina(...)" }
3050
+ ]
3051
+ }`,
3052
+ flex: `{
3053
+ "type": "flex",
3054
+ "sizing": "container",
3055
+ "direction": "horizontal",
3056
+ "elements": [
3057
+ { "type": "image", "src": "./brains.png", "size": "half", "fill": true },
3058
+ { "type": "content", "size": "half", "valign": "center", "padding": "xl", "gap": "md", "elements": [
3059
+ { "type": "title", "text": "Visual Story", "size": "2xl" },
3060
+ { "type": "bullets", "items": ["Image fills height", "Content centered", "No coordinates"] },
3061
+ { "type": "button", "label": "Learn More", "variant": "primary" }
3062
+ ]}
3063
+ ]
3064
+ }` ,
3065
+ video: `{
3066
+ "type": "statement",
3067
+ "sizing": "container",
3068
+ "meta": { "orbColor": "#ec4899" },
3069
+ "background": {
3070
+ "type": "video",
3071
+ "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
3072
+ "opacity": 0.4
3073
+ },
3074
+ "tag": "Cinematic",
3075
+ "title": "Video Backgrounds",
3076
+ "subtitle": "Immersive support."
3077
+ }`,
3078
+ video_half: `{
3079
+ "type": "half",
3080
+ "sizing": "container",
3081
+ "video": {
3082
+ "type": "video",
3083
+ "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
3084
+ "autoplay": true,
3085
+ "muted": true
3086
+ },
3087
+ "title": "Watch This"
3088
+ }`,
3089
+ video_flex: `{
3090
+ "type": "flex",
3091
+ "sizing": "container",
3092
+ "elements": [
3093
+ {
3094
+ "type": "video",
3095
+ "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
3096
+ "controls": true,
3097
+ "size": "half"
3098
+ }
3099
+ ]
3100
+ }`,
3101
+ video_slide: `{
3102
+ "type": "video",
3103
+ "sizing": "container",
3104
+ "video": {
3105
+ "type": "video",
3106
+ "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
3107
+ "autoplay": true,
3108
+ "muted": true,
3109
+ "loop": true
3110
+ },
3111
+ "title": "Full-screen video"
3112
+ }`
3113
+ };
3114
+
3115
+ // Watch input and parse
3116
+ watch(demoInput, (val) => {
3117
+ if (!demoEngine) return;
3118
+ const parsed = parsePartialJson(val);
3119
+ if (parsed) {
3120
+ // Handle Deck vs Slide
3121
+ if (parsed.slides && Array.isArray(parsed.slides)) {
3122
+ // Apply sizing: container to all slides if missing
3123
+ parsed.slides.forEach((s: any) => s.sizing = 'container');
3124
+ demoEngine.load(parsed);
3125
+
3126
+ // Auto-advance logic: check if we have a new slide that is ready
3127
+ const availableSlides = parsed.slides.filter((s: any) => s.title || s.type);
3128
+ const targetIndex = availableSlides.length - 1;
3129
+
3130
+ if (targetIndex > demoEngine.currentSlideIndex) {
3131
+ demoEngine.goTo(targetIndex);
3132
+ }
3133
+ } else {
3134
+ // Wrap single slide
3135
+ demoEngine.load({
3136
+ meta: { title: "Demo" },
3137
+ slides: [{ ...parsed, sizing: 'container' }]
3138
+ });
3139
+ }
3140
+ }
3141
+ });
3142
+
3143
+ onUnmounted(() => {
3144
+ if (demoEngine) demoEngine.destroy();
3145
+ });
3146
+
3147
+
3148
+
3149
+ const navigation = [
3150
+ {
3151
+ title: 'Getting Started',
3152
+ items: [
3153
+ { id: 'intro', label: 'Introduction' },
3154
+ { id: 'install', label: 'Installation' },
3155
+ { id: 'setup', label: 'Quick Start' },
3156
+ { id: 'theming', label: 'Theming & Presets' }
3157
+ ]
3158
+ },
3159
+ {
3160
+ title: 'Concepts',
3161
+ items: [
3162
+ { id: 'deck', label: 'Deck Structure' },
3163
+ { id: 'slides', label: 'Slide Layouts' },
3164
+ { id: 'media', label: 'Media & Video' },
3165
+ { id: 'sizing', label: 'Embedding & Sizing' },
3166
+ { id: 'agents-intro', label: 'Agent Protocol' },
3167
+ { id: 'agents-tokens', label: 'Token Optimization' }
3168
+ ]
3169
+ },
3170
+ {
3171
+ title: 'Layouts',
3172
+ items: [
3173
+ { id: 'ref-statement', label: 'Statement' },
3174
+ { id: 'ref-half', label: 'Half / Split' },
3175
+ { id: 'ref-features', label: 'Features' },
3176
+ { id: 'ref-timeline', label: 'Timeline' },
3177
+ { id: 'ref-steps', label: 'Steps' },
3178
+ { id: 'ref-flex', label: 'Flex' },
3179
+ { id: 'ref-chart', label: 'Chart' },
3180
+ { id: 'ref-video', label: 'Video' },
3181
+ { id: 'ref-diagram', label: 'Diagram' },
3182
+ { id: 'ref-free', label: 'Free / Composition' },
3183
+ { id: 'ref-custom', label: 'Custom HTML' },
3184
+ { id: 'ref-auto', label: 'Auto Strategy' }
3185
+ ]
3186
+ },
3187
+ {
3188
+ title: 'AI & LLM Integration',
3189
+ items: [
3190
+ { id: 'agents-streaming', label: 'Streaming & Realtime' },
3191
+ { id: 'agents-intro', label: 'Agent Protocol' },
3192
+ { id: 'agents-tokens', label: 'Token Optimization' }
3193
+ ]
3194
+ },
3195
+ {
3196
+ title: 'Advanced',
3197
+ items: [
3198
+ { id: 'config', label: 'Configuration' },
3199
+ { id: 'events', label: 'Events & API' },
3200
+ { id: 'element-control', label: 'Element Control' },
3201
+ { id: 'animations', label: 'Animations (reveal, presets, stagger)' },
3202
+ { id: 'timeline-keyframes', label: 'Timeline (keyframes)' },
3203
+ { id: 'sizing', label: 'Embedding' },
3204
+ { id: 'speaker-notes', label: 'Speaker Notes' }
3205
+ ]
3206
+ }
3207
+ ];
3208
+
3209
+
3210
+ function scroll(id: string) {
3211
+ nextTick(() => {
3212
+ const el = document.getElementById(id);
3213
+ if (el) {
3214
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
3215
+ }
3216
+ });
3217
+ }
3218
+
3219
+ function scrollToTop() {
3220
+ window.scrollTo({ top: 0, behavior: 'smooth' });
3221
+ }
3222
+ </script>
3223
+
3224
+ <style scoped>
3225
+ .doc-content h1 {
3226
+ @apply text-4xl md:text-5xl font-black tracking-tight text-white mb-8 leading-tight font-heading;
3227
+ }
3228
+
3229
+ .doc-content h2 {
3230
+ @apply text-2xl md:text-3xl font-bold text-white mt-16 mb-6 tracking-tight font-heading;
3231
+ }
3232
+
3233
+ .doc-content h3 {
3234
+ @apply text-[20px] font-extrabold text-white mb-[15px] mt-8 font-heading;
3235
+ }
3236
+
3237
+ .doc-content p {
3238
+ @apply text-lg text-white/70 leading-relaxed mb-6 font-sans;
3239
+ }
3240
+
3241
+ .doc-content p.lead {
3242
+ @apply text-2xl text-white font-light mb-10 leading-relaxed;
3243
+ }
3244
+
3245
+ .doc-content ul {
3246
+ @apply list-disc list-outside ml-6 mb-8 text-white/70;
3247
+ }
3248
+
3249
+ .doc-content li {
3250
+ @apply mb-2 pl-2 marker:text-blue-500 text-lg;
3251
+ }
3252
+
3253
+ .doc-content code {
3254
+ @apply font-mono text-sm bg-blue-500/10 text-blue-300 px-1.5 py-0.5 rounded border border-blue-500/20;
3255
+ }
3256
+
3257
+ .doc-content pre {
3258
+ @apply bg-[#0A0A0A] border border-white/10 rounded-xl p-5 overflow-x-auto mb-10 shadow-2xl;
3259
+ }
3260
+
3261
+ .doc-content pre code {
3262
+ @apply bg-transparent p-0 text-white/90 border-none text-sm leading-relaxed;
3263
+ }
3264
+
3265
+ /* Specific overrides for the "not-prose" areas if needed, though scoped css shouldn't leak usually.
3266
+ But since we use .doc-content class, we are safe. */
3267
+ </style>