lumina-slides 9.0.4 → 9.0.6
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 +21984 -19455
- package/dist/lumina-slides.umd.cjs +223 -223
- package/dist/style.css +1 -1
- package/package.json +3 -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 +123 -123
- package/src/components/layouts/LayoutFlex.vue +212 -172
- package/src/components/layouts/LayoutStatement.vue +5 -2
- package/src/components/layouts/LayoutSteps.vue +108 -108
- package/src/components/parts/FlexHtml.vue +65 -0
- package/src/components/parts/FlexImage.vue +81 -54
- package/src/components/site/SiteDocs.vue +3313 -3182
- 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/StudioSettings.vue +658 -7
- package/src/components/studio/StudioToolbar.vue +20 -2
- package/src/composables/useElementState.ts +12 -1
- package/src/composables/useFlexLayout.ts +128 -122
- 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 -478
- package/src/core/store.ts +465 -465
- package/src/core/types.ts +59 -14
- package/src/index.ts +2 -2
- package/src/utils/templateInterpolation.ts +52 -52
- package/src/views/DeckView.vue +313 -313
package/src/core/types.ts
CHANGED
|
@@ -160,7 +160,9 @@ export interface FlexElementImage {
|
|
|
160
160
|
/** Fill entire container edge-to-edge. Default: true */
|
|
161
161
|
fill?: boolean;
|
|
162
162
|
/** Object-fit mode when fill is true. Default: 'cover' */
|
|
163
|
-
fit?: 'cover' | 'contain';
|
|
163
|
+
fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
164
|
+
/** Object-position for image placement. Default: 'center' */
|
|
165
|
+
position?: string;
|
|
164
166
|
/** Border radius. Default: 'none' when fill, 'lg' otherwise */
|
|
165
167
|
rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
166
168
|
/** Link URL when image is clicked. */
|
|
@@ -169,6 +171,8 @@ export interface FlexElementImage {
|
|
|
169
171
|
target?: '_blank' | '_self';
|
|
170
172
|
/** Custom CSS class */
|
|
171
173
|
class?: string;
|
|
174
|
+
/** Custom CSS style object */
|
|
175
|
+
style?: Record<string, string>;
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
/**
|
|
@@ -248,13 +252,30 @@ export interface FlexElementSpacer {
|
|
|
248
252
|
}
|
|
249
253
|
|
|
250
254
|
/**
|
|
251
|
-
*
|
|
255
|
+
* HTML element - Raw HTML content.
|
|
256
|
+
*/
|
|
257
|
+
export interface FlexElementHtml {
|
|
258
|
+
type: 'html';
|
|
259
|
+
/** Optional id for element control (engine.element(id)). */
|
|
260
|
+
id?: string;
|
|
261
|
+
/** Raw HTML string to render */
|
|
262
|
+
html: string;
|
|
263
|
+
/** Custom CSS class */
|
|
264
|
+
class?: string;
|
|
265
|
+
/** Custom CSS style object */
|
|
266
|
+
style?: Record<string, string>;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Content container - Groups child elements with alignment control.
|
|
252
271
|
*/
|
|
253
272
|
export interface FlexElementContent {
|
|
254
273
|
type: 'content';
|
|
255
274
|
/** Optional id for element control (engine.element(id)). */
|
|
256
275
|
id?: string;
|
|
257
276
|
elements: FlexChildElement[];
|
|
277
|
+
/** Layout direction. Default: 'vertical' */
|
|
278
|
+
direction?: 'horizontal' | 'vertical';
|
|
258
279
|
/** Vertical alignment of content. Default: 'center' */
|
|
259
280
|
valign?: VAlign;
|
|
260
281
|
/** Horizontal alignment of content. Default: 'left' */
|
|
@@ -263,10 +284,15 @@ export interface FlexElementContent {
|
|
|
263
284
|
gap?: SpacingToken;
|
|
264
285
|
/** Internal padding. Default: 'lg' */
|
|
265
286
|
padding?: SpacingToken;
|
|
287
|
+
/** Custom CSS class */
|
|
288
|
+
class?: string;
|
|
289
|
+
/** Custom CSS style object */
|
|
290
|
+
style?: Record<string, string>;
|
|
266
291
|
}
|
|
267
292
|
|
|
268
293
|
/**
|
|
269
294
|
* Child elements that can appear inside a content container.
|
|
295
|
+
* Supports nested content containers for complex layouts.
|
|
270
296
|
*/
|
|
271
297
|
export type FlexChildElement =
|
|
272
298
|
| FlexElementTitle
|
|
@@ -276,7 +302,10 @@ export type FlexChildElement =
|
|
|
276
302
|
| FlexElementButton
|
|
277
303
|
| FlexElementTimeline
|
|
278
304
|
| FlexElementStepper
|
|
279
|
-
| FlexElementSpacer
|
|
305
|
+
| FlexElementSpacer
|
|
306
|
+
| FlexElementHtml
|
|
307
|
+
| FlexElementImage
|
|
308
|
+
| FlexElementContent;
|
|
280
309
|
|
|
281
310
|
/**
|
|
282
311
|
* Top-level flex elements that can have size.
|
|
@@ -285,6 +314,7 @@ export type FlexElement =
|
|
|
285
314
|
| (FlexElementImage & { size?: FlexSize })
|
|
286
315
|
| (FlexElementVideo & { size?: FlexSize })
|
|
287
316
|
| (FlexElementContent & { size?: FlexSize })
|
|
317
|
+
| (FlexElementHtml & { size?: FlexSize })
|
|
288
318
|
| (FlexElementTitle & { size?: FlexSize })
|
|
289
319
|
| (FlexElementText & { size?: FlexSize })
|
|
290
320
|
| (FlexElementBullets & { size?: FlexSize })
|
|
@@ -561,7 +591,7 @@ export interface VideoProperties {
|
|
|
561
591
|
* "type": "statement",
|
|
562
592
|
* "meta": { "orbColor": "#3b82f6" },
|
|
563
593
|
* "tag": "Declarative Engine",
|
|
564
|
-
* "title": "Lumina
|
|
594
|
+
* "title": "Lumina V9",
|
|
565
595
|
* "subtitle": "A highly modular, performance-first presentation engine."
|
|
566
596
|
* }
|
|
567
597
|
* ```
|
|
@@ -864,8 +894,7 @@ export interface DeckMeta {
|
|
|
864
894
|
*/
|
|
865
895
|
initialElementState?: InitialElementState;
|
|
866
896
|
/**
|
|
867
|
-
* Element control defaults.
|
|
868
|
-
* 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.
|
|
869
898
|
*/
|
|
870
899
|
elementControl?: { defaultVisible?: boolean };
|
|
871
900
|
/** Default reveal options for all slides. Overridden by slide.reveal. */
|
|
@@ -881,8 +910,8 @@ export interface DeckMeta {
|
|
|
881
910
|
* Complete deck object. Primary input to `engine.load(deck)`.
|
|
882
911
|
*
|
|
883
912
|
* @description
|
|
884
|
-
* Must have `meta.title` and `slides` (array of slide objects). meta.initialElementState
|
|
885
|
-
* 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.
|
|
886
915
|
*
|
|
887
916
|
* @example
|
|
888
917
|
* { meta: { title: "Demo", initialElementState: { "s0-title": { visible: false } } }, slides: [{ type: "statement", title: "Hi" }] }
|
|
@@ -1358,6 +1387,12 @@ export interface LuminaAnimationOptions {
|
|
|
1358
1387
|
/** GSAP ease for reveal. Default: 'power2.out'. */
|
|
1359
1388
|
revealEase?: string;
|
|
1360
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
|
+
|
|
1361
1396
|
// --- element().animate() ---
|
|
1362
1397
|
/** Default duration in seconds. Default: 0.5. */
|
|
1363
1398
|
elementDuration?: number;
|
|
@@ -1420,7 +1455,7 @@ export interface LuminaAnimationOptions {
|
|
|
1420
1455
|
*
|
|
1421
1456
|
* @example
|
|
1422
1457
|
* new Lumina("#app", { theme: "midnight", loop: true, animation: { durationIn: 1.2 } });
|
|
1423
|
-
* new Lumina("#app", { elementControl: { defaultVisible:
|
|
1458
|
+
* new Lumina("#app", { elementControl: { defaultVisible: true } }); // legacy: show all
|
|
1424
1459
|
*
|
|
1425
1460
|
* @see Lumina
|
|
1426
1461
|
* @see LuminaAnimationOptions
|
|
@@ -1447,16 +1482,23 @@ export interface LuminaOptions {
|
|
|
1447
1482
|
/** Animation settings. */
|
|
1448
1483
|
animation?: LuminaAnimationOptions;
|
|
1449
1484
|
/**
|
|
1450
|
-
* Element control defaults.
|
|
1451
|
-
*
|
|
1452
|
-
* 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).
|
|
1453
1487
|
*/
|
|
1454
1488
|
elementControl?: {
|
|
1455
|
-
/** If
|
|
1489
|
+
/** If true, all elements start visible. Default: false (elements hidden, cascade on enter). */
|
|
1456
1490
|
defaultVisible?: boolean;
|
|
1457
1491
|
};
|
|
1458
1492
|
/** Enable Studio (Page Builder) mode. */
|
|
1459
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;
|
|
1460
1502
|
}
|
|
1461
1503
|
|
|
1462
1504
|
// --- Events ---
|
|
@@ -1481,7 +1523,8 @@ export type LuminaEventType =
|
|
|
1481
1523
|
| 'revealComplete'
|
|
1482
1524
|
| 'revealElement'
|
|
1483
1525
|
| 'diagram-node-update'
|
|
1484
|
-
| 'studio:update'
|
|
1526
|
+
| 'studio:update'
|
|
1527
|
+
| 'save';
|
|
1485
1528
|
|
|
1486
1529
|
/**
|
|
1487
1530
|
* Payload for the 'slideChange' event.
|
|
@@ -1594,6 +1637,8 @@ export interface LuminaEventMap {
|
|
|
1594
1637
|
'diagram-node-update': { slideIndex: number; nodeId: string; key: string; value: any };
|
|
1595
1638
|
/** Fired when Studio updates a node (path, value). Internal. */
|
|
1596
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;
|
|
1597
1642
|
}
|
|
1598
1643
|
|
|
1599
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';
|
|
@@ -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
|
+
}
|