@squadbase/vantage 0.5.3-dev.71d2349 → 0.6.0-dev.3cc6138

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 (120) hide show
  1. package/NOTICE +73 -0
  2. package/README.md +3 -3
  3. package/dist/add-S62E5MSP.js +6 -0
  4. package/dist/add-S62E5MSP.js.map +1 -0
  5. package/dist/{build-756WIEVI.js → build-XXKKX6KB.js} +10 -10
  6. package/dist/{build-756WIEVI.js.map → build-XXKKX6KB.js.map} +1 -1
  7. package/dist/{check-KZ5PHCA6.js → check-X2TDOILH.js} +8 -8
  8. package/dist/{check-KZ5PHCA6.js.map → check-X2TDOILH.js.map} +1 -1
  9. package/dist/{chunk-L3TW4FNA.js → chunk-46PPUW6Y.js} +34 -13
  10. package/dist/chunk-46PPUW6Y.js.map +1 -0
  11. package/dist/chunk-5ZOP4SQR.js +47 -0
  12. package/dist/chunk-5ZOP4SQR.js.map +1 -0
  13. package/dist/{chunk-BAM33YZ6.js → chunk-B45WAO5Q.js} +6 -5
  14. package/dist/chunk-B45WAO5Q.js.map +1 -0
  15. package/dist/{chunk-HQDEYCDP.js → chunk-CIJWVFJD.js} +3 -3
  16. package/dist/{chunk-HQDEYCDP.js.map → chunk-CIJWVFJD.js.map} +1 -1
  17. package/dist/chunk-FVWU275X.js +91 -0
  18. package/dist/chunk-FVWU275X.js.map +1 -0
  19. package/dist/{chunk-VXAV2435.js → chunk-KLEIVDUV.js} +30 -7
  20. package/dist/chunk-KLEIVDUV.js.map +1 -0
  21. package/dist/{chunk-RA6Z3OX3.js → chunk-LCN36MYX.js} +61 -9
  22. package/dist/chunk-LCN36MYX.js.map +1 -0
  23. package/dist/{chunk-JC6MT5UU.js → chunk-MXI3PZFT.js} +3 -3
  24. package/dist/{chunk-JC6MT5UU.js.map → chunk-MXI3PZFT.js.map} +1 -1
  25. package/dist/chunk-PLST5Q7T.js +2221 -0
  26. package/dist/chunk-PLST5Q7T.js.map +1 -0
  27. package/dist/chunk-PVTHHV7X.js +105 -0
  28. package/dist/chunk-PVTHHV7X.js.map +1 -0
  29. package/dist/{chunk-EHPJXDJU.js → chunk-SVNOWE5U.js} +4 -2
  30. package/dist/chunk-SVNOWE5U.js.map +1 -0
  31. package/dist/{chunk-C4MQRWXS.js → chunk-TDUIW7LD.js} +17 -3
  32. package/dist/chunk-TDUIW7LD.js.map +1 -0
  33. package/dist/{chunk-X44ZTKID.js → chunk-V2FV5OJ6.js} +61 -9
  34. package/dist/chunk-V2FV5OJ6.js.map +1 -0
  35. package/dist/{chunk-PSEYFYAP.js → chunk-VVOXG6ZU.js} +68 -8
  36. package/dist/chunk-VVOXG6ZU.js.map +1 -0
  37. package/dist/{chunk-D4Y5RWJA.js → chunk-YE4CPCXX.js} +81 -21
  38. package/dist/chunk-YE4CPCXX.js.map +1 -0
  39. package/dist/cli.js +15 -14
  40. package/dist/cli.js.map +1 -1
  41. package/dist/client/index.d.ts +5 -35
  42. package/dist/client/index.js +1 -1
  43. package/dist/components/index.js +3 -2212
  44. package/dist/components/index.js.map +1 -1
  45. package/dist/{dev-EILX2VTF.js → dev-4MQQDHA4.js} +10 -10
  46. package/dist/{dev-EILX2VTF.js.map → dev-4MQQDHA4.js.map} +1 -1
  47. package/dist/{docs-GP2ZHIQF.js → docs-OTFWTCPW.js} +4 -4
  48. package/dist/{docs-GP2ZHIQF.js.map → docs-OTFWTCPW.js.map} +1 -1
  49. package/dist/{doctor-FXZLMGC3.js → doctor-QXA6DURZ.js} +8 -8
  50. package/dist/{doctor-FXZLMGC3.js.map → doctor-QXA6DURZ.js.map} +1 -1
  51. package/dist/manifest-ttGMFi-Z.d.ts +43 -0
  52. package/dist/{preview-WBQPCTTT.js → preview-HVOM27FS.js} +4 -4
  53. package/dist/{preview-WBQPCTTT.js.map → preview-HVOM27FS.js.map} +1 -1
  54. package/dist/query/index.js +1 -102
  55. package/dist/query/index.js.map +1 -1
  56. package/dist/router/index.d.ts +10 -0
  57. package/dist/router/index.js +2 -89
  58. package/dist/router/index.js.map +1 -1
  59. package/dist/{routes-WCN4KSZY.js → routes-2QRAKMGK.js} +17 -8
  60. package/dist/routes-2QRAKMGK.js.map +1 -0
  61. package/dist/{search-GLKCJXSI.js → search-7A43AZ7M.js} +4 -4
  62. package/dist/{search-GLKCJXSI.js.map → search-7A43AZ7M.js.map} +1 -1
  63. package/dist/server/node.d.ts +1 -1
  64. package/dist/server/node.js +6 -1
  65. package/dist/server/node.js.map +1 -1
  66. package/dist/slide/index.d.ts +230 -0
  67. package/dist/slide/index.js +1571 -0
  68. package/dist/slide/index.js.map +1 -0
  69. package/dist/table/index.d.ts +107 -0
  70. package/dist/table/index.js +680 -0
  71. package/dist/table/index.js.map +1 -0
  72. package/dist/types-DQjLpLCs.d.ts +121 -0
  73. package/dist/ui/index.js +3 -41
  74. package/dist/ui/index.js.map +1 -1
  75. package/dist/{upgrade-S64IXL42.js → upgrade-2QCP7UQQ.js} +9 -9
  76. package/dist/upgrade-2QCP7UQQ.js.map +1 -0
  77. package/dist/vite/index.d.ts +1 -1
  78. package/dist/vite/index.js +5 -5
  79. package/docs/en/agent-skills.md +19 -12
  80. package/docs/en/changelog.md +77 -0
  81. package/docs/en/cli-reference.md +8 -4
  82. package/docs/en/pages-and-metadata.md +17 -0
  83. package/docs/en/routing.md +75 -3
  84. package/docs/en/ui-and-theming.md +76 -0
  85. package/docs/index.json +2 -2
  86. package/docs/ja/agent-skills.md +18 -11
  87. package/docs/ja/changelog.md +75 -0
  88. package/docs/ja/cli-reference.md +8 -4
  89. package/docs/ja/pages-and-metadata.md +17 -0
  90. package/docs/ja/routing.md +71 -3
  91. package/docs/ja/ui-and-theming.md +74 -0
  92. package/package.json +17 -3
  93. package/skills/vantage-app/SKILL.md +4 -0
  94. package/skills/vantage-pitfalls/SKILL.md +41 -1
  95. package/skills/vantage-slide/SKILL.md +264 -0
  96. package/skills/vantage-slide/references/morph.md +102 -0
  97. package/skills/vantage-slide/references/steps.md +57 -0
  98. package/skills/vantage-slide/references/tokens.md +87 -0
  99. package/skills/vantage-slide/references/transitions.md +196 -0
  100. package/templates/AGENTS.md +28 -230
  101. package/templates/agents/api.md +49 -0
  102. package/templates/agents/csv.md +32 -0
  103. package/templates/agents/page.md +185 -0
  104. package/templates/agents/slide.md +34 -0
  105. package/templates/agents/xlsx.md +33 -0
  106. package/theme.css +28 -0
  107. package/dist/add-PPCBMWEN.js +0 -6
  108. package/dist/add-PPCBMWEN.js.map +0 -1
  109. package/dist/chunk-BAM33YZ6.js.map +0 -1
  110. package/dist/chunk-C4MQRWXS.js.map +0 -1
  111. package/dist/chunk-D4Y5RWJA.js.map +0 -1
  112. package/dist/chunk-EHPJXDJU.js.map +0 -1
  113. package/dist/chunk-L3TW4FNA.js.map +0 -1
  114. package/dist/chunk-PSEYFYAP.js.map +0 -1
  115. package/dist/chunk-RA6Z3OX3.js.map +0 -1
  116. package/dist/chunk-VXAV2435.js.map +0 -1
  117. package/dist/chunk-X44ZTKID.js.map +0 -1
  118. package/dist/routes-WCN4KSZY.js.map +0 -1
  119. package/dist/types-De3mo_hv.d.ts +0 -59
  120. package/dist/upgrade-S64IXL42.js.map +0 -1
@@ -0,0 +1,1571 @@
1
+ import { useSearchState } from '../chunk-FVWU275X.js';
2
+ import '../chunk-SVNOWE5U.js';
3
+ import { createContext, useRef, useState, useLayoutEffect, useContext, createElement, Children, isValidElement, useCallback, cloneElement, useMemo, useEffect } from 'react';
4
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
+ import { createRoot } from 'react-dom/client';
6
+ import { ChevronLeftIcon, ChevronRightIcon, PanelLeftCloseIcon, PanelLeftOpenIcon, MinimizeIcon, MaximizeIcon, DownloadIcon } from 'lucide-react';
7
+
8
+ // @squadbase/vantage — generated build. Do not edit.
9
+
10
+ // src/slide/constants.ts
11
+ var CANVAS_WIDTH = 1920;
12
+ var CANVAS_HEIGHT = 1080;
13
+ function SlideCanvas({ children, scale, themeVars }) {
14
+ const containerRef = useRef(null);
15
+ const [fitScale, setFitScale] = useState(null);
16
+ useLayoutEffect(() => {
17
+ if (scale !== void 0) return;
18
+ const el = containerRef.current;
19
+ if (!el) return;
20
+ const measure = () => {
21
+ const { width, height } = el.getBoundingClientRect();
22
+ if (width === 0 || height === 0) return;
23
+ setFitScale(Math.min(width / CANVAS_WIDTH, height / CANVAS_HEIGHT));
24
+ };
25
+ measure();
26
+ const observer = new ResizeObserver(measure);
27
+ observer.observe(el);
28
+ return () => observer.disconnect();
29
+ }, [scale]);
30
+ const measured = scale ?? fitScale;
31
+ const s = measured ?? 1;
32
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, style: { position: "relative", height: "100%", width: "100%" }, children: /* @__PURE__ */ jsx(
33
+ "div",
34
+ {
35
+ style: {
36
+ position: "absolute",
37
+ left: "50%",
38
+ top: "50%",
39
+ transform: "translate(-50%, -50%)",
40
+ width: CANVAS_WIDTH * s,
41
+ height: CANVAS_HEIGHT * s,
42
+ overflow: "hidden",
43
+ background: "var(--slide-bg)",
44
+ color: "var(--slide-fg)",
45
+ // Hidden until measured — the same flash the layout effect avoids,
46
+ // for the frame this time.
47
+ visibility: measured === null ? "hidden" : void 0,
48
+ ...themeVars
49
+ },
50
+ children: /* @__PURE__ */ jsx(
51
+ "div",
52
+ {
53
+ "data-slide-canvas": "",
54
+ style: {
55
+ width: CANVAS_WIDTH,
56
+ height: CANVAS_HEIGHT,
57
+ transform: `scale(${s})`,
58
+ transformOrigin: "top left",
59
+ fontFamily: "var(--slide-font-body)",
60
+ fontSize: "var(--slide-size-body)",
61
+ ...themeVars
62
+ },
63
+ children
64
+ }
65
+ )
66
+ }
67
+ ) });
68
+ }
69
+
70
+ // src/slide/dom.ts
71
+ function sleep(ms) {
72
+ return new Promise((resolve) => setTimeout(resolve, ms));
73
+ }
74
+ function nextFrame() {
75
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
76
+ }
77
+ function nextPaint() {
78
+ return new Promise((resolve) => {
79
+ let settled = false;
80
+ const settle = () => {
81
+ if (settled) return;
82
+ settled = true;
83
+ resolve();
84
+ };
85
+ requestAnimationFrame(settle);
86
+ setTimeout(settle, 50);
87
+ });
88
+ }
89
+ function downloadBlob(blob, filename) {
90
+ const url = URL.createObjectURL(blob);
91
+ const a = document.createElement("a");
92
+ a.href = url;
93
+ a.download = filename;
94
+ a.rel = "noopener";
95
+ document.body.appendChild(a);
96
+ a.click();
97
+ a.remove();
98
+ setTimeout(() => URL.revokeObjectURL(url), 0);
99
+ }
100
+
101
+ // src/slide/print-ready.ts
102
+ var DEFAULT_WAITFOR_TIMEOUT_MS = 1e4;
103
+ async function waitForFonts() {
104
+ if (!("fonts" in document)) return;
105
+ await document.fonts.ready;
106
+ }
107
+ async function waitForDataWaitfor(root, timeoutMs = DEFAULT_WAITFOR_TIMEOUT_MS) {
108
+ const targets = Array.from(root.querySelectorAll("[data-waitfor]"));
109
+ if (targets.length === 0) return;
110
+ const deadline = performance.now() + timeoutMs;
111
+ await Promise.all(
112
+ targets.map(async (el) => {
113
+ const selector = el.getAttribute("data-waitfor");
114
+ if (!selector) return;
115
+ while (performance.now() < deadline) {
116
+ try {
117
+ if (el.querySelector(selector)) return;
118
+ } catch {
119
+ return;
120
+ }
121
+ await nextFrame();
122
+ }
123
+ })
124
+ );
125
+ }
126
+ function isFrameAnimationSettled(frame) {
127
+ if (typeof document.getAnimations !== "function") return true;
128
+ for (const animation of document.getAnimations()) {
129
+ const effect = animation.effect;
130
+ if (!effect) continue;
131
+ const target = effect.target;
132
+ if (!target || !frame.contains(target)) continue;
133
+ if (effect.getComputedTiming().iterations === Infinity) continue;
134
+ if (animation.playState !== "finished") return false;
135
+ }
136
+ return true;
137
+ }
138
+ var SlideNumberContext = createContext(null);
139
+ function SlideNumberProvider({
140
+ index,
141
+ total,
142
+ children
143
+ }) {
144
+ const value = useMemo(() => ({ index, total }), [index, total]);
145
+ return /* @__PURE__ */ jsx(SlideNumberContext.Provider, { value, children });
146
+ }
147
+ function useSlideNumber() {
148
+ const ctx = useContext(SlideNumberContext);
149
+ if (!ctx) return { current: 0, total: 0 };
150
+ return { current: ctx.index + 1, total: ctx.total };
151
+ }
152
+
153
+ // src/slide/theme.ts
154
+ function themeToCssVars(theme) {
155
+ if (!theme) return void 0;
156
+ const vars = {};
157
+ if (theme.bg) vars["--slide-bg"] = theme.bg;
158
+ if (theme.fg) vars["--slide-fg"] = theme.fg;
159
+ if (theme.accent) vars["--slide-accent"] = theme.accent;
160
+ if (theme.muted) vars["--slide-muted"] = theme.muted;
161
+ if (theme.fontDisplay) vars["--slide-font-display"] = theme.fontDisplay;
162
+ if (theme.fontBody) vars["--slide-font-body"] = theme.fontBody;
163
+ if (theme.sizeHero !== void 0) vars["--slide-size-hero"] = `${theme.sizeHero}px`;
164
+ if (theme.sizeBody !== void 0) vars["--slide-size-body"] = `${theme.sizeBody}px`;
165
+ if (theme.radius !== void 0) vars["--slide-radius"] = `${theme.radius}px`;
166
+ return Object.keys(vars).length > 0 ? vars : void 0;
167
+ }
168
+
169
+ // src/slide/export-pptx.ts
170
+ var EMU_W = 12192e3;
171
+ var EMU_H = 6858e3;
172
+ var CAPTURE_PIXEL_RATIO = 2;
173
+ var ANIMATION_TIMEOUT_MS = 15e3;
174
+ var POLL_INTERVAL_MS = 100;
175
+ var CAPTURE_CLASS = "vantage-slide-capture";
176
+ var FROZEN_PROPS = ["opacity", "transform", "filter", "clip-path"];
177
+ async function exportDeckAsPptx({
178
+ slides,
179
+ fileName,
180
+ theme,
181
+ onProgress
182
+ }) {
183
+ if (slides.length === 0) return;
184
+ const total = slides.length;
185
+ onProgress?.({ phase: "processing", current: 0, total, percent: 0 });
186
+ const container = document.createElement("div");
187
+ container.className = CAPTURE_CLASS;
188
+ container.setAttribute("aria-hidden", "true");
189
+ Object.assign(container.style, {
190
+ position: "fixed",
191
+ left: "-99999px",
192
+ top: "0",
193
+ pointerEvents: "none"
194
+ });
195
+ document.body.appendChild(container);
196
+ const captureStyle = document.createElement("style");
197
+ captureStyle.textContent = `.${CAPTURE_CLASS} *, .${CAPTURE_CLASS} *::before, .${CAPTURE_CLASS} *::after {
198
+ animation-delay: -1s !important;
199
+ animation-duration: 1ms !important;
200
+ animation-iteration-count: 1 !important;
201
+ animation-fill-mode: forwards !important;
202
+ transition: none !important;
203
+ }`;
204
+ document.head.appendChild(captureStyle);
205
+ const themeVars = themeToCssVars(theme);
206
+ const roots = [];
207
+ const frames = [];
208
+ for (let i = 0; i < slides.length; i++) {
209
+ const slide = slides[i];
210
+ if (!slide) continue;
211
+ const host = document.createElement("div");
212
+ host.setAttribute("data-slide-canvas", "");
213
+ Object.assign(host.style, {
214
+ width: `${CANVAS_WIDTH}px`,
215
+ height: `${CANVAS_HEIGHT}px`,
216
+ overflow: "hidden",
217
+ background: "var(--slide-bg)",
218
+ color: "var(--slide-fg)",
219
+ fontFamily: "var(--slide-font-body)",
220
+ fontSize: "var(--slide-size-body)"
221
+ });
222
+ if (themeVars) {
223
+ for (const [key, value] of Object.entries(themeVars)) {
224
+ host.style.setProperty(key, String(value));
225
+ }
226
+ }
227
+ container.appendChild(host);
228
+ frames.push(host);
229
+ const root = createRoot(host);
230
+ root.render(createElement(SlideNumberProvider, { index: i, total }, slide));
231
+ roots.push(root);
232
+ }
233
+ await nextPaint();
234
+ try {
235
+ await waitForFonts();
236
+ const deadline = performance.now() + ANIMATION_TIMEOUT_MS;
237
+ while (performance.now() < deadline) {
238
+ if (frames.every((frame) => isFrameAnimationSettled(frame))) break;
239
+ await sleep(POLL_INTERVAL_MS);
240
+ }
241
+ await waitForDataWaitfor(container);
242
+ const { toBlob, getFontEmbedCSS } = await import('html-to-image');
243
+ const fontEmbedCSS = await getFontEmbedCSS(container);
244
+ const images = [];
245
+ for (let i = 0; i < frames.length; i++) {
246
+ const frame = frames[i];
247
+ freezeForCapture(frame);
248
+ const blob = await toBlob(frame, {
249
+ width: CANVAS_WIDTH,
250
+ height: CANVAS_HEIGHT,
251
+ pixelRatio: CAPTURE_PIXEL_RATIO,
252
+ fontEmbedCSS,
253
+ cacheBust: true
254
+ });
255
+ if (!blob) throw new Error(`Failed to capture slide ${i + 1}.`);
256
+ images.push(new Uint8Array(await blob.arrayBuffer()));
257
+ onProgress?.({
258
+ phase: "processing",
259
+ current: i + 1,
260
+ total,
261
+ percent: Math.min(95, (i + 1) / total * 95)
262
+ });
263
+ }
264
+ onProgress?.({ phase: "generating", current: total, total, percent: 98 });
265
+ const pptx = await buildImagePptx(images);
266
+ downloadBlob(
267
+ new Blob([pptx], {
268
+ type: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
269
+ }),
270
+ `${fileName}.pptx`
271
+ );
272
+ } finally {
273
+ onProgress?.({ phase: "done", current: total, total, percent: 100 });
274
+ for (const root of roots) root.unmount();
275
+ container.remove();
276
+ captureStyle.remove();
277
+ }
278
+ }
279
+ function freezeForCapture(root) {
280
+ for (const el of root.querySelectorAll("*")) {
281
+ const styles = getComputedStyle(el);
282
+ for (const prop of FROZEN_PROPS) {
283
+ el.style.setProperty(prop, styles.getPropertyValue(prop), "important");
284
+ }
285
+ el.style.setProperty("animation", "none", "important");
286
+ el.style.setProperty("transition", "none", "important");
287
+ }
288
+ }
289
+ var XML_DECL = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
290
+ var REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
291
+ var OD_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
292
+ async function buildImagePptx(images) {
293
+ const { zipSync, strToU8 } = await import('fflate');
294
+ const n = images.length;
295
+ const files = {};
296
+ files["[Content_Types].xml"] = strToU8(contentTypesXml(n));
297
+ files["_rels/.rels"] = strToU8(rootRelsXml());
298
+ files["ppt/presentation.xml"] = strToU8(presentationXml(n));
299
+ files["ppt/_rels/presentation.xml.rels"] = strToU8(presentationRelsXml(n));
300
+ files["ppt/presProps.xml"] = strToU8(presPropsXml());
301
+ files["ppt/theme/theme1.xml"] = strToU8(themeXml());
302
+ files["ppt/slideMasters/slideMaster1.xml"] = strToU8(slideMasterXml());
303
+ files["ppt/slideMasters/_rels/slideMaster1.xml.rels"] = strToU8(slideMasterRelsXml());
304
+ files["ppt/slideLayouts/slideLayout1.xml"] = strToU8(slideLayoutXml());
305
+ files["ppt/slideLayouts/_rels/slideLayout1.xml.rels"] = strToU8(slideLayoutRelsXml());
306
+ for (let i = 0; i < n; i++) {
307
+ const image = images[i];
308
+ if (!image) continue;
309
+ const idx = i + 1;
310
+ files[`ppt/slides/slide${idx}.xml`] = strToU8(slideXml());
311
+ files[`ppt/slides/_rels/slide${idx}.xml.rels`] = strToU8(slideRelsXml(idx));
312
+ files[`ppt/media/image${idx}.png`] = image;
313
+ }
314
+ return zipSync(files);
315
+ }
316
+ function contentTypesXml(n) {
317
+ const slideOverrides = Array.from(
318
+ { length: n },
319
+ (_, i) => `<Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`
320
+ ).join("");
321
+ return `${XML_DECL}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Default Extension="png" ContentType="image/png"/><Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/><Override PartName="/ppt/presProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"/><Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/><Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/><Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>${slideOverrides}</Types>`;
322
+ }
323
+ function rootRelsXml() {
324
+ return `${XML_DECL}<Relationships xmlns="${REL_NS}"><Relationship Id="rId1" Type="${OD_REL}/officeDocument" Target="ppt/presentation.xml"/></Relationships>`;
325
+ }
326
+ function presentationXml(n) {
327
+ const sldIds = Array.from(
328
+ { length: n },
329
+ (_, i) => `<p:sldId id="${256 + i}" r:id="rId${i + 3}"/>`
330
+ ).join("");
331
+ return `${XML_DECL}<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="${OD_REL}" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst><p:sldIdLst>${sldIds}</p:sldIdLst><p:sldSz cx="${EMU_W}" cy="${EMU_H}"/><p:notesSz cx="6858000" cy="9144000"/></p:presentation>`;
332
+ }
333
+ function presentationRelsXml(n) {
334
+ const rels = [
335
+ `<Relationship Id="rId1" Type="${OD_REL}/slideMaster" Target="slideMasters/slideMaster1.xml"/>`,
336
+ `<Relationship Id="rId2" Type="${OD_REL}/presProps" Target="presProps.xml"/>`
337
+ ];
338
+ for (let i = 0; i < n; i++) {
339
+ rels.push(
340
+ `<Relationship Id="rId${i + 3}" Type="${OD_REL}/slide" Target="slides/slide${i + 1}.xml"/>`
341
+ );
342
+ }
343
+ return `${XML_DECL}<Relationships xmlns="${REL_NS}">${rels.join("")}</Relationships>`;
344
+ }
345
+ function presPropsXml() {
346
+ return `${XML_DECL}<p:presentationPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="${OD_REL}" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"/>`;
347
+ }
348
+ function slideMasterXml() {
349
+ return `${XML_DECL}<p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="${OD_REL}" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld><p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/><p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst></p:sldMaster>`;
350
+ }
351
+ function slideMasterRelsXml() {
352
+ return `${XML_DECL}<Relationships xmlns="${REL_NS}"><Relationship Id="rId1" Type="${OD_REL}/slideLayout" Target="../slideLayouts/slideLayout1.xml"/><Relationship Id="rId2" Type="${OD_REL}/theme" Target="../theme/theme1.xml"/></Relationships>`;
353
+ }
354
+ function slideLayoutXml() {
355
+ return `${XML_DECL}<p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="${OD_REL}" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank" preserve="1"><p:cSld name="Blank"><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sldLayout>`;
356
+ }
357
+ function slideLayoutRelsXml() {
358
+ return `${XML_DECL}<Relationships xmlns="${REL_NS}"><Relationship Id="rId1" Type="${OD_REL}/slideMaster" Target="../slideMasters/slideMaster1.xml"/></Relationships>`;
359
+ }
360
+ function slideXml() {
361
+ return `${XML_DECL}<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="${OD_REL}" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr><p:pic><p:nvPicPr><p:cNvPr id="2" name="Slide"/><p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill><a:blip r:embed="rId2"/><a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${EMU_W}" cy="${EMU_H}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic></p:spTree></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sld>`;
362
+ }
363
+ function slideRelsXml(idx) {
364
+ return `${XML_DECL}<Relationships xmlns="${REL_NS}"><Relationship Id="rId1" Type="${OD_REL}/slideLayout" Target="../slideLayouts/slideLayout1.xml"/><Relationship Id="rId2" Type="${OD_REL}/image" Target="../media/image${idx}.png"/></Relationships>`;
365
+ }
366
+ function themeXml() {
367
+ return `${XML_DECL}<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="44546A"/></a:dk2><a:lt2><a:srgbClr val="E7E6E6"/></a:lt2><a:accent1><a:srgbClr val="4472C4"/></a:accent1><a:accent2><a:srgbClr val="ED7D31"/></a:accent2><a:accent3><a:srgbClr val="A5A5A5"/></a:accent3><a:accent4><a:srgbClr val="FFC000"/></a:accent4><a:accent5><a:srgbClr val="5B9BD5"/></a:accent5><a:accent6><a:srgbClr val="70AD47"/></a:accent6><a:hlink><a:srgbClr val="0563C1"/></a:hlink><a:folHlink><a:srgbClr val="954F72"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Calibri Light"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:lumMod val="110000"/><a:satMod val="105000"/><a:tint val="67000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="103000"/><a:tint val="73000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="109000"/><a:tint val="81000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><a:shade val="100000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="99000"/><a:satMod val="120000"/><a:shade val="78000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements></a:theme>`;
368
+ }
369
+
370
+ // src/slide/keys.ts
371
+ function isTypingTarget(target) {
372
+ return target instanceof HTMLElement && (target.isContentEditable || target.matches("input, textarea"));
373
+ }
374
+ function hasModifier(e) {
375
+ return e.altKey || e.ctrlKey || e.metaKey;
376
+ }
377
+ function isForwardKey(e) {
378
+ return e.key === "ArrowRight" || e.key === "ArrowDown" || e.key === " " || e.key === "PageDown";
379
+ }
380
+ function isBackwardKey(e) {
381
+ return e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp";
382
+ }
383
+ var THUMB_WIDTH = 176;
384
+ var THUMB_HEIGHT = Math.round(THUMB_WIDTH * CANVAS_HEIGHT / CANVAS_WIDTH);
385
+ var NUMBER_COLUMN = 20;
386
+ var GUTTER = 10;
387
+ var RAIL_WIDTH = NUMBER_COLUMN + THUMB_WIDTH + GUTTER * 3;
388
+ var ITEM_BUTTON = {
389
+ display: "flex",
390
+ alignItems: "center",
391
+ gap: GUTTER,
392
+ width: "100%",
393
+ padding: 0,
394
+ border: "none",
395
+ background: "transparent",
396
+ color: "inherit",
397
+ font: "inherit",
398
+ cursor: "pointer",
399
+ textAlign: "left"
400
+ };
401
+ function SlideRail({ slides, index, onSelect, themeVars }) {
402
+ const listRef = useRef(null);
403
+ useEffect(() => {
404
+ const item = listRef.current?.children[index];
405
+ item?.scrollIntoView({ block: "nearest" });
406
+ }, [index]);
407
+ return /* @__PURE__ */ jsx(
408
+ "div",
409
+ {
410
+ "data-slide-chrome": "",
411
+ "aria-label": "\u30B9\u30E9\u30A4\u30C9\u4E00\u89A7",
412
+ style: {
413
+ flex: "0 0 auto",
414
+ width: RAIL_WIDTH,
415
+ height: "100%",
416
+ overflowY: "auto",
417
+ overscrollBehavior: "contain",
418
+ background: "color-mix(in oklab, var(--slide-fg) 6%, var(--slide-bg))",
419
+ color: "var(--slide-fg)",
420
+ borderRight: "1px solid color-mix(in oklab, var(--slide-fg) 14%, transparent)",
421
+ fontFamily: 'system-ui, -apple-system, "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, sans-serif',
422
+ userSelect: "none",
423
+ // An embedded deck does not put the theme on its root, so the rail
424
+ // carries it too rather than reading whatever cascaded in.
425
+ ...themeVars
426
+ },
427
+ children: /* @__PURE__ */ jsx(
428
+ "ol",
429
+ {
430
+ ref: listRef,
431
+ style: {
432
+ display: "flex",
433
+ flexDirection: "column",
434
+ gap: GUTTER,
435
+ margin: 0,
436
+ padding: GUTTER,
437
+ listStyle: "none"
438
+ },
439
+ children: slides.map((slide, i) => {
440
+ const active = i === index;
441
+ return /* @__PURE__ */ jsx(
442
+ "li",
443
+ {
444
+ style: {
445
+ contentVisibility: "auto",
446
+ containIntrinsicSize: `${THUMB_HEIGHT}px ${RAIL_WIDTH - GUTTER * 2}px`
447
+ },
448
+ children: /* @__PURE__ */ jsxs(
449
+ "button",
450
+ {
451
+ type: "button",
452
+ style: ITEM_BUTTON,
453
+ onClick: () => onSelect(i),
454
+ "aria-current": active ? "true" : void 0,
455
+ "aria-label": `\u30B9\u30E9\u30A4\u30C9 ${i + 1}`,
456
+ children: [
457
+ /* @__PURE__ */ jsx(
458
+ "span",
459
+ {
460
+ style: {
461
+ flex: `0 0 ${NUMBER_COLUMN}px`,
462
+ textAlign: "right",
463
+ fontSize: 11,
464
+ fontVariantNumeric: "tabular-nums",
465
+ color: active ? "var(--slide-fg)" : "var(--slide-muted)",
466
+ fontWeight: active ? 600 : 400
467
+ },
468
+ children: i + 1
469
+ }
470
+ ),
471
+ /* @__PURE__ */ jsx(
472
+ "span",
473
+ {
474
+ style: {
475
+ position: "relative",
476
+ display: "block",
477
+ width: THUMB_WIDTH,
478
+ height: THUMB_HEIGHT,
479
+ overflow: "hidden",
480
+ borderRadius: 4,
481
+ background: "var(--slide-bg)",
482
+ outline: active ? "2px solid color-mix(in oklab, var(--slide-fg) 55%, transparent)" : "1px solid color-mix(in oklab, var(--slide-fg) 14%, transparent)",
483
+ outlineOffset: active ? 1 : 0,
484
+ // The thumbnail is a picture of a slide, not a live copy:
485
+ // clicks belong to the button, never to the slide's own
486
+ // links and buttons.
487
+ pointerEvents: "none"
488
+ },
489
+ children: /* @__PURE__ */ jsx(SlideNumberProvider, { index: i, total: slides.length, children: /* @__PURE__ */ jsx(SlideCanvas, { scale: THUMB_WIDTH / CANVAS_WIDTH, themeVars, children: slide }) })
490
+ }
491
+ )
492
+ ]
493
+ }
494
+ )
495
+ },
496
+ i
497
+ );
498
+ })
499
+ }
500
+ )
501
+ }
502
+ );
503
+ }
504
+ var BUTTON = {
505
+ display: "inline-flex",
506
+ alignItems: "center",
507
+ justifyContent: "center",
508
+ gap: 6,
509
+ height: 32,
510
+ minWidth: 32,
511
+ padding: "0 8px",
512
+ border: "none",
513
+ borderRadius: 8,
514
+ background: "transparent",
515
+ color: "inherit",
516
+ font: "inherit",
517
+ fontSize: 13,
518
+ cursor: "pointer"
519
+ };
520
+ function SlideToolbar({
521
+ current,
522
+ total,
523
+ onPrev,
524
+ onNext,
525
+ onFullscreen,
526
+ onExport,
527
+ exportProgress,
528
+ railOpen,
529
+ onToggleRail,
530
+ isFullscreen
531
+ }) {
532
+ const [hovered, setHovered] = useState(false);
533
+ const busy = exportProgress !== null && exportProgress.phase !== "done";
534
+ return /* @__PURE__ */ jsxs(
535
+ "div",
536
+ {
537
+ "data-slide-chrome": "",
538
+ onMouseEnter: () => setHovered(true),
539
+ onMouseLeave: () => setHovered(false),
540
+ style: {
541
+ position: "absolute",
542
+ bottom: 16,
543
+ left: "50%",
544
+ transform: "translateX(-50%)",
545
+ display: "flex",
546
+ alignItems: "center",
547
+ gap: 2,
548
+ padding: 4,
549
+ borderRadius: 12,
550
+ background: "rgb(0 0 0 / 0.55)",
551
+ color: "#fff",
552
+ backdropFilter: "blur(8px)",
553
+ opacity: hovered || busy ? 1 : 0.25,
554
+ transition: "opacity 150ms ease",
555
+ fontFamily: 'system-ui, -apple-system, "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, sans-serif',
556
+ userSelect: "none"
557
+ },
558
+ children: [
559
+ /* @__PURE__ */ jsx("button", { type: "button", style: BUTTON, onClick: onPrev, "aria-label": "\u524D\u306E\u30B9\u30E9\u30A4\u30C9", children: /* @__PURE__ */ jsx(ChevronLeftIcon, { size: 16, "aria-hidden": "true" }) }),
560
+ /* @__PURE__ */ jsxs("span", { style: { padding: "0 8px", fontSize: 13, fontVariantNumeric: "tabular-nums" }, children: [
561
+ current,
562
+ " / ",
563
+ total
564
+ ] }),
565
+ /* @__PURE__ */ jsx("button", { type: "button", style: BUTTON, onClick: onNext, "aria-label": "\u6B21\u306E\u30B9\u30E9\u30A4\u30C9", children: /* @__PURE__ */ jsx(ChevronRightIcon, { size: 16, "aria-hidden": "true" }) }),
566
+ isFullscreen ? null : /* @__PURE__ */ jsx(
567
+ "button",
568
+ {
569
+ type: "button",
570
+ style: BUTTON,
571
+ onClick: onToggleRail,
572
+ "aria-label": railOpen ? "\u30B9\u30E9\u30A4\u30C9\u4E00\u89A7\u3092\u9589\u3058\u308B (L)" : "\u30B9\u30E9\u30A4\u30C9\u4E00\u89A7\u3092\u958B\u304F (L)",
573
+ title: railOpen ? "\u30B9\u30E9\u30A4\u30C9\u4E00\u89A7\u3092\u9589\u3058\u308B (L)" : "\u30B9\u30E9\u30A4\u30C9\u4E00\u89A7\u3092\u958B\u304F (L)",
574
+ "aria-pressed": railOpen,
575
+ children: railOpen ? /* @__PURE__ */ jsx(PanelLeftCloseIcon, { size: 16, "aria-hidden": "true" }) : /* @__PURE__ */ jsx(PanelLeftOpenIcon, { size: 16, "aria-hidden": "true" })
576
+ }
577
+ ),
578
+ /* @__PURE__ */ jsx(
579
+ "button",
580
+ {
581
+ type: "button",
582
+ style: BUTTON,
583
+ onClick: onFullscreen,
584
+ "aria-label": isFullscreen ? "\u5168\u753B\u9762\u8868\u793A\u3092\u7D42\u4E86 (F)" : "\u5168\u753B\u9762\u8868\u793A (F)",
585
+ title: isFullscreen ? "\u5168\u753B\u9762\u8868\u793A\u3092\u7D42\u4E86 (F)" : "\u5168\u753B\u9762\u8868\u793A (F)",
586
+ children: isFullscreen ? /* @__PURE__ */ jsx(MinimizeIcon, { size: 16, "aria-hidden": "true" }) : /* @__PURE__ */ jsx(MaximizeIcon, { size: 16, "aria-hidden": "true" })
587
+ }
588
+ ),
589
+ /* @__PURE__ */ jsxs(
590
+ "button",
591
+ {
592
+ type: "button",
593
+ style: { ...BUTTON, opacity: busy ? 0.6 : 1, cursor: busy ? "progress" : "pointer" },
594
+ onClick: onExport,
595
+ disabled: busy,
596
+ "aria-label": "PowerPoint \u3068\u3057\u3066\u66F8\u304D\u51FA\u3059",
597
+ title: "PowerPoint \u3068\u3057\u3066\u66F8\u304D\u51FA\u3059",
598
+ children: [
599
+ /* @__PURE__ */ jsx(DownloadIcon, { size: 16, "aria-hidden": "true" }),
600
+ busy ? /* @__PURE__ */ jsxs("span", { children: [
601
+ Math.round(exportProgress.percent),
602
+ "%"
603
+ ] }) : null
604
+ ]
605
+ }
606
+ )
607
+ ]
608
+ }
609
+ );
610
+ }
611
+ var QUERY = "(prefers-reduced-motion: reduce)";
612
+ function matches() {
613
+ if (typeof window === "undefined") return false;
614
+ return window.matchMedia(QUERY).matches;
615
+ }
616
+ function usePrefersReducedMotion() {
617
+ const [reduced, setReduced] = useState(matches);
618
+ useEffect(() => {
619
+ const mql = window.matchMedia(QUERY);
620
+ setReduced(mql.matches);
621
+ const onChange = (e) => setReduced(e.matches);
622
+ mql.addEventListener("change", onChange);
623
+ return () => mql.removeEventListener("change", onChange);
624
+ }, []);
625
+ return reduced;
626
+ }
627
+ var StepHostContext = createContext(null);
628
+ function StepHost({
629
+ isActiveSlide,
630
+ entryDirection,
631
+ controllerRef,
632
+ children
633
+ }) {
634
+ const registrations = useRef([]);
635
+ const composite = useMemo(
636
+ () => ({
637
+ advance: () => {
638
+ for (const r of registrations.current) {
639
+ if (r.controller.advance()) return true;
640
+ }
641
+ return false;
642
+ },
643
+ retreat: () => {
644
+ for (let i = registrations.current.length - 1; i >= 0; i--) {
645
+ if (registrations.current[i].controller.retreat()) return true;
646
+ }
647
+ return false;
648
+ }
649
+ }),
650
+ []
651
+ );
652
+ useLayoutEffect(() => {
653
+ if (!isActiveSlide) return;
654
+ controllerRef.current = composite;
655
+ return () => {
656
+ if (controllerRef.current === composite) controllerRef.current = null;
657
+ };
658
+ }, [isActiveSlide, composite, controllerRef]);
659
+ const value = useMemo(
660
+ () => ({
661
+ register: (reg) => {
662
+ registrations.current.push(reg);
663
+ return () => {
664
+ const i = registrations.current.indexOf(reg);
665
+ if (i !== -1) registrations.current.splice(i, 1);
666
+ };
667
+ },
668
+ entryDirection,
669
+ isActiveSlide
670
+ }),
671
+ [entryDirection, isActiveSlide]
672
+ );
673
+ return /* @__PURE__ */ jsx(StepHostContext.Provider, { value, children });
674
+ }
675
+ function useIsActiveSlide() {
676
+ return useContext(StepHostContext)?.isActiveSlide ?? false;
677
+ }
678
+ function Steps({ children }) {
679
+ const host = useContext(StepHostContext);
680
+ const flat = Children.toArray(children);
681
+ const stepCount = flat.filter((c) => isValidElement(c) && c.type === Step).length;
682
+ const initial = host?.entryDirection === "forward" ? 0 : stepCount;
683
+ const revealedRef = useRef(initial);
684
+ const [revealed, setRevealed] = useState(initial);
685
+ const idRef = useRef({});
686
+ const applyRevealed = useCallback((n) => {
687
+ revealedRef.current = n;
688
+ setRevealed(n);
689
+ }, []);
690
+ useLayoutEffect(() => {
691
+ if (!host) return;
692
+ const controller = {
693
+ advance: () => {
694
+ if (revealedRef.current >= stepCount) return false;
695
+ applyRevealed(revealedRef.current + 1);
696
+ return true;
697
+ },
698
+ retreat: () => {
699
+ if (revealedRef.current <= 0) return false;
700
+ applyRevealed(revealedRef.current - 1);
701
+ return true;
702
+ }
703
+ };
704
+ return host.register({
705
+ id: idRef.current,
706
+ stepCount,
707
+ initialRevealed: revealedRef.current,
708
+ controller,
709
+ setRevealed: applyRevealed
710
+ });
711
+ }, [host, stepCount, applyRevealed]);
712
+ const effective = host ? revealed : stepCount;
713
+ let stepIndex = 0;
714
+ return /* @__PURE__ */ jsx(Fragment, { children: flat.map((child, key) => {
715
+ if (isValidElement(child) && child.type === Step) {
716
+ const index = stepIndex++;
717
+ return cloneElement(child, {
718
+ key: child.key ?? key,
719
+ _revealed: index < effective
720
+ });
721
+ }
722
+ return child;
723
+ }) });
724
+ }
725
+ function Step({ children, duration = 180, _revealed }) {
726
+ const reduceMotion = usePrefersReducedMotion();
727
+ const revealed = _revealed ?? true;
728
+ const ms = reduceMotion ? 0 : duration;
729
+ return /* @__PURE__ */ jsx(
730
+ "div",
731
+ {
732
+ "data-slide-step": revealed ? "revealed" : "pending",
733
+ style: {
734
+ opacity: revealed ? 1 : 0,
735
+ // `visibility` as well as opacity so a pending step is out of the
736
+ // accessibility tree and cannot be tabbed into.
737
+ visibility: revealed ? "visible" : "hidden",
738
+ transition: `opacity ${ms}ms cubic-bezier(0, 0, 0.2, 1)`
739
+ },
740
+ children
741
+ }
742
+ );
743
+ }
744
+ var DEFAULT_EASING = "cubic-bezier(.4, 0, .2, 1)";
745
+ var MORPH_SELECTOR = "[data-slide-morph]";
746
+ var TEXT_FILL_COLOR_PROPERTY = "-webkit-text-fill-color";
747
+ var MORPH_VISUAL_PROPERTIES = [
748
+ "opacity",
749
+ "color",
750
+ "background-color",
751
+ "border-top-color",
752
+ "border-right-color",
753
+ "border-bottom-color",
754
+ "border-left-color",
755
+ "outline-color",
756
+ "text-decoration-color",
757
+ TEXT_FILL_COLOR_PROPERTY,
758
+ "-webkit-text-stroke-color",
759
+ "fill",
760
+ "stroke"
761
+ ];
762
+ var BORDER_COLOR_PROPERTIES = [
763
+ "border-top-color",
764
+ "border-right-color",
765
+ "border-bottom-color",
766
+ "border-left-color"
767
+ ];
768
+ var BORDER_SIDE_PROPERTIES = [
769
+ {
770
+ width: "borderTopWidth",
771
+ style: "borderTopStyle",
772
+ color: "borderTopColor",
773
+ cssWidth: "border-top-width",
774
+ cssStyle: "border-top-style",
775
+ cssColor: "border-top-color"
776
+ },
777
+ {
778
+ width: "borderRightWidth",
779
+ style: "borderRightStyle",
780
+ color: "borderRightColor",
781
+ cssWidth: "border-right-width",
782
+ cssStyle: "border-right-style",
783
+ cssColor: "border-right-color"
784
+ },
785
+ {
786
+ width: "borderBottomWidth",
787
+ style: "borderBottomStyle",
788
+ color: "borderBottomColor",
789
+ cssWidth: "border-bottom-width",
790
+ cssStyle: "border-bottom-style",
791
+ cssColor: "border-bottom-color"
792
+ },
793
+ {
794
+ width: "borderLeftWidth",
795
+ style: "borderLeftStyle",
796
+ color: "borderLeftColor",
797
+ cssWidth: "border-left-width",
798
+ cssStyle: "border-left-style",
799
+ cssColor: "border-left-color"
800
+ }
801
+ ];
802
+ var INHERITED_VISUAL_PROPERTIES = /* @__PURE__ */ new Set([
803
+ "color",
804
+ "text-decoration-color",
805
+ TEXT_FILL_COLOR_PROPERTY,
806
+ "-webkit-text-stroke-color",
807
+ "fill",
808
+ "stroke"
809
+ ]);
810
+ function runPhase(el, phase, fallbackDuration, fallbackEasing) {
811
+ if (!phase) return null;
812
+ return el.animate(phase.keyframes, {
813
+ duration: phase.duration ?? fallbackDuration,
814
+ easing: phase.easing ?? fallbackEasing,
815
+ delay: phase.delay ?? 0,
816
+ fill: "both"
817
+ });
818
+ }
819
+ function resolveMorph(morph, fallbackDuration, fallbackEasing) {
820
+ if (!morph) return null;
821
+ if (morph === true) return { duration: fallbackDuration, easing: fallbackEasing, delay: 0 };
822
+ return {
823
+ duration: morph.duration ?? fallbackDuration,
824
+ easing: morph.easing ?? fallbackEasing,
825
+ delay: morph.delay ?? 0
826
+ };
827
+ }
828
+ function localTransform(styles) {
829
+ return styles.transform && styles.transform !== "none" ? styles.transform : "";
830
+ }
831
+ function measureUntransformedClientRect(el) {
832
+ const styles = getComputedStyle(el);
833
+ if (!localTransform(styles)) return el.getBoundingClientRect();
834
+ const value = el.style.getPropertyValue("transform");
835
+ const priority = el.style.getPropertyPriority("transform");
836
+ el.style.setProperty("transform", "none", "important");
837
+ try {
838
+ return el.getBoundingClientRect();
839
+ } finally {
840
+ if (value) el.style.setProperty("transform", value, priority);
841
+ else el.style.removeProperty("transform");
842
+ }
843
+ }
844
+ function measureLocalRect(el, wrapper, wrapperRect) {
845
+ const rect = measureUntransformedClientRect(el);
846
+ const scaleX = wrapperRect.width / (wrapper.offsetWidth || wrapperRect.width || 1) || 1;
847
+ const scaleY = wrapperRect.height / (wrapper.offsetHeight || wrapperRect.height || 1) || 1;
848
+ return {
849
+ left: (rect.left - wrapperRect.left) / scaleX,
850
+ top: (rect.top - wrapperRect.top) / scaleY,
851
+ width: rect.width / scaleX,
852
+ height: rect.height / scaleY
853
+ };
854
+ }
855
+ function hasUsableRect(rect) {
856
+ return rect.width > 0 && rect.height > 0;
857
+ }
858
+ function collectMorphElements(root) {
859
+ const elements = /* @__PURE__ */ new Map();
860
+ for (const el of root.querySelectorAll(MORPH_SELECTOR)) {
861
+ const id = el.dataset.slideMorph;
862
+ if (id && !elements.has(id)) elements.set(id, el);
863
+ }
864
+ return elements;
865
+ }
866
+ function shouldSkipCopiedStyle(source, styles, prop, preserveInherited) {
867
+ if (prop === TEXT_FILL_COLOR_PROPERTY && styles.getPropertyValue(prop) === styles.color) {
868
+ return true;
869
+ }
870
+ if (!preserveInherited || !INHERITED_VISUAL_PROPERTIES.has(prop)) return false;
871
+ const parent = source.parentElement;
872
+ if (!parent) return false;
873
+ return styles.getPropertyValue(prop) === getComputedStyle(parent).getPropertyValue(prop);
874
+ }
875
+ function copyComputedStyles(source, target, preserveInherited = false) {
876
+ const styles = getComputedStyle(source);
877
+ for (let i = 0; i < styles.length; i++) {
878
+ const prop = styles[i];
879
+ if (shouldSkipCopiedStyle(source, styles, prop, preserveInherited)) continue;
880
+ target.style.setProperty(prop, styles.getPropertyValue(prop), styles.getPropertyPriority(prop));
881
+ }
882
+ const sourceChildren = source.querySelectorAll("*");
883
+ const targetChildren = target.querySelectorAll("*");
884
+ for (let i = 0; i < sourceChildren.length; i++) {
885
+ const child = targetChildren[i];
886
+ if (!child) continue;
887
+ copyComputedStyles(sourceChildren[i], child, true);
888
+ }
889
+ }
890
+ function cloneMorphElement(source) {
891
+ const clone = source.cloneNode(true);
892
+ copyComputedStyles(source, clone);
893
+ clone.removeAttribute("data-slide-morph");
894
+ return clone;
895
+ }
896
+ function parsePx(value) {
897
+ const match = value.trim().match(/^(-?\d*\.?\d+)px$/);
898
+ if (!match) return null;
899
+ const n = Number.parseFloat(match[1]);
900
+ return Number.isFinite(n) ? n : null;
901
+ }
902
+ function scaleRadius(value, scaleX, scaleY) {
903
+ const parts = value.trim().split(/\s+/);
904
+ const x = parsePx(parts[0] ?? "");
905
+ const y = parsePx(parts[1] ?? parts[0] ?? "");
906
+ if (x === null || y === null) return value;
907
+ const nextX = x / scaleX;
908
+ const nextY = y / scaleY;
909
+ return Math.abs(nextX - nextY) < 1e-3 ? `${nextX}px` : `${nextX}px ${nextY}px`;
910
+ }
911
+ function radiusKeyframe(styles, scaleX = 1, scaleY = 1) {
912
+ return {
913
+ borderTopLeftRadius: scaleRadius(styles.borderTopLeftRadius, scaleX, scaleY),
914
+ borderTopRightRadius: scaleRadius(styles.borderTopRightRadius, scaleX, scaleY),
915
+ borderBottomRightRadius: scaleRadius(styles.borderBottomRightRadius, scaleX, scaleY),
916
+ borderBottomLeftRadius: scaleRadius(styles.borderBottomLeftRadius, scaleX, scaleY)
917
+ };
918
+ }
919
+ function morphTransform(rect, scaleX, scaleY, local) {
920
+ return [`translate(${rect.left}px, ${rect.top}px)`, `scale(${scaleX}, ${scaleY})`, local].filter(Boolean).join(" ");
921
+ }
922
+ function morphTranslateTransform(rect, local) {
923
+ return [`translate(${rect.left}px, ${rect.top}px)`, local].filter(Boolean).join(" ");
924
+ }
925
+ function keyframeProperty(css) {
926
+ return css.replace(/^-/, "").replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
927
+ }
928
+ function visualStyleKeyframe(styles, opacity, options = {}) {
929
+ const frame = {};
930
+ for (const prop of MORPH_VISUAL_PROPERTIES) {
931
+ if (options.includeBorderColors === false && BORDER_COLOR_PROPERTIES.includes(prop)) {
932
+ continue;
933
+ }
934
+ const value = styles.getPropertyValue(prop);
935
+ if (prop === TEXT_FILL_COLOR_PROPERTY && value === styles.color) continue;
936
+ if (value) frame[keyframeProperty(prop)] = value;
937
+ }
938
+ if (opacity !== void 0) frame.opacity = opacity;
939
+ return frame;
940
+ }
941
+ function hideBorderColors(el) {
942
+ for (const { color } of BORDER_SIDE_PROPERTIES) {
943
+ el.style[color] = "transparent";
944
+ }
945
+ }
946
+ function isVisibleBorderSide(styles, side) {
947
+ const width = parsePx(styles.getPropertyValue(side.cssWidth)) ?? 0;
948
+ const borderStyle = styles.getPropertyValue(side.cssStyle);
949
+ return width > 0 && borderStyle !== "none" && borderStyle !== "hidden";
950
+ }
951
+ function hasVisibleBorder(styles) {
952
+ return BORDER_SIDE_PROPERTIES.some((side) => isVisibleBorderSide(styles, side));
953
+ }
954
+ function borderFrameKeyframe(rect, styles, opacity) {
955
+ const frame = {
956
+ width: `${rect.width}px`,
957
+ height: `${rect.height}px`,
958
+ opacity,
959
+ transform: morphTranslateTransform(rect, localTransform(styles)),
960
+ ...radiusKeyframe(styles)
961
+ };
962
+ for (const side of BORDER_SIDE_PROPERTIES) {
963
+ frame[side.width] = styles.getPropertyValue(side.cssWidth);
964
+ frame[side.color] = styles.getPropertyValue(side.cssColor);
965
+ }
966
+ return frame;
967
+ }
968
+ function appendBorderFrame(wrapper, overlay, sourceStyles, targetStyles, rect) {
969
+ if (!overlay.parentElement) wrapper.appendChild(overlay);
970
+ const frame = document.createElement("div");
971
+ Object.assign(frame.style, {
972
+ position: "absolute",
973
+ left: "0",
974
+ top: "0",
975
+ width: `${rect.width}px`,
976
+ height: `${rect.height}px`,
977
+ margin: "0",
978
+ transformOrigin: "top left",
979
+ pointerEvents: "none",
980
+ boxSizing: "border-box",
981
+ background: "transparent"
982
+ });
983
+ for (const side of BORDER_SIDE_PROPERTIES) {
984
+ const styles = isVisibleBorderSide(sourceStyles, side) ? sourceStyles : targetStyles;
985
+ frame.style[side.style] = styles.getPropertyValue(side.cssStyle);
986
+ frame.style[side.width] = sourceStyles.getPropertyValue(side.cssWidth);
987
+ frame.style[side.color] = sourceStyles.getPropertyValue(side.cssColor);
988
+ }
989
+ overlay.appendChild(frame);
990
+ return frame;
991
+ }
992
+ function hasVisualStyleChange(from, to) {
993
+ for (const prop of MORPH_VISUAL_PROPERTIES) {
994
+ if (from.getPropertyValue(prop) !== to.getPropertyValue(prop)) return true;
995
+ }
996
+ return false;
997
+ }
998
+ function effectiveOpacity(el, boundary) {
999
+ let opacity = 1;
1000
+ let node = el;
1001
+ while (node && node !== boundary) {
1002
+ const value = Number.parseFloat(getComputedStyle(node).opacity);
1003
+ if (Number.isFinite(value)) opacity *= value;
1004
+ node = node.parentElement;
1005
+ }
1006
+ return String(opacity);
1007
+ }
1008
+ function hideOriginal(el) {
1009
+ const value = el.style.getPropertyValue("visibility");
1010
+ const priority = el.style.getPropertyPriority("visibility");
1011
+ el.style.setProperty("visibility", "hidden", "important");
1012
+ return () => {
1013
+ if (value) el.style.setProperty("visibility", value, priority);
1014
+ else el.style.removeProperty("visibility");
1015
+ };
1016
+ }
1017
+ function appendPositionedClone(wrapper, overlay, source, rect) {
1018
+ if (!overlay.parentElement) wrapper.appendChild(overlay);
1019
+ const clone = cloneMorphElement(source);
1020
+ Object.assign(clone.style, {
1021
+ position: "absolute",
1022
+ left: "0",
1023
+ top: "0",
1024
+ width: `${rect.width}px`,
1025
+ height: `${rect.height}px`,
1026
+ margin: "0",
1027
+ transformOrigin: "top left",
1028
+ pointerEvents: "none",
1029
+ boxSizing: "border-box"
1030
+ });
1031
+ overlay.appendChild(clone);
1032
+ return clone;
1033
+ }
1034
+ function morphOptions(phase) {
1035
+ return { duration: phase.duration, easing: phase.easing, delay: phase.delay, fill: "both" };
1036
+ }
1037
+ function runDescendantVisualStyleTransitions(clone, source, target, phase) {
1038
+ const animations = [];
1039
+ const cloneChildren = clone.querySelectorAll("*");
1040
+ const sourceChildren = source.querySelectorAll("*");
1041
+ const targetChildren = target.querySelectorAll("*");
1042
+ for (let i = 0; i < sourceChildren.length; i++) {
1043
+ const cloneChild = cloneChildren[i];
1044
+ const targetChild = targetChildren[i];
1045
+ if (!cloneChild || !targetChild) continue;
1046
+ const sourceStyles = getComputedStyle(sourceChildren[i]);
1047
+ const targetStyles = getComputedStyle(targetChild);
1048
+ if (!hasVisualStyleChange(sourceStyles, targetStyles)) continue;
1049
+ animations.push(
1050
+ cloneChild.animate(
1051
+ [visualStyleKeyframe(sourceStyles), visualStyleKeyframe(targetStyles)],
1052
+ morphOptions(phase)
1053
+ )
1054
+ );
1055
+ }
1056
+ return animations;
1057
+ }
1058
+ function runStationaryMorph(clone, rect, styles, fromOpacity, toOpacity, phase) {
1059
+ const transform = morphTransform(rect, 1, 1, localTransform(styles));
1060
+ return clone.animate(
1061
+ [
1062
+ { ...visualStyleKeyframe(styles, fromOpacity), ...radiusKeyframe(styles), transform },
1063
+ { ...visualStyleKeyframe(styles, toOpacity), ...radiusKeyframe(styles), transform }
1064
+ ],
1065
+ morphOptions(phase)
1066
+ );
1067
+ }
1068
+ function runMorphTransition(wrapper, outgoingLayer, incomingLayer, phase) {
1069
+ const wrapperRect = wrapper.getBoundingClientRect();
1070
+ if (wrapperRect.width === 0 || wrapperRect.height === 0) {
1071
+ return { animations: [], cleanup: () => {
1072
+ } };
1073
+ }
1074
+ const incoming = collectMorphElements(incomingLayer);
1075
+ const outgoing = collectMorphElements(outgoingLayer);
1076
+ const overlay = document.createElement("div");
1077
+ overlay.setAttribute("data-slide-morph-layer", "");
1078
+ Object.assign(overlay.style, {
1079
+ position: "absolute",
1080
+ inset: "0",
1081
+ zIndex: "2147483647",
1082
+ pointerEvents: "none"
1083
+ });
1084
+ const animations = [];
1085
+ const restore = [];
1086
+ const fadeOutSource = (source, from) => {
1087
+ const clone = appendPositionedClone(wrapper, overlay, source, from);
1088
+ restore.push(hideOriginal(source));
1089
+ animations.push(
1090
+ runStationaryMorph(
1091
+ clone,
1092
+ from,
1093
+ getComputedStyle(source),
1094
+ effectiveOpacity(source, outgoingLayer),
1095
+ "0",
1096
+ phase
1097
+ )
1098
+ );
1099
+ };
1100
+ const fadeInTarget = (target, to) => {
1101
+ const clone = appendPositionedClone(wrapper, overlay, target, to);
1102
+ restore.push(hideOriginal(target));
1103
+ animations.push(
1104
+ runStationaryMorph(
1105
+ clone,
1106
+ to,
1107
+ getComputedStyle(target),
1108
+ "0",
1109
+ effectiveOpacity(target, incomingLayer),
1110
+ phase
1111
+ )
1112
+ );
1113
+ };
1114
+ for (const [id, source] of outgoing) {
1115
+ const target = incoming.get(id);
1116
+ const from = measureLocalRect(source, wrapper, wrapperRect);
1117
+ if (!hasUsableRect(from)) {
1118
+ if (target) {
1119
+ const to2 = measureLocalRect(target, wrapper, wrapperRect);
1120
+ if (hasUsableRect(to2)) fadeInTarget(target, to2);
1121
+ }
1122
+ continue;
1123
+ }
1124
+ if (!target) {
1125
+ fadeOutSource(source, from);
1126
+ continue;
1127
+ }
1128
+ const to = measureLocalRect(target, wrapper, wrapperRect);
1129
+ if (!hasUsableRect(to)) {
1130
+ fadeOutSource(source, from);
1131
+ continue;
1132
+ }
1133
+ const clone = appendPositionedClone(wrapper, overlay, source, from);
1134
+ restore.push(hideOriginal(source), hideOriginal(target));
1135
+ const sourceStyles = getComputedStyle(source);
1136
+ const targetStyles = getComputedStyle(target);
1137
+ const fromOpacity = effectiveOpacity(source, outgoingLayer);
1138
+ const toOpacity = effectiveOpacity(target, incomingLayer);
1139
+ const needsBorderFrame = hasVisibleBorder(sourceStyles) || hasVisibleBorder(targetStyles);
1140
+ const scaleX = to.width / from.width;
1141
+ const scaleY = to.height / from.height;
1142
+ if (needsBorderFrame) {
1143
+ hideBorderColors(clone);
1144
+ const borderFrame = appendBorderFrame(wrapper, overlay, sourceStyles, targetStyles, from);
1145
+ animations.push(
1146
+ borderFrame.animate(
1147
+ [
1148
+ borderFrameKeyframe(from, sourceStyles, fromOpacity),
1149
+ borderFrameKeyframe(to, targetStyles, toOpacity)
1150
+ ],
1151
+ morphOptions(phase)
1152
+ )
1153
+ );
1154
+ }
1155
+ animations.push(
1156
+ clone.animate(
1157
+ [
1158
+ {
1159
+ ...visualStyleKeyframe(sourceStyles, fromOpacity, {
1160
+ includeBorderColors: !needsBorderFrame
1161
+ }),
1162
+ ...radiusKeyframe(sourceStyles),
1163
+ transform: morphTransform(from, 1, 1, localTransform(sourceStyles))
1164
+ },
1165
+ {
1166
+ ...visualStyleKeyframe(targetStyles, toOpacity, {
1167
+ includeBorderColors: !needsBorderFrame
1168
+ }),
1169
+ ...radiusKeyframe(targetStyles, scaleX, scaleY),
1170
+ transform: morphTransform(to, scaleX, scaleY, localTransform(targetStyles))
1171
+ }
1172
+ ],
1173
+ morphOptions(phase)
1174
+ ),
1175
+ ...runDescendantVisualStyleTransitions(clone, source, target, phase)
1176
+ );
1177
+ }
1178
+ for (const [id, target] of incoming) {
1179
+ if (outgoing.has(id)) continue;
1180
+ const to = measureLocalRect(target, wrapper, wrapperRect);
1181
+ if (!hasUsableRect(to)) continue;
1182
+ fadeInTarget(target, to);
1183
+ }
1184
+ if (animations.length === 0) overlay.remove();
1185
+ let cleaned = false;
1186
+ return {
1187
+ animations,
1188
+ cleanup: () => {
1189
+ if (cleaned) return;
1190
+ cleaned = true;
1191
+ for (const fn of restore) fn();
1192
+ overlay.remove();
1193
+ }
1194
+ };
1195
+ }
1196
+ function SlideTransitionLayer({
1197
+ slides,
1198
+ transitions,
1199
+ index,
1200
+ disabled,
1201
+ stepControllerRef,
1202
+ entryDirection = "jump"
1203
+ }) {
1204
+ const [current, setCurrent] = useState(index);
1205
+ const [outgoing, setOutgoing] = useState(null);
1206
+ const [direction, setDirection] = useState("forward");
1207
+ const wrapperRef = useRef(null);
1208
+ const outgoingLayerRef = useRef(null);
1209
+ const incomingLayerRef = useRef(null);
1210
+ const animationsRef = useRef([]);
1211
+ const cleanupRef = useRef(null);
1212
+ const currentRef = useRef(current);
1213
+ currentRef.current = current;
1214
+ useEffect(() => {
1215
+ if (index === currentRef.current) return;
1216
+ const prev = currentRef.current;
1217
+ const next = index;
1218
+ for (const animation of animationsRef.current) {
1219
+ try {
1220
+ animation.cancel();
1221
+ } catch {
1222
+ }
1223
+ }
1224
+ animationsRef.current = [];
1225
+ cleanupRef.current?.();
1226
+ cleanupRef.current = null;
1227
+ if (disabled || !transitions[next]) {
1228
+ setCurrent(next);
1229
+ setOutgoing(null);
1230
+ return;
1231
+ }
1232
+ setDirection(next > prev ? "forward" : "backward");
1233
+ setOutgoing(prev);
1234
+ setCurrent(next);
1235
+ }, [index, transitions, disabled]);
1236
+ useLayoutEffect(() => {
1237
+ if (outgoing === null) return;
1238
+ const transition = transitions[current];
1239
+ const wrapper = wrapperRef.current;
1240
+ const out = outgoingLayerRef.current;
1241
+ const inc = incomingLayerRef.current;
1242
+ if (!transition || !wrapper || !out || !inc) {
1243
+ setOutgoing(null);
1244
+ return;
1245
+ }
1246
+ wrapper.dataset.slideDir = direction;
1247
+ wrapper.style.setProperty("--slide-dir", direction === "forward" ? "1" : "-1");
1248
+ const easing = transition.easing ?? DEFAULT_EASING;
1249
+ const duration = transition.duration;
1250
+ const morphPhase = resolveMorph(transition.morph, duration, easing);
1251
+ const morphRun = morphPhase ? runMorphTransition(wrapper, out, inc, morphPhase) : null;
1252
+ const animations = [];
1253
+ const exitAnimation = runPhase(out, transition.exit, duration, easing);
1254
+ const enterAnimation = runPhase(inc, transition.enter, duration, easing);
1255
+ if (exitAnimation) animations.push(exitAnimation);
1256
+ if (enterAnimation) animations.push(enterAnimation);
1257
+ const cleanups = [];
1258
+ if (morphRun) {
1259
+ animations.push(...morphRun.animations);
1260
+ cleanups.push(morphRun.cleanup);
1261
+ if (!exitAnimation && morphRun.animations.length > 0) cleanups.push(hideOriginal(out));
1262
+ }
1263
+ animationsRef.current = animations;
1264
+ if (animations.length === 0) {
1265
+ for (const fn of cleanups) fn();
1266
+ setOutgoing(null);
1267
+ return;
1268
+ }
1269
+ let cleaned = false;
1270
+ const cleanup = () => {
1271
+ if (cleaned) return;
1272
+ cleaned = true;
1273
+ for (const fn of cleanups) fn();
1274
+ if (cleanupRef.current === cleanup) cleanupRef.current = null;
1275
+ };
1276
+ cleanupRef.current = cleanup;
1277
+ let cancelled = false;
1278
+ Promise.all(animations.map((a) => a.finished)).then(() => {
1279
+ if (cancelled) return;
1280
+ cleanup();
1281
+ animationsRef.current = [];
1282
+ setOutgoing(null);
1283
+ }).catch(() => {
1284
+ });
1285
+ return () => {
1286
+ cancelled = true;
1287
+ };
1288
+ }, [outgoing, current, direction, transitions]);
1289
+ useEffect(() => {
1290
+ return () => {
1291
+ for (const animation of animationsRef.current) {
1292
+ try {
1293
+ animation.cancel();
1294
+ } catch {
1295
+ }
1296
+ }
1297
+ animationsRef.current = [];
1298
+ cleanupRef.current?.();
1299
+ cleanupRef.current = null;
1300
+ };
1301
+ }, []);
1302
+ const total = slides.length;
1303
+ const currentSlide = slides[current];
1304
+ const outgoingSlide = outgoing !== null ? slides[outgoing] : null;
1305
+ const outgoingEntryDirection = entryDirection === "backward" ? "forward" : "backward";
1306
+ const fallbackControllerRef = useRef(null);
1307
+ const controllerRef = stepControllerRef ?? fallbackControllerRef;
1308
+ return /* @__PURE__ */ jsxs(
1309
+ "div",
1310
+ {
1311
+ ref: wrapperRef,
1312
+ style: {
1313
+ position: "relative",
1314
+ height: "100%",
1315
+ width: "100%",
1316
+ overflow: "hidden",
1317
+ background: "var(--slide-bg)"
1318
+ },
1319
+ children: [
1320
+ outgoingSlide && outgoing !== null ? /* @__PURE__ */ jsx("div", { ref: outgoingLayerRef, style: { position: "absolute", inset: 0 }, children: /* @__PURE__ */ jsx(SlideNumberProvider, { index: outgoing, total, children: /* @__PURE__ */ jsx(
1321
+ StepHost,
1322
+ {
1323
+ isActiveSlide: false,
1324
+ entryDirection: outgoingEntryDirection,
1325
+ controllerRef,
1326
+ children: outgoingSlide
1327
+ }
1328
+ ) }) }) : null,
1329
+ currentSlide ? /* @__PURE__ */ jsx("div", { ref: incomingLayerRef, style: { position: "absolute", inset: 0 }, children: /* @__PURE__ */ jsx(SlideNumberProvider, { index: current, total, children: /* @__PURE__ */ jsx(StepHost, { isActiveSlide: true, entryDirection, controllerRef, children: currentSlide }) }) }) : null
1330
+ ]
1331
+ }
1332
+ );
1333
+ }
1334
+ var PASSTHROUGH = 'a, button, input, textarea, select, label, summary, iframe, video, audio, embed, object, [role="button"], [role="link"], [contenteditable="true"], [data-slide-interactive], [data-slide-chrome]';
1335
+ function useClickNav({
1336
+ ref,
1337
+ enabled = true,
1338
+ edgeRatio = 0.3,
1339
+ canPrev,
1340
+ canNext,
1341
+ onPrev,
1342
+ onNext
1343
+ }) {
1344
+ useEffect(() => {
1345
+ const el = ref.current;
1346
+ if (!el || !enabled) return;
1347
+ const onClick = (event) => {
1348
+ if (event.button !== 0 || event.defaultPrevented) return;
1349
+ const target = event.target;
1350
+ if (target instanceof Element && target.closest(PASSTHROUGH)) return;
1351
+ if (window.getSelection()?.toString()) return;
1352
+ const rect = el.getBoundingClientRect();
1353
+ if (rect.width === 0) return;
1354
+ const x = (event.clientX - rect.left) / rect.width;
1355
+ if (x < edgeRatio) {
1356
+ onPrev();
1357
+ } else if (x > 1 - edgeRatio) {
1358
+ onNext();
1359
+ }
1360
+ };
1361
+ el.addEventListener("click", onClick);
1362
+ return () => el.removeEventListener("click", onClick);
1363
+ }, [ref, enabled, edgeRatio, canPrev, canNext, onPrev, onNext]);
1364
+ }
1365
+ function Slide({ children }) {
1366
+ return /* @__PURE__ */ jsx(Fragment, { children });
1367
+ }
1368
+ Slide.displayName = "Slide";
1369
+ function collectSlides(children) {
1370
+ const slides = [];
1371
+ const transitions = [];
1372
+ for (const child of Children.toArray(children)) {
1373
+ if (!isValidElement(child) || child.type !== Slide) continue;
1374
+ slides.push(child);
1375
+ transitions.push(child.props.transition);
1376
+ }
1377
+ return { slides, transitions };
1378
+ }
1379
+ function defaultExportName() {
1380
+ if (typeof window === "undefined") return "slides";
1381
+ const parts = window.location.pathname.split("/").filter(Boolean);
1382
+ return parts[parts.length - 1] ?? "slides";
1383
+ }
1384
+ function SlideDeck({
1385
+ theme,
1386
+ embedded = false,
1387
+ exportName,
1388
+ // Reads `embedded` from earlier in this same pattern, which is why the two
1389
+ // must stay in this order.
1390
+ rail = !embedded,
1391
+ children
1392
+ }) {
1393
+ const { slides, transitions } = useMemo(() => collectSlides(children), [children]);
1394
+ const total = slides.length;
1395
+ const [page, setPage] = useSearchState("p", 1);
1396
+ const index = Math.min(Math.max(Math.floor(page) - 1, 0), Math.max(total - 1, 0));
1397
+ const [railOpen, setRailOpen] = useSearchState("rail", rail);
1398
+ const [isFullscreen, setIsFullscreen] = useState(false);
1399
+ const [entryDirection, setEntryDirection] = useState("jump");
1400
+ const [exporting, setExporting] = useState(null);
1401
+ const stepControllerRef = useRef(null);
1402
+ const viewportRef = useRef(null);
1403
+ const stageRef = useRef(null);
1404
+ const reduceMotion = usePrefersReducedMotion();
1405
+ const goTo = useCallback(
1406
+ (next, direction) => {
1407
+ const clamped = Math.min(Math.max(next, 0), Math.max(total - 1, 0));
1408
+ setEntryDirection(direction);
1409
+ setPage(clamped + 1);
1410
+ },
1411
+ [setPage, total]
1412
+ );
1413
+ const goNext = useCallback(() => {
1414
+ if (stepControllerRef.current?.advance()) return;
1415
+ if (index < total - 1) goTo(index + 1, "forward");
1416
+ }, [goTo, index, total]);
1417
+ const goPrev = useCallback(() => {
1418
+ if (stepControllerRef.current?.retreat()) return;
1419
+ if (index > 0) goTo(index - 1, "backward");
1420
+ }, [goTo, index]);
1421
+ const toggleRail = useCallback(() => setRailOpen((open) => !open), [setRailOpen]);
1422
+ const toggleFullscreen = useCallback(() => {
1423
+ const el = viewportRef.current;
1424
+ if (!el) return;
1425
+ if (document.fullscreenElement) void document.exitFullscreen();
1426
+ else void el.requestFullscreen?.();
1427
+ }, []);
1428
+ useEffect(() => {
1429
+ const onChange = () => setIsFullscreen(document.fullscreenElement === viewportRef.current);
1430
+ document.addEventListener("fullscreenchange", onChange);
1431
+ return () => document.removeEventListener("fullscreenchange", onChange);
1432
+ }, []);
1433
+ useEffect(() => {
1434
+ const onKeyDown = (event) => {
1435
+ if (isTypingTarget(event.target)) return;
1436
+ if (isForwardKey(event)) {
1437
+ event.preventDefault();
1438
+ goNext();
1439
+ return;
1440
+ }
1441
+ if (isBackwardKey(event)) {
1442
+ event.preventDefault();
1443
+ goPrev();
1444
+ return;
1445
+ }
1446
+ if (hasModifier(event)) return;
1447
+ if (event.key === "Home") {
1448
+ event.preventDefault();
1449
+ goTo(0, "jump");
1450
+ return;
1451
+ }
1452
+ if (event.key === "End") {
1453
+ event.preventDefault();
1454
+ goTo(total - 1, "jump");
1455
+ return;
1456
+ }
1457
+ if (event.key === "f" || event.key === "F") {
1458
+ event.preventDefault();
1459
+ toggleFullscreen();
1460
+ return;
1461
+ }
1462
+ if (event.key === "l" || event.key === "L") {
1463
+ event.preventDefault();
1464
+ toggleRail();
1465
+ return;
1466
+ }
1467
+ if (event.key === "Escape" && !document.fullscreenElement) {
1468
+ window.history.back();
1469
+ }
1470
+ };
1471
+ window.addEventListener("keydown", onKeyDown);
1472
+ return () => window.removeEventListener("keydown", onKeyDown);
1473
+ }, [goNext, goPrev, goTo, toggleFullscreen, toggleRail, total]);
1474
+ useClickNav({
1475
+ // The stage, not the whole deck: the edge thirds that turn the page are
1476
+ // measured against the slide, so opening the rail must not shift them.
1477
+ ref: stageRef,
1478
+ canPrev: true,
1479
+ canNext: true,
1480
+ onPrev: goPrev,
1481
+ onNext: goNext
1482
+ });
1483
+ useEffect(() => {
1484
+ if (embedded) return;
1485
+ const previous = document.body.style.overflow;
1486
+ document.body.style.overflow = "hidden";
1487
+ return () => {
1488
+ document.body.style.overflow = previous;
1489
+ };
1490
+ }, [embedded]);
1491
+ const selectSlide = useCallback(
1492
+ (next) => goTo(next, "jump"),
1493
+ [goTo]
1494
+ );
1495
+ const onExport = useCallback(async () => {
1496
+ if (exporting) return;
1497
+ try {
1498
+ await exportDeckAsPptx({
1499
+ slides,
1500
+ theme,
1501
+ fileName: exportName ?? defaultExportName(),
1502
+ onProgress: setExporting
1503
+ });
1504
+ } finally {
1505
+ setExporting(null);
1506
+ }
1507
+ }, [exporting, slides, theme, exportName]);
1508
+ const themeVars = themeToCssVars(theme);
1509
+ const showRail = railOpen && !isFullscreen;
1510
+ return /* @__PURE__ */ jsx(
1511
+ "div",
1512
+ {
1513
+ ref: viewportRef,
1514
+ "data-slide-deck": "",
1515
+ style: embedded ? { position: "relative", width: "100%", aspectRatio: "16 / 9", background: "var(--slide-bg)" } : {
1516
+ position: "fixed",
1517
+ inset: 0,
1518
+ zIndex: 50,
1519
+ background: "var(--slide-bg)",
1520
+ ...themeVars
1521
+ },
1522
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", height: "100%", width: "100%" }, children: [
1523
+ showRail ? /* @__PURE__ */ jsx(SlideRail, { slides, index, onSelect: selectSlide, themeVars }) : null,
1524
+ /* @__PURE__ */ jsxs("div", { ref: stageRef, style: { position: "relative", flex: "1 1 auto", minWidth: 0 }, children: [
1525
+ /* @__PURE__ */ jsx(SlideCanvas, { themeVars, children: /* @__PURE__ */ jsx(
1526
+ SlideTransitionLayer,
1527
+ {
1528
+ slides,
1529
+ transitions,
1530
+ index,
1531
+ disabled: reduceMotion,
1532
+ stepControllerRef,
1533
+ entryDirection
1534
+ }
1535
+ ) }),
1536
+ /* @__PURE__ */ jsx(
1537
+ SlideToolbar,
1538
+ {
1539
+ current: index + 1,
1540
+ total,
1541
+ onPrev: goPrev,
1542
+ onNext: goNext,
1543
+ onFullscreen: toggleFullscreen,
1544
+ onExport,
1545
+ exportProgress: exporting,
1546
+ railOpen,
1547
+ onToggleRail: toggleRail,
1548
+ isFullscreen
1549
+ }
1550
+ )
1551
+ ] })
1552
+ ] })
1553
+ }
1554
+ );
1555
+ }
1556
+ SlideDeck.displayName = "SlideDeck";
1557
+ function MorphElement({ id, children, className, style }) {
1558
+ const child = Children.toArray(children)[0] ?? null;
1559
+ if (Children.count(children) === 1 && isValidElement(child) && typeof child.type === "string") {
1560
+ return cloneElement(child, {
1561
+ "data-slide-morph": id,
1562
+ className: [child.props.className, className].filter(Boolean).join(" ") || void 0,
1563
+ style: style ? { ...child.props.style, ...style } : child.props.style
1564
+ });
1565
+ }
1566
+ return /* @__PURE__ */ jsx("div", { className, style, "data-slide-morph": id, children });
1567
+ }
1568
+
1569
+ export { CANVAS_HEIGHT, CANVAS_WIDTH, MorphElement, Slide, SlideCanvas, SlideDeck, Step, Steps, exportDeckAsPptx, useIsActiveSlide, useSlideNumber };
1570
+ //# sourceMappingURL=index.js.map
1571
+ //# sourceMappingURL=index.js.map