lumina-slides 8.9.4 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LUMINA_LLM_EXAMPLES.json +234 -0
- package/README.md +18 -18
- package/dist/lumina-slides.js +13207 -12659
- package/dist/lumina-slides.umd.cjs +215 -215
- package/dist/style.css +1 -1
- package/package.json +5 -4
- package/src/App.vue +16 -0
- package/src/animation/index.ts +11 -0
- package/src/animation/registry.ts +126 -0
- package/src/animation/stagger.ts +95 -0
- package/src/animation/types.ts +53 -0
- package/src/components/LandingPage.vue +229 -0
- package/src/components/LuminaDeck.vue +224 -0
- package/src/components/LuminaSpeakerNotes.vue +701 -0
- package/src/components/base/BaseSlide.vue +122 -0
- package/src/components/base/LuminaElement.vue +67 -0
- package/src/components/base/VideoPlayer.vue +204 -0
- package/src/components/layouts/LayoutAuto.vue +71 -0
- package/src/components/layouts/LayoutChart.vue +287 -0
- package/src/components/layouts/LayoutCustom.vue +92 -0
- package/src/components/layouts/LayoutDiagram.vue +253 -0
- package/src/components/layouts/LayoutFeatures.vue +121 -0
- package/src/components/layouts/LayoutFlex.vue +172 -0
- package/src/components/layouts/LayoutFree.vue +62 -0
- package/src/components/layouts/LayoutHalf.vue +127 -0
- package/src/components/layouts/LayoutStatement.vue +74 -0
- package/src/components/layouts/LayoutSteps.vue +106 -0
- package/src/components/layouts/LayoutTimeline.vue +104 -0
- package/src/components/layouts/LayoutVideo.vue +41 -0
- package/src/components/parts/FlexBullets.vue +45 -0
- package/src/components/parts/FlexButton.vue +132 -0
- package/src/components/parts/FlexImage.vue +54 -0
- package/src/components/parts/FlexOrdered.vue +44 -0
- package/src/components/parts/FlexSpacer.vue +13 -0
- package/src/components/parts/FlexStepper.vue +59 -0
- package/src/components/parts/FlexText.vue +29 -0
- package/src/components/parts/FlexTimeline.vue +67 -0
- package/src/components/parts/FlexTitle.vue +39 -0
- package/src/components/parts/LuminaBackground.vue +100 -0
- package/src/components/site/LivePreview.vue +101 -0
- package/src/components/site/SiteApi.vue +301 -0
- package/src/components/site/SiteDashboard.vue +604 -0
- package/src/components/site/SiteDocs.vue +3267 -0
- package/src/components/site/SiteExamples.vue +65 -0
- package/src/components/site/SiteFooter.vue +6 -0
- package/src/components/site/SiteHome.vue +362 -0
- package/src/components/site/SiteNavBar.vue +122 -0
- package/src/components/site/SitePlayground.vue +389 -0
- package/src/components/site/SitePromptBuilder.vue +266 -0
- package/src/components/site/SiteUserMenu.vue +90 -0
- package/src/components/studio/ActionEditor.vue +108 -0
- package/src/components/studio/ArrayEditor.vue +124 -0
- package/src/components/studio/CollapsibleSection.vue +33 -0
- package/src/components/studio/ColorField.vue +22 -0
- package/src/components/studio/EditorCanvas.vue +326 -0
- package/src/components/studio/EditorLayoutFeatures.vue +18 -0
- package/src/components/studio/EditorLayoutFixed.vue +46 -0
- package/src/components/studio/EditorLayoutFlex.vue +133 -0
- package/src/components/studio/EditorLayoutHalf.vue +18 -0
- package/src/components/studio/EditorLayoutStatement.vue +18 -0
- package/src/components/studio/EditorLayoutSteps.vue +18 -0
- package/src/components/studio/EditorLayoutTimeline.vue +18 -0
- package/src/components/studio/EditorNode.vue +89 -0
- package/src/components/studio/FieldEditor.vue +133 -0
- package/src/components/studio/IconPicker.vue +109 -0
- package/src/components/studio/LayerItem.vue +117 -0
- package/src/components/studio/LuminaStudio.vue +30 -0
- package/src/components/studio/SaveSuccessModal.vue +138 -0
- package/src/components/studio/SlideNavigator.vue +373 -0
- package/src/components/studio/SliderField.vue +44 -0
- package/src/components/studio/StudioInspector.vue +595 -0
- package/src/components/studio/StudioJsonEditor.vue +191 -0
- package/src/components/studio/StudioLayers.vue +145 -0
- package/src/components/studio/StudioSettings.vue +514 -0
- package/src/components/studio/StudioSidebar.vue +29 -0
- package/src/components/studio/StudioToolbar.vue +222 -0
- package/src/components/studio/fieldLabels.ts +224 -0
- package/src/components/studio/inspectors/DiagramEdgeEditor.vue +77 -0
- package/src/components/studio/inspectors/DiagramNodeEditor.vue +117 -0
- package/src/components/studio/nodes/StudioDiagramNode.vue +138 -0
- package/src/composables/useAuth.ts +87 -0
- package/src/composables/useEditor.ts +224 -0
- package/src/composables/useElementState.ts +81 -0
- package/src/composables/useFlexLayout.ts +122 -0
- package/src/composables/useKeyboard.ts +45 -0
- package/src/composables/useLumina.ts +32 -0
- package/src/composables/useStudio.ts +87 -0
- package/src/composables/useSwipeNav.ts +53 -0
- package/src/composables/useTransition.ts +373 -0
- package/src/core/Lumina.ts +819 -0
- package/src/core/animationConfig.ts +251 -0
- package/src/core/compression.ts +34 -0
- package/src/core/elementController.ts +170 -0
- package/src/core/elementId.ts +27 -0
- package/src/core/elementResolver.ts +207 -0
- package/src/core/events.ts +53 -0
- package/src/core/fonts.ts +100 -0
- package/src/core/presets.ts +231 -0
- package/src/core/prompts.ts +272 -0
- package/src/core/schema.ts +478 -0
- package/src/core/speaker-channel.ts +250 -0
- package/src/core/store.ts +461 -0
- package/src/core/theme.ts +666 -0
- package/src/core/types.ts +1611 -0
- package/src/directives/vStudio.ts +45 -0
- package/src/index.ts +175 -0
- package/src/main.ts +17 -0
- package/src/router/index.ts +92 -0
- package/src/style/main.css +462 -0
- package/src/utils/deep.ts +127 -0
- package/src/utils/firebase.ts +184 -0
- package/src/utils/streaming.ts +134 -0
- package/src/views/DashboardView.vue +32 -0
- package/src/views/DeckView.vue +289 -0
- package/src/views/HomeView.vue +17 -0
- package/src/views/SiteLayout.vue +21 -0
- package/src/views/StudioView.vue +61 -0
- package/src/vite-env.d.ts +6 -0
- package/IMPLEMENTATION.md +0 -418
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="isOpen" class="fixed inset-0 z-[60] flex items-center justify-center p-4">
|
|
3
|
+
<!-- Backdrop -->
|
|
4
|
+
<div class="absolute inset-0 bg-black/80 backdrop-blur-md transition-opacity" @click="$emit('close')"></div>
|
|
5
|
+
|
|
6
|
+
<!-- Modal -->
|
|
7
|
+
<div
|
|
8
|
+
class="relative w-full max-w-4xl h-[80vh] bg-[#1a1a1a] border border-[#333] rounded-2xl shadow-2xl flex flex-col overflow-hidden transform transition-all animate-in fade-in zoom-in-95 duration-200">
|
|
9
|
+
|
|
10
|
+
<!-- Header -->
|
|
11
|
+
<div class="px-6 py-4 border-b border-[#333] flex items-center justify-between bg-[#222]">
|
|
12
|
+
<div class="flex items-center gap-4">
|
|
13
|
+
<div class="flex items-center gap-2">
|
|
14
|
+
<i class="ph-thin ph-code text-blue-400 text-lg"></i>
|
|
15
|
+
<h2 class="text-lg font-bold text-white tracking-wide">JSON EDITOR</h2>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Mode Switcher -->
|
|
19
|
+
<div class="flex bg-black/40 p-1 rounded-lg border border-white/5">
|
|
20
|
+
<button @click="mode = 'slide'"
|
|
21
|
+
:class="['px-3 py-1 text-[10px] font-bold uppercase tracking-wider rounded-md transition-all',
|
|
22
|
+
mode === 'slide' ? 'bg-blue-600 text-white shadow-lg' : 'text-white/40 hover:text-white']">
|
|
23
|
+
Current Slide
|
|
24
|
+
</button>
|
|
25
|
+
<button @click="mode = 'deck'"
|
|
26
|
+
:class="['px-3 py-1 text-[10px] font-bold uppercase tracking-wider rounded-md transition-all',
|
|
27
|
+
mode === 'deck' ? 'bg-blue-600 text-white shadow-lg' : 'text-white/40 hover:text-white']">
|
|
28
|
+
Full Deck
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="flex items-center gap-3">
|
|
34
|
+
<span v-if="error"
|
|
35
|
+
class="text-red-400 text-[10px] font-mono animate-pulse bg-red-400/10 px-2 py-1 rounded">
|
|
36
|
+
<i class="ph-thin ph-warning mr-1"></i> {{ error }}
|
|
37
|
+
</span>
|
|
38
|
+
<button @click="$emit('close')" class="text-white/40 hover:text-white transition-colors p-1">
|
|
39
|
+
<i class="ph-thin ph-x text-xl"></i>
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- Content Area -->
|
|
45
|
+
<div class="flex-1 flex overflow-hidden relative">
|
|
46
|
+
<!-- Line Numbers (Simulated) -->
|
|
47
|
+
<div
|
|
48
|
+
class="w-12 bg-black/20 border-r border-[#333] flex flex-col items-center py-4 text-[10px] font-mono text-white/20 select-none">
|
|
49
|
+
<div v-for="n in lineCount" :key="n" class="leading-relaxed h-5">{{ n }}</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Textarea -->
|
|
53
|
+
<div class="flex-1 relative group">
|
|
54
|
+
<textarea ref="textareaRef" v-model="jsonContent" spellcheck="false"
|
|
55
|
+
class="w-full h-full bg-transparent p-4 text-[13px] font-mono text-blue-100/90 leading-relaxed outline-none resize-none selection:bg-blue-500/30"
|
|
56
|
+
@input="validateJson" placeholder="{ ... }"></textarea>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Footer -->
|
|
61
|
+
<div class="px-6 py-4 border-t border-[#333] flex items-center justify-between bg-[#222]">
|
|
62
|
+
<div class="text-[10px] text-white/30 font-mono">
|
|
63
|
+
<i class="ph-thin ph-info mr-1"></i>
|
|
64
|
+
Changes are applied to the local store only. Remember to SAVE to the cloud.
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="flex items-center gap-3">
|
|
68
|
+
<button @click="$emit('close')"
|
|
69
|
+
class="px-5 py-2 text-sm font-medium text-white/60 hover:text-white hover:bg-white/5 rounded-lg transition-all">
|
|
70
|
+
Cancel
|
|
71
|
+
</button>
|
|
72
|
+
<button @click="handleApply" :disabled="!!error || !isDirty"
|
|
73
|
+
class="px-6 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-blue-900 disabled:opacity-50 text-white text-sm font-bold rounded-lg shadow-lg shadow-blue-900/20 transition-all flex items-center gap-2 active:scale-95">
|
|
74
|
+
<i class="ph-thin ph-check"></i>
|
|
75
|
+
APPLY CHANGES
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<script setup lang="ts">
|
|
84
|
+
import { ref, watch, computed } from 'vue';
|
|
85
|
+
import { useEditor } from '../../composables/useEditor';
|
|
86
|
+
|
|
87
|
+
const props = defineProps<{
|
|
88
|
+
isOpen: boolean;
|
|
89
|
+
}>();
|
|
90
|
+
|
|
91
|
+
const emit = defineEmits(['close']);
|
|
92
|
+
|
|
93
|
+
const editor = useEditor();
|
|
94
|
+
const mode = ref<'slide' | 'deck'>('slide');
|
|
95
|
+
const jsonContent = ref('');
|
|
96
|
+
const error = ref<string | null>(null);
|
|
97
|
+
const isDirty = ref(false);
|
|
98
|
+
const textareaRef = ref<HTMLTextAreaElement | null>(null);
|
|
99
|
+
|
|
100
|
+
// Get initial content
|
|
101
|
+
const refreshContent = () => {
|
|
102
|
+
try {
|
|
103
|
+
if (mode.value === 'slide') {
|
|
104
|
+
const index = editor.store.state.currentIndex;
|
|
105
|
+
const slide = editor.store.state.deck?.slides[index];
|
|
106
|
+
jsonContent.value = slide ? JSON.stringify(slide, null, 4) : '';
|
|
107
|
+
} else {
|
|
108
|
+
jsonContent.value = JSON.stringify(editor.store.state.deck, null, 4);
|
|
109
|
+
}
|
|
110
|
+
error.value = null;
|
|
111
|
+
isDirty.value = false;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error('Failed to stringify JSON', e);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Auto-refresh when opening or switching mode
|
|
118
|
+
watch(() => props.isOpen, (open) => {
|
|
119
|
+
if (open) refreshContent();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
watch(mode, refreshContent);
|
|
123
|
+
|
|
124
|
+
const lineCount = computed(() => {
|
|
125
|
+
return jsonContent.value.split('\n').length;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const validateJson = () => {
|
|
129
|
+
isDirty.value = true;
|
|
130
|
+
try {
|
|
131
|
+
JSON.parse(jsonContent.value);
|
|
132
|
+
error.value = null;
|
|
133
|
+
} catch (e: any) {
|
|
134
|
+
error.value = e.message;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handleApply = () => {
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(jsonContent.value);
|
|
141
|
+
|
|
142
|
+
if (mode.value === 'slide') {
|
|
143
|
+
const index = editor.store.state.currentIndex;
|
|
144
|
+
editor.store.updateNode(`slides.${index}`, parsed);
|
|
145
|
+
} else {
|
|
146
|
+
editor.store.updateNode('', parsed);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// editor.commit(); // Handled by watch
|
|
150
|
+
isDirty.value = false;
|
|
151
|
+
emit('close');
|
|
152
|
+
} catch (e: any) {
|
|
153
|
+
error.value = "Invalid JSON: " + e.message;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<style scoped>
|
|
159
|
+
.animate-in {
|
|
160
|
+
animation: enter 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@keyframes enter {
|
|
164
|
+
from {
|
|
165
|
+
opacity: 0;
|
|
166
|
+
transform: translateY(10px) scale(0.98);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
to {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
transform: translateY(0) scale(1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
textarea::-webkit-scrollbar {
|
|
176
|
+
width: 8px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
textarea::-webkit-scrollbar-track {
|
|
180
|
+
background: transparent;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
textarea::-webkit-scrollbar-thumb {
|
|
184
|
+
background: #333;
|
|
185
|
+
border-radius: 4px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
textarea::-webkit-scrollbar-thumb:hover {
|
|
189
|
+
background: #444;
|
|
190
|
+
}
|
|
191
|
+
</style>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col h-full text-xs">
|
|
3
|
+
<!-- Header -->
|
|
4
|
+
<div
|
|
5
|
+
class="px-3 py-2 font-bold text-white/50 uppercase tracking-wider border-b border-[#333] flex items-center justify-between">
|
|
6
|
+
<span>Elements</span>
|
|
7
|
+
<span class="text-white/30 font-mono">Slide {{ currentSlideIndex + 1 }}</span>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<!-- Layer Tree -->
|
|
11
|
+
<div class="flex-1 overflow-y-auto p-2">
|
|
12
|
+
<template v-if="currentSlide">
|
|
13
|
+
<LayerItem :data="currentSlide" :path="`slides.${currentSlideIndex}`" />
|
|
14
|
+
</template>
|
|
15
|
+
<div v-else class="text-white/30 italic text-center mt-4">
|
|
16
|
+
No slide selected
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed, ref } from 'vue';
|
|
24
|
+
import { useEditor } from '../../composables/useEditor';
|
|
25
|
+
import LayerItem from './LayerItem.vue';
|
|
26
|
+
|
|
27
|
+
const editor = useEditor();
|
|
28
|
+
|
|
29
|
+
const slides = computed(() => editor.store.state.deck?.slides || []);
|
|
30
|
+
const totalSlides = computed(() => slides.value.length);
|
|
31
|
+
const currentSlideIndex = computed(() => editor.store.state.currentIndex);
|
|
32
|
+
const currentSlide = computed(() => slides.value[currentSlideIndex.value]);
|
|
33
|
+
|
|
34
|
+
const selectedLayout = ref('flex');
|
|
35
|
+
|
|
36
|
+
const availableLayouts = [
|
|
37
|
+
{ value: 'flex', label: 'Flex (Free Layout)' },
|
|
38
|
+
{ value: 'statement', label: 'Statement' },
|
|
39
|
+
{ value: 'half', label: 'Half (Image + Content)' },
|
|
40
|
+
{ value: 'features', label: 'Features Grid' },
|
|
41
|
+
{ value: 'timeline', label: 'Timeline' },
|
|
42
|
+
{ value: 'steps', label: 'Steps' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const goToSlide = (index: number) => {
|
|
46
|
+
editor.store.goto(index);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// CORRECT templates matching actual type definitions
|
|
50
|
+
const slideTemplates: Record<string, any> = {
|
|
51
|
+
'flex': {
|
|
52
|
+
type: 'flex',
|
|
53
|
+
direction: 'horizontal',
|
|
54
|
+
gap: 'md',
|
|
55
|
+
padding: 'lg',
|
|
56
|
+
elements: [
|
|
57
|
+
{
|
|
58
|
+
type: 'content',
|
|
59
|
+
size: 'half',
|
|
60
|
+
halign: 'left',
|
|
61
|
+
valign: 'center',
|
|
62
|
+
elements: [
|
|
63
|
+
{ type: 'title', text: 'New Slide', size: 'xl' },
|
|
64
|
+
{ type: 'text', text: 'Start editing your content here.' }
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'image',
|
|
69
|
+
size: 'half',
|
|
70
|
+
src: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=800',
|
|
71
|
+
alt: 'Placeholder image',
|
|
72
|
+
fill: true,
|
|
73
|
+
rounded: 'lg'
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
'statement': {
|
|
78
|
+
type: 'statement',
|
|
79
|
+
tag: 'Tagline',
|
|
80
|
+
title: 'Big Statement Here',
|
|
81
|
+
subtitle: 'Supporting description text goes below the main statement.'
|
|
82
|
+
},
|
|
83
|
+
'half': {
|
|
84
|
+
type: 'half',
|
|
85
|
+
imageSide: 'right',
|
|
86
|
+
image: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=800',
|
|
87
|
+
tag: 'Category',
|
|
88
|
+
title: 'Split Layout Title',
|
|
89
|
+
paragraphs: [
|
|
90
|
+
'First paragraph of content.',
|
|
91
|
+
'Second paragraph with more details.'
|
|
92
|
+
],
|
|
93
|
+
cta: 'Learn More'
|
|
94
|
+
},
|
|
95
|
+
'features': {
|
|
96
|
+
type: 'features',
|
|
97
|
+
title: 'Key Features',
|
|
98
|
+
description: 'Discover what makes us different.',
|
|
99
|
+
features: [
|
|
100
|
+
{ icon: 'star', title: 'Feature One', desc: 'Description of the first feature.' },
|
|
101
|
+
{ icon: 'rocket', title: 'Feature Two', desc: 'Description of the second feature.' },
|
|
102
|
+
{ icon: 'lightning', title: 'Feature Three', desc: 'Description of the third feature.' }
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
'timeline': {
|
|
106
|
+
type: 'timeline',
|
|
107
|
+
title: 'Our Journey',
|
|
108
|
+
subtitle: 'Key milestones in our history.',
|
|
109
|
+
timeline: [
|
|
110
|
+
{ date: '2020', title: 'Founded', description: 'Company was established.' },
|
|
111
|
+
{ date: '2022', title: 'Growth', description: 'Expanded to new markets.' },
|
|
112
|
+
{ date: '2024', title: 'Today', description: 'Serving customers worldwide.' }
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
'steps': {
|
|
116
|
+
type: 'steps',
|
|
117
|
+
title: 'How It Works',
|
|
118
|
+
subtitle: 'Simple steps to get started.',
|
|
119
|
+
steps: [
|
|
120
|
+
{ step: '1', title: 'Step One', description: 'First thing to do.' },
|
|
121
|
+
{ step: '2', title: 'Step Two', description: 'Second thing to do.' },
|
|
122
|
+
{ step: '3', title: 'Step Three', description: 'Final step.' }
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const addSlide = () => {
|
|
128
|
+
const template = slideTemplates[selectedLayout.value] || slideTemplates['flex'];
|
|
129
|
+
const newSlide = JSON.parse(JSON.stringify(template));
|
|
130
|
+
|
|
131
|
+
// Get current slides array and add new slide
|
|
132
|
+
const currentSlides = [...(editor.store.state.deck?.slides || [])];
|
|
133
|
+
currentSlides.push(newSlide);
|
|
134
|
+
|
|
135
|
+
// Update via patchDeck for proper reactivity
|
|
136
|
+
editor.store.patchDeck({ slides: currentSlides });
|
|
137
|
+
editor.commit();
|
|
138
|
+
|
|
139
|
+
// Navigate to the new slide after a small delay
|
|
140
|
+
const newIndex = currentSlides.length - 1;
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
editor.store.goto(newIndex);
|
|
143
|
+
}, 50);
|
|
144
|
+
};
|
|
145
|
+
</script>
|