lumina-slides 9.0.5 → 9.0.7
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/README.md +63 -0
- package/dist/lumina-slides.js +21750 -19334
- package/dist/lumina-slides.umd.cjs +223 -223
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/LandingPage.vue +1 -1
- package/src/components/LuminaDeck.vue +237 -232
- package/src/components/base/LuminaElement.vue +2 -0
- package/src/components/layouts/LayoutFeatures.vue +125 -123
- package/src/components/layouts/LayoutFlex.vue +212 -212
- package/src/components/layouts/LayoutStatement.vue +5 -2
- package/src/components/layouts/LayoutSteps.vue +110 -108
- package/src/components/parts/FlexHtml.vue +65 -65
- package/src/components/parts/FlexImage.vue +81 -81
- package/src/components/site/SiteDocs.vue +3313 -3314
- package/src/components/site/SiteExamples.vue +66 -66
- package/src/components/studio/EditorLayoutChart.vue +18 -0
- package/src/components/studio/EditorLayoutCustom.vue +18 -0
- package/src/components/studio/EditorLayoutVideo.vue +18 -0
- package/src/components/studio/LuminaStudioEmbed.vue +68 -0
- package/src/components/studio/StudioEmbedRoot.vue +19 -0
- package/src/components/studio/StudioInspector.vue +1113 -7
- package/src/components/studio/StudioJsonEditor.vue +10 -3
- package/src/components/studio/StudioSettings.vue +658 -7
- package/src/components/studio/StudioToolbar.vue +26 -7
- package/src/composables/useElementState.ts +12 -1
- package/src/composables/useFlexLayout.ts +128 -128
- package/src/core/Lumina.ts +174 -113
- package/src/core/animationConfig.ts +10 -0
- package/src/core/elementController.ts +18 -0
- package/src/core/elementResolver.ts +4 -2
- package/src/core/schema.ts +503 -503
- package/src/core/store.ts +465 -465
- package/src/core/types.ts +26 -11
- package/src/index.ts +2 -2
- package/src/utils/prepareDeckForExport.ts +47 -0
- package/src/utils/templateInterpolation.ts +52 -52
- package/src/views/DeckView.vue +313 -313
package/src/core/types.ts
CHANGED
|
@@ -591,7 +591,7 @@ export interface VideoProperties {
|
|
|
591
591
|
* "type": "statement",
|
|
592
592
|
* "meta": { "orbColor": "#3b82f6" },
|
|
593
593
|
* "tag": "Declarative Engine",
|
|
594
|
-
* "title": "Lumina
|
|
594
|
+
* "title": "Lumina V9",
|
|
595
595
|
* "subtitle": "A highly modular, performance-first presentation engine."
|
|
596
596
|
* }
|
|
597
597
|
* ```
|
|
@@ -894,8 +894,7 @@ export interface DeckMeta {
|
|
|
894
894
|
*/
|
|
895
895
|
initialElementState?: InitialElementState;
|
|
896
896
|
/**
|
|
897
|
-
* Element control defaults.
|
|
898
|
-
* defaultVisible: false = start all elements hidden; override per-id via initialElementState.
|
|
897
|
+
* Element control defaults. Elements are hidden by default. Set defaultVisible: true to show all.
|
|
899
898
|
*/
|
|
900
899
|
elementControl?: { defaultVisible?: boolean };
|
|
901
900
|
/** Default reveal options for all slides. Overridden by slide.reveal. */
|
|
@@ -911,8 +910,8 @@ export interface DeckMeta {
|
|
|
911
910
|
* Complete deck object. Primary input to `engine.load(deck)`.
|
|
912
911
|
*
|
|
913
912
|
* @description
|
|
914
|
-
* Must have `meta.title` and `slides` (array of slide objects). meta.initialElementState
|
|
915
|
-
* meta.elementControl
|
|
913
|
+
* Must have `meta.title` and `slides` (array of slide objects). meta.initialElementState overrides per-element
|
|
914
|
+
* initial state. meta.elementControl.defaultVisible: true shows all. meta.effects overrides animation.
|
|
916
915
|
*
|
|
917
916
|
* @example
|
|
918
917
|
* { meta: { title: "Demo", initialElementState: { "s0-title": { visible: false } } }, slides: [{ type: "statement", title: "Hi" }] }
|
|
@@ -1388,6 +1387,12 @@ export interface LuminaAnimationOptions {
|
|
|
1388
1387
|
/** GSAP ease for reveal. Default: 'power2.out'. */
|
|
1389
1388
|
revealEase?: string;
|
|
1390
1389
|
|
|
1390
|
+
// --- Default cascade (when no meta.reveal or slide.reveal) ---
|
|
1391
|
+
/** Delay in ms between elements for implicit default cascade. Default: 120. Override via meta.effects. */
|
|
1392
|
+
defaultCascadeDelayMs?: number;
|
|
1393
|
+
/** Per-element duration (s) for implicit default cascade. Default: 0.3. Override via meta.effects. */
|
|
1394
|
+
defaultCascadeDuration?: number;
|
|
1395
|
+
|
|
1391
1396
|
// --- element().animate() ---
|
|
1392
1397
|
/** Default duration in seconds. Default: 0.5. */
|
|
1393
1398
|
elementDuration?: number;
|
|
@@ -1450,7 +1455,7 @@ export interface LuminaAnimationOptions {
|
|
|
1450
1455
|
*
|
|
1451
1456
|
* @example
|
|
1452
1457
|
* new Lumina("#app", { theme: "midnight", loop: true, animation: { durationIn: 1.2 } });
|
|
1453
|
-
* new Lumina("#app", { elementControl: { defaultVisible:
|
|
1458
|
+
* new Lumina("#app", { elementControl: { defaultVisible: true } }); // legacy: show all
|
|
1454
1459
|
*
|
|
1455
1460
|
* @see Lumina
|
|
1456
1461
|
* @see LuminaAnimationOptions
|
|
@@ -1477,16 +1482,23 @@ export interface LuminaOptions {
|
|
|
1477
1482
|
/** Animation settings. */
|
|
1478
1483
|
animation?: LuminaAnimationOptions;
|
|
1479
1484
|
/**
|
|
1480
|
-
* Element control defaults.
|
|
1481
|
-
*
|
|
1482
|
-
* engine.element(id).show().
|
|
1485
|
+
* Element control defaults. Elements are hidden by default and cascade-reveal on slide enter.
|
|
1486
|
+
* Set defaultVisible: true to show all elements immediately (legacy behavior).
|
|
1483
1487
|
*/
|
|
1484
1488
|
elementControl?: {
|
|
1485
|
-
/** If
|
|
1489
|
+
/** If true, all elements start visible. Default: false (elements hidden, cascade on enter). */
|
|
1486
1490
|
defaultVisible?: boolean;
|
|
1487
1491
|
};
|
|
1488
1492
|
/** Enable Studio (Page Builder) mode. */
|
|
1489
1493
|
studio?: boolean;
|
|
1494
|
+
/**
|
|
1495
|
+
* Enable Studio Embed mode: same editor as Studio but without back button;
|
|
1496
|
+
* Save button emits a "save" event with the deck JSON instead of persisting to Firestore.
|
|
1497
|
+
* Use with vanilla JS: new Lumina("#app", { studioEmbed: true, initialDeck: deck }); engine.on("save", (d) => { ... }).
|
|
1498
|
+
*/
|
|
1499
|
+
studioEmbed?: boolean;
|
|
1500
|
+
/** Initial deck when using studioEmbed. Can be updated later with engine.load(deck). */
|
|
1501
|
+
initialDeck?: Deck;
|
|
1490
1502
|
}
|
|
1491
1503
|
|
|
1492
1504
|
// --- Events ---
|
|
@@ -1511,7 +1523,8 @@ export type LuminaEventType =
|
|
|
1511
1523
|
| 'revealComplete'
|
|
1512
1524
|
| 'revealElement'
|
|
1513
1525
|
| 'diagram-node-update'
|
|
1514
|
-
| 'studio:update'
|
|
1526
|
+
| 'studio:update'
|
|
1527
|
+
| 'save';
|
|
1515
1528
|
|
|
1516
1529
|
/**
|
|
1517
1530
|
* Payload for the 'slideChange' event.
|
|
@@ -1624,6 +1637,8 @@ export interface LuminaEventMap {
|
|
|
1624
1637
|
'diagram-node-update': { slideIndex: number; nodeId: string; key: string; value: any };
|
|
1625
1638
|
/** Fired when Studio updates a node (path, value). Internal. */
|
|
1626
1639
|
'studio:update': { path: string; value: any };
|
|
1640
|
+
/** Fired when user clicks Save in Studio Embed mode; payload is the current deck JSON. */
|
|
1641
|
+
save: Deck;
|
|
1627
1642
|
}
|
|
1628
1643
|
|
|
1629
1644
|
/**
|
package/src/index.ts
CHANGED
|
@@ -13,8 +13,7 @@
|
|
|
13
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
|
-
* Ids: `engine.elements(slideIndex)` or `s{N}-{path}` (e.g. s0-tag, s1-features-0). `meta.initialElementState
|
|
17
|
-
* `{ [id]: { visible?: false } }` to start hidden; then `engine.element(id).show()`.
|
|
16
|
+
* Ids: `engine.elements(slideIndex)` or `s{N}-{path}` (e.g. s0-tag, s1-features-0). Elements hidden by default, cascade on enter. `meta.initialElementState` overrides per-id. `elementControl.defaultVisible: true` shows all.
|
|
18
17
|
*
|
|
19
18
|
* **Helpers:** `hideAll(slideIndex?)`, `showAll(slideIndex?)`, `showOnly(ids?)`, `hideAllExcept(exceptIds?)`,
|
|
20
19
|
* `resetSlide(slideIndex?)`, `delay(ms)`, `revealInSequence(slideIndex?, { delayMs?, staggerMode?, preset?, from?, to?, duration?, ease?, hideFirst?, only?, exclude? })`.
|
|
@@ -175,4 +174,5 @@ export type {
|
|
|
175
174
|
|
|
176
175
|
export { default as LuminaDeck } from './components/LuminaDeck.vue';
|
|
177
176
|
export { default as LuminaStudio } from './components/studio/LuminaStudio.vue';
|
|
177
|
+
export { default as LuminaStudioEmbed } from './components/studio/LuminaStudioEmbed.vue';
|
|
178
178
|
import './style/main.css';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prepares a deck for export: strips internal dragKey and adds explicit element ids.
|
|
3
|
+
* Used by Studio export, save, and JSON view.
|
|
4
|
+
* - slide.ids: path → id for every element
|
|
5
|
+
* - each element object that can have an id (features[i], timeline[i], elements[i], nodes[i], etc.) gets id set
|
|
6
|
+
*/
|
|
7
|
+
import { toRaw } from 'vue';
|
|
8
|
+
import type { Deck, BaseSlideData } from '../core/types';
|
|
9
|
+
import { getElementPaths, resolveId, pathToKey, getValueAt } from '../core/elementResolver';
|
|
10
|
+
|
|
11
|
+
/** Recursively remove all dragKey properties from an object (mutates in place). */
|
|
12
|
+
export function stripDragKeys(obj: unknown): void {
|
|
13
|
+
if (obj == null || typeof obj !== 'object') return;
|
|
14
|
+
if (Array.isArray(obj)) {
|
|
15
|
+
obj.forEach(stripDragKeys);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
delete (obj as Record<string, unknown>)['dragKey'];
|
|
19
|
+
Object.values(obj as Record<string, unknown>).forEach(stripDragKeys);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Prepare deck for export: clone, strip dragKey, add slide.ids, and set id on each element object. */
|
|
23
|
+
export function prepareDeckForExport(deck: Deck): Deck {
|
|
24
|
+
const clone = JSON.parse(JSON.stringify(toRaw(deck))) as Deck;
|
|
25
|
+
stripDragKeys(clone);
|
|
26
|
+
const slides = clone.slides ?? [];
|
|
27
|
+
slides.forEach((slide: BaseSlideData, slideIndex: number) => {
|
|
28
|
+
const paths = getElementPaths(slide);
|
|
29
|
+
if (paths.length === 0) return;
|
|
30
|
+
const slideAny = slide as { ids?: Record<string, string> };
|
|
31
|
+
if (!slideAny.ids) slideAny.ids = {};
|
|
32
|
+
paths.forEach((path) => {
|
|
33
|
+
const pathKey = pathToKey(path);
|
|
34
|
+
const resolvedId = resolveId(slide, slideIndex, path);
|
|
35
|
+
slideAny.ids![pathKey] = resolvedId;
|
|
36
|
+
|
|
37
|
+
// Set id on the element object when it's an object (e.g. features[0], elements[0], nodes[0])
|
|
38
|
+
if (path.length > 0 && path[0] !== 'slide') {
|
|
39
|
+
const value = getValueAt(slide, path);
|
|
40
|
+
if (value != null && typeof value === 'object' && !Array.isArray(value)) {
|
|
41
|
+
(value as Record<string, string>).id = resolvedId;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return clone;
|
|
47
|
+
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template interpolation for Lumina slide content.
|
|
3
|
-
*
|
|
4
|
-
* Replaces `{{key}}` placeholders in strings with values from a key-value store
|
|
5
|
-
* (e.g. engine.data / store.userData). When the store changes, any component
|
|
6
|
-
* that uses the interpolated result will reactively update.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* interpolateString('Hello {{name}}', { name: 'World' }) // 'Hello World'
|
|
10
|
-
* interpolateObject({ title: '{{product}}', subtitle: 'v{{version}}' }, { product: 'Lumina', version: 1 })
|
|
11
|
-
* // { title: 'Lumina', subtitle: 'v1' }
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/** Matches {{key}} or {{ key }}. Key: alphanumeric, underscore. */
|
|
15
|
-
const TAG_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Replaces all {{key}} placeholders in a string with values from `data`.
|
|
19
|
-
* Missing keys render as empty string. Non-string values are coerced with String().
|
|
20
|
-
*
|
|
21
|
-
* @param str - Raw string possibly containing `{{key}}` tags.
|
|
22
|
-
* @param data - Key-value map (e.g. engine.data / store.state.userData).
|
|
23
|
-
* @returns Interpolated string.
|
|
24
|
-
*/
|
|
25
|
-
export function interpolateString(str: string, data: Record<string, unknown>): string {
|
|
26
|
-
if (typeof str !== 'string') return str;
|
|
27
|
-
return str.replace(TAG_RE, (_, key: string) => {
|
|
28
|
-
const v = data[key];
|
|
29
|
-
return v == null ? '' : String(v);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Recursively interpolates all string values in an object/array with `{{key}}`
|
|
35
|
-
* placeholders. Other types (number, boolean, null, etc.) are returned unchanged.
|
|
36
|
-
* Does not mutate the input; returns a new structure.
|
|
37
|
-
*
|
|
38
|
-
* @param obj - Object, array, or primitive (strings are interpolated).
|
|
39
|
-
* @param data - Key-value map (e.g. engine.data / store.state.userData).
|
|
40
|
-
* @returns New structure with all strings interpolated.
|
|
41
|
-
*/
|
|
42
|
-
export function interpolateObject<T>(obj: T, data: Record<string, unknown>): T {
|
|
43
|
-
if (obj == null) return obj;
|
|
44
|
-
if (typeof obj === 'string') return interpolateString(obj, data) as T;
|
|
45
|
-
if (Array.isArray(obj)) return obj.map((item) => interpolateObject(item, data)) as T;
|
|
46
|
-
if (typeof obj === 'object' && obj.constructor === Object) {
|
|
47
|
-
return Object.fromEntries(
|
|
48
|
-
Object.entries(obj).map(([k, v]) => [k, interpolateObject(v, data)])
|
|
49
|
-
) as T;
|
|
50
|
-
}
|
|
51
|
-
return obj;
|
|
52
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template interpolation for Lumina slide content.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `{{key}}` placeholders in strings with values from a key-value store
|
|
5
|
+
* (e.g. engine.data / store.userData). When the store changes, any component
|
|
6
|
+
* that uses the interpolated result will reactively update.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* interpolateString('Hello {{name}}', { name: 'World' }) // 'Hello World'
|
|
10
|
+
* interpolateObject({ title: '{{product}}', subtitle: 'v{{version}}' }, { product: 'Lumina', version: 1 })
|
|
11
|
+
* // { title: 'Lumina', subtitle: 'v1' }
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Matches {{key}} or {{ key }}. Key: alphanumeric, underscore. */
|
|
15
|
+
const TAG_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Replaces all {{key}} placeholders in a string with values from `data`.
|
|
19
|
+
* Missing keys render as empty string. Non-string values are coerced with String().
|
|
20
|
+
*
|
|
21
|
+
* @param str - Raw string possibly containing `{{key}}` tags.
|
|
22
|
+
* @param data - Key-value map (e.g. engine.data / store.state.userData).
|
|
23
|
+
* @returns Interpolated string.
|
|
24
|
+
*/
|
|
25
|
+
export function interpolateString(str: string, data: Record<string, unknown>): string {
|
|
26
|
+
if (typeof str !== 'string') return str;
|
|
27
|
+
return str.replace(TAG_RE, (_, key: string) => {
|
|
28
|
+
const v = data[key];
|
|
29
|
+
return v == null ? '' : String(v);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Recursively interpolates all string values in an object/array with `{{key}}`
|
|
35
|
+
* placeholders. Other types (number, boolean, null, etc.) are returned unchanged.
|
|
36
|
+
* Does not mutate the input; returns a new structure.
|
|
37
|
+
*
|
|
38
|
+
* @param obj - Object, array, or primitive (strings are interpolated).
|
|
39
|
+
* @param data - Key-value map (e.g. engine.data / store.state.userData).
|
|
40
|
+
* @returns New structure with all strings interpolated.
|
|
41
|
+
*/
|
|
42
|
+
export function interpolateObject<T>(obj: T, data: Record<string, unknown>): T {
|
|
43
|
+
if (obj == null) return obj;
|
|
44
|
+
if (typeof obj === 'string') return interpolateString(obj, data) as T;
|
|
45
|
+
if (Array.isArray(obj)) return obj.map((item) => interpolateObject(item, data)) as T;
|
|
46
|
+
if (typeof obj === 'object' && obj.constructor === Object) {
|
|
47
|
+
return Object.fromEntries(
|
|
48
|
+
Object.entries(obj).map(([k, v]) => [k, interpolateObject(v, data)])
|
|
49
|
+
) as T;
|
|
50
|
+
}
|
|
51
|
+
return obj;
|
|
52
|
+
}
|