lumina-slides 9.0.2 → 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.
- package/dist/lumina-slides.js +29 -9
- package/dist/lumina-slides.umd.cjs +2 -2
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/site/SiteDocs.vue +93 -72
- package/src/core/Lumina.ts +30 -8
- package/src/core/elementResolver.ts +7 -1
- package/src/core/types.ts +3 -2
- package/src/index.ts +1 -1
|
@@ -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
|
|
5
|
-
<div class="
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
|
|
@@ -205,7 +204,7 @@ engine.load(myDeckData);</code></pre>
|
|
|
205
204
|
</div>
|
|
206
205
|
|
|
207
206
|
<!-- SLIDE LAYOUTS -->
|
|
208
|
-
<div v-else-if="activeSection === 'slides'">
|
|
207
|
+
<div v-else-if="activeSection === 'slides'" id="slides">
|
|
209
208
|
<h1>Slide Layouts</h1>
|
|
210
209
|
<p>Lumina includes responsive layouts for common scenarios. Each layout has a reference section with a full property table.</p>
|
|
211
210
|
|
|
@@ -335,7 +334,7 @@ engine.load(myDeckData);</code></pre>
|
|
|
335
334
|
|
|
336
335
|
|
|
337
336
|
<!-- SIZING / EMBEDDING -->
|
|
338
|
-
<div v-else-if="activeSection === 'sizing'">
|
|
337
|
+
<div v-else-if="activeSection === 'sizing'" id="sizing">
|
|
339
338
|
<h1>Embedding & Sizing</h1>
|
|
340
339
|
<p>By default, Lumina slides take up the full viewport height (<code>100vh</code>) to create an
|
|
341
340
|
immersive experience.</p>
|
|
@@ -364,7 +363,7 @@ engine.load(myDeckData);</code></pre>
|
|
|
364
363
|
</div>
|
|
365
364
|
|
|
366
365
|
<!-- CONFIGURATION -->
|
|
367
|
-
<div v-else-if="activeSection === 'config'">
|
|
366
|
+
<div v-else-if="activeSection === 'config'" id="config">
|
|
368
367
|
<h1>Configuration</h1>
|
|
369
368
|
<p>Pass options to <code>new Lumina(selector, options)</code>. All properties are optional.</p>
|
|
370
369
|
|
|
@@ -408,7 +407,7 @@ engine.load(myDeckData);</code></pre>
|
|
|
408
407
|
</div>
|
|
409
408
|
|
|
410
409
|
<!-- EVENTS -->
|
|
411
|
-
<div v-else-if="activeSection === 'events'">
|
|
410
|
+
<div v-else-if="activeSection === 'events'" id="events">
|
|
412
411
|
<h1>Events & API</h1>
|
|
413
412
|
<p>Subscribe with <code>engine.on(event, handler)</code>; unsubscribe with <code>engine.off(event, handler)</code> using the same function reference.</p>
|
|
414
413
|
|
|
@@ -443,7 +442,7 @@ engine.<span class="text-yellow-400">on</span>('navigate', ({ direction, toIndex
|
|
|
443
442
|
</div>
|
|
444
443
|
|
|
445
444
|
<!-- ELEMENT CONTROL -->
|
|
446
|
-
<div v-else-if="activeSection === 'element-control'">
|
|
445
|
+
<div v-else-if="activeSection === 'element-control'" id="element-control">
|
|
447
446
|
<h1>Element Control (Reveal on Demand)</h1>
|
|
448
447
|
<p class="lead">Start with elements hidden and reveal them in sequence: ideal for blank slides that
|
|
449
448
|
fill in as you speak, or for effects driven by <code>engine.element(id)</code>.</p>
|
|
@@ -570,7 +569,7 @@ engine.load(deck);</code></pre>
|
|
|
570
569
|
</div>
|
|
571
570
|
|
|
572
571
|
<!-- ANIMATIONS: revealInSequence, presets, stagger -->
|
|
573
|
-
<div v-else-if="activeSection === 'animations'">
|
|
572
|
+
<div v-else-if="activeSection === 'animations'" id="animations">
|
|
574
573
|
<h1>Animations</h1>
|
|
575
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>
|
|
576
575
|
|
|
@@ -602,7 +601,7 @@ engine.load(deck);</code></pre>
|
|
|
602
601
|
</div>
|
|
603
602
|
|
|
604
603
|
<!-- TIMELINE (Remotion-style keyframes) -->
|
|
605
|
-
<div v-else-if="activeSection === 'timeline-keyframes'">
|
|
604
|
+
<div v-else-if="activeSection === 'timeline-keyframes'" id="timeline-keyframes">
|
|
606
605
|
<h1>Timeline (Remotion-style keyframes)</h1>
|
|
607
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>
|
|
608
607
|
|
|
@@ -638,7 +637,7 @@ engine.load(deck);</code></pre>
|
|
|
638
637
|
</div>
|
|
639
638
|
|
|
640
639
|
<!-- THEMING -->
|
|
641
|
-
<div v-else-if="activeSection === 'theming'">
|
|
640
|
+
<div v-else-if="activeSection === 'theming'" id="theming">
|
|
642
641
|
<h1>Theming</h1>
|
|
643
642
|
<p class="lead">Customize colors, fonts, and visual style with built-in presets or create your
|
|
644
643
|
own theme.</p>
|
|
@@ -1403,7 +1402,7 @@ engine.load(myDeck);</code></pre>
|
|
|
1403
1402
|
</div>
|
|
1404
1403
|
|
|
1405
1404
|
<!-- SPEAKER NOTES -->
|
|
1406
|
-
<div v-else-if="activeSection === 'speaker-notes'">
|
|
1405
|
+
<div v-else-if="activeSection === 'speaker-notes'" id="speaker-notes">
|
|
1407
1406
|
<h1>Speaker Notes</h1>
|
|
1408
1407
|
<p class="lead">Open a separate presenter view with notes, timer, and bidirectional navigation
|
|
1409
1408
|
controls.</p>
|
|
@@ -1487,7 +1486,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1487
1486
|
|
|
1488
1487
|
|
|
1489
1488
|
<!-- AGENTS: INTRO -->
|
|
1490
|
-
<div v-else-if="activeSection === 'agents-intro'">
|
|
1489
|
+
<div v-else-if="activeSection === 'agents-intro'" id="agents-intro">
|
|
1491
1490
|
<h1>The Agent Protocol</h1>
|
|
1492
1491
|
<p class="lead">Lumina is the first presentation engine designed for <strong>Agentic
|
|
1493
1492
|
AI</strong>.</p>
|
|
@@ -1533,7 +1532,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1533
1532
|
</div>
|
|
1534
1533
|
|
|
1535
1534
|
<!-- AGENTS: TOKENS -->
|
|
1536
|
-
<div v-else-if="activeSection === 'agents-tokens'">
|
|
1535
|
+
<div v-else-if="activeSection === 'agents-tokens'" id="agents-tokens">
|
|
1537
1536
|
<h1>Token Optimization</h1>
|
|
1538
1537
|
<p class="lead">Reduce output size with short property aliases. Less characters = faster
|
|
1539
1538
|
rendering and lower costs.</p>
|
|
@@ -1641,7 +1640,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1641
1640
|
</div>
|
|
1642
1641
|
|
|
1643
1642
|
<!-- AGENTS: STREAMING -->
|
|
1644
|
-
<div v-else-if="activeSection === 'agents-streaming'">
|
|
1643
|
+
<div v-else-if="activeSection === 'agents-streaming'" id="agents-streaming">
|
|
1645
1644
|
<h1>Streaming & Realtime</h1>
|
|
1646
1645
|
<p>Lumina enables a <strong>"Type & See"</strong> experience. Using our partial parser, you can
|
|
1647
1646
|
feed raw LLM streams directly into the engine.</p>
|
|
@@ -1689,12 +1688,11 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1689
1688
|
</div>
|
|
1690
1689
|
|
|
1691
1690
|
<!-- AGENTS: LAYOUT GALLERY -->
|
|
1692
|
-
<div v-else-if="activeSection === 'agents-layouts'">
|
|
1691
|
+
<div v-else-if="activeSection === 'agents-layouts'" id="agents-layouts">
|
|
1693
1692
|
<h1>Layout Gallery</h1>
|
|
1694
1693
|
<p>Lumina provides 5 core layouts optimized for different types of information. Click <strong><i
|
|
1695
1694
|
class="fa-solid fa-play text-blue-400"></i> Visualize</strong> to load them into the
|
|
1696
|
-
<a @click.prevent="
|
|
1697
|
-
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>.
|
|
1698
1696
|
</p>
|
|
1699
1697
|
|
|
1700
1698
|
<div class="space-y-12 mt-12">
|
|
@@ -1750,7 +1748,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1750
1748
|
</div>
|
|
1751
1749
|
</div>
|
|
1752
1750
|
|
|
1753
|
-
<div v-else-if="activeSection === 'agents-auto'">
|
|
1751
|
+
<div v-else-if="activeSection === 'agents-auto'" id="agents-auto">
|
|
1754
1752
|
<h1>Auto-Layouts</h1>
|
|
1755
1753
|
<p>Sometimes the Agent doesn't know which layout is best. Just use <code>type: 'auto'</code>.
|
|
1756
1754
|
</p>
|
|
@@ -1828,7 +1826,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1828
1826
|
</div>
|
|
1829
1827
|
|
|
1830
1828
|
<!-- AGENTS: STATE -->
|
|
1831
|
-
<div v-else-if="activeSection === 'agents-state'">
|
|
1829
|
+
<div v-else-if="activeSection === 'agents-state'" id="agents-state">
|
|
1832
1830
|
<h1>State & Feedback Loop</h1>
|
|
1833
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>
|
|
1834
1832
|
<pre><code><span class="text-blue-400">const</span> state = engine.exportState();
|
|
@@ -1836,7 +1834,7 @@ engine.<span class="text-yellow-400">closeSpeakerNotes</span>();</code></pre>
|
|
|
1836
1834
|
</div>
|
|
1837
1835
|
|
|
1838
1836
|
<!-- TEMPLATE TAGS & engine.data -->
|
|
1839
|
-
<div v-else-if="activeSection === 'template-tags'">
|
|
1837
|
+
<div v-else-if="activeSection === 'template-tags'" id="template-tags">
|
|
1840
1838
|
<h1>Template Tags & Key-Value Store</h1>
|
|
1841
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>
|
|
1842
1840
|
|
|
@@ -1879,8 +1877,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
1879
1877
|
</p>
|
|
1880
1878
|
</div>
|
|
1881
1879
|
|
|
1880
|
+
<!-- LAYOUTS (single page with anchors) -->
|
|
1881
|
+
<div v-else-if="LAYOUT_IDS.includes(activeSection)" class="space-y-24">
|
|
1882
1882
|
<!-- REF: STATEMENT -->
|
|
1883
|
-
<
|
|
1883
|
+
<section id="ref-statement">
|
|
1884
1884
|
<h1>Statement Slide</h1>
|
|
1885
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>
|
|
1886
1886
|
|
|
@@ -1910,10 +1910,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
1910
1910
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
1911
1911
|
<LivePreview :initial-code="EXAMPLES.statement" />
|
|
1912
1912
|
</div>
|
|
1913
|
-
</
|
|
1913
|
+
</section>
|
|
1914
1914
|
|
|
1915
1915
|
<!-- REF: HALF -->
|
|
1916
|
-
<
|
|
1916
|
+
<section id="ref-half">
|
|
1917
1917
|
<h1>Half / Split Slide</h1>
|
|
1918
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>
|
|
1919
1919
|
|
|
@@ -1944,10 +1944,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
1944
1944
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
1945
1945
|
<LivePreview :initial-code="EXAMPLES.half" />
|
|
1946
1946
|
</div>
|
|
1947
|
-
</
|
|
1947
|
+
</section>
|
|
1948
1948
|
|
|
1949
1949
|
<!-- REF: FEATURES -->
|
|
1950
|
-
<
|
|
1950
|
+
<section id="ref-features">
|
|
1951
1951
|
<h1>Features Slide</h1>
|
|
1952
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>
|
|
1953
1953
|
|
|
@@ -1989,10 +1989,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
1989
1989
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
1990
1990
|
<LivePreview :initial-code="EXAMPLES.features" />
|
|
1991
1991
|
</div>
|
|
1992
|
-
</
|
|
1992
|
+
</section>
|
|
1993
1993
|
|
|
1994
1994
|
<!-- REF: TIMELINE -->
|
|
1995
|
-
<
|
|
1995
|
+
<section id="ref-timeline">
|
|
1996
1996
|
<h1>Timeline Slide</h1>
|
|
1997
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>
|
|
1998
1998
|
|
|
@@ -2035,10 +2035,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2035
2035
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
2036
2036
|
<LivePreview :initial-code="EXAMPLES.timeline" />
|
|
2037
2037
|
</div>
|
|
2038
|
-
</
|
|
2038
|
+
</section>
|
|
2039
2039
|
|
|
2040
2040
|
<!-- REF: STEPS -->
|
|
2041
|
-
<
|
|
2041
|
+
<section id="ref-steps">
|
|
2042
2042
|
<h1>Steps Slide</h1>
|
|
2043
2043
|
<p class="lead">Numbered steps for tutorials, how-to guides, or process flows. Responsive grid. SlideBase applies.</p>
|
|
2044
2044
|
|
|
@@ -2081,10 +2081,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2081
2081
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
2082
2082
|
<LivePreview :initial-code="EXAMPLES.steps" />
|
|
2083
2083
|
</div>
|
|
2084
|
-
</
|
|
2084
|
+
</section>
|
|
2085
2085
|
|
|
2086
2086
|
<!-- REF: FLEX -->
|
|
2087
|
-
<
|
|
2087
|
+
<section id="ref-flex">
|
|
2088
2088
|
<h1>Flex Layout</h1>
|
|
2089
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>
|
|
2090
2090
|
|
|
@@ -2147,10 +2147,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2147
2147
|
<h2 class="text-xl font-bold text-white mb-4">Example</h2>
|
|
2148
2148
|
<LivePreview :initial-code="EXAMPLES.flex" />
|
|
2149
2149
|
</div>
|
|
2150
|
-
</
|
|
2150
|
+
</section>
|
|
2151
2151
|
|
|
2152
2152
|
<!-- REF: CHART -->
|
|
2153
|
-
<
|
|
2153
|
+
<section id="ref-chart">
|
|
2154
2154
|
<h1>Chart Slide</h1>
|
|
2155
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>
|
|
2156
2156
|
|
|
@@ -2439,10 +2439,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2439
2439
|
charts automatically</li>
|
|
2440
2440
|
</ul>
|
|
2441
2441
|
</div>
|
|
2442
|
-
</
|
|
2442
|
+
</section>
|
|
2443
2443
|
|
|
2444
2444
|
<!-- REF: VIDEO -->
|
|
2445
|
-
<
|
|
2445
|
+
<section id="ref-video">
|
|
2446
2446
|
<h1>Video Slide</h1>
|
|
2447
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>
|
|
2448
2448
|
|
|
@@ -2508,10 +2508,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2508
2508
|
</div>
|
|
2509
2509
|
|
|
2510
2510
|
<p class="text-white/60 text-sm">Element control ids: <code>s{N}-video</code>, <code>s{N}-title</code>.</p>
|
|
2511
|
-
</
|
|
2511
|
+
</section>
|
|
2512
2512
|
|
|
2513
2513
|
<!-- REF: CUSTOM HTML -->
|
|
2514
|
-
<
|
|
2514
|
+
<section id="ref-custom">
|
|
2515
2515
|
<h1>Custom HTML Slide</h1>
|
|
2516
2516
|
<p class="lead">Raw <code>html</code> and optional <code>css</code>. For iframes, D3, or brand-specific layouts. SlideBase applies. Sanitized: <code><script></code> and <code>on*</code> handlers removed.</p>
|
|
2517
2517
|
|
|
@@ -2585,10 +2585,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2585
2585
|
creative freedom</li>
|
|
2586
2586
|
</ul>
|
|
2587
2587
|
</div>
|
|
2588
|
-
</
|
|
2588
|
+
</section>
|
|
2589
2589
|
|
|
2590
2590
|
<!-- REF: DIAGRAM -->
|
|
2591
|
-
<
|
|
2591
|
+
<section id="ref-diagram">
|
|
2592
2592
|
<h1>Diagram Slide</h1>
|
|
2593
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>
|
|
2594
2594
|
|
|
@@ -2610,10 +2610,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2610
2610
|
</div>
|
|
2611
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>
|
|
2612
2612
|
</div>
|
|
2613
|
-
</
|
|
2613
|
+
</section>
|
|
2614
2614
|
|
|
2615
2615
|
<!-- REF: FREE -->
|
|
2616
|
-
<
|
|
2616
|
+
<section id="ref-free">
|
|
2617
2617
|
<h1>Free / Composition Slide</h1>
|
|
2618
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>
|
|
2619
2619
|
|
|
@@ -2675,10 +2675,10 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2675
2675
|
}
|
|
2676
2676
|
}</code></pre>
|
|
2677
2677
|
</div>
|
|
2678
|
-
</
|
|
2678
|
+
</section>
|
|
2679
2679
|
|
|
2680
2680
|
<!-- REF: AUTO -->
|
|
2681
|
-
<
|
|
2681
|
+
<section id="ref-auto">
|
|
2682
2682
|
<h1>Auto Strategy</h1>
|
|
2683
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>
|
|
2684
2684
|
|
|
@@ -2742,22 +2742,50 @@ engine.data.set(<span class="text-green-400">"user"</span>, <span class="text-gr
|
|
|
2742
2742
|
]
|
|
2743
2743
|
}' />
|
|
2744
2744
|
</div>
|
|
2745
|
+
</section>
|
|
2746
|
+
|
|
2745
2747
|
</div>
|
|
2746
2748
|
|
|
2747
2749
|
</div>
|
|
2748
|
-
</Transition>
|
|
2749
2750
|
</main>
|
|
2750
2751
|
</div>
|
|
2751
2752
|
</template>
|
|
2752
2753
|
|
|
2753
2754
|
<script setup lang="ts">
|
|
2754
|
-
import { ref, watch, onUnmounted, nextTick } from 'vue';
|
|
2755
|
+
import { ref, watch, onUnmounted, onMounted, nextTick } from 'vue';
|
|
2755
2756
|
import { Lumina } from '../../core/Lumina';
|
|
2756
2757
|
import { parsePartialJson } from '../../utils/streaming';
|
|
2757
2758
|
import LivePreview from './LivePreview.vue';
|
|
2758
2759
|
|
|
2759
2760
|
const activeSection = ref('intro');
|
|
2760
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
|
+
|
|
2761
2789
|
// --- DEMO LOGIC ---
|
|
2762
2790
|
const demoInput = ref('');
|
|
2763
2791
|
const demoStarted = ref(false);
|
|
@@ -2847,7 +2875,7 @@ async function runDemo() {
|
|
|
2847
2875
|
|
|
2848
2876
|
function loadIntoDemo(json: string) {
|
|
2849
2877
|
activeSection.value = 'agents-streaming';
|
|
2850
|
-
|
|
2878
|
+
nextTick(() => document.getElementById('streaming-demo-container')?.scrollIntoView({ behavior: 'smooth', block: 'start' }));
|
|
2851
2879
|
demoInput.value = '';
|
|
2852
2880
|
demoStarted.value = true;
|
|
2853
2881
|
|
|
@@ -3057,6 +3085,7 @@ const navigation = [
|
|
|
3057
3085
|
title: 'AI & LLM Integration',
|
|
3058
3086
|
items: [
|
|
3059
3087
|
{ id: 'agents-streaming', label: 'Streaming & Realtime' },
|
|
3088
|
+
{ id: 'agents-layouts', label: 'Layout Gallery' },
|
|
3060
3089
|
{ id: 'agents-intro', label: 'Agent Protocol' },
|
|
3061
3090
|
{ id: 'agents-tokens', label: 'Token Optimization' }
|
|
3062
3091
|
]
|
|
@@ -3077,21 +3106,13 @@ const navigation = [
|
|
|
3077
3106
|
];
|
|
3078
3107
|
|
|
3079
3108
|
|
|
3080
|
-
function scroll(id: string) {
|
|
3081
|
-
nextTick(() => {
|
|
3082
|
-
const el = document.getElementById(id);
|
|
3083
|
-
if (el) {
|
|
3084
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
3085
|
-
}
|
|
3086
|
-
});
|
|
3087
|
-
}
|
|
3088
|
-
|
|
3089
|
-
function scrollToTop() {
|
|
3090
|
-
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
3091
|
-
}
|
|
3092
3109
|
</script>
|
|
3093
3110
|
|
|
3094
3111
|
<style scoped>
|
|
3112
|
+
.doc-content [id] {
|
|
3113
|
+
scroll-margin-top: 8.5rem;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3095
3116
|
.doc-content h1 {
|
|
3096
3117
|
@apply text-4xl md:text-5xl font-black tracking-tight text-white mb-8 leading-tight font-heading;
|
|
3097
3118
|
}
|
package/src/core/Lumina.ts
CHANGED
|
@@ -433,28 +433,50 @@ export class Lumina {
|
|
|
433
433
|
return createElementController(this.store, this, idOrSlide);
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Returns the element at the given path on the current slide. Shorthand for
|
|
438
|
+
* engine.element(engine.currentSlideIndex, path). Use when you don't need the slide index.
|
|
439
|
+
*
|
|
440
|
+
* @param path - Logical path (e.g. "title", "features.0", ["elements", 0, "elements", 1]).
|
|
441
|
+
* @returns ElementController for that element.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* engine.elementInCurrent("title").show();
|
|
445
|
+
* engine.elementInCurrent("features.0").animate({ from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.5 });
|
|
446
|
+
*
|
|
447
|
+
* @see element
|
|
448
|
+
* @see elements
|
|
449
|
+
*/
|
|
450
|
+
public elementInCurrent(path: string | ElementPath): ElementController {
|
|
451
|
+
const i = this.store.state.currentIndex ?? 0;
|
|
452
|
+
return this.element(i, path);
|
|
453
|
+
}
|
|
454
|
+
|
|
436
455
|
/**
|
|
437
456
|
* Returns all element ids for a slide. Use to discover which ids exist (e.g. for
|
|
438
457
|
* meta.initialElementState, or to iterate and control elements). Each id can be passed to
|
|
439
458
|
* engine.element(id). Uses the same path→id logic as element(slideIndex, path); ids follow
|
|
440
|
-
* resolveId (explicit id, slide.ids, or elemId(slideIndex, ...path)).
|
|
459
|
+
* resolveId (explicit id, slide.ids, or slide.id / elemId(slideIndex, ...path) as fallback).
|
|
441
460
|
*
|
|
442
|
-
* @param slideIndex - Zero-based slide index. If out of range or deck not loaded, returns [].
|
|
443
|
-
* @returns Array of id strings (e.g. ["s0-slide","s0-tag","s0-title","s0-subtitle"]
|
|
461
|
+
* @param slideIndex - Zero-based slide index. Omit to use the current slide. If out of range or deck not loaded, returns [].
|
|
462
|
+
* @returns Array of id strings (e.g. ["s0-slide","s0-tag","s0-title","s0-subtitle"] or ["intro-slide","intro-tag",...] when slide.id is set).
|
|
444
463
|
*
|
|
445
464
|
* @example
|
|
446
|
-
*
|
|
447
|
-
*
|
|
465
|
+
* engine.elements(); // current slide
|
|
466
|
+
* engine.elements(0);
|
|
467
|
+
* engine.elements().forEach(id => { if (id.endsWith('-title')) engine.element(id).hide(); });
|
|
448
468
|
*
|
|
449
469
|
* @see element
|
|
470
|
+
* @see elementInCurrent
|
|
450
471
|
* @see getElementIds
|
|
451
472
|
* @see resolveId
|
|
452
473
|
* @see DeckMeta.initialElementState
|
|
453
474
|
*/
|
|
454
|
-
public elements(slideIndex
|
|
455
|
-
const
|
|
475
|
+
public elements(slideIndex?: number): string[] {
|
|
476
|
+
const i = slideIndex ?? this.store.state.currentIndex ?? 0;
|
|
477
|
+
const slide = this.store.state.deck?.slides[i];
|
|
456
478
|
if (!slide) return [];
|
|
457
|
-
return getElementIds(slide,
|
|
479
|
+
return getElementIds(slide, i);
|
|
458
480
|
}
|
|
459
481
|
|
|
460
482
|
/**
|
|
@@ -71,7 +71,8 @@ export function parsePath(input: string | ElementPath): ElementPath {
|
|
|
71
71
|
* 1. Path [] or ['slide'] → slide.id or elemId(slideIndex, 'slide').
|
|
72
72
|
* 2. If the value at path is an object with string `id` → use it (e.g. feature.id, node.id).
|
|
73
73
|
* 3. If slide.ids[pathToKey(path)] exists → use it (supports compound keys: 'tag', 'features.0', 'elements.0.elements.1').
|
|
74
|
-
* 4. Otherwise →
|
|
74
|
+
* 4. Otherwise → if slide.id is set, "{slide.id}-{path}"; else elemId(slideIndex, ...path)
|
|
75
|
+
* (e.g. "intro-tag" with slide.id "intro", or "s0-tag" when no slide.id).
|
|
75
76
|
*
|
|
76
77
|
* @param slide - The slide data.
|
|
77
78
|
* @param slideIndex - Zero-based slide index (for elemId fallback).
|
|
@@ -99,6 +100,11 @@ export function resolveId(
|
|
|
99
100
|
if (key && slideAny?.ids?.[key]) {
|
|
100
101
|
return slideAny.ids[key];
|
|
101
102
|
}
|
|
103
|
+
// Fallback: use slide.id as namespace when present, so IDs stay stable when slides are
|
|
104
|
+
// inserted, removed or reordered. Otherwise s{N}-{path} (same as before).
|
|
105
|
+
if (slideAny?.id != null && slideAny.id !== '') {
|
|
106
|
+
return `${slideAny.id}-${path.map(String).join('-')}`;
|
|
107
|
+
}
|
|
102
108
|
return elemId(slideIndex, ...path);
|
|
103
109
|
}
|
|
104
110
|
|
package/src/core/types.ts
CHANGED
|
@@ -448,8 +448,8 @@ export type TimelineKeyframes = Record<string, TimelineKeyframeState>;
|
|
|
448
448
|
export type TimelineTracks = Record<string, TimelineKeyframes>;
|
|
449
449
|
|
|
450
450
|
/**
|
|
451
|
-
* Fluent API to control one slide element in real time. Returned by `engine.element(id)
|
|
452
|
-
*
|
|
451
|
+
* Fluent API to control one slide element in real time. Returned by `engine.element(id)`,
|
|
452
|
+
* `engine.element(slideIndex, path)`, and `engine.elementInCurrent(path)`. All methods return `this` for chaining.
|
|
453
453
|
*
|
|
454
454
|
* Use for: starting with an empty slide and revealing items in order, hiding highlights,
|
|
455
455
|
* animating entrances on demand, or syncing with voice/agent.
|
|
@@ -462,6 +462,7 @@ export type TimelineTracks = Record<string, TimelineKeyframes>;
|
|
|
462
462
|
* engine.element(0, 'subtitle').show();
|
|
463
463
|
*
|
|
464
464
|
* @see Lumina.element
|
|
465
|
+
* @see Lumina.elementInCurrent
|
|
465
466
|
* @see Lumina.elements
|
|
466
467
|
* @see ElementState
|
|
467
468
|
* @see AnimateOptions
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* **Deck:** `{ meta: { title, initialElementState?, elementControl?, effects? }, slides: [...] }`
|
|
11
11
|
* Slide types: statement, features, half, timeline, steps, flex, chart, diagram, custom, video.
|
|
12
12
|
*
|
|
13
|
-
* **Element control:** `engine.element(id)
|
|
13
|
+
* **Element control:** `engine.element(id)`, `engine.element(slideIndex, path)`, or `engine.elementInCurrent(path)` → ElementController:
|
|
14
14
|
* `.show()`, `.hide()`, `.toggle()`, `.opacity(n)`, `.transform(s)`, `.animate({ preset?, from?, to?, duration?, ease? })`.
|
|
15
15
|
* Presets: fadeUp, fadeIn, scaleIn, slideLeft, slideRight, zoomIn, blurIn, spring, drop, fadeOut. `to` optional when using preset.
|
|
16
16
|
* Ids: `engine.elements(slideIndex)` or `s{N}-{path}` (e.g. s0-tag, s1-features-0). `meta.initialElementState`:
|