starry-slides 0.1.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.
Files changed (160) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +131 -0
  3. package/dist/assets/index---ub3Dty.css +1 -0
  4. package/dist/assets/index-7Xs_Dyp0.js +354 -0
  5. package/dist/assets/index-8ul7coaw.js +451 -0
  6. package/dist/assets/index-B4_bJa9t.js +412 -0
  7. package/dist/assets/index-B9p6L9Wx.js +324 -0
  8. package/dist/assets/index-BF-Vi1Nh.js +413 -0
  9. package/dist/assets/index-BKvVbgXz.css +1 -0
  10. package/dist/assets/index-BNZJ_Wz-.js +417 -0
  11. package/dist/assets/index-BO2gtiKn.js +413 -0
  12. package/dist/assets/index-BYpD8kgo.js +456 -0
  13. package/dist/assets/index-BaZRfz_9.css +1 -0
  14. package/dist/assets/index-BlsWm4PI.js +412 -0
  15. package/dist/assets/index-BnUIBFtw.js +393 -0
  16. package/dist/assets/index-BvhCbfCi.js +412 -0
  17. package/dist/assets/index-BxEkxfy1.js +412 -0
  18. package/dist/assets/index-BzrA7O0L.js +422 -0
  19. package/dist/assets/index-C0xGkdRg.js +413 -0
  20. package/dist/assets/index-C1Rjncf1.js +456 -0
  21. package/dist/assets/index-CCgb2gqc.js +413 -0
  22. package/dist/assets/index-CDFJQRFx.js +413 -0
  23. package/dist/assets/index-CEOai0RW.js +456 -0
  24. package/dist/assets/index-CG2uWTey.css +1 -0
  25. package/dist/assets/index-CLe3_iTu.js +412 -0
  26. package/dist/assets/index-CS_optob.js +324 -0
  27. package/dist/assets/index-CXgwXOZH.css +1 -0
  28. package/dist/assets/index-CcBvAcE2.js +408 -0
  29. package/dist/assets/index-CllmR_MT.js +408 -0
  30. package/dist/assets/index-CnMy6wxq.js +412 -0
  31. package/dist/assets/index-CoWNjYgb.js +422 -0
  32. package/dist/assets/index-CtfA3BPy.css +1 -0
  33. package/dist/assets/index-D-JiuJIv.js +324 -0
  34. package/dist/assets/index-D2uEYXyQ.css +1 -0
  35. package/dist/assets/index-D70beOHW.js +456 -0
  36. package/dist/assets/index-D8xuxnF1.js +456 -0
  37. package/dist/assets/index-DKAtkRd8.css +1 -0
  38. package/dist/assets/index-DU3l9_k0.css +1 -0
  39. package/dist/assets/index-DaLY98jk.js +456 -0
  40. package/dist/assets/index-DkNHJHQl.js +412 -0
  41. package/dist/assets/index-DmFnifK8.js +456 -0
  42. package/dist/assets/index-DndZR7Ds.css +1 -0
  43. package/dist/assets/index-DuRa7Y6g.js +324 -0
  44. package/dist/assets/index-EnDjTeHF.js +451 -0
  45. package/dist/assets/index-J3nQgjqJ.js +451 -0
  46. package/dist/assets/index-wD_iWTBn.js +413 -0
  47. package/dist/chunk-AK5J4CXH.js +1671 -0
  48. package/dist/chunk-ARFDESSF.js +1583 -0
  49. package/dist/chunk-DHWTBXGS.js +985 -0
  50. package/dist/chunk-FCRRFL7N.js +1571 -0
  51. package/dist/chunk-J7W4Y7WJ.js +1620 -0
  52. package/dist/chunk-OCJULB7Z.js +1140 -0
  53. package/dist/chunk-WCJWV5SO.js +36 -0
  54. package/dist/cli/index.d.ts +2 -0
  55. package/dist/cli/index.js +1119 -0
  56. package/dist/cli/index.test.d.ts +1 -0
  57. package/dist/core/deck-slide-operations.d.ts +5 -0
  58. package/dist/core/generated-deck.d.ts +8 -0
  59. package/dist/core/generated-deck.test.d.ts +1 -0
  60. package/dist/core/group-operations.d.ts +24 -0
  61. package/dist/core/history.d.ts +20 -0
  62. package/dist/core/history.test.d.ts +1 -0
  63. package/dist/core/html-export.d.ts +20 -0
  64. package/dist/core/index.d.ts +11 -0
  65. package/dist/core/layout.d.ts +36 -0
  66. package/dist/core/layout.test.d.ts +1 -0
  67. package/dist/core/pdf-export.d.ts +20 -0
  68. package/dist/core/presentation.d.ts +7 -0
  69. package/dist/core/slide-contract.d.ts +41 -0
  70. package/dist/core/slide-document.d.ts +5 -0
  71. package/dist/core/slide-document.test.d.ts +1 -0
  72. package/dist/core/slide-html-document.d.ts +3 -0
  73. package/dist/core/slide-operation-reducer.d.ts +4 -0
  74. package/dist/core/slide-operation-reducer.test.d.ts +1 -0
  75. package/dist/core/slide-operation-types.d.ts +123 -0
  76. package/dist/core/slide-operations-helpers.d.ts +14 -0
  77. package/dist/core/slide-operations.d.ts +5 -0
  78. package/dist/core/slide-operations.test.d.ts +1 -0
  79. package/dist/core/verify-deck.d.ts +48 -0
  80. package/dist/core/verify-deck.test.d.ts +1 -0
  81. package/dist/editor/app/App.d.ts +2 -0
  82. package/dist/editor/app/main.d.ts +1 -0
  83. package/dist/editor/app/use-slides-data.d.ts +14 -0
  84. package/dist/editor/components/block-manipulation-overlay.d.ts +32 -0
  85. package/dist/editor/components/color-picker.d.ts +10 -0
  86. package/dist/editor/components/context-menu.d.ts +26 -0
  87. package/dist/editor/components/editor-header.d.ts +18 -0
  88. package/dist/editor/components/floating-toolbar-feature.d.ts +8 -0
  89. package/dist/editor/components/floating-toolbar-parts.d.ts +46 -0
  90. package/dist/editor/components/floating-toolbar.d.ts +27 -0
  91. package/dist/editor/components/presenter-view.d.ts +13 -0
  92. package/dist/editor/components/slide-sidebar.d.ts +18 -0
  93. package/dist/editor/components/stage-canvas.d.ts +77 -0
  94. package/dist/editor/components/ui/accordion.d.ts +7 -0
  95. package/dist/editor/components/ui/button.d.ts +10 -0
  96. package/dist/editor/components/ui/context-menu.d.ts +25 -0
  97. package/dist/editor/components/ui/dialog.d.ts +10 -0
  98. package/dist/editor/components/ui/input.d.ts +3 -0
  99. package/dist/editor/components/ui/popover.d.ts +10 -0
  100. package/dist/editor/components/ui/scroll-area.d.ts +5 -0
  101. package/dist/editor/components/ui/select.d.ts +15 -0
  102. package/dist/editor/components/ui/separator.d.ts +4 -0
  103. package/dist/editor/components/ui/tabs.d.ts +11 -0
  104. package/dist/editor/components/ui/textarea.d.ts +3 -0
  105. package/dist/editor/components/ui/toggle-group.d.ts +9 -0
  106. package/dist/editor/components/ui/toggle.d.ts +9 -0
  107. package/dist/editor/components/ui/tooltip.d.ts +7 -0
  108. package/dist/editor/editor-operations.d.ts +15 -0
  109. package/dist/editor/hooks/block-manipulation-geometry.d.ts +16 -0
  110. package/dist/editor/hooks/block-manipulation-operations.d.ts +10 -0
  111. package/dist/editor/hooks/block-manipulation-overlay.d.ts +11 -0
  112. package/dist/editor/hooks/block-manipulation-types.d.ts +73 -0
  113. package/dist/editor/hooks/editor-keyboard-geometry.d.ts +22 -0
  114. package/dist/editor/hooks/editor-keyboard-operations.d.ts +10 -0
  115. package/dist/editor/hooks/editor-keyboard-types.d.ts +35 -0
  116. package/dist/editor/hooks/iframe-editing-session.d.ts +10 -0
  117. package/dist/editor/hooks/iframe-text-editing-dom.d.ts +6 -0
  118. package/dist/editor/hooks/iframe-text-editing-types.d.ts +38 -0
  119. package/dist/editor/hooks/object-clipboard-commands.d.ts +28 -0
  120. package/dist/editor/hooks/use-block-manipulation.d.ts +3 -0
  121. package/dist/editor/hooks/use-editor-keyboard-shortcuts.d.ts +3 -0
  122. package/dist/editor/hooks/use-iframe-text-editing.d.ts +3 -0
  123. package/dist/editor/hooks/use-slide-history.d.ts +17 -0
  124. package/dist/editor/hooks/use-slide-inspector.d.ts +24 -0
  125. package/dist/editor/hooks/use-slide-thumbnails.d.ts +2 -0
  126. package/dist/editor/hooks/use-stage-viewport.d.ts +13 -0
  127. package/dist/editor/index.d.ts +13 -0
  128. package/dist/editor/index.js +8260 -0
  129. package/dist/editor/lib/block-snap-constants.d.ts +8 -0
  130. package/dist/editor/lib/block-snap-guides.d.ts +6 -0
  131. package/dist/editor/lib/block-snap-targets.d.ts +13 -0
  132. package/dist/editor/lib/block-snap-types.d.ts +30 -0
  133. package/dist/editor/lib/block-snapping.d.ts +17 -0
  134. package/dist/editor/lib/collect-css-properties.d.ts +5 -0
  135. package/dist/editor/lib/element-tool-commit.d.ts +18 -0
  136. package/dist/editor/lib/element-tool-model.d.ts +16 -0
  137. package/dist/editor/lib/element-tool-types.d.ts +35 -0
  138. package/dist/editor/lib/element-tool-values.d.ts +23 -0
  139. package/dist/editor/lib/motion.d.ts +8 -0
  140. package/dist/editor/lib/selection-overlay.d.ts +4 -0
  141. package/dist/editor/lib/style-controls.d.ts +17 -0
  142. package/dist/editor/lib/thumbnail-renderer.d.ts +2 -0
  143. package/dist/editor/lib/utils.d.ts +3 -0
  144. package/dist/index.html +13 -0
  145. package/dist/node/deck-runtime-middleware.d.ts +8 -0
  146. package/dist/node/deck-source.d.ts +1 -0
  147. package/dist/node/html-export.d.ts +16 -0
  148. package/dist/node/open-browser.d.ts +1 -0
  149. package/dist/node/pdf-export.d.ts +20 -0
  150. package/dist/node/ports.d.ts +1 -0
  151. package/dist/node/view-renderer.d.ts +31 -0
  152. package/dist/runtime/deck-source.d.ts +1 -0
  153. package/dist/runtime/html-export.d.ts +16 -0
  154. package/dist/runtime/open-browser.d.ts +1 -0
  155. package/dist/runtime/pdf-export.d.ts +20 -0
  156. package/dist/runtime/ports.d.ts +1 -0
  157. package/dist/runtime/view-renderer.d.ts +31 -0
  158. package/dist/runtime/view-renderer.test.d.ts +1 -0
  159. package/dist/test/deck-fixtures.d.ts +14 -0
  160. package/package.json +94 -0
@@ -0,0 +1,985 @@
1
+ // src/core/slide-contract.ts
2
+ var SELECTOR_ATTR = "data-editor-id";
3
+ var SLIDE_ROOT_ATTR = "data-slide-root";
4
+ var DEFAULT_SLIDE_WIDTH = 1920;
5
+ var DEFAULT_SLIDE_HEIGHT = 1080;
6
+ function getSlideElementSelector(elementId) {
7
+ return `[${SELECTOR_ATTR}="${elementId}"]`;
8
+ }
9
+ function getSlideRootSelector(rootId) {
10
+ return `[${SELECTOR_ATTR}="${rootId}"]`;
11
+ }
12
+ function slugify(value) {
13
+ return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "slide";
14
+ }
15
+ function createElementId(index, type) {
16
+ return `${type}-${index + 1}`;
17
+ }
18
+ function parseDimension(value, fallback) {
19
+ const numericValue = Number.parseInt(value || "", 10);
20
+ return Number.isFinite(numericValue) && numericValue > 0 ? numericValue : fallback;
21
+ }
22
+ function normalizeSlideId(slideId) {
23
+ return slugify(slideId);
24
+ }
25
+
26
+ // src/core/slide-document.ts
27
+ function parseHtmlDocument(html) {
28
+ if (typeof DOMParser === "undefined") {
29
+ return null;
30
+ }
31
+ const parser = new DOMParser();
32
+ return parser.parseFromString(html, "text/html");
33
+ }
34
+ function ensureSlideRoot(doc) {
35
+ const existingRoot = doc.querySelector(`[${SLIDE_ROOT_ATTR}]`);
36
+ if (existingRoot) {
37
+ if (!existingRoot.getAttribute("data-slide-width")) {
38
+ existingRoot.setAttribute("data-slide-width", String(DEFAULT_SLIDE_WIDTH));
39
+ }
40
+ if (!existingRoot.getAttribute("data-slide-height")) {
41
+ existingRoot.setAttribute("data-slide-height", String(DEFAULT_SLIDE_HEIGHT));
42
+ }
43
+ return existingRoot;
44
+ }
45
+ const container = doc.querySelector(".slide-container");
46
+ if (container) {
47
+ container.setAttribute(SLIDE_ROOT_ATTR, "true");
48
+ if (!container.getAttribute("data-slide-width")) {
49
+ container.setAttribute("data-slide-width", String(DEFAULT_SLIDE_WIDTH));
50
+ }
51
+ if (!container.getAttribute("data-slide-height")) {
52
+ container.setAttribute("data-slide-height", String(DEFAULT_SLIDE_HEIGHT));
53
+ }
54
+ return container;
55
+ }
56
+ return null;
57
+ }
58
+ function serializeHtmlDocument(doc) {
59
+ return `<!DOCTYPE html>
60
+ ${doc.documentElement.outerHTML}`;
61
+ }
62
+ function ensureEditableSelectors(html) {
63
+ const doc = parseHtmlDocument(html);
64
+ if (!doc) {
65
+ return html;
66
+ }
67
+ const root = ensureSlideRoot(doc);
68
+ const editableNodes = Array.from(doc.querySelectorAll("[data-editable]"));
69
+ if (root && !root.getAttribute(SELECTOR_ATTR)) {
70
+ root.setAttribute(SELECTOR_ATTR, "slide-root");
71
+ }
72
+ editableNodes.forEach((node, index) => {
73
+ if (!node.getAttribute(SELECTOR_ATTR)) {
74
+ const type = node.getAttribute("data-editable") || "block";
75
+ node.setAttribute(SELECTOR_ATTR, createElementId(index, type));
76
+ }
77
+ });
78
+ return serializeHtmlDocument(doc);
79
+ }
80
+ function parseSlide(html, slideId = "slide-1") {
81
+ const doc = parseHtmlDocument(html);
82
+ if (!doc) {
83
+ return {
84
+ id: slideId,
85
+ title: "Untitled Slide",
86
+ htmlSource: html,
87
+ rootSelector: `[${SLIDE_ROOT_ATTR}]`,
88
+ width: DEFAULT_SLIDE_WIDTH,
89
+ height: DEFAULT_SLIDE_HEIGHT,
90
+ elements: []
91
+ };
92
+ }
93
+ const normalizedHtml = ensureEditableSelectors(html);
94
+ const normalizedDoc = parseHtmlDocument(normalizedHtml);
95
+ if (!normalizedDoc) {
96
+ return {
97
+ id: slideId,
98
+ title: "Untitled Slide",
99
+ htmlSource: normalizedHtml,
100
+ rootSelector: `[${SLIDE_ROOT_ATTR}]`,
101
+ width: DEFAULT_SLIDE_WIDTH,
102
+ height: DEFAULT_SLIDE_HEIGHT,
103
+ elements: []
104
+ };
105
+ }
106
+ const root = ensureSlideRoot(normalizedDoc);
107
+ const editableNodes = Array.from(normalizedDoc.querySelectorAll("[data-editable]"));
108
+ const rootSelector = root?.getAttribute(SELECTOR_ATTR) ? `[${SELECTOR_ATTR}="${root.getAttribute(SELECTOR_ATTR)}"]` : `[${SLIDE_ROOT_ATTR}]`;
109
+ const width = parseDimension(root?.getAttribute("data-slide-width") ?? null, DEFAULT_SLIDE_WIDTH);
110
+ const height = parseDimension(
111
+ root?.getAttribute("data-slide-height") ?? null,
112
+ DEFAULT_SLIDE_HEIGHT
113
+ );
114
+ const elements = editableNodes.map((node, index) => {
115
+ const type = node.getAttribute("data-editable") || "block";
116
+ const selectorValue = node.getAttribute(SELECTOR_ATTR) || createElementId(index, type);
117
+ return {
118
+ id: selectorValue,
119
+ selector: getSlideElementSelector(selectorValue),
120
+ type: type === "block" && node.getAttribute("data-group") === "true" ? "group" : type,
121
+ content: node instanceof HTMLImageElement ? node.src : node.textContent || "",
122
+ tagName: node.tagName.toLowerCase()
123
+ };
124
+ });
125
+ const firstHeading = normalizedDoc.querySelector("h1, h2, title");
126
+ const title = firstHeading?.textContent?.trim() || `Slide ${slideId}`;
127
+ return {
128
+ id: normalizeSlideId(slideId),
129
+ title,
130
+ htmlSource: normalizedHtml,
131
+ rootSelector,
132
+ width,
133
+ height,
134
+ elements
135
+ };
136
+ }
137
+ function querySlideElement(doc, elementId) {
138
+ return doc.querySelector(getSlideElementSelector(elementId));
139
+ }
140
+ function getSlideInlineStyleValue(slide, elementId, propertyName) {
141
+ const doc = parseHtmlDocument(slide.htmlSource);
142
+ if (!doc) {
143
+ return "";
144
+ }
145
+ const node = querySlideElement(doc, elementId);
146
+ return node?.style.getPropertyValue(propertyName).trim() || "";
147
+ }
148
+
149
+ // src/core/slide-html-document.ts
150
+ function parseHtmlDocument2(html) {
151
+ if (typeof DOMParser === "undefined") {
152
+ return null;
153
+ }
154
+ const parser = new DOMParser();
155
+ return parser.parseFromString(html, "text/html");
156
+ }
157
+ function serializeHtmlDocument2(doc) {
158
+ return `<!DOCTYPE html>
159
+ ${doc.documentElement.outerHTML}`;
160
+ }
161
+ function updateHtmlSource(html, updater) {
162
+ const doc = parseHtmlDocument2(html);
163
+ if (!doc) {
164
+ return html;
165
+ }
166
+ updater(doc);
167
+ return serializeHtmlDocument2(doc);
168
+ }
169
+
170
+ // src/core/layout.ts
171
+ var ELEMENT_LAYOUT_STYLE_KEYS = [
172
+ "position",
173
+ "left",
174
+ "top",
175
+ "width",
176
+ "height",
177
+ "transform",
178
+ "transformOrigin",
179
+ "margin",
180
+ "zIndex"
181
+ ];
182
+ function createEmptyElementLayoutStyleSnapshot() {
183
+ return {
184
+ position: null,
185
+ left: null,
186
+ top: null,
187
+ width: null,
188
+ height: null,
189
+ transform: null,
190
+ transformOrigin: null,
191
+ margin: null,
192
+ zIndex: null
193
+ };
194
+ }
195
+ function captureElementLayoutStyleSnapshot(node) {
196
+ const snapshot = createEmptyElementLayoutStyleSnapshot();
197
+ for (const key of ELEMENT_LAYOUT_STYLE_KEYS) {
198
+ const value = node.style[key];
199
+ snapshot[key] = value ? value : null;
200
+ }
201
+ return snapshot;
202
+ }
203
+ function normalizeElementLayoutStyleSnapshot(snapshot) {
204
+ return {
205
+ ...createEmptyElementLayoutStyleSnapshot(),
206
+ ...snapshot
207
+ };
208
+ }
209
+ function parseTransformParts(transformValue) {
210
+ const rawValue = transformValue || "";
211
+ const translateMatch = rawValue.match(/translate\(([-\d.]+)px,\s*([-\d.]+)px\)/i);
212
+ const rotateMatch = rawValue.match(/rotate\(([-\d.]+)deg\)/i);
213
+ return {
214
+ translateX: translateMatch ? Number.parseFloat(translateMatch[1] || "0") || 0 : 0,
215
+ translateY: translateMatch ? Number.parseFloat(translateMatch[2] || "0") || 0 : 0,
216
+ rotate: rotateMatch ? Number.parseFloat(rotateMatch[1] || "0") || 0 : 0
217
+ };
218
+ }
219
+ function roundNumber(value) {
220
+ return Math.round(value * 100) / 100;
221
+ }
222
+ function composeTransform(translateX, translateY, rotate) {
223
+ const parts = [];
224
+ if (Math.abs(translateX) > 0.01 || Math.abs(translateY) > 0.01) {
225
+ parts.push(`translate(${roundNumber(translateX)}px, ${roundNumber(translateY)}px)`);
226
+ }
227
+ if (Math.abs(rotate) > 0.01) {
228
+ parts.push(`rotate(${roundNumber(rotate)}deg)`);
229
+ }
230
+ return parts.length ? parts.join(" ") : null;
231
+ }
232
+ function elementRectToStageRect(elementRect, rootRect, geometry) {
233
+ return {
234
+ x: geometry.offsetX + (elementRect.left - rootRect.left) * geometry.scale,
235
+ y: geometry.offsetY + (elementRect.top - rootRect.top) * geometry.scale,
236
+ width: elementRect.width * geometry.scale,
237
+ height: elementRect.height * geometry.scale
238
+ };
239
+ }
240
+ function stageDeltaToSlideDelta(deltaX, deltaY, geometry) {
241
+ return {
242
+ x: deltaX / geometry.scale,
243
+ y: deltaY / geometry.scale
244
+ };
245
+ }
246
+
247
+ // src/core/slide-operations.ts
248
+ function numericStyleValue(value) {
249
+ const parsed = Number.parseFloat(value || "");
250
+ return Number.isFinite(parsed) ? parsed : 0;
251
+ }
252
+ function getNodeRect(node) {
253
+ const transform = parseTransformParts(node.style.transform);
254
+ return {
255
+ x: numericStyleValue(node.style.left) + transform.translateX,
256
+ y: numericStyleValue(node.style.top) + transform.translateY,
257
+ width: numericStyleValue(node.style.width) || DEFAULT_SLIDE_WIDTH,
258
+ height: numericStyleValue(node.style.height) || DEFAULT_SLIDE_HEIGHT
259
+ };
260
+ }
261
+ function getAbsoluteNodeRect(node, elementRects = {}) {
262
+ let current = node;
263
+ let x = 0;
264
+ let y = 0;
265
+ const ownElementId = node.getAttribute(SELECTOR_ATTR) ?? "";
266
+ const ownRect = elementRects[ownElementId] ?? getNodeRect(node);
267
+ const width = ownRect.width;
268
+ const height = ownRect.height;
269
+ while (current) {
270
+ const elementId = current.getAttribute(SELECTOR_ATTR) ?? "";
271
+ const rect = elementRects[elementId] ?? getNodeRect(current);
272
+ x += rect.x;
273
+ y += rect.y;
274
+ const parent = current.parentElement;
275
+ if (!parent || !parent.hasAttribute("data-editable")) {
276
+ break;
277
+ }
278
+ current = parent;
279
+ }
280
+ return { x, y, width, height };
281
+ }
282
+ function getEditableAncestorRect(node, elementRects = {}) {
283
+ const parent = node.parentElement;
284
+ if (!parent || !parent.hasAttribute("data-editable")) {
285
+ return { x: 0, y: 0 };
286
+ }
287
+ const rect = getAbsoluteNodeRect(parent, elementRects);
288
+ return { x: rect.x, y: rect.y };
289
+ }
290
+ function setNodeRect(node, rect) {
291
+ node.style.position = node.style.position || "absolute";
292
+ node.style.left = `${Math.round(rect.x * 100) / 100}px`;
293
+ node.style.top = `${Math.round(rect.y * 100) / 100}px`;
294
+ node.style.width = `${Math.round(rect.width * 100) / 100}px`;
295
+ node.style.height = `${Math.round(rect.height * 100) / 100}px`;
296
+ const transform = parseTransformParts(node.style.transform);
297
+ const nextTransform = composeTransform(0, 0, transform.rotate);
298
+ if (nextTransform) {
299
+ node.style.transform = nextTransform;
300
+ } else {
301
+ node.style.removeProperty("transform");
302
+ }
303
+ }
304
+ function getDirectEditableOwner(node) {
305
+ const parent = node.parentElement;
306
+ if (!parent) {
307
+ return null;
308
+ }
309
+ if (parent.hasAttribute(SLIDE_ROOT_ATTR) || parent.hasAttribute("data-editable")) {
310
+ return parent;
311
+ }
312
+ return null;
313
+ }
314
+ function childEditableElements(node) {
315
+ return Array.from(node.children).filter(
316
+ (child) => child instanceof HTMLElement && child.hasAttribute("data-editable")
317
+ );
318
+ }
319
+ function updateSlideText(html, elementId, value) {
320
+ return updateHtmlSource(html, (doc) => {
321
+ const node = querySlideElement(doc, elementId);
322
+ if (node) {
323
+ node.textContent = value;
324
+ }
325
+ });
326
+ }
327
+ function updateSlideStyle(html, elementId, propertyName, value) {
328
+ return updateHtmlSource(html, (doc) => {
329
+ const node = querySlideElement(doc, elementId);
330
+ if (!node) {
331
+ return;
332
+ }
333
+ if (value.trim().length === 0) {
334
+ node.style.removeProperty(propertyName);
335
+ } else {
336
+ node.style.setProperty(propertyName, value);
337
+ }
338
+ if (!node.getAttribute("style")?.trim()) {
339
+ node.removeAttribute("style");
340
+ }
341
+ });
342
+ }
343
+ function updateSlideAttribute(html, elementId, attributeName, value) {
344
+ return updateHtmlSource(html, (doc) => {
345
+ const node = querySlideElement(doc, elementId);
346
+ if (!node) {
347
+ return;
348
+ }
349
+ if (value.trim().length === 0) {
350
+ node.removeAttribute(attributeName);
351
+ } else {
352
+ node.setAttribute(attributeName, value);
353
+ }
354
+ });
355
+ }
356
+ function duplicateSlideElement(html, sourceElementId, nextElementId) {
357
+ return updateHtmlSource(html, (doc) => {
358
+ const sourceNode = querySlideElement(doc, sourceElementId);
359
+ if (!sourceNode) {
360
+ return;
361
+ }
362
+ const clonedNode = sourceNode.cloneNode(true);
363
+ if (!(clonedNode instanceof HTMLElement)) {
364
+ return;
365
+ }
366
+ clonedNode.setAttribute(SELECTOR_ATTR, nextElementId);
367
+ clonedNode.removeAttribute("data-hse-editing");
368
+ sourceNode.insertAdjacentElement("afterend", clonedNode);
369
+ });
370
+ }
371
+ function applyElementLayoutStyleSnapshot(node, snapshot) {
372
+ for (const key of ELEMENT_LAYOUT_STYLE_KEYS) {
373
+ const value = snapshot[key];
374
+ if (value === null) {
375
+ node.style[key] = "";
376
+ continue;
377
+ }
378
+ node.style[key] = value;
379
+ }
380
+ if (!node.getAttribute("style")?.trim()) {
381
+ node.removeAttribute("style");
382
+ }
383
+ }
384
+ function updateSlideElementLayout(html, elementId, snapshot) {
385
+ return updateHtmlSource(html, (doc) => {
386
+ const node = querySlideElement(doc, elementId);
387
+ if (!node) {
388
+ return;
389
+ }
390
+ applyElementLayoutStyleSnapshot(node, snapshot);
391
+ });
392
+ }
393
+ function createElementPlacement(html, elementId) {
394
+ const doc = parseHtmlDocument2(html);
395
+ if (!doc) {
396
+ return null;
397
+ }
398
+ const node = querySlideElement(doc, elementId);
399
+ if (!node || !(node.parentElement instanceof HTMLElement)) {
400
+ return null;
401
+ }
402
+ const previousSiblingElementId = node.previousElementSibling instanceof HTMLElement ? node.previousElementSibling.getAttribute(SELECTOR_ATTR) : null;
403
+ const nextSiblingElementId = node.nextElementSibling instanceof HTMLElement ? node.nextElementSibling.getAttribute(SELECTOR_ATTR) : null;
404
+ return {
405
+ parentElementId: node.parentElement.getAttribute(SELECTOR_ATTR),
406
+ previousSiblingElementId,
407
+ nextSiblingElementId
408
+ };
409
+ }
410
+ function getSlideElementHtml(html, elementId) {
411
+ const doc = parseHtmlDocument2(html);
412
+ if (!doc) {
413
+ return null;
414
+ }
415
+ return querySlideElement(doc, elementId)?.outerHTML ?? null;
416
+ }
417
+ function updateSlideElementHtmlIds(elementHtml, idMap) {
418
+ const doc = parseHtmlDocument2(`<template>${elementHtml}</template>`);
419
+ const root = doc?.querySelector("template")?.content.firstElementChild;
420
+ if (!(root instanceof HTMLElement)) {
421
+ return elementHtml;
422
+ }
423
+ const nodes = [root, ...Array.from(root.querySelectorAll(`[${SELECTOR_ATTR}]`))];
424
+ for (const node of nodes) {
425
+ const currentId = node.getAttribute(SELECTOR_ATTR);
426
+ if (currentId && idMap[currentId]) {
427
+ node.setAttribute(SELECTOR_ATTR, idMap[currentId]);
428
+ }
429
+ }
430
+ return root.outerHTML;
431
+ }
432
+ function createUniqueElementId(html, preferredId) {
433
+ const doc = parseHtmlDocument2(html);
434
+ if (!doc) {
435
+ return preferredId;
436
+ }
437
+ const existingIds = new Set(
438
+ Array.from(doc.querySelectorAll(`[${SELECTOR_ATTR}]`)).map((node) => node.getAttribute(SELECTOR_ATTR)).filter((value) => Boolean(value))
439
+ );
440
+ if (!existingIds.has(preferredId)) {
441
+ return preferredId;
442
+ }
443
+ const match = preferredId.match(/^(.*?)(?:-(\d+))?$/);
444
+ const base = match?.[1] || preferredId;
445
+ let index = Number.parseInt(match?.[2] || "1", 10) + 1;
446
+ while (existingIds.has(`${base}-${index}`)) {
447
+ index += 1;
448
+ }
449
+ return `${base}-${index}`;
450
+ }
451
+ function insertSlideElement(html, operation) {
452
+ return updateHtmlSource(html, (doc) => {
453
+ if (querySlideElement(doc, operation.elementId)) {
454
+ return;
455
+ }
456
+ const fragmentDoc = parseHtmlDocument2(`<template>${operation.html}</template>`);
457
+ const node = fragmentDoc?.querySelector("template")?.content.firstElementChild;
458
+ if (!(node instanceof HTMLElement)) {
459
+ return;
460
+ }
461
+ const parent = (operation.parentElementId ? querySlideElement(doc, operation.parentElementId) : null) ?? doc.querySelector(`[${SLIDE_ROOT_ATTR}]`) ?? doc.body;
462
+ const nextSibling = operation.nextSiblingElementId ? querySlideElement(doc, operation.nextSiblingElementId) : null;
463
+ const previousSibling = operation.previousSiblingElementId ? querySlideElement(doc, operation.previousSiblingElementId) : null;
464
+ if (nextSibling?.parentElement === parent) {
465
+ parent.insertBefore(doc.importNode(node, true), nextSibling);
466
+ } else if (previousSibling?.parentElement === parent) {
467
+ previousSibling.after(doc.importNode(node, true));
468
+ } else {
469
+ parent.appendChild(doc.importNode(node, true));
470
+ }
471
+ });
472
+ }
473
+ function removeSlideElement(html, elementId) {
474
+ return updateHtmlSource(html, (doc) => {
475
+ querySlideElement(doc, elementId)?.remove();
476
+ });
477
+ }
478
+ function updateSlideElementTransform(html, elementId, deltaX, deltaY) {
479
+ return updateHtmlSource(html, (doc) => {
480
+ const node = querySlideElement(doc, elementId);
481
+ if (!node) {
482
+ return;
483
+ }
484
+ const currentTransform = parseTransformParts(node.style.transform);
485
+ const nextTransform = composeTransform(
486
+ currentTransform.translateX + deltaX,
487
+ currentTransform.translateY + deltaY,
488
+ currentTransform.rotate
489
+ );
490
+ if (nextTransform) {
491
+ node.style.transform = nextTransform;
492
+ } else {
493
+ node.style.removeProperty("transform");
494
+ }
495
+ if (!node.style.position) {
496
+ node.style.position = "relative";
497
+ }
498
+ });
499
+ }
500
+ function createGroupCreateOperation({
501
+ html,
502
+ slideId,
503
+ groupElementId,
504
+ elementIds,
505
+ elementRects = {},
506
+ timestamp = Date.now()
507
+ }) {
508
+ const doc = parseHtmlDocument2(html);
509
+ if (!doc || !elementIds.length) {
510
+ return null;
511
+ }
512
+ const selectedNodes = elementIds.map((elementId) => querySlideElement(doc, elementId)).filter((node) => Boolean(node));
513
+ if (selectedNodes.length !== elementIds.length) {
514
+ return null;
515
+ }
516
+ const flattenedNodes = selectedNodes.flatMap(
517
+ (node) => node.getAttribute("data-group") === "true" ? childEditableElements(node) : [node]
518
+ );
519
+ const uniqueNodes = flattenedNodes.filter(
520
+ (node, index, nodes) => nodes.findIndex((candidate) => candidate === node) === index
521
+ );
522
+ if (uniqueNodes.length < 2) {
523
+ return null;
524
+ }
525
+ const parent = getDirectEditableOwner(selectedNodes[0]);
526
+ if (!parent || selectedNodes.some((node) => getDirectEditableOwner(node) !== parent)) {
527
+ return null;
528
+ }
529
+ if (querySlideElement(doc, groupElementId)) {
530
+ return null;
531
+ }
532
+ const selectedGroups = selectedNodes.filter((node) => node.getAttribute("data-group") === "true");
533
+ const rects = uniqueNodes.map((node) => getAbsoluteNodeRect(node, elementRects));
534
+ const left = Math.min(...rects.map((rect) => rect.x));
535
+ const top = Math.min(...rects.map((rect) => rect.y));
536
+ const right = Math.max(...rects.map((rect) => rect.x + rect.width));
537
+ const bottom = Math.max(...rects.map((rect) => rect.y + rect.height));
538
+ const parentRect = getEditableAncestorRect(selectedNodes[0], elementRects);
539
+ const groupNode = doc.createElement("div");
540
+ groupNode.setAttribute("data-editable", "block");
541
+ groupNode.setAttribute("data-group", "true");
542
+ groupNode.setAttribute(SELECTOR_ATTR, groupElementId);
543
+ setNodeRect(groupNode, {
544
+ x: left - parentRect.x,
545
+ y: top - parentRect.y,
546
+ width: right - left,
547
+ height: bottom - top
548
+ });
549
+ const orderedNodes = Array.from(parent.children).flatMap((child) => {
550
+ if (!(child instanceof HTMLElement)) {
551
+ return [];
552
+ }
553
+ if (selectedGroups.includes(child)) {
554
+ return childEditableElements(child).filter((node) => uniqueNodes.includes(node));
555
+ }
556
+ return uniqueNodes.includes(child) ? [child] : [];
557
+ });
558
+ const insertionAnchor = selectedNodes.find((node) => getDirectEditableOwner(node) === parent) ?? null;
559
+ if (insertionAnchor?.parentElement === parent) {
560
+ parent.insertBefore(groupNode, insertionAnchor);
561
+ } else {
562
+ parent.appendChild(groupNode);
563
+ }
564
+ for (const node of orderedNodes) {
565
+ const rect = getAbsoluteNodeRect(node, elementRects);
566
+ setNodeRect(node, {
567
+ x: rect.x - left,
568
+ y: rect.y - top,
569
+ width: rect.width,
570
+ height: rect.height
571
+ });
572
+ groupNode.appendChild(node);
573
+ }
574
+ for (const group of selectedGroups) {
575
+ if (!group.children.length) {
576
+ group.remove();
577
+ }
578
+ }
579
+ const nextHtmlSource = serializeHtmlDocument2(doc);
580
+ if (nextHtmlSource === html) {
581
+ return null;
582
+ }
583
+ return {
584
+ type: "group.create",
585
+ slideId,
586
+ groupElementId,
587
+ elementIds: orderedNodes.map((node) => node.getAttribute(SELECTOR_ATTR)).filter((elementId) => Boolean(elementId)),
588
+ previousHtmlSource: html,
589
+ nextHtmlSource,
590
+ timestamp
591
+ };
592
+ }
593
+ function createGroupUngroupOperation({
594
+ html,
595
+ slideId,
596
+ groupElementId,
597
+ elementRects = {},
598
+ timestamp = Date.now()
599
+ }) {
600
+ const doc = parseHtmlDocument2(html);
601
+ if (!doc) {
602
+ return null;
603
+ }
604
+ const groupNode = querySlideElement(doc, groupElementId);
605
+ if (!groupNode || groupNode.getAttribute("data-group") !== "true" || !groupNode.parentElement) {
606
+ return null;
607
+ }
608
+ const parent = groupNode.parentElement;
609
+ const parentRect = getEditableAncestorRect(groupNode, elementRects);
610
+ const children = childEditableElements(groupNode);
611
+ if (!children.length) {
612
+ return null;
613
+ }
614
+ const childElementIds = children.map((child) => child.getAttribute(SELECTOR_ATTR)).filter((elementId) => Boolean(elementId));
615
+ for (const child of children) {
616
+ const rect = getAbsoluteNodeRect(child, elementRects);
617
+ setNodeRect(child, {
618
+ x: rect.x - parentRect.x,
619
+ y: rect.y - parentRect.y,
620
+ width: rect.width,
621
+ height: rect.height
622
+ });
623
+ parent.insertBefore(child, groupNode);
624
+ }
625
+ groupNode.remove();
626
+ const nextHtmlSource = serializeHtmlDocument2(doc);
627
+ if (nextHtmlSource === html) {
628
+ return null;
629
+ }
630
+ return {
631
+ type: "group.ungroup",
632
+ slideId,
633
+ groupElementId,
634
+ childElementIds,
635
+ previousHtmlSource: html,
636
+ nextHtmlSource,
637
+ timestamp
638
+ };
639
+ }
640
+
641
+ // src/core/slide-operation-reducer.ts
642
+ function preserveSlideSource(sourceSlide, nextSlide) {
643
+ return {
644
+ ...nextSlide,
645
+ sourceFile: sourceSlide.sourceFile
646
+ };
647
+ }
648
+ function applySlideOperation(slide, operation) {
649
+ if (slide.id !== operation.slideId) {
650
+ return slide;
651
+ }
652
+ switch (operation.type) {
653
+ case "operation.batch":
654
+ return operation.operations.reduce(
655
+ (currentSlide, childOperation) => applySlideOperation(currentSlide, childOperation),
656
+ slide
657
+ );
658
+ case "text.update":
659
+ return preserveSlideSource(
660
+ slide,
661
+ parseSlide(
662
+ updateSlideText(slide.htmlSource, operation.elementId, operation.nextText),
663
+ slide.id
664
+ )
665
+ );
666
+ case "style.update":
667
+ return preserveSlideSource(
668
+ slide,
669
+ parseSlide(
670
+ updateSlideStyle(
671
+ slide.htmlSource,
672
+ operation.elementId,
673
+ operation.propertyName,
674
+ operation.nextValue
675
+ ),
676
+ slide.id
677
+ )
678
+ );
679
+ case "attribute.update":
680
+ return preserveSlideSource(
681
+ slide,
682
+ parseSlide(
683
+ updateSlideAttribute(
684
+ slide.htmlSource,
685
+ operation.elementId,
686
+ operation.attributeName,
687
+ operation.nextValue
688
+ ),
689
+ slide.id
690
+ )
691
+ );
692
+ case "element.layout.update":
693
+ return preserveSlideSource(
694
+ slide,
695
+ parseSlide(
696
+ updateSlideElementLayout(slide.htmlSource, operation.elementId, operation.nextStyle),
697
+ slide.id
698
+ )
699
+ );
700
+ case "element.insert":
701
+ return preserveSlideSource(
702
+ slide,
703
+ parseSlide(insertSlideElement(slide.htmlSource, operation), slide.id)
704
+ );
705
+ case "element.remove":
706
+ return preserveSlideSource(
707
+ slide,
708
+ parseSlide(removeSlideElement(slide.htmlSource, operation.elementId), slide.id)
709
+ );
710
+ case "group.create":
711
+ case "group.ungroup":
712
+ return preserveSlideSource(slide, parseSlide(operation.nextHtmlSource, slide.id));
713
+ }
714
+ }
715
+ function invertSlideOperation(operation) {
716
+ switch (operation.type) {
717
+ case "operation.batch":
718
+ return {
719
+ type: "operation.batch",
720
+ slideId: operation.slideId,
721
+ operations: operation.operations.map((childOperation) => invertSlideOperation(childOperation)).reverse(),
722
+ timestamp: operation.timestamp
723
+ };
724
+ case "text.update":
725
+ return {
726
+ ...operation,
727
+ previousText: operation.nextText,
728
+ nextText: operation.previousText
729
+ };
730
+ case "style.update":
731
+ return {
732
+ ...operation,
733
+ previousValue: operation.nextValue,
734
+ nextValue: operation.previousValue
735
+ };
736
+ case "attribute.update":
737
+ return {
738
+ ...operation,
739
+ previousValue: operation.nextValue,
740
+ nextValue: operation.previousValue
741
+ };
742
+ case "element.layout.update":
743
+ return {
744
+ ...operation,
745
+ previousStyle: operation.nextStyle,
746
+ nextStyle: operation.previousStyle
747
+ };
748
+ case "element.insert":
749
+ return {
750
+ type: "element.remove",
751
+ slideId: operation.slideId,
752
+ elementId: operation.elementId,
753
+ parentElementId: operation.parentElementId,
754
+ previousSiblingElementId: operation.previousSiblingElementId,
755
+ nextSiblingElementId: operation.nextSiblingElementId,
756
+ html: operation.html,
757
+ timestamp: operation.timestamp
758
+ };
759
+ case "element.remove":
760
+ return {
761
+ type: "element.insert",
762
+ slideId: operation.slideId,
763
+ elementId: operation.elementId,
764
+ parentElementId: operation.parentElementId,
765
+ previousSiblingElementId: operation.previousSiblingElementId,
766
+ nextSiblingElementId: operation.nextSiblingElementId,
767
+ html: operation.html,
768
+ timestamp: operation.timestamp
769
+ };
770
+ case "group.create":
771
+ return {
772
+ type: "group.ungroup",
773
+ slideId: operation.slideId,
774
+ groupElementId: operation.groupElementId,
775
+ childElementIds: operation.elementIds,
776
+ previousHtmlSource: operation.nextHtmlSource,
777
+ nextHtmlSource: operation.previousHtmlSource,
778
+ timestamp: operation.timestamp
779
+ };
780
+ case "group.ungroup":
781
+ return {
782
+ type: "group.create",
783
+ slideId: operation.slideId,
784
+ groupElementId: operation.groupElementId,
785
+ elementIds: operation.childElementIds,
786
+ previousHtmlSource: operation.nextHtmlSource,
787
+ nextHtmlSource: operation.previousHtmlSource,
788
+ timestamp: operation.timestamp
789
+ };
790
+ }
791
+ }
792
+
793
+ // src/core/history.ts
794
+ function applyOperationToSlides(slides, operation) {
795
+ return slides.map((slide) => applySlideOperation(slide, operation));
796
+ }
797
+ function createHistoryState(slides) {
798
+ return {
799
+ slides,
800
+ undoStack: [],
801
+ redoStack: []
802
+ };
803
+ }
804
+ function reduceHistory(state, action) {
805
+ switch (action.type) {
806
+ case "history.reset":
807
+ return createHistoryState(action.slides);
808
+ case "history.commit":
809
+ return {
810
+ slides: applyOperationToSlides(state.slides, action.operation),
811
+ undoStack: [...state.undoStack, action.operation],
812
+ redoStack: []
813
+ };
814
+ case "history.undo": {
815
+ const operation = state.undoStack[state.undoStack.length - 1];
816
+ if (!operation) {
817
+ return state;
818
+ }
819
+ return {
820
+ slides: applyOperationToSlides(state.slides, invertSlideOperation(operation)),
821
+ undoStack: state.undoStack.slice(0, -1),
822
+ redoStack: [...state.redoStack, operation]
823
+ };
824
+ }
825
+ case "history.redo": {
826
+ const operation = state.redoStack[state.redoStack.length - 1];
827
+ if (!operation) {
828
+ return state;
829
+ }
830
+ return {
831
+ slides: applyOperationToSlides(state.slides, operation),
832
+ undoStack: [...state.undoStack, operation],
833
+ redoStack: state.redoStack.slice(0, -1)
834
+ };
835
+ }
836
+ }
837
+ }
838
+
839
+ // src/core/generated-deck.ts
840
+ async function loadSlidesFromManifest({
841
+ manifestUrl,
842
+ fetchImpl,
843
+ requestInit,
844
+ slideIdPrefix = "generated-slide-"
845
+ }) {
846
+ const activeFetch = fetchImpl ?? globalThis.fetch;
847
+ if (!activeFetch) {
848
+ throw new Error("loadSlidesFromManifest requires a fetch implementation.");
849
+ }
850
+ const effectiveRequestInit = {
851
+ cache: "no-store",
852
+ ...requestInit
853
+ };
854
+ const manifestResponse = await activeFetch(manifestUrl, effectiveRequestInit);
855
+ if (!manifestResponse.ok) {
856
+ return null;
857
+ }
858
+ const manifest = await manifestResponse.json();
859
+ if (!manifest.slides?.length) {
860
+ return null;
861
+ }
862
+ const manifestBaseUrl = manifestResponse.url || manifestUrl;
863
+ const manifestUrlObject = new URL(manifestBaseUrl);
864
+ const slides = await Promise.all(
865
+ manifest.slides.map(async (slide, index) => {
866
+ const slideUrl = new URL(slide.file, manifestBaseUrl);
867
+ for (const [key, value] of manifestUrlObject.searchParams) {
868
+ if (!slideUrl.searchParams.has(key)) {
869
+ slideUrl.searchParams.set(key, value);
870
+ }
871
+ }
872
+ const slideResponse = await activeFetch(slideUrl.toString(), effectiveRequestInit);
873
+ if (!slideResponse.ok) {
874
+ throw new Error(`Failed to load slide HTML: ${slide.file}`);
875
+ }
876
+ const html = await slideResponse.text();
877
+ const parsedSlide = parseSlide(html, `${slideIdPrefix}${index + 1}`);
878
+ return {
879
+ ...parsedSlide,
880
+ sourceFile: slide.file,
881
+ title: slide.title || parsedSlide.title
882
+ };
883
+ })
884
+ );
885
+ return {
886
+ manifest,
887
+ slides
888
+ };
889
+ }
890
+
891
+ // src/core/pdf-export.ts
892
+ function planPdfExport({
893
+ slides,
894
+ selection
895
+ }) {
896
+ const resolvedSelection = selection ?? { mode: "all" };
897
+ const mode = resolvedSelection.mode ?? "all";
898
+ if (mode === "all") {
899
+ return { mode: "all", slides: [...slides] };
900
+ }
901
+ if (mode === "slide") {
902
+ const slideFile = "slideFile" in resolvedSelection ? resolvedSelection.slideFile?.trim() : "";
903
+ if (!slideFile) {
904
+ throw new Error("--slide requires a manifest slide file value");
905
+ }
906
+ const slide = findSlide(slides, slideFile);
907
+ if (!slide) {
908
+ throw new Error(`--slide must match a manifest slide file exactly: ${slideFile}`);
909
+ }
910
+ return { mode: "single", slides: [slide] };
911
+ }
912
+ const slideFiles = "slideFiles" in resolvedSelection ? resolvedSelection.slideFiles ?? [] : [];
913
+ const requestedFiles = slideFiles.map((file) => file.trim()).filter(Boolean);
914
+ if (requestedFiles.length === 0) {
915
+ throw new Error("--slides requires at least one manifest slide file value");
916
+ }
917
+ const selectedSlides = [];
918
+ const missingFiles = [];
919
+ for (const slideFile of requestedFiles) {
920
+ const slide = findSlide(slides, slideFile);
921
+ if (!slide) {
922
+ missingFiles.push(slideFile);
923
+ continue;
924
+ }
925
+ selectedSlides.push(slide);
926
+ }
927
+ if (missingFiles.length > 0) {
928
+ throw new Error(`--slides must match manifest slide files exactly: ${missingFiles.join(", ")}`);
929
+ }
930
+ return { mode: "slides", slides: selectedSlides };
931
+ }
932
+ function planPdfExportSlides(slides, selection) {
933
+ return planPdfExport({ slides, selection }).slides;
934
+ }
935
+ function findSlide(slides, slideFile) {
936
+ return slides.find((slide) => slide.file === slideFile);
937
+ }
938
+
939
+ export {
940
+ SELECTOR_ATTR,
941
+ SLIDE_ROOT_ATTR,
942
+ DEFAULT_SLIDE_WIDTH,
943
+ DEFAULT_SLIDE_HEIGHT,
944
+ getSlideElementSelector,
945
+ getSlideRootSelector,
946
+ createElementId,
947
+ parseDimension,
948
+ normalizeSlideId,
949
+ ensureEditableSelectors,
950
+ parseSlide,
951
+ querySlideElement,
952
+ getSlideInlineStyleValue,
953
+ parseHtmlDocument2 as parseHtmlDocument,
954
+ serializeHtmlDocument2 as serializeHtmlDocument,
955
+ updateHtmlSource,
956
+ ELEMENT_LAYOUT_STYLE_KEYS,
957
+ createEmptyElementLayoutStyleSnapshot,
958
+ captureElementLayoutStyleSnapshot,
959
+ normalizeElementLayoutStyleSnapshot,
960
+ parseTransformParts,
961
+ composeTransform,
962
+ elementRectToStageRect,
963
+ stageDeltaToSlideDelta,
964
+ applySlideOperation,
965
+ invertSlideOperation,
966
+ updateSlideText,
967
+ updateSlideStyle,
968
+ updateSlideAttribute,
969
+ duplicateSlideElement,
970
+ updateSlideElementLayout,
971
+ createElementPlacement,
972
+ getSlideElementHtml,
973
+ updateSlideElementHtmlIds,
974
+ createUniqueElementId,
975
+ insertSlideElement,
976
+ removeSlideElement,
977
+ updateSlideElementTransform,
978
+ createGroupCreateOperation,
979
+ createGroupUngroupOperation,
980
+ createHistoryState,
981
+ reduceHistory,
982
+ loadSlidesFromManifest,
983
+ planPdfExport,
984
+ planPdfExportSlides
985
+ };