lumina-slides 9.0.1 → 9.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  <template>
2
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
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">
4
+ <aside class="w-full md:w-64 flex-shrink-0 block sticky top-32 self-start max-h-[calc(100vh-10rem)] overflow-y-auto">
5
+ <div class="space-y-10 py-1">
6
6
  <div v-for="(group, i) in navigation" :key="i">
7
7
  <!-- Group Title -->
8
8
  <h3 class="text-xs font-bold uppercase tracking-widest text-white/30 mb-4 pl-3">
@@ -12,14 +12,14 @@
12
12
  <!-- Links -->
13
13
  <ul class="space-y-1 relative border-l border-white/5">
14
14
  <li v-for="item in group.items" :key="item.id">
15
- <button @click="activeSection = item.id; scrollToTop()" :class="[
15
+ <a v-if="group.title === 'Layouts'" :href="'#' + item.id" @click.prevent="goToLayout(item.id)" :class="[
16
+ 'block group w-full text-left px-4 py-2 text-sm transition-all duration-300 border-l-2 -ml-[1px]',
17
+ activeSection === item.id ? 'border-blue-500 text-white font-medium bg-blue-500/5' : 'border-transparent text-white/50 hover:text-white hover:border-white/20'
18
+ ]">{{ item.label }}</a>
19
+ <button v-else @click="activeSection = item.id; scrollToTop()" :class="[
16
20
  '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>
21
+ activeSection === item.id ? 'border-blue-500 text-white font-medium bg-blue-500/5' : 'border-transparent text-white/50 hover:text-white hover:border-white/20'
22
+ ]">{{ item.label }}</button>
23
23
  </li>
24
24
  </ul>
25
25
  </div>
@@ -28,11 +28,10 @@
28
28
 
29
29
  <!-- Main Content Area -->
30
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">
31
+ <div class="doc-content max-w-6xl">
33
32
 
34
33
  <!-- MEDIA & VIDEO -->
35
- <div v-if="activeSection === 'media'">
34
+ <div v-if="activeSection === 'media'" id="media">
36
35
  <h1>Media & Video</h1>
37
36
  <p class="lead">Lumina supports video backgrounds on any slide, and video elements in Half and Flex layouts.</p>
38
37
 
@@ -61,7 +60,7 @@
61
60
  </div>
62
61
 
63
62
  <!-- INTRODUCTION -->
64
- <div v-else-if="activeSection === 'intro'">
63
+ <div v-else-if="activeSection === 'intro'" id="intro">
65
64
  <h1>Introduction</h1>
66
65
  <p class="lead text-2xl text-white font-light">
67
66
  Lumina is a high-performance, Universal Presentation Engine.
@@ -102,7 +101,7 @@
102
101
  </div>
103
102
 
104
103
  <!-- INSTALLATION -->
105
- <div v-else-if="activeSection === 'install'">
104
+ <div v-else-if="activeSection === 'install'" id="install">
106
105
  <h1>Installation</h1>
107
106
  <p>Lumina is available as a Universal NPM package. It works in any JavaScript environment
108
107
  (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>
@@ -137,7 +136,7 @@
137
136
  </div>
138
137
 
139
138
  <!-- SETUP -->
140
- <div v-else-if="activeSection === 'setup'">
139
+ <div v-else-if="activeSection === 'setup'" id="setup">
141
140
  <h1>Quick Start</h1>
142
141
  <p>Import the stylesheet and initialize the engine in your main entry file.
143
142
  </p>
@@ -158,7 +157,7 @@ engine.load(myDeckData);</code></pre>
158
157
  </div>
159
158
 
160
159
  <!-- DECK STRUCTURE -->
161
- <div v-else-if="activeSection === 'deck'">
160
+ <div v-else-if="activeSection === 'deck'" id="deck">
162
161
  <h1>Deck Structure</h1>
163
162
  <p>A deck is a JSON object with <code>meta</code> and <code>slides</code>. Both are required.</p>
164
163
 
@@ -180,12 +179,32 @@ engine.load(myDeckData);</code></pre>
180
179
  ]
181
180
  }</code></pre>
182
181
 
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>
182
+ <h3>Common slide-level properties (SlideBase)</h3>
183
+ <p>Every slide inherits these optional properties. Layout-specific props are in each layout reference.</p>
184
+ <div class="overflow-x-auto border border-white/10 rounded-lg my-6">
185
+ <table class="w-full text-left text-sm text-gray-400">
186
+ <thead class="bg-white/5 text-white font-bold">
187
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
188
+ </thead>
189
+ <tbody class="divide-y divide-white/5">
190
+ <tr><td class="p-3 font-mono text-blue-400">sizing</td><td class="p-3 font-mono text-xs">"viewport" | "container"</td><td class="p-3">Height behavior. Default: "viewport" (min 100vh). "container" = 100% of parent.</td></tr>
191
+ <tr><td class="p-3 font-mono text-blue-400">background</td><td class="p-3 font-mono text-xs">string | VideoProperties</td><td class="p-3">Image URL or <code>{ type: "video", src, poster?, autoplay?, loop?, muted? }</code>.</td></tr>
192
+ <tr><td class="p-3 font-mono text-blue-400">backgroundOpacity</td><td class="p-3 font-mono text-xs">number</td><td class="p-3">0–1. Default: 1 (or 0.2 for default overlay).</td></tr>
193
+ <tr><td class="p-3 font-mono text-blue-400">notes</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Speaker notes (basic markdown). For exportState and speaker window.</td></tr>
194
+ <tr><td class="p-3 font-mono text-blue-400">class</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Extra CSS class on the slide content container.</td></tr>
195
+ <tr><td class="p-3 font-mono text-blue-400">timelineTracks</td><td class="p-3 font-mono text-xs">Record&lt;id, Keyframes&gt;</td><td class="p-3">Remotion-style: <code>{ [elementId]: { "0": { opacity, x, y, scale, rotate?, visible? }, "1": {...} } }</code>. Use with <code>engine.seekTo(progress)</code>.</td></tr>
196
+ <tr><td class="p-3 font-mono text-blue-400">reveal</td><td class="p-3 font-mono text-xs">RevealInSequenceOptions?</td><td class="p-3">When set, engine runs <code>revealInSequence</code> on ready and slideChange. <code>delayMs</code>, <code>staggerMode</code>, <code>preset</code>, etc.</td></tr>
197
+ <tr><td class="p-3 font-mono text-blue-400">ids</td><td class="p-3 font-mono text-xs">Record&lt;path, string&gt;?</td><td class="p-3">Override element ids: <code>{ "tag": "intro-tag", "features.0": "hero" }</code>. For <code>engine.element(id)</code>.</td></tr>
198
+ <tr><td class="p-3 font-mono text-blue-400">meta</td><td class="p-3 font-mono text-xs">object?</td><td class="p-3">Slide-specific: <code>orbColor</code>, <code>orbPos</code>, <code>backgroundSize</code>, or custom.</td></tr>
199
+ <tr><td class="p-3 font-mono text-blue-400">id</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Unique slide ID. Used by exportState and navigation.</td></tr>
200
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Fallback for exportState/speaker notes when the layout has no required title.</td></tr>
201
+ </tbody>
202
+ </table>
203
+ </div>
185
204
  </div>
186
205
 
187
206
  <!-- SLIDE LAYOUTS -->
188
- <div v-else-if="activeSection === 'slides'">
207
+ <div v-else-if="activeSection === 'slides'" id="slides">
189
208
  <h1>Slide Layouts</h1>
190
209
  <p>Lumina includes responsive layouts for common scenarios. Each layout has a reference section with a full property table.</p>
191
210
 
@@ -315,7 +334,7 @@ engine.load(myDeckData);</code></pre>
315
334
 
316
335
 
317
336
  <!-- SIZING / EMBEDDING -->
318
- <div v-else-if="activeSection === 'sizing'">
337
+ <div v-else-if="activeSection === 'sizing'" id="sizing">
319
338
  <h1>Embedding & Sizing</h1>
320
339
  <p>By default, Lumina slides take up the full viewport height (<code>100vh</code>) to create an
321
340
  immersive experience.</p>
@@ -344,7 +363,7 @@ engine.load(myDeckData);</code></pre>
344
363
  </div>
345
364
 
346
365
  <!-- CONFIGURATION -->
347
- <div v-else-if="activeSection === 'config'">
366
+ <div v-else-if="activeSection === 'config'" id="config">
348
367
  <h1>Configuration</h1>
349
368
  <p>Pass options to <code>new Lumina(selector, options)</code>. All properties are optional.</p>
350
369
 
@@ -360,7 +379,7 @@ engine.load(myDeckData);</code></pre>
360
379
  <h3>Navigation</h3>
361
380
  <p>Control how users move between slides (buttons, keyboard, touch):</p>
362
381
  <ul>
363
- <li><code>navigation</code> — Master switch. If <code>false</code>, prev/next buttons are disabled and keyboard/touch do nothing. Default: <code>true</code>.</li>
382
+ <li><code>navigation</code> — Master switch. If <code>false</code>, prev/next arrow buttons are hidden and keyboard/touch do nothing. Default: <code>true</code>.</li>
364
383
  <li><code>keyboard</code> — Enable keyboard shortcuts for next/prev. Only applies when <code>navigation</code> is true. Default: <code>true</code>.</li>
365
384
  <li><code>touch</code> — Enable touch/swipe for next/prev. Only applies when <code>navigation</code> is true. Default: <code>true</code>.</li>
366
385
  <li><code>ui.showControls</code> — Show or hide the prev/next (and speaker-notes) buttons in the footer. When hidden, keyboard and touch still work if enabled. Default: <code>true</code>.</li>
@@ -388,7 +407,7 @@ engine.load(myDeckData);</code></pre>
388
407
  </div>
389
408
 
390
409
  <!-- EVENTS -->
391
- <div v-else-if="activeSection === 'events'">
410
+ <div v-else-if="activeSection === 'events'" id="events">
392
411
  <h1>Events & API</h1>
393
412
  <p>Subscribe with <code>engine.on(event, handler)</code>; unsubscribe with <code>engine.off(event, handler)</code> using the same function reference.</p>
394
413
 
@@ -423,7 +442,7 @@ engine.<span class="text-yellow-400">on</span>('navigate', ({ direction, toIndex
423
442
  </div>
424
443
 
425
444
  <!-- ELEMENT CONTROL -->
426
- <div v-else-if="activeSection === 'element-control'">
445
+ <div v-else-if="activeSection === 'element-control'" id="element-control">
427
446
  <h1>Element Control (Reveal on Demand)</h1>
428
447
  <p class="lead">Start with elements hidden and reveal them in sequence: ideal for blank slides that
429
448
  fill in as you speak, or for effects driven by <code>engine.element(id)</code>.</p>
@@ -550,7 +569,7 @@ engine.load(deck);</code></pre>
550
569
  </div>
551
570
 
552
571
  <!-- ANIMATIONS: revealInSequence, presets, stagger -->
553
- <div v-else-if="activeSection === 'animations'">
572
+ <div v-else-if="activeSection === 'animations'" id="animations">
554
573
  <h1>Animations</h1>
555
574
  <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>
556
575
 
@@ -582,7 +601,7 @@ engine.load(deck);</code></pre>
582
601
  </div>
583
602
 
584
603
  <!-- TIMELINE (Remotion-style keyframes) -->
585
- <div v-else-if="activeSection === 'timeline-keyframes'">
604
+ <div v-else-if="activeSection === 'timeline-keyframes'" id="timeline-keyframes">
586
605
  <h1>Timeline (Remotion-style keyframes)</h1>
587
606
  <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>
588
607
 
@@ -618,7 +637,7 @@ engine.load(deck);</code></pre>
618
637
  </div>
619
638
 
620
639
  <!-- THEMING -->
621
- <div v-else-if="activeSection === 'theming'">
640
+ <div v-else-if="activeSection === 'theming'" id="theming">
622
641
  <h1>Theming</h1>
623
642
  <p class="lead">Customize colors, fonts, and visual style with built-in presets or create your
624
643
  own theme.</p>
@@ -1383,7 +1402,7 @@ engine.load(myDeck);</code></pre>
1383
1402
  </div>
1384
1403
 
1385
1404
  <!-- SPEAKER NOTES -->
1386
- <div v-else-if="activeSection === 'speaker-notes'">
1405
+ <div v-else-if="activeSection === 'speaker-notes'" id="speaker-notes">
1387
1406
  <h1>Speaker Notes</h1>
1388
1407
  <p class="lead">Open a separate presenter view with notes, timer, and bidirectional navigation
1389
1408
  controls.</p>
@@ -1467,7 +1486,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1467
1486
 
1468
1487
 
1469
1488
  <!-- AGENTS: INTRO -->
1470
- <div v-else-if="activeSection === 'agents-intro'">
1489
+ <div v-else-if="activeSection === 'agents-intro'" id="agents-intro">
1471
1490
  <h1>The Agent Protocol</h1>
1472
1491
  <p class="lead">Lumina is the first presentation engine designed for <strong>Agentic
1473
1492
  AI</strong>.</p>
@@ -1513,7 +1532,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1513
1532
  </div>
1514
1533
 
1515
1534
  <!-- AGENTS: TOKENS -->
1516
- <div v-else-if="activeSection === 'agents-tokens'">
1535
+ <div v-else-if="activeSection === 'agents-tokens'" id="agents-tokens">
1517
1536
  <h1>Token Optimization</h1>
1518
1537
  <p class="lead">Reduce output size with short property aliases. Less characters = faster
1519
1538
  rendering and lower costs.</p>
@@ -1621,7 +1640,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1621
1640
  </div>
1622
1641
 
1623
1642
  <!-- AGENTS: STREAMING -->
1624
- <div v-else-if="activeSection === 'agents-streaming'">
1643
+ <div v-else-if="activeSection === 'agents-streaming'" id="agents-streaming">
1625
1644
  <h1>Streaming & Realtime</h1>
1626
1645
  <p>Lumina enables a <strong>"Type & See"</strong> experience. Using our partial parser, you can
1627
1646
  feed raw LLM streams directly into the engine.</p>
@@ -1669,12 +1688,11 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1669
1688
  </div>
1670
1689
 
1671
1690
  <!-- AGENTS: LAYOUT GALLERY -->
1672
- <div v-else-if="activeSection === 'agents-layouts'">
1691
+ <div v-else-if="activeSection === 'agents-layouts'" id="agents-layouts">
1673
1692
  <h1>Layout Gallery</h1>
1674
1693
  <p>Lumina provides 5 core layouts optimized for different types of information. Click <strong><i
1675
1694
  class="fa-solid fa-play text-blue-400"></i> Visualize</strong> to load them into the
1676
- <a @click.prevent="activeSection = 'agents-streaming'; scroll('streaming-demo-container')"
1677
- href="#" class="text-blue-400 hover:underline">Streaming Demo</a>.
1695
+ <a href="#streaming-demo-container" @click.prevent="goToStreamingDemo" class="text-blue-400 hover:underline">Streaming Demo</a>.
1678
1696
  </p>
1679
1697
 
1680
1698
  <div class="space-y-12 mt-12">
@@ -1730,7 +1748,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1730
1748
  </div>
1731
1749
  </div>
1732
1750
 
1733
- <div v-else-if="activeSection === 'agents-auto'">
1751
+ <div v-else-if="activeSection === 'agents-auto'" id="agents-auto">
1734
1752
  <h1>Auto-Layouts</h1>
1735
1753
  <p>Sometimes the Agent doesn't know which layout is best. Just use <code>type: 'auto'</code>.
1736
1754
  </p>
@@ -1808,18 +1826,63 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1808
1826
  </div>
1809
1827
 
1810
1828
  <!-- AGENTS: STATE -->
1811
- <div v-else-if="activeSection === 'agents-state'">
1829
+ <div v-else-if="activeSection === 'agents-state'" id="agents-state">
1812
1830
  <h1>State & Feedback Loop</h1>
1813
1831
  <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>
1814
1832
  <pre><code><span class="text-blue-400">const</span> state = engine.exportState();
1815
1833
  <span class="text-gray-500">// "User is on slide ${state.currentSlide.index + 1} (${state.currentSlide.type}). ${state.narrative}"</span></code></pre>
1816
1834
  </div>
1817
1835
 
1836
+ <!-- TEMPLATE TAGS & engine.data -->
1837
+ <div v-else-if="activeSection === 'template-tags'" id="template-tags">
1838
+ <h1>Template Tags & Key-Value Store</h1>
1839
+ <p class="lead">Use <code>engine.data</code> as a key-value store and <code v-pre>{{key}}</code> placeholders in slide strings. Values resolve at render time and update reactively when <code>engine.data</code> changes.</p>
1840
+
1841
+ <h2>Key-Value Store (engine.data)</h2>
1842
+ <p>Store and read arbitrary values at any time. Persists across <code>load()</code> and <code>patch()</code>; useful for preferences, flags, or app state to share with your agent or between handlers.</p>
1843
+ <pre><code><span class="text-gray-500">// Set and get</span>
1844
+ engine.data.set(<span class="text-green-400">"theme"</span>, <span class="text-green-400">"dark"</span>);
1845
+ engine.data.get(<span class="text-green-400">"theme"</span>); <span class="text-gray-500">// "dark"</span>
1846
+
1847
+ <span class="text-gray-500">// Check, delete, list keys</span>
1848
+ engine.data.has(<span class="text-green-400">"theme"</span>); <span class="text-gray-500">// true</span>
1849
+ engine.data.delete(<span class="text-green-400">"theme"</span>);
1850
+ engine.data.keys(); <span class="text-gray-500">// ["otherKey", ...]</span>
1851
+
1852
+ <span class="text-gray-500">// Chainable set; clear all</span>
1853
+ engine.data.set(<span class="text-green-400">"a"</span>, <span class="text-cyan-400">1</span>).set(<span class="text-green-400">"b"</span>, <span class="text-cyan-400">2</span>);
1854
+ engine.data.clear();</code></pre>
1855
+
1856
+ <h2>Template Tags in Slide Content</h2>
1857
+ <p>Any string in slide content (title, subtitle, description, features, steps, timeline, etc.) can use <code v-pre>{{key}}</code> placeholders. At render time they are replaced with <code>engine.data.get(key)</code>; missing keys become an empty string. When you change values via <code>engine.data.set(key, value)</code>, the presentation updates automatically.</p>
1858
+ <pre><code>engine.data.set(<span class="text-green-400">"product"</span>, <span class="text-green-400">"Lumina"</span>);
1859
+ engine.data.set(<span class="text-green-400">"version"</span>, <span class="text-green-400">"2.0"</span>);
1860
+ engine.load({
1861
+ meta: { title: <span class="text-green-400">"Welcome"</span> },
1862
+ slides: [
1863
+ { type: <span class="text-green-400">"statement"</span>, title: <span class="text-green-400">"&#123;&#123;product&#125;&#125; v&#123;&#123;version&#125;&#125;"</span>, subtitle: <span class="text-green-400">"Hello &#123;&#123;user&#125;&#125;"</span> }
1864
+ ]
1865
+ });
1866
+ <span class="text-gray-500">// Renders: "Lumina v2.0" / "Hello " (user not set)</span>
1867
+
1868
+ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-green-400">"World"</span>);
1869
+ <span class="text-gray-500">// Renders: "Lumina v2.0" / "Hello World" (updates in place)</span></code></pre>
1870
+ <p>Keys: letters, numbers, underscore (e.g. <code v-pre>{{name}}</code>, <code v-pre>{{user_name}}</code>). Values are coerced to string; <code>null</code>/<code>undefined</code> → <code>""</code>.</p>
1871
+
1872
+ <p class="mt-8">
1873
+ <router-link to="/deck/layout-template-tags"
1874
+ 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">
1875
+ View Template Tags demo →
1876
+ </router-link>
1877
+ </p>
1878
+ </div>
1879
+
1880
+ <!-- LAYOUTS (single page with anchors) -->
1881
+ <div v-else-if="LAYOUT_IDS.includes(activeSection)" class="space-y-24">
1818
1882
  <!-- REF: STATEMENT -->
1819
- <div v-else-if="activeSection === 'ref-statement'">
1883
+ <section id="ref-statement">
1820
1884
  <h1>Statement Slide</h1>
1821
- <p class="lead">Used for high-impact titles, opening covers, or emphatic quotes. It is designed
1822
- to be punchy and minimal.</p>
1885
+ <p class="lead">High-impact titles, opening covers, or emphatic quotes. Punchy and minimal. Supports <code>sizing</code>, <code>background</code>, <code>notes</code>, <code>class</code>, <code>timelineTracks</code>, <code>reveal</code>, <code>ids</code>, <code>meta</code> (SlideBase).</p>
1823
1886
 
1824
1887
  <div class="my-8">
1825
1888
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
@@ -1833,451 +1896,263 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
1833
1896
  </tr>
1834
1897
  </thead>
1835
1898
  <tbody class="divide-y divide-white/5">
1836
- <tr>
1837
- <td class="p-3 font-mono text-purple-400">type</td>
1838
- <td class="p-3 font-mono text-xs">"statement"</td>
1839
- <td class="p-3">Required. Identifies the layout.</td>
1840
- </tr>
1841
- <tr>
1842
- <td class="p-3 font-mono text-blue-400">title</td>
1843
- <td class="p-3 font-mono text-xs">string</td>
1844
- <td class="p-3">Main headline. Keep it short (3-6 words).</td>
1845
- </tr>
1846
- <tr>
1847
- <td class="p-3 font-mono text-blue-400">subtitle</td>
1848
- <td class="p-3 font-mono text-xs">string?</td>
1849
- <td class="p-3">Optional supporting text.</td>
1850
- </tr>
1851
- <tr>
1852
- <td class="p-3 font-mono text-blue-400">tag</td>
1853
- <td class="p-3 font-mono text-xs">string?</td>
1854
- <td class="p-3">Small eyebrow tag above the title.</td>
1855
- </tr>
1856
- <tr>
1857
- <td class="p-3 font-mono text-cyan-400">notes</td>
1858
- <td class="p-3 font-mono text-xs">string?</td>
1859
- <td class="p-3">Speaker notes (available on any slide via <code>SlideBase</code>).</td>
1860
- </tr>
1899
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"statement"</td><td class="p-3">Required. Identifies the layout.</td></tr>
1900
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Main headline. Keep it short (3–6 words). Alias: <code>t</code>.</td></tr>
1901
+ <tr><td class="p-3 font-mono text-blue-400">subtitle</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Optional supporting text. Alias: <code>s</code>.</td></tr>
1902
+ <tr><td class="p-3 font-mono text-blue-400">tag</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Small eyebrow tag above the title.</td></tr>
1861
1903
  </tbody>
1862
1904
  </table>
1863
1905
  </div>
1906
+ <p class="text-white/50 text-sm mt-2">Element ids: <code>s{N}-tag</code>, <code>s{N}-title</code>, <code>s{N}-subtitle</code>. Override via <code>ids</code>.</p>
1864
1907
  </div>
1865
1908
 
1866
1909
  <div class="my-8">
1867
1910
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1868
1911
  <LivePreview :initial-code="EXAMPLES.statement" />
1869
1912
  </div>
1870
- </div>
1913
+ </section>
1871
1914
 
1872
1915
  <!-- REF: HALF -->
1873
- <div v-else-if="activeSection === 'ref-half'">
1916
+ <section id="ref-half">
1874
1917
  <h1>Half / Split Slide</h1>
1875
- <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>
1918
+ <p class="lead">Image or video on one side, text on the other. Product showcases, "about me", explainers. <strong>Provide <code>image</code> or <code>video</code> (one required).</strong> Plus SlideBase (sizing, background, notes, class, timelineTracks, reveal, ids, meta).</p>
1876
1919
 
1877
1920
  <div class="my-8">
1878
1921
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1879
1922
  <div class="overflow-x-auto border border-white/10 rounded-lg">
1880
1923
  <table class="w-full text-left text-sm text-gray-400">
1881
1924
  <thead class="bg-white/5 text-white font-bold">
1882
- <tr>
1883
- <th class="p-3 border-b border-white/10">Property</th>
1884
- <th class="p-3 border-b border-white/10">Type</th>
1885
- <th class="p-3 border-b border-white/10">Description</th>
1886
- </tr>
1925
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
1887
1926
  </thead>
1888
1927
  <tbody class="divide-y divide-white/5">
1889
- <tr>
1890
- <td class="p-3 font-mono text-purple-400">type</td>
1891
- <td class="p-3 font-mono text-xs">"half"</td>
1892
- <td class="p-3">Required.</td>
1893
- </tr>
1894
- <tr>
1895
- <td class="p-3 font-mono text-blue-400">image</td>
1896
- <td class="p-3 font-mono text-xs">string?</td>
1897
- <td class="p-3">Image URL. Omit if using <code>video</code>.</td>
1898
- </tr>
1899
- <tr>
1900
- <td class="p-3 font-mono text-blue-400">video</td>
1901
- <td class="p-3 font-mono text-xs">VideoProperties?</td>
1902
- <td class="p-3"><code>{ type: "video", src, autoplay?, muted?, loop? }</code>. Use instead of <code>image</code>.</td>
1903
- </tr>
1904
- <tr>
1905
- <td class="p-3 font-mono text-blue-400">imageSide</td>
1906
- <td class="p-3 font-mono text-xs">"left" | "right"</td>
1907
- <td class="p-3">Which side the media is on. Default: "left".</td>
1908
- </tr>
1909
- <tr>
1910
- <td class="p-3 font-mono text-blue-400">tag</td>
1911
- <td class="p-3 font-mono text-xs">string?</td>
1912
- <td class="p-3">Optional eyebrow label above the title.</td>
1913
- </tr>
1914
- <tr>
1915
- <td class="p-3 font-mono text-blue-400">title</td>
1916
- <td class="p-3 font-mono text-xs">string</td>
1917
- <td class="p-3">Headline text.</td>
1918
- </tr>
1919
- <tr>
1920
- <td class="p-3 font-mono text-blue-400">paragraphs</td>
1921
- <td class="p-3 font-mono text-xs">string[]</td>
1922
- <td class="p-3">Array of text blocks.</td>
1923
- </tr>
1924
- <tr>
1925
- <td class="p-3 font-mono text-blue-400">cta</td>
1926
- <td class="p-3 font-mono text-xs">string?</td>
1927
- <td class="p-3">Optional button label.</td>
1928
- </tr>
1928
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"half"</td><td class="p-3">Required.</td></tr>
1929
+ <tr><td class="p-3 font-mono text-blue-400">image</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Image URL. Omit if using <code>video</code>.</td></tr>
1930
+ <tr><td class="p-3 font-mono text-blue-400">video</td><td class="p-3 font-mono text-xs">VideoProperties?</td><td class="p-3"><code>{ type: "video", src, poster?, autoplay?, loop?, muted?, controls?, className? }</code>. Use instead of <code>image</code>.</td></tr>
1931
+ <tr><td class="p-3 font-mono text-blue-400">imageSide</td><td class="p-3 font-mono text-xs">"left" | "right"</td><td class="p-3">Which side the media is on. Default: "left".</td></tr>
1932
+ <tr><td class="p-3 font-mono text-blue-400">imageClass</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Extra CSS class on the <code>img</code> (e.g. filters, scale).</td></tr>
1933
+ <tr><td class="p-3 font-mono text-blue-400">tag</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Eyebrow label above the title.</td></tr>
1934
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Headline.</td></tr>
1935
+ <tr><td class="p-3 font-mono text-blue-400">paragraphs</td><td class="p-3 font-mono text-xs">string[]</td><td class="p-3">Array of text blocks. Rendered as separate <code>&lt;p&gt;</code>.</td></tr>
1936
+ <tr><td class="p-3 font-mono text-blue-400">cta</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Button label. Click emits <code>action</code> with <code>type: "cta"</code>, <code>label</code>.</td></tr>
1929
1937
  </tbody>
1930
1938
  </table>
1931
1939
  </div>
1940
+ <p class="text-white/50 text-sm mt-2">Element ids: <code>s{N}-media</code>, <code>s{N}-tag</code>, <code>s{N}-title</code>, <code>s{N}-paragraphs</code>, <code>s{N}-cta</code>.</p>
1932
1941
  </div>
1933
1942
 
1934
1943
  <div class="my-8">
1935
1944
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1936
1945
  <LivePreview :initial-code="EXAMPLES.half" />
1937
1946
  </div>
1938
- </div>
1947
+ </section>
1939
1948
 
1940
1949
  <!-- REF: FEATURES -->
1941
- <div v-else-if="activeSection === 'ref-features'">
1950
+ <section id="ref-features">
1942
1951
  <h1>Features Slide</h1>
1943
- <p class="lead">Displays a grid of cards, perfect for listing benefits, stats, or services.</p>
1952
+ <p class="lead">Grid of cards for benefits, stats, or services. Responsive: 1 column on mobile, auto-fit on desktop. SlideBase (sizing, background, notes, class, timelineTracks, reveal, ids, meta) applies.</p>
1944
1953
 
1945
1954
  <div class="my-8">
1946
- <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
1955
+ <h2 class="text-xl font-bold text-white mb-4">Slide configuration</h2>
1947
1956
  <div class="overflow-x-auto border border-white/10 rounded-lg">
1948
1957
  <table class="w-full text-left text-sm text-gray-400">
1949
1958
  <thead class="bg-white/5 text-white font-bold">
1950
- <tr>
1951
- <th class="p-3 border-b border-white/10">Property</th>
1952
- <th class="p-3 border-b border-white/10">Type</th>
1953
- <th class="p-3 border-b border-white/10">Description</th>
1954
- </tr>
1959
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
1955
1960
  </thead>
1956
1961
  <tbody class="divide-y divide-white/5">
1957
- <tr>
1958
- <td class="p-3 font-mono text-purple-400">type</td>
1959
- <td class="p-3 font-mono text-xs">"features"</td>
1960
- <td class="p-3">Required.</td>
1961
- </tr>
1962
- <tr>
1963
- <td class="p-3 font-mono text-blue-400">title</td>
1964
- <td class="p-3 font-mono text-xs">string</td>
1965
- <td class="p-3">Section header.</td>
1966
- </tr>
1967
- <tr>
1968
- <td class="p-3 font-mono text-blue-400">description</td>
1969
- <td class="p-3 font-mono text-xs">string?</td>
1970
- <td class="p-3">Optional context.</td>
1971
- </tr>
1972
- <tr>
1973
- <td class="p-3 font-mono text-blue-400">features</td>
1974
- <td class="p-3 font-mono text-xs">FeatureItem[]</td>
1975
- <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>
1976
- </tr>
1962
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"features"</td><td class="p-3">Required.</td></tr>
1963
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Section header.</td></tr>
1964
+ <tr><td class="p-3 font-mono text-blue-400">description</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Context below the header. Alias: <code>d</code>, <code>desc</code>.</td></tr>
1965
+ <tr><td class="p-3 font-mono text-blue-400">features</td><td class="p-3 font-mono text-xs">FeatureItem[]</td><td class="p-3">Array of feature cards. See FeatureItem below.</td></tr>
1966
+ </tbody>
1967
+ </table>
1968
+ </div>
1969
+
1970
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">FeatureItem (each card)</h3>
1971
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
1972
+ <table class="w-full text-left text-sm text-gray-400">
1973
+ <thead class="bg-white/5 text-white font-bold">
1974
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
1975
+ </thead>
1976
+ <tbody class="divide-y divide-white/5">
1977
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Card title.</td></tr>
1978
+ <tr><td class="p-3 font-mono text-blue-400">description</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Body text. Alias: <code>desc</code>.</td></tr>
1979
+ <tr><td class="p-3 font-mono text-blue-400">icon</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Icon name. Phosphor: <code>ph-star</code>, <code>ph-check</code>; or <code>star</code> (prefix <code>ph-</code> applied).</td></tr>
1980
+ <tr><td class="p-3 font-mono text-blue-400">class</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Extra CSS class on the card. Use <code>""</code> if omitted.</td></tr>
1981
+ <tr><td class="p-3 font-mono text-blue-400">id</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Override for <code>engine.element(id)</code>. Default: <code>s{N}-features-{i}</code>.</td></tr>
1977
1982
  </tbody>
1978
1983
  </table>
1979
1984
  </div>
1985
+ <p class="text-white/50 text-sm mt-2">Element ids: <code>s{N}-header</code>, <code>s{N}-features-0</code>, <code>s{N}-features-1</code>, …</p>
1980
1986
  </div>
1981
1987
 
1982
1988
  <div class="my-8">
1983
1989
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
1984
1990
  <LivePreview :initial-code="EXAMPLES.features" />
1985
1991
  </div>
1986
- </div>
1992
+ </section>
1987
1993
 
1988
1994
  <!-- REF: TIMELINE -->
1989
- <div v-else-if="activeSection === 'ref-timeline'">
1995
+ <section id="ref-timeline">
1990
1996
  <h1>Timeline Slide</h1>
1991
- <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>
1997
+ <p class="lead">Vertical chronological list for events, roadmaps, or history. Alternating left/right on desktop. <em>Not</em> the same as <code>slide.timelineTracks</code> (keyframe animation). SlideBase applies.</p>
1992
1998
 
1993
1999
  <div class="my-8">
1994
- <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2000
+ <h2 class="text-xl font-bold text-white mb-4">Slide configuration</h2>
1995
2001
  <div class="overflow-x-auto border border-white/10 rounded-lg">
1996
2002
  <table class="w-full text-left text-sm text-gray-400">
1997
2003
  <thead class="bg-white/5 text-white font-bold">
1998
- <tr>
1999
- <th class="p-3 border-b border-white/10">Property</th>
2000
- <th class="p-3 border-b border-white/10">Type</th>
2001
- <th class="p-3 border-b border-white/10">Description</th>
2002
- </tr>
2004
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2003
2005
  </thead>
2004
2006
  <tbody class="divide-y divide-white/5">
2005
- <tr>
2006
- <td class="p-3 font-mono text-purple-400">type</td>
2007
- <td class="p-3 font-mono text-xs">"timeline"</td>
2008
- <td class="p-3">Required.</td>
2009
- </tr>
2010
- <tr>
2011
- <td class="p-3 font-mono text-blue-400">title</td>
2012
- <td class="p-3 font-mono text-xs">string</td>
2013
- <td class="p-3">Header.</td>
2014
- </tr>
2015
- <tr>
2016
- <td class="p-3 font-mono text-blue-400">subtitle</td>
2017
- <td class="p-3 font-mono text-xs">string?</td>
2018
- <td class="p-3">Optional.</td>
2019
- </tr>
2020
- <tr>
2021
- <td class="p-3 font-mono text-blue-400">timeline</td>
2022
- <td class="p-3 font-mono text-xs">TimelineItem[]</td>
2023
- <td class="p-3">Array: <code>{ date, title, description, icon?, id? }</code>. Aliases: <code>t</code>→title, <code>desc</code>→description.</td>
2024
- </tr>
2007
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"timeline"</td><td class="p-3">Required.</td></tr>
2008
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Header. Alias: <code>t</code>.</td></tr>
2009
+ <tr><td class="p-3 font-mono text-blue-400">subtitle</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Subheader. Alias: <code>s</code>.</td></tr>
2010
+ <tr><td class="p-3 font-mono text-blue-400">timeline</td><td class="p-3 font-mono text-xs">TimelineItem[]</td><td class="p-3">Ordered events. See TimelineItem below. Alias: <code>tl</code>.</td></tr>
2011
+ <tr><td class="p-3 font-mono text-blue-400">lineClass</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Extra CSS class on the vertical connector line.</td></tr>
2012
+ </tbody>
2013
+ </table>
2014
+ </div>
2015
+
2016
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">TimelineItem (each event)</h3>
2017
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2018
+ <table class="w-full text-left text-sm text-gray-400">
2019
+ <thead class="bg-white/5 text-white font-bold">
2020
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2021
+ </thead>
2022
+ <tbody class="divide-y divide-white/5">
2023
+ <tr><td class="p-3 font-mono text-blue-400">date</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Time marker (e.g. "Q1 2024", "Jan 15", "Now").</td></tr>
2024
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Event title. Alias: <code>t</code>.</td></tr>
2025
+ <tr><td class="p-3 font-mono text-blue-400">description</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Details. Alias: <code>desc</code>, <code>d</code>.</td></tr>
2026
+ <tr><td class="p-3 font-mono text-blue-400">icon</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Phosphor icon. <code>ph-*</code> or short name.</td></tr>
2027
+ <tr><td class="p-3 font-mono text-blue-400">id</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Override for element control. Default: <code>s{N}-timeline-{i}</code>.</td></tr>
2025
2028
  </tbody>
2026
2029
  </table>
2027
2030
  </div>
2031
+ <p class="text-white/50 text-sm mt-2">Element ids: <code>s{N}-title</code>, <code>s{N}-subtitle</code>, <code>s{N}-timeline-0</code>, …</p>
2028
2032
  </div>
2029
2033
 
2030
2034
  <div class="my-8">
2031
2035
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2032
2036
  <LivePreview :initial-code="EXAMPLES.timeline" />
2033
2037
  </div>
2034
- </div>
2038
+ </section>
2035
2039
 
2036
2040
  <!-- REF: STEPS -->
2037
- <div v-else-if="activeSection === 'ref-steps'">
2041
+ <section id="ref-steps">
2038
2042
  <h1>Steps Slide</h1>
2039
- <p class="lead">Used for tutorials, "how-to" guides, or process flows.</p>
2043
+ <p class="lead">Numbered steps for tutorials, how-to guides, or process flows. Responsive grid. SlideBase applies.</p>
2040
2044
 
2041
2045
  <div class="my-8">
2042
- <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2046
+ <h2 class="text-xl font-bold text-white mb-4">Slide configuration</h2>
2043
2047
  <div class="overflow-x-auto border border-white/10 rounded-lg">
2044
2048
  <table class="w-full text-left text-sm text-gray-400">
2045
2049
  <thead class="bg-white/5 text-white font-bold">
2046
- <tr>
2047
- <th class="p-3 border-b border-white/10">Property</th>
2048
- <th class="p-3 border-b border-white/10">Type</th>
2049
- <th class="p-3 border-b border-white/10">Description</th>
2050
- </tr>
2050
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2051
2051
  </thead>
2052
2052
  <tbody class="divide-y divide-white/5">
2053
- <tr>
2054
- <td class="p-3 font-mono text-purple-400">type</td>
2055
- <td class="p-3 font-mono text-xs">"steps"</td>
2056
- <td class="p-3">Required.</td>
2057
- </tr>
2058
- <tr>
2059
- <td class="p-3 font-mono text-blue-400">title</td>
2060
- <td class="p-3 font-mono text-xs">string</td>
2061
- <td class="p-3">Process name.</td>
2062
- </tr>
2063
- <tr>
2064
- <td class="p-3 font-mono text-blue-400">subtitle</td>
2065
- <td class="p-3 font-mono text-xs">string?</td>
2066
- <td class="p-3">Optional.</td>
2067
- </tr>
2068
- <tr>
2069
- <td class="p-3 font-mono text-blue-400">steps</td>
2070
- <td class="p-3 font-mono text-xs">StepItem[]</td>
2071
- <td class="p-3">Array: <code>{ step, title, description?, icon?, id? }</code>. <code>step</code> is e.g. <code>"01"</code>.</td>
2072
- </tr>
2053
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"steps"</td><td class="p-3">Required.</td></tr>
2054
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Process or section header.</td></tr>
2055
+ <tr><td class="p-3 font-mono text-blue-400">subtitle</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Optional subheader.</td></tr>
2056
+ <tr><td class="p-3 font-mono text-blue-400">steps</td><td class="p-3 font-mono text-xs">StepItem[]</td><td class="p-3">Ordered steps. See StepItem below. Alias: <code>st</code>.</td></tr>
2073
2057
  </tbody>
2074
2058
  </table>
2075
2059
  </div>
2060
+
2061
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">StepItem (each step)</h3>
2062
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2063
+ <table class="w-full text-left text-sm text-gray-400">
2064
+ <thead class="bg-white/5 text-white font-bold">
2065
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2066
+ </thead>
2067
+ <tbody class="divide-y divide-white/5">
2068
+ <tr><td class="p-3 font-mono text-blue-400">step</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Badge label: <code>"01"</code>, <code>"1"</code>, <code>"A"</code>, etc.</td></tr>
2069
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string</td><td class="p-3">Step title.</td></tr>
2070
+ <tr><td class="p-3 font-mono text-blue-400">description</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Body text.</td></tr>
2071
+ <tr><td class="p-3 font-mono text-blue-400">icon</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Phosphor icon; shown as watermark. <code>ph-*</code> or short name.</td></tr>
2072
+ <tr><td class="p-3 font-mono text-blue-400">class</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Extra CSS class on the step card.</td></tr>
2073
+ <tr><td class="p-3 font-mono text-blue-400">id</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Override for element control. Default: <code>s{N}-steps-{i}</code>.</td></tr>
2074
+ </tbody>
2075
+ </table>
2076
+ </div>
2077
+ <p class="text-white/50 text-sm mt-2">Element ids: <code>s{N}-header</code>, <code>s{N}-steps-0</code>, <code>s{N}-steps-1</code>, …</p>
2076
2078
  </div>
2077
2079
 
2078
2080
  <div class="my-8">
2079
2081
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2080
2082
  <LivePreview :initial-code="EXAMPLES.steps" />
2081
2083
  </div>
2082
- </div>
2084
+ </section>
2083
2085
 
2084
2086
  <!-- REF: FLEX -->
2085
- <div v-else-if="activeSection === 'ref-flex'">
2087
+ <section id="ref-flex">
2086
2088
  <h1>Flex Layout</h1>
2087
- <p class="lead">Flow-based layout for composing slides using semantic sizing. No coordinates
2088
- needed—perfect for LLM generation.</p>
2089
+ <p class="lead">Flow-based layout with semantic sizing (quarter, half, full, etc.). No coordinates—ideal for LLMs. SlideBase applies. Top-level and content children can have <code>size?: FlexSize</code>.</p>
2089
2090
 
2090
2091
  <div class="my-8">
2091
- <h2 class="text-xl font-bold text-white mb-4">Slide Options</h2>
2092
+ <h2 class="text-xl font-bold text-white mb-4">Slide options</h2>
2092
2093
  <div class="overflow-x-auto border border-white/10 rounded-lg">
2093
2094
  <table class="w-full text-left text-sm text-gray-400">
2094
2095
  <thead class="bg-white/5 text-white font-bold">
2095
- <tr>
2096
- <th class="p-3 border-b border-white/10">Property</th>
2097
- <th class="p-3 border-b border-white/10">Type</th>
2098
- <th class="p-3 border-b border-white/10">Description</th>
2099
- </tr>
2096
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2100
2097
  </thead>
2101
2098
  <tbody class="divide-y divide-white/5">
2102
- <tr>
2103
- <td class="p-3 font-mono text-purple-400">type</td>
2104
- <td class="p-3 font-mono text-xs">"flex"</td>
2105
- <td class="p-3">Required.</td>
2106
- </tr>
2107
- <tr>
2108
- <td class="p-3 font-mono text-blue-400">direction</td>
2109
- <td class="p-3 font-mono text-xs">"horizontal" | "vertical"</td>
2110
- <td class="p-3">Main flow direction. Default: "horizontal".</td>
2111
- </tr>
2112
- <tr>
2113
- <td class="p-3 font-mono text-blue-400">gap</td>
2114
- <td class="p-3 font-mono text-xs">SpacingToken</td>
2115
- <td class="p-3">Gap between elements. Default: "none".</td>
2116
- </tr>
2117
- <tr>
2118
- <td class="p-3 font-mono text-blue-400">padding</td>
2119
- <td class="p-3 font-mono text-xs">SpacingToken</td>
2120
- <td class="p-3">Container padding. Default: "none".</td>
2121
- </tr>
2122
- <tr>
2123
- <td class="p-3 font-mono text-blue-400">halign</td>
2124
- <td class="p-3 font-mono text-xs">"left" | "center" | "right"</td>
2125
- <td class="p-3">Horizontal alignment of the flex group. Default: "left".</td>
2126
- </tr>
2127
- <tr>
2128
- <td class="p-3 font-mono text-blue-400">valign</td>
2129
- <td class="p-3 font-mono text-xs">"top" | "center" | "bottom"</td>
2130
- <td class="p-3">Vertical alignment of the flex group. Default: "top".</td>
2131
- </tr>
2132
- <tr>
2133
- <td class="p-3 font-mono text-blue-400">elements</td>
2134
- <td class="p-3 font-mono text-xs">FlexElement[]</td>
2135
- <td class="p-3">Array of flex elements in flow order. Each can have <code>size</code> (FlexSize).</td>
2136
- </tr>
2099
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"flex"</td><td class="p-3">Required.</td></tr>
2100
+ <tr><td class="p-3 font-mono text-blue-400">direction</td><td class="p-3 font-mono text-xs">"horizontal" | "vertical"</td><td class="p-3">Main flow. Default: "horizontal".</td></tr>
2101
+ <tr><td class="p-3 font-mono text-blue-400">gap</td><td class="p-3 font-mono text-xs">SpacingToken</td><td class="p-3">none | xs | sm | md | lg | xl | 2xl. Default: "none".</td></tr>
2102
+ <tr><td class="p-3 font-mono text-blue-400">padding</td><td class="p-3 font-mono text-xs">SpacingToken</td><td class="p-3">Container padding. Default: "none".</td></tr>
2103
+ <tr><td class="p-3 font-mono text-blue-400">halign</td><td class="p-3 font-mono text-xs">left | center | right</td><td class="p-3">Default: "left".</td></tr>
2104
+ <tr><td class="p-3 font-mono text-blue-400">valign</td><td class="p-3 font-mono text-xs">top | center | bottom</td><td class="p-3">Default: "top".</td></tr>
2105
+ <tr><td class="p-3 font-mono text-blue-400">elements</td><td class="p-3 font-mono text-xs">FlexElement[]</td><td class="p-3">Flow order. Each: <code>type</code> + type-specific props + optional <code>size</code> (FlexSize).</td></tr>
2137
2106
  </tbody>
2138
2107
  </table>
2139
2108
  </div>
2140
2109
  </div>
2141
2110
 
2142
2111
  <div class="my-8">
2143
- <h2 class="text-xl font-bold text-white mb-4">Size Tokens</h2>
2144
- <p>Each element can have a <code>size</code> property to control how much space it occupies:
2145
- </p>
2146
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4 my-6 not-prose">
2147
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>auto</code><br><span
2148
- class="text-xs text-white/40">flexible</span></div>
2149
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>quarter</code><br><span
2150
- class="text-xs text-white/40">25%</span></div>
2151
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>third</code><br><span
2152
- class="text-xs text-white/40">33%</span></div>
2153
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>half</code><br><span
2154
- class="text-xs text-white/40">50%</span></div>
2155
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>two-thirds</code><br><span
2156
- class="text-xs text-white/40">66%</span></div>
2157
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>three-quarters</code><br><span
2158
- class="text-xs text-white/40">75%</span></div>
2159
- <div class="p-4 bg-white/5 rounded-lg text-center"><code>full</code><br><span
2160
- class="text-xs text-white/40">100%</span></div>
2161
- </div>
2112
+ <h2 class="text-xl font-bold text-white mb-4">FlexSize (element width)</h2>
2113
+ <p>Optional <code>size</code> on elements: <code>auto</code> (flex) | <code>quarter</code> (25%) | <code>third</code> (33%) | <code>half</code> (50%) | <code>two-thirds</code> (66%) | <code>three-quarters</code> (75%) | <code>full</code> (100%).</p>
2162
2114
  </div>
2163
2115
 
2164
2116
  <div class="my-8">
2165
- <h2 class="text-xl font-bold text-white mb-4">Element Types</h2>
2117
+ <h2 class="text-xl font-bold text-white mb-4">Element types (top-level and in content)</h2>
2166
2118
  <div class="overflow-x-auto border border-white/10 rounded-lg">
2167
2119
  <table class="w-full text-left text-sm text-gray-400">
2168
2120
  <thead class="bg-white/5 text-white font-bold">
2169
- <tr>
2170
- <th class="p-3 border-b border-white/10">Type</th>
2171
- <th class="p-3 border-b border-white/10">Properties</th>
2172
- <th class="p-3 border-b border-white/10">Description</th>
2173
- </tr>
2121
+ <tr><th class="p-3 border-b border-white/10">type</th><th class="p-3 border-b border-white/10">Properties</th><th class="p-3 border-b border-white/10">Description</th></tr>
2174
2122
  </thead>
2175
2123
  <tbody class="divide-y divide-white/5">
2176
- <tr>
2177
- <td class="p-3 font-mono text-cyan-400">content</td>
2178
- <td class="p-3 font-mono text-xs">elements, valign, halign, gap, padding
2179
- </td>
2180
- <td class="p-3">Container that groups child elements vertically.</td>
2181
- </tr>
2182
- <tr>
2183
- <td class="p-3 font-mono text-cyan-400">image</td>
2184
- <td class="p-3 font-mono text-xs">src, alt?, fill?, fit?, rounded?</td>
2185
- <td class="p-3">Visual media element.</td>
2186
- </tr>
2187
- <tr>
2188
- <td class="p-3 font-mono text-cyan-400">title</td>
2189
- <td class="p-3 font-mono text-xs">text, size?, align?</td>
2190
- <td class="p-3">Large heading (lg | xl | 2xl | 3xl).</td>
2191
- </tr>
2192
- <tr>
2193
- <td class="p-3 font-mono text-cyan-400">text</td>
2194
- <td class="p-3 font-mono text-xs">text, align?, muted?</td>
2195
- <td class="p-3">Body paragraph text.</td>
2196
- </tr>
2197
- <tr>
2198
- <td class="p-3 font-mono text-cyan-400">bullets</td>
2199
- <td class="p-3 font-mono text-xs">items: string[]</td>
2200
- <td class="p-3">Unordered bullet list.</td>
2201
- </tr>
2202
- <tr>
2203
- <td class="p-3 font-mono text-cyan-400">ordered</td>
2204
- <td class="p-3 font-mono text-xs">items: string[]</td>
2205
- <td class="p-3">Numbered list.</td>
2206
- </tr>
2207
- <tr>
2208
- <td class="p-3 font-mono text-cyan-400">button</td>
2209
- <td class="p-3 font-mono text-xs">label, action?, variant?, fullWidth?</td>
2210
- <td class="p-3">CTA button (primary | secondary | outline | ghost).</td>
2211
- </tr>
2212
- <tr>
2213
- <td class="p-3 font-mono text-cyan-400">timeline</td>
2214
- <td class="p-3 font-mono text-xs">items: TimelineItem[], compact?</td>
2215
- <td class="p-3">Embedded timeline.</td>
2216
- </tr>
2217
- <tr>
2218
- <td class="p-3 font-mono text-cyan-400">stepper</td>
2219
- <td class="p-3 font-mono text-xs">items: StepItem[], compact?</td>
2220
- <td class="p-3">Embedded step process.</td>
2221
- </tr>
2222
- <tr>
2223
- <td class="p-3 font-mono text-cyan-400">spacer</td>
2224
- <td class="p-3 font-mono text-xs">size?: SpacingToken</td>
2225
- <td class="p-3">Adds visual spacing.</td>
2226
- </tr>
2124
+ <tr><td class="p-3 font-mono text-cyan-400">content</td><td class="p-3 font-mono text-xs">elements, valign?, halign?, gap?, padding?, size?</td><td class="p-3">Groups children vertically. Children: title, text, bullets, ordered, button, timeline, stepper, spacer (no image/video/nested content).</td></tr>
2125
+ <tr><td class="p-3 font-mono text-cyan-400">image</td><td class="p-3 font-mono text-xs">src, alt?, fill?, fit?, rounded?, href?, target?, class?, size?, id?</td><td class="p-3">Media. fill: edge-to-edge. fit: cover|contain. href+target: link. Rounded: none|sm|md|lg|xl|full.</td></tr>
2126
+ <tr><td class="p-3 font-mono text-cyan-400">video</td><td class="p-3 font-mono text-xs">src, poster?, autoplay?, loop?, muted?, controls?, fill?, fit?, rounded?, class?, size?, id?</td><td class="p-3">Video element. Same fill/fit/rounded as image.</td></tr>
2127
+ <tr><td class="p-3 font-mono text-cyan-400">title</td><td class="p-3 font-mono text-xs">text, size? (lg|xl|2xl|3xl), align?, id?</td><td class="p-3">Heading. align: left|center|right. Top-level only: optional FlexSize <code>size</code>.</td></tr>
2128
+ <tr><td class="p-3 font-mono text-cyan-400">text</td><td class="p-3 font-mono text-xs">text, align?, muted?, size?, id?</td><td class="p-3">Paragraph. muted: subtle style.</td></tr>
2129
+ <tr><td class="p-3 font-mono text-cyan-400">bullets</td><td class="p-3 font-mono text-xs">items: string[], size?, id?</td><td class="p-3">Unordered list.</td></tr>
2130
+ <tr><td class="p-3 font-mono text-cyan-400">ordered</td><td class="p-3 font-mono text-xs">items: string[], size?, id?</td><td class="p-3">Numbered list.</td></tr>
2131
+ <tr><td class="p-3 font-mono text-cyan-400">button</td><td class="p-3 font-mono text-xs">label, action?, actionType?, href?, gotoSlide?, target?, variant?, fullWidth?, size?, id?</td><td class="p-3">actionType: event|url|slide|download. url: href+target. slide: gotoSlide. variant: primary|secondary|outline|ghost.</td></tr>
2132
+ <tr><td class="p-3 font-mono text-cyan-400">timeline</td><td class="p-3 font-mono text-xs">items: TimelineItem[], compact?, size?, id?</td><td class="p-3">Embedded timeline. compact: tighter layout.</td></tr>
2133
+ <tr><td class="p-3 font-mono text-cyan-400">stepper</td><td class="p-3 font-mono text-xs">items: StepItem[], compact?, size?, id?</td><td class="p-3">Embedded steps.</td></tr>
2134
+ <tr><td class="p-3 font-mono text-cyan-400">spacer</td><td class="p-3 font-mono text-xs">size?: SpacingToken, id?</td><td class="p-3">Vertical gap. size: none|xs|sm|md|lg|xl|2xl. Default: md.</td></tr>
2227
2135
  </tbody>
2228
2136
  </table>
2229
2137
  </div>
2138
+ <p class="text-white/50 text-sm mt-2">SpacingToken: <code>none | xs | sm | md | lg | xl | 2xl</code>. Element ids: <code>s{N}-elements-{i}</code>, <code>s{N}-elements-{i}-elements-{j}</code> for content children.</p>
2230
2139
  </div>
2231
2140
 
2232
2141
  <div class="my-8">
2233
- <h2 class="text-xl font-bold text-white mb-4">Content Container</h2>
2234
- <p>Use <code>content</code> to group related elements with alignment control:</p>
2235
- <div class="overflow-x-auto border border-white/10 rounded-lg mt-6">
2236
- <table class="w-full text-left text-sm text-gray-400">
2237
- <thead class="bg-white/5 text-white font-bold">
2238
- <tr>
2239
- <th class="p-3 border-b border-white/10">Property</th>
2240
- <th class="p-3 border-b border-white/10">Values</th>
2241
- <th class="p-3 border-b border-white/10">Default</th>
2242
- </tr>
2243
- </thead>
2244
- <tbody class="divide-y divide-white/5">
2245
- <tr>
2246
- <td class="p-3 font-mono text-blue-400">valign</td>
2247
- <td class="p-3 font-mono text-xs">top | center | bottom</td>
2248
- <td class="p-3">center</td>
2249
- </tr>
2250
- <tr>
2251
- <td class="p-3 font-mono text-blue-400">halign</td>
2252
- <td class="p-3 font-mono text-xs">left | center | right</td>
2253
- <td class="p-3">left</td>
2254
- </tr>
2255
- <tr>
2256
- <td class="p-3 font-mono text-blue-400">gap</td>
2257
- <td class="p-3 font-mono text-xs">none | xs | sm | md | lg | xl | 2xl</td>
2258
- <td class="p-3">md</td>
2259
- </tr>
2260
- <tr>
2261
- <td class="p-3 font-mono text-blue-400">padding</td>
2262
- <td class="p-3 font-mono text-xs">none | xs | sm | md | lg | xl | 2xl</td>
2263
- <td class="p-3">lg</td>
2264
- </tr>
2265
- </tbody>
2266
- </table>
2267
- </div>
2142
+ <h2 class="text-xl font-bold text-white mb-4">Content container (valign, halign, gap, padding)</h2>
2143
+ <p>Defaults: valign <code>center</code>, halign <code>left</code>, gap <code>md</code>, padding <code>lg</code>.</p>
2268
2144
  </div>
2269
2145
 
2270
2146
  <div class="my-8">
2271
2147
  <h2 class="text-xl font-bold text-white mb-4">Example</h2>
2272
2148
  <LivePreview :initial-code="EXAMPLES.flex" />
2273
2149
  </div>
2274
- </div>
2150
+ </section>
2275
2151
 
2276
2152
  <!-- REF: CHART -->
2277
- <div v-else-if="activeSection === 'ref-chart'">
2153
+ <section id="ref-chart">
2278
2154
  <h1>Chart Slide</h1>
2279
- <p class="lead">Visualize data extracted from CSV/Excel files. Perfect for LLM-generated reports
2280
- and dashboards.</p>
2155
+ <p class="lead">Data visualization with Chart.js. Types: <code>bar</code>, <code>line</code>, <code>pie</code>, <code>doughnut</code>. Requires <code>chart.js</code> as an optional peer. SlideBase applies.</p>
2281
2156
 
2282
2157
  <div class="my-8">
2283
2158
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
@@ -2499,31 +2374,10 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
2499
2374
  "data": {
2500
2375
  "labels": ["Q1", "Q2", "Q3", "Q4"],
2501
2376
  "datasets": [
2502
- {
2503
- "label": "2023",
2504
- "values": [100, 120, 140, 160],
2505
- "color": "#6b7280"
2506
- },
2507
- {
2508
- "label": "2024",
2509
- "values": [120, 150, 180, 220],
2510
- "color": "c:p"
2511
- }
2377
+ { "label": "2023", "values": [100, 120, 140, 160], "color": "#6b7280" },
2378
+ { "label": "2024", "values": [120, 150, 180, 220], "color": "c:p" }
2512
2379
  ]
2513
- },
2514
- video: `{
2515
- "type": "statement",
2516
- "sizing": "container",
2517
- "meta": { "orbColor": "#ec4899" },
2518
- "background": {
2519
- "type": "video",
2520
- "src": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
2521
- "opacity": 0.4
2522
- },
2523
- "tag": "Cinematic",
2524
- "title": "Video Backgrounds",
2525
- "subtitle": "Immersive support."
2526
- }`
2380
+ }
2527
2381
  }' />
2528
2382
  </div>
2529
2383
 
@@ -2585,12 +2439,12 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
2585
2439
  charts automatically</li>
2586
2440
  </ul>
2587
2441
  </div>
2588
- </div>
2442
+ </section>
2589
2443
 
2590
2444
  <!-- REF: VIDEO -->
2591
- <div v-else-if="activeSection === 'ref-video'">
2445
+ <section id="ref-video">
2592
2446
  <h1>Video Slide</h1>
2593
- <p class="lead">Full-screen video player with optional title overlay (shown on hover).</p>
2447
+ <p class="lead">Full-screen video. <code>video</code> (VideoProperties) is required. Optional <code>title</code> overlay at bottom-left (on hover). SlideBase applies.</p>
2594
2448
 
2595
2449
  <div class="my-8">
2596
2450
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
@@ -2654,13 +2508,12 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
2654
2508
  </div>
2655
2509
 
2656
2510
  <p class="text-white/60 text-sm">Element control ids: <code>s{N}-video</code>, <code>s{N}-title</code>.</p>
2657
- </div>
2511
+ </section>
2658
2512
 
2659
2513
  <!-- REF: CUSTOM HTML -->
2660
- <div v-else-if="activeSection === 'ref-custom'">
2514
+ <section id="ref-custom">
2661
2515
  <h1>Custom HTML Slide</h1>
2662
- <p class="lead">Full control with raw HTML content. Inject any HTML structure with custom
2663
- styling.</p>
2516
+ <p class="lead">Raw <code>html</code> and optional <code>css</code>. For iframes, D3, or brand-specific layouts. SlideBase applies. Sanitized: <code>&lt;script&gt;</code> and <code>on*</code> handlers removed.</p>
2664
2517
 
2665
2518
  <div class="my-8">
2666
2519
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
@@ -2732,104 +2585,114 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
2732
2585
  creative freedom</li>
2733
2586
  </ul>
2734
2587
  </div>
2735
- </div>
2588
+ </section>
2736
2589
 
2737
2590
  <!-- REF: DIAGRAM -->
2738
- <div v-else-if="activeSection === 'ref-diagram'">
2591
+ <section id="ref-diagram">
2739
2592
  <h1>Diagram Slide</h1>
2740
- <p class="lead">Interactive node-based diagram using Vue Flow. Supports nodes, edges, and (in Studio) drag-and-drop and inline editing.</p>
2593
+ <p class="lead">Node-based diagram (Vue Flow). <code>nodes</code> and <code>edges</code> required. In Studio: drag-and-drop, connect, resize. SlideBase applies.</p>
2741
2594
 
2742
2595
  <div class="my-8">
2743
2596
  <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2744
2597
  <div class="overflow-x-auto border border-white/10 rounded-lg">
2745
2598
  <table class="w-full text-left text-sm text-gray-400">
2746
2599
  <thead class="bg-white/5 text-white font-bold">
2747
- <tr>
2748
- <th class="p-3 border-b border-white/10">Property</th>
2749
- <th class="p-3 border-b border-white/10">Type</th>
2750
- <th class="p-3 border-b border-white/10">Description</th>
2751
- </tr>
2600
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2752
2601
  </thead>
2753
2602
  <tbody class="divide-y divide-white/5">
2754
- <tr>
2755
- <td class="p-3 font-mono text-purple-400">type</td>
2756
- <td class="p-3 font-mono text-xs">"diagram"</td>
2757
- <td class="p-3">Required.</td>
2758
- </tr>
2759
- <tr>
2760
- <td class="p-3 font-mono text-blue-400">nodes</td>
2761
- <td class="p-3 font-mono text-xs">object[]</td>
2762
- <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>
2763
- </tr>
2764
- <tr>
2765
- <td class="p-3 font-mono text-blue-400">edges</td>
2766
- <td class="p-3 font-mono text-xs">object[]</td>
2767
- <td class="p-3">Vue Flow edges: <code>id</code>, <code>source</code>, <code>target</code>, <code>sourceHandle</code>, <code>targetHandle</code> (optional).</td>
2768
- </tr>
2769
- <tr>
2770
- <td class="p-3 font-mono text-blue-400">config</td>
2771
- <td class="p-3 font-mono text-xs">object?</td>
2772
- <td class="p-3"><code>fitView</code> and other Vue Flow options.</td>
2773
- </tr>
2603
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"diagram"</td><td class="p-3">Required.</td></tr>
2604
+ <tr><td class="p-3 font-mono text-blue-400">title</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Optional header.</td></tr>
2605
+ <tr><td class="p-3 font-mono text-blue-400">nodes</td><td class="p-3 font-mono text-xs">object[]</td><td class="p-3">Vue Flow: <code>id</code>, <code>type</code> (default|input|...), <code>position: { x, y }</code>, <code>label</code>, <code>data?</code> (shape, style).</td></tr>
2606
+ <tr><td class="p-3 font-mono text-blue-400">edges</td><td class="p-3 font-mono text-xs">object[]</td><td class="p-3">Vue Flow: <code>id</code>, <code>source</code>, <code>target</code>, <code>sourceHandle?</code>, <code>targetHandle?</code>.</td></tr>
2607
+ <tr><td class="p-3 font-mono text-blue-400">config</td><td class="p-3 font-mono text-xs">object?</td><td class="p-3"><code>fitView?: boolean</code> and other Vue Flow options.</td></tr>
2774
2608
  </tbody>
2775
2609
  </table>
2776
2610
  </div>
2611
+ <p class="text-white/50 text-sm mt-2">Element control: <code>s{N}-nodes-{i}</code>, <code>s{N}-edges-{i}</code>. Studio: palette, drag, connect, resize.</p>
2777
2612
  </div>
2778
- <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>
2779
- </div>
2613
+ </section>
2780
2614
 
2781
2615
  <!-- REF: FREE -->
2782
- <div v-else-if="activeSection === 'ref-free'">
2616
+ <section id="ref-free">
2783
2617
  <h1>Free / Composition Slide</h1>
2784
- <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>
2618
+ <p class="lead">Absolutely positioned elements for Remotion-style storytelling. Three types: <code>text</code>, <code>image</code>, <code>box</code>. Position and animation are driven by <code>slide.timelineTracks</code> (x, y as translate). SlideBase applies.</p>
2785
2619
 
2786
2620
  <div class="my-8">
2787
- <h2 class="text-xl font-bold text-white mb-4">Configuration</h2>
2621
+ <h2 class="text-xl font-bold text-white mb-4">Slide configuration</h2>
2788
2622
  <div class="overflow-x-auto border border-white/10 rounded-lg">
2789
2623
  <table class="w-full text-left text-sm text-gray-400">
2790
2624
  <thead class="bg-white/5 text-white font-bold">
2791
- <tr>
2792
- <th class="p-3 border-b border-white/10">Property</th>
2793
- <th class="p-3 border-b border-white/10">Type</th>
2794
- <th class="p-3 border-b border-white/10">Description</th>
2795
- </tr>
2625
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2796
2626
  </thead>
2797
2627
  <tbody class="divide-y divide-white/5">
2798
- <tr>
2799
- <td class="p-3 font-mono text-purple-400">type</td>
2800
- <td class="p-3 font-mono text-xs">"free"</td>
2801
- <td class="p-3">Required.</td>
2802
- </tr>
2803
- <tr>
2804
- <td class="p-3 font-mono text-blue-400">elements</td>
2805
- <td class="p-3 font-mono text-xs">FreeElement[]</td>
2806
- <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>
2807
- </tr>
2628
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"free"</td><td class="p-3">Required.</td></tr>
2629
+ <tr><td class="p-3 font-mono text-blue-400">elements</td><td class="p-3 font-mono text-xs">FreeElement[]</td><td class="p-3">Array of text, image, or box. See FreeElement below.</td></tr>
2630
+ </tbody>
2631
+ </table>
2632
+ </div>
2633
+
2634
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">FreeElement (each item)</h3>
2635
+ <div class="overflow-x-auto border border-white/10 rounded-lg">
2636
+ <table class="w-full text-left text-sm text-gray-400">
2637
+ <thead class="bg-white/5 text-white font-bold">
2638
+ <tr><th class="p-3 border-b border-white/10">Property</th><th class="p-3 border-b border-white/10">Type</th><th class="p-3 border-b border-white/10">Description</th></tr>
2639
+ </thead>
2640
+ <tbody class="divide-y divide-white/5">
2641
+ <tr><td class="p-3 font-mono text-purple-400">type</td><td class="p-3 font-mono text-xs">"text" | "image" | "box"</td><td class="p-3">Required. <code>text</code>: string content. <code>image</code>: use <code>src</code>. <code>box</code>: colored block, use <code>backgroundColor</code>.</td></tr>
2642
+ <tr><td class="p-3 font-mono text-blue-400">text</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">For <code>type: "text"</code>. Rendered as block.</td></tr>
2643
+ <tr><td class="p-3 font-mono text-blue-400">src</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">For <code>type: "image"</code>. Image URL.</td></tr>
2644
+ <tr><td class="p-3 font-mono text-blue-400">width</td><td class="p-3 font-mono text-xs">number | string?</td><td class="p-3">CSS width. Number → px. String: e.g. <code>"50%"</code>, <code>"10rem"</code>.</td></tr>
2645
+ <tr><td class="p-3 font-mono text-blue-400">height</td><td class="p-3 font-mono text-xs">number | string?</td><td class="p-3">CSS height. Same as width.</td></tr>
2646
+ <tr><td class="p-3 font-mono text-blue-400">fontSize</td><td class="p-3 font-mono text-xs">number | string?</td><td class="p-3">For text. Number → px.</td></tr>
2647
+ <tr><td class="p-3 font-mono text-blue-400">color</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Text color. CSS value.</td></tr>
2648
+ <tr><td class="p-3 font-mono text-blue-400">fontWeight</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">e.g. <code>"bold"</code>, <code>"600"</code>.</td></tr>
2649
+ <tr><td class="p-3 font-mono text-blue-400">backgroundColor</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">For <code>type: "box"</code>. CSS color.</td></tr>
2650
+ <tr><td class="p-3 font-mono text-blue-400">id</td><td class="p-3 font-mono text-xs">string?</td><td class="p-3">Override for <code>timelineTracks</code> and <code>engine.element(id)</code>. Default: <code>s{N}-elements-{i}</code>.</td></tr>
2808
2651
  </tbody>
2809
2652
  </table>
2810
2653
  </div>
2654
+
2655
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">Positioning and animation (timelineTracks)</h3>
2656
+ <p>Elements are placed at <code>left: 0; top: 0</code> by default. To move or animate them, set <code>slide.timelineTracks</code> on the slide (SlideBase). Keys are element ids: <code>s{N}-elements-0</code>, <code>s{N}-elements-1</code>, or the element’s <code>id</code>. Value: keyframes from progress string to state:</p>
2657
+ <ul class="list-disc list-inside text-white/70 my-4 space-y-1">
2658
+ <li><code>opacity</code> (0–1)</li>
2659
+ <li><code>x</code>, <code>y</code> (number, px) → applied as <code>translate(x, y)</code></li>
2660
+ <li><code>scale</code>, <code>rotate</code></li>
2661
+ <li><code>visible</code> (boolean)</li>
2662
+ </ul>
2663
+ <p class="text-white/60 text-sm">Keyframe keys: <code>"0"</code>, <code>"0.25"</code>, <code>"0.5"</code>, <code>"1"</code>, etc. Linear interpolation. Use <code>engine.seekTo(progress)</code> or <code>engine.playTimeline(duration)</code>.</p>
2664
+
2665
+ <h3 class="text-lg font-bold text-white mt-8 mb-4">Example</h3>
2666
+ <pre class="bg-[#0A0A0A] border border-white/10 rounded-xl p-5 overflow-x-auto mb-6 text-sm"><code>{
2667
+ <span class="text-blue-400">"type"</span>: <span class="text-green-400">"free"</span>,
2668
+ <span class="text-blue-400">"elements"</span>: [
2669
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"text"</span>, <span class="text-blue-400">"text"</span>: <span class="text-green-400">"Hello"</span>, <span class="text-blue-400">"fontSize"</span>: <span class="text-cyan-400">48</span> },
2670
+ { <span class="text-blue-400">"type"</span>: <span class="text-green-400">"box"</span>, <span class="text-blue-400">"width"</span>: <span class="text-cyan-400">200</span>, <span class="text-blue-400">"height"</span>: <span class="text-cyan-400">100</span>, <span class="text-blue-400">"backgroundColor"</span>: <span class="text-green-400">"#3b82f6"</span>, <span class="text-blue-400">"id"</span>: <span class="text-green-400">"myBox"</span> }
2671
+ ],
2672
+ <span class="text-blue-400">"timelineTracks"</span>: {
2673
+ <span class="text-green-400">"s0-elements-0"</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.5"</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> } },
2674
+ <span class="text-green-400">"myBox"</span>: { <span class="text-green-400">"0"</span>: { <span class="text-blue-400">x</span>: <span class="text-cyan-400">-50</span>, <span class="text-blue-400">visible</span>: <span class="text-yellow-400">false</span> }, <span class="text-green-400">"0.5"</span>: { <span class="text-blue-400">x</span>: <span class="text-cyan-400">100</span>, <span class="text-blue-400">visible</span>: <span class="text-yellow-400">true</span> } }
2675
+ }
2676
+ }</code></pre>
2811
2677
  </div>
2812
- <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>
2813
- </div>
2678
+ </section>
2814
2679
 
2815
2680
  <!-- REF: AUTO -->
2816
- <div v-else-if="activeSection === 'ref-auto'">
2681
+ <section id="ref-auto">
2817
2682
  <h1>Auto Strategy</h1>
2818
- <p class="lead">Smartly chooses the best layout based on the data structure provided. Useful for
2819
- AI agents or rapid prototyping.</p>
2683
+ <p class="lead">Picks a layout from the data shape. Use <code>type: "auto"</code>; the engine resolves to one of: Chart, Timeline, Steps, Features, Half, Statement. All SlideBase and layout-specific props are passed through to the chosen layout. Best for LLMs and rapid prototyping.</p>
2820
2684
 
2821
2685
  <div class="p-6 bg-blue-500/10 border border-blue-500/20 rounded-xl my-8">
2822
- <h3 class="text-blue-400 font-bold mb-2">How it works</h3>
2686
+ <h3 class="text-blue-400 font-bold mb-2">Resolution order (first match wins)</h3>
2823
2687
  <ul class="list-disc list-inside text-sm text-blue-200/80 space-y-1">
2824
- <li>If <code>chartType</code> + <code>data.datasets</code> exist ->
2825
- <strong>Chart</strong>
2826
- </li>
2827
- <li>If <code>timeline</code> array exists -> <strong>Timeline</strong></li>
2828
- <li>If <code>steps</code> array exists -> <strong>Steps</strong></li>
2829
- <li>If <code>features</code> array exists -> <strong>Features</strong></li>
2830
- <li>If <code>image</code> exists -> <strong>Half</strong></li>
2831
- <li>Otherwise -> <strong>Statement</strong></li>
2688
+ <li><code>chartType</code> and <code>data.datasets</code> <strong>Chart</strong></li>
2689
+ <li><code>timeline</code> (array) → <strong>Timeline</strong></li>
2690
+ <li><code>steps</code> (array) → <strong>Steps</strong></li>
2691
+ <li><code>features</code> (array) <strong>Features</strong></li>
2692
+ <li><code>image</code> or <code>video</code> <strong>Half</strong></li>
2693
+ <li>else <strong>Statement</strong> (<code>title</code>, <code>subtitle</code>)</li>
2832
2694
  </ul>
2695
+ <p class="text-blue-200/70 text-sm mt-3">Provide the fields required by the target layout (e.g. <code>features</code> must be FeatureItem[] if resolved to Features).</p>
2833
2696
  </div>
2834
2697
 
2835
2698
  <div class="my-8">
@@ -2879,22 +2742,50 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
2879
2742
  ]
2880
2743
  }' />
2881
2744
  </div>
2745
+ </section>
2746
+
2882
2747
  </div>
2883
2748
 
2884
2749
  </div>
2885
- </Transition>
2886
2750
  </main>
2887
2751
  </div>
2888
2752
  </template>
2889
2753
 
2890
2754
  <script setup lang="ts">
2891
- import { ref, watch, onUnmounted, nextTick } from 'vue';
2755
+ import { ref, watch, onUnmounted, onMounted, nextTick } from 'vue';
2892
2756
  import { Lumina } from '../../core/Lumina';
2893
2757
  import { parsePartialJson } from '../../utils/streaming';
2894
2758
  import LivePreview from './LivePreview.vue';
2895
2759
 
2896
2760
  const activeSection = ref('intro');
2897
2761
 
2762
+ const LAYOUT_IDS = ['ref-statement', 'ref-half', 'ref-features', 'ref-timeline', 'ref-steps', 'ref-flex', 'ref-chart', 'ref-video', 'ref-diagram', 'ref-free', 'ref-custom', 'ref-auto'];
2763
+
2764
+ function scrollToTop() {
2765
+ window.scrollTo({ top: 0, behavior: 'smooth' });
2766
+ }
2767
+
2768
+ function goToLayout(id: string) {
2769
+ activeSection.value = id;
2770
+ nextTick(() => {
2771
+ document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
2772
+ if (typeof location !== 'undefined') location.hash = id;
2773
+ });
2774
+ }
2775
+
2776
+ function goToStreamingDemo() {
2777
+ activeSection.value = 'agents-streaming';
2778
+ nextTick(() => document.getElementById('streaming-demo-container')?.scrollIntoView({ behavior: 'smooth', block: 'start' }));
2779
+ }
2780
+
2781
+ onMounted(() => {
2782
+ if (typeof window === 'undefined') return;
2783
+ const h = window.location.hash.slice(1) || '';
2784
+ if (!h) return;
2785
+ activeSection.value = h;
2786
+ nextTick(() => document.getElementById(h)?.scrollIntoView({ behavior: 'smooth', block: 'start' }));
2787
+ });
2788
+
2898
2789
  // --- DEMO LOGIC ---
2899
2790
  const demoInput = ref('');
2900
2791
  const demoStarted = ref(false);
@@ -2984,7 +2875,7 @@ async function runDemo() {
2984
2875
 
2985
2876
  function loadIntoDemo(json: string) {
2986
2877
  activeSection.value = 'agents-streaming';
2987
- window.scrollTo({ top: 0, behavior: 'smooth' });
2878
+ nextTick(() => document.getElementById('streaming-demo-container')?.scrollIntoView({ behavior: 'smooth', block: 'start' }));
2988
2879
  demoInput.value = '';
2989
2880
  demoStarted.value = true;
2990
2881
 
@@ -3194,6 +3085,7 @@ const navigation = [
3194
3085
  title: 'AI & LLM Integration',
3195
3086
  items: [
3196
3087
  { id: 'agents-streaming', label: 'Streaming & Realtime' },
3088
+ { id: 'agents-layouts', label: 'Layout Gallery' },
3197
3089
  { id: 'agents-intro', label: 'Agent Protocol' },
3198
3090
  { id: 'agents-tokens', label: 'Token Optimization' }
3199
3091
  ]
@@ -3206,6 +3098,7 @@ const navigation = [
3206
3098
  { id: 'element-control', label: 'Element Control' },
3207
3099
  { id: 'animations', label: 'Animations (reveal, presets, stagger)' },
3208
3100
  { id: 'timeline-keyframes', label: 'Timeline (keyframes)' },
3101
+ { id: 'template-tags', label: 'Template Tags & engine.data' },
3209
3102
  { id: 'sizing', label: 'Embedding' },
3210
3103
  { id: 'speaker-notes', label: 'Speaker Notes' }
3211
3104
  ]
@@ -3213,21 +3106,13 @@ const navigation = [
3213
3106
  ];
3214
3107
 
3215
3108
 
3216
- function scroll(id: string) {
3217
- nextTick(() => {
3218
- const el = document.getElementById(id);
3219
- if (el) {
3220
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
3221
- }
3222
- });
3223
- }
3224
-
3225
- function scrollToTop() {
3226
- window.scrollTo({ top: 0, behavior: 'smooth' });
3227
- }
3228
3109
  </script>
3229
3110
 
3230
3111
  <style scoped>
3112
+ .doc-content [id] {
3113
+ scroll-margin-top: 8.5rem;
3114
+ }
3115
+
3231
3116
  .doc-content h1 {
3232
3117
  @apply text-4xl md:text-5xl font-black tracking-tight text-white mb-8 leading-tight font-heading;
3233
3118
  }