lumina-slides 9.0.6 → 9.0.8

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.
@@ -83,6 +83,7 @@
83
83
  <script setup lang="ts">
84
84
  import { ref, watch, computed } from 'vue';
85
85
  import { useEditor } from '../../composables/useEditor';
86
+ import { prepareDeckForExport } from '../../utils/prepareDeckForExport';
86
87
 
87
88
  const props = defineProps<{
88
89
  isOpen: boolean;
@@ -97,15 +98,21 @@ const error = ref<string | null>(null);
97
98
  const isDirty = ref(false);
98
99
  const textareaRef = ref<HTMLTextAreaElement | null>(null);
99
100
 
100
- // Get initial content
101
+ // Get initial content (same format as export: ids added, dragKey removed)
101
102
  const refreshContent = () => {
102
103
  try {
104
+ const deck = editor.store.state.deck;
105
+ if (!deck) {
106
+ jsonContent.value = mode.value === 'deck' ? '{}' : '';
107
+ return;
108
+ }
109
+ const prepared = prepareDeckForExport(deck);
103
110
  if (mode.value === 'slide') {
104
111
  const index = editor.store.state.currentIndex;
105
- const slide = editor.store.state.deck?.slides[index];
112
+ const slide = prepared.slides?.[index];
106
113
  jsonContent.value = slide ? JSON.stringify(slide, null, 4) : '';
107
114
  } else {
108
- jsonContent.value = JSON.stringify(editor.store.state.deck, null, 4);
115
+ jsonContent.value = JSON.stringify(prepared, null, 4);
109
116
  }
110
117
  error.value = null;
111
118
  isDirty.value = false;
@@ -83,12 +83,13 @@
83
83
  </template>
84
84
 
85
85
  <script setup lang="ts">
86
- import { computed, ref, onMounted, watch, toRaw } from 'vue';
87
- import { useRoute } from 'vue-router';
86
+ import { computed, inject, ref, onMounted, watch } from 'vue';
87
+ import { routeLocationKey } from 'vue-router';
88
88
  import { useEditor } from '../../composables/useEditor';
89
89
  import { useAuth } from '../../composables/useAuth';
90
90
  import { saveDeck, updateDeck } from '../../utils/firebase';
91
91
  import type { Deck } from '../../core/types';
92
+ import { prepareDeckForExport } from '../../utils/prepareDeckForExport';
92
93
  // import SaveSuccessModal from './SaveSuccessModal.vue';
93
94
  import StudioJsonEditor from './StudioJsonEditor.vue';
94
95
 
@@ -96,7 +97,7 @@ const props = withDefaults(defineProps<{ embedMode?: boolean }>(), { embedMode:
96
97
  const emit = defineEmits<{ save: [deck: Deck] }>();
97
98
 
98
99
  const editor = useEditor();
99
- const route = useRoute();
100
+ const route = inject(routeLocationKey) ?? { params: {} as Record<string, string | string[]> };
100
101
  const { user } = useAuth();
101
102
  const isSaving = ref(false);
102
103
  // const showSaveModal = ref(false);
@@ -160,10 +161,10 @@ const addElement = (type: string) => {
160
161
  };
161
162
 
162
163
  const emitSave = () => {
163
- const rawDeck = toRaw(editor.store.state.deck);
164
+ const rawDeck = editor.store.state.deck;
164
165
  if (!rawDeck) return;
165
166
  isSaving.value = true;
166
- const deckCopy = JSON.parse(JSON.stringify(rawDeck)) as Deck;
167
+ const deckCopy = prepareDeckForExport(rawDeck);
167
168
  emit('save', deckCopy);
168
169
  isSaving.value = false;
169
170
  };
@@ -171,14 +172,13 @@ const emitSave = () => {
171
172
  const saveToFirestore = async () => {
172
173
  isSaving.value = true;
173
174
  try {
174
- // Use toRaw to strip Vue proxies for a cleaner JSON stringify
175
- const rawDeck = toRaw(editor.store.state.deck);
175
+ const rawDeck = editor.store.state.deck;
176
176
  if (!rawDeck) {
177
177
  alert('No deck found to save.');
178
178
  return;
179
179
  }
180
180
 
181
- const deckToSave = JSON.parse(JSON.stringify(rawDeck));
181
+ const deckToSave = prepareDeckForExport(rawDeck);
182
182
 
183
183
 
184
184
 
@@ -226,7 +226,8 @@ const exportDeck = () => {
226
226
  const deck = editor.store.state.deck;
227
227
  if (!deck) return;
228
228
 
229
- const json = JSON.stringify(deck, null, 2);
229
+ const deckForExport = prepareDeckForExport(deck);
230
+ const json = JSON.stringify(deckForExport, null, 2);
230
231
  const blob = new Blob([json], { type: 'application/json' });
231
232
  const url = URL.createObjectURL(blob);
232
233
 
@@ -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
+ }