hono-decks 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/advanced.js CHANGED
@@ -1,719 +1,692 @@
1
- // src/source/manifest-source.ts
1
+ import { Hono } from "hono";
2
+ import { raw } from "hono/html";
3
+ import { jsx } from "hono/jsx/jsx-runtime";
4
+ import { HtmlEscapedCallbackPhase, resolveCallback } from "hono/utils/html";
5
+ //#region src/source/manifest-source.ts
2
6
  function manifestDeckSource(manifest) {
3
- const decks = new Map(manifest.decks.map((deck) => [deck.slug, deck]));
4
- return {
5
- async listDecks() {
6
- return manifest.decks.map((deck) => ({
7
- slug: deck.slug,
8
- title: deck.meta.title,
9
- description: deck.meta.description,
10
- draft: deck.meta.draft,
11
- sourcePath: deck.sourcePath
12
- }));
13
- },
14
- async getCompiledDeck(_c, slug) {
15
- return decks.get(slug) ?? null;
16
- },
17
- async getAsset(_c, slug, assetPath) {
18
- const deck = decks.get(slug);
19
- if (!deck) return null;
20
- const asset = findLocalAsset(deck.assets, slug, assetPath);
21
- if (!asset || asset.body == null) return null;
22
- return new Response(asset.body, {
23
- headers: localAssetHeaders(asset)
24
- });
25
- }
26
- };
7
+ const decks = new Map(manifest.decks.map((deck) => [deck.slug, deck]));
8
+ return {
9
+ async listDecks() {
10
+ return manifest.decks.map((deck) => ({
11
+ slug: deck.slug,
12
+ title: deck.meta.title,
13
+ description: deck.meta.description,
14
+ draft: deck.meta.draft,
15
+ sourcePath: deck.sourcePath
16
+ }));
17
+ },
18
+ async getCompiledDeck(_c, slug) {
19
+ return decks.get(slug) ?? null;
20
+ },
21
+ async getAsset(_c, slug, assetPath) {
22
+ const deck = decks.get(slug);
23
+ if (!deck) return null;
24
+ const asset = findLocalAsset(deck.assets, slug, assetPath);
25
+ if (!asset || asset.body == null) return null;
26
+ return new Response(asset.body, { headers: localAssetHeaders(asset) });
27
+ }
28
+ };
27
29
  }
28
30
  function localAssetHeaders(asset) {
29
- const headers = {};
30
- if (asset.contentType) headers["content-type"] = asset.contentType;
31
- if (asset.cacheControl !== false) headers["cache-control"] = asset.cacheControl ?? "public, max-age=300";
32
- return headers;
31
+ const headers = {};
32
+ if (asset.contentType) headers["content-type"] = asset.contentType;
33
+ if (asset.cacheControl !== false) headers["cache-control"] = asset.cacheControl ?? "public, max-age=300";
34
+ return headers;
33
35
  }
34
36
  function findLocalAsset(assets, slug, assetPath) {
35
- const normalized = assetPath.replace(/^\/+/, "");
36
- return assets.find((asset) => {
37
- if (asset.type !== "local") return false;
38
- const suffix = `/${slug}/assets/${normalized}`;
39
- return asset.publicPath.endsWith(suffix);
40
- });
41
- }
42
-
43
- // src/server/router.ts
44
- import { Hono } from "hono";
45
- import { raw as raw2 } from "hono/html";
46
-
47
- // src/deck/model.ts
48
- var SLIDE_TRANSITIONS = [
49
- "none",
50
- "fade",
51
- "fade-out",
52
- "slide-left",
53
- "slide-right",
54
- "slide-up",
55
- "slide-down",
56
- "view-transition"
37
+ const normalized = assetPath.replace(/^\/+/, "");
38
+ return assets.find((asset) => {
39
+ if (asset.type !== "local") return false;
40
+ const suffix = `/${slug}/assets/${normalized}`;
41
+ return asset.publicPath.endsWith(suffix);
42
+ });
43
+ }
44
+ //#endregion
45
+ //#region src/deck/model.ts
46
+ const SLIDE_TRANSITIONS = [
47
+ "none",
48
+ "fade",
49
+ "fade-out",
50
+ "slide-left",
51
+ "slide-right",
52
+ "slide-up",
53
+ "slide-down",
54
+ "view-transition"
57
55
  ];
58
56
  var RenderError = class extends Error {
59
- constructor(message, code, cause) {
60
- super(message);
61
- this.code = code;
62
- this.cause = cause;
63
- this.name = "RenderError";
64
- }
57
+ code;
58
+ cause;
59
+ constructor(message, code, cause) {
60
+ super(message);
61
+ this.code = code;
62
+ this.cause = cause;
63
+ this.name = "RenderError";
64
+ }
65
65
  };
66
-
67
- // src/renderer/asset-rewrite.ts
66
+ //#endregion
67
+ //#region src/renderer/asset-rewrite.ts
68
68
  function rewriteLocalAssetUrls(html, assets) {
69
- const localAssets = assets.filter((asset) => asset.type === "local");
70
- if (localAssets.length === 0) return html;
71
- return html.replace(/\b(src|href)=["']([^"']+)["']/g, (match, attr, value) => {
72
- const asset = findAssetForHtmlUrl(localAssets, value);
73
- return asset ? `${attr}="${escapeHtml(asset.publicPath)}"` : match;
74
- }).replace(/<dd>([^<]+)<\/dd>/g, (match, value) => {
75
- const asset = findAssetForHtmlUrl(localAssets, decodeHtml(value));
76
- return asset ? `<dd>${escapeHtml(asset.publicPath)}</dd>` : match;
77
- });
69
+ const localAssets = assets.filter((asset) => asset.type === "local");
70
+ if (localAssets.length === 0) return html;
71
+ return html.replace(/\b(src|href)=["']([^"']+)["']/g, (match, attr, value) => {
72
+ const asset = findAssetForHtmlUrl$1(localAssets, value);
73
+ return asset ? `${attr}="${escapeHtml$8(asset.publicPath)}"` : match;
74
+ }).replace(/<dd>([^<]+)<\/dd>/g, (match, value) => {
75
+ const asset = findAssetForHtmlUrl$1(localAssets, decodeHtml(value));
76
+ return asset ? `<dd>${escapeHtml$8(asset.publicPath)}</dd>` : match;
77
+ });
78
78
  }
79
79
  function backgroundStyle(value, assets) {
80
- const asset = findAssetForHtmlUrl(assets.filter((candidate) => candidate.type === "local"), value);
81
- const url = asset?.publicPath ?? value;
82
- return `background-image:url("${escapeCssUrl(url)}")`;
80
+ return `background-image:url("${escapeCssUrl(findAssetForHtmlUrl$1(assets.filter((candidate) => candidate.type === "local"), value)?.publicPath ?? value)}")`;
83
81
  }
84
- function findAssetForHtmlUrl(assets, value) {
85
- const normalized = decodeURIComponent(value).replace(/^\.?\//, "");
86
- return assets.find((asset) => {
87
- const assetPath = localAssetRelativePath(asset);
88
- return normalized === assetPath || normalized === `assets/${assetPath}`;
89
- });
82
+ function findAssetForHtmlUrl$1(assets, value) {
83
+ const normalized = decodeURIComponent(value).replace(/^\.?\//, "");
84
+ return assets.find((asset) => {
85
+ const assetPath = localAssetRelativePath$1(asset);
86
+ return normalized === assetPath || normalized === `assets/${assetPath}`;
87
+ });
90
88
  }
91
- function localAssetRelativePath(asset) {
92
- const marker = "/assets/";
93
- const normalized = asset.sourcePath.replaceAll("\\", "/");
94
- const markerIndex = normalized.indexOf(marker);
95
- return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + marker.length);
89
+ function localAssetRelativePath$1(asset) {
90
+ const marker = "/assets/";
91
+ const normalized = asset.sourcePath.replaceAll("\\", "/");
92
+ const markerIndex = normalized.indexOf(marker);
93
+ return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + 8);
96
94
  }
97
95
  function escapeCssUrl(value) {
98
- return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
96
+ return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
99
97
  }
100
- function escapeHtml(value) {
101
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
98
+ function escapeHtml$8(value) {
99
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
102
100
  }
103
101
  function decodeHtml(value) {
104
- return value.replaceAll("&quot;", '"').replaceAll("&#39;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
105
- }
106
-
107
- // src/renderer/jsx-renderer.ts
108
- import { jsx } from "hono/jsx/jsx-runtime";
109
- import { HtmlEscapedCallbackPhase, resolveCallback } from "hono/utils/html";
110
- var builtInSlideComponents = defineSlideComponents({
111
- Fire: (props) => {
112
- if (props.order !== void 0) {
113
- throw new Error('The Fire "order" prop is not supported. Fires reveal in source order.');
114
- }
115
- const effect = stringProp(props.effect);
116
- const at = fireAtProp(props.at);
117
- return jsx("div", {
118
- "data-hono-decks-fire": true,
119
- ...at ? { "data-fire-at": at } : {},
120
- ...effect ? { "data-fire-effect": safeToken(effect).toLowerCase() } : {},
121
- children: props.children
122
- });
123
- },
124
- Hero: (props) => jsx("section", {
125
- class: `mdx-hero${props.featured === true ? " is-featured" : ""}${props.image || props.src ? " has-image" : ""}`,
126
- "data-component": "Hero",
127
- children: [
128
- heroImage(props),
129
- jsx("div", {
130
- class: "mdx-hero-copy",
131
- children: [
132
- typeof props.eyebrow === "string" && props.eyebrow ? jsx("p", { class: "mdx-hero-eyebrow", children: props.eyebrow }) : "",
133
- typeof props.title === "string" && props.title ? jsx("h1", { children: props.title }) : "",
134
- typeof props.subtitle === "string" && props.subtitle ? jsx("p", { class: "mdx-hero-subtitle", children: props.subtitle }) : typeof props.description === "string" && props.description ? jsx("p", { class: "mdx-hero-subtitle", children: props.description }) : ""
135
- ]
136
- })
137
- ]
138
- }),
139
- CodeBlock: (props) => {
140
- const lang = stringProp(props.lang);
141
- const filename = stringProp(props.filename ?? props.title);
142
- const highlight = stringProp(props.highlight);
143
- const code = codeBlockText(props.children);
144
- const highlightedHtml = typeof props.highlightedHtml === "string" ? props.highlightedHtml : void 0;
145
- return jsx("figure", {
146
- class: "hono-decks-code-block",
147
- ...lang ? { "data-lang": lang } : {},
148
- ...filename ? { "data-filename": filename } : {},
149
- ...highlight ? { "data-highlight": highlight } : {},
150
- children: [
151
- filename ? jsx("figcaption", { class: "hono-decks-code-caption", children: filename }) : "",
152
- highlightedHtml ? jsx("div", {
153
- class: "hono-decks-code-highlight",
154
- dangerouslySetInnerHTML: { __html: highlightedHtml }
155
- }) : jsx("pre", {
156
- children: jsx("code", {
157
- ...lang ? { class: `language-${safeToken(lang)}`, "data-lang": lang } : {},
158
- children: code
159
- })
160
- })
161
- ]
162
- });
163
- },
164
- EmbedFrame: (props) => {
165
- const src = stringProp(props.src);
166
- const provider = stringProp(props.provider)?.trim().toLowerCase();
167
- const title = stringProp(props.title) ?? "Embedded content";
168
- const aspectRatio = stringProp(props.aspectRatio) ?? "16 / 9";
169
- const loading = stringProp(props.loading) ?? "lazy";
170
- const allow = stringProp(props.allow) ?? "fullscreen; picture-in-picture";
171
- const sandbox = props.sandbox === false ? void 0 : stringProp(props.sandbox) ?? "allow-scripts allow-same-origin allow-presentation allow-popups";
172
- const referrerPolicy = stringProp(props.referrerPolicy ?? props.referrerpolicy) ?? "strict-origin-when-cross-origin";
173
- const fallbackHref = stringProp(props.fallbackHref ?? props.fallbackUrl ?? props.href) ?? src;
174
- const fallback = codeBlockText(props.children) || "Open embed";
175
- const printPoster = provider === "youtube" ? youtubePrintPoster(src) : void 0;
176
- return jsx("figure", {
177
- class: "hono-decks-embed-frame",
178
- "data-component": "EmbedFrame",
179
- ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
180
- children: [
181
- jsx("div", {
182
- class: "hono-decks-embed-viewport",
183
- style: `aspect-ratio:${safeAspectRatio(aspectRatio)}`,
184
- children: [
185
- src ? jsx("iframe", {
186
- src,
187
- title,
188
- loading,
189
- referrerpolicy: referrerPolicy,
190
- ...sandbox ? { sandbox } : {},
191
- ...allow ? { allow } : {},
192
- allowfullscreen: true
193
- }) : "",
194
- fallbackHref ? jsx("a", {
195
- class: "hono-decks-embed-print-fallback",
196
- href: fallbackHref,
197
- children: [
198
- printPoster ? jsx("img", {
199
- class: "hono-decks-embed-print-poster",
200
- src: printPoster,
201
- alt: `${title} thumbnail`
202
- }) : "",
203
- jsx("span", { children: fallback })
204
- ]
205
- }) : ""
206
- ]
207
- }),
208
- src ? jsx("figcaption", {
209
- class: "hono-decks-embed-fallback",
210
- children: jsx("a", { href: fallbackHref, target: "_blank", rel: "noreferrer", children: fallback })
211
- }) : ""
212
- ]
213
- });
214
- },
215
- SocialEmbed: (props) => {
216
- const href = stringProp(props.href ?? props.url);
217
- const provider = stringProp(props.provider ?? props.service);
218
- const author = stringProp(props.author);
219
- const label = stringProp(props.label) ?? (provider ? `Open on ${provider}` : "Open social post");
220
- const quote = codeBlockText(props.children);
221
- return jsx("figure", {
222
- class: "hono-decks-social-embed",
223
- "data-component": "SocialEmbed",
224
- ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
225
- children: jsx("blockquote", {
226
- class: "hono-decks-social-card",
227
- ...href ? { cite: href } : {},
228
- children: [
229
- quote ? jsx("p", { children: quote }) : "",
230
- jsx("footer", {
231
- children: [
232
- author ? jsx("span", { class: "hono-decks-social-author", children: author }) : "",
233
- href ? jsx("a", {
234
- href,
235
- target: "_blank",
236
- rel: "noreferrer",
237
- children: label
238
- }) : ""
239
- ]
240
- })
241
- ]
242
- })
243
- });
244
- },
245
- TweetEmbed: (props) => {
246
- const href = stringProp(props.href ?? props.url);
247
- const label = stringProp(props.label) ?? "Open post on X";
248
- return jsx("figure", {
249
- class: "hono-decks-tweet-embed",
250
- "data-component": "TweetEmbed",
251
- children: [
252
- jsx("blockquote", {
253
- class: "twitter-tweet",
254
- "data-dnt": "true",
255
- children: href ? jsx("a", {
256
- href,
257
- target: "_blank",
258
- rel: "noreferrer",
259
- children: label
260
- }) : label
261
- }),
262
- href ? jsx("script", {
263
- async: true,
264
- src: "https://platform.twitter.com/widgets.js",
265
- charset: "utf-8"
266
- }) : "",
267
- href ? jsx("a", {
268
- class: "hono-decks-tweet-print-fallback",
269
- href,
270
- children: label
271
- }) : ""
272
- ]
273
- });
274
- },
275
- LinkCard: (props) => {
276
- const href = stringProp(props.href ?? props.url);
277
- const title = stringProp(props.title) ?? href ?? "Link";
278
- const description = stringProp(props.description);
279
- const image = stringProp(props.image ?? props.imageUrl);
280
- const siteName = stringProp(props.siteName ?? props.site);
281
- const label = codeBlockText(props.children) || "Open link";
282
- return jsx("figure", {
283
- class: "hono-decks-link-card",
284
- "data-component": "LinkCard",
285
- children: href ? jsx("a", {
286
- class: "hono-decks-link-card-anchor",
287
- href,
288
- target: "_blank",
289
- rel: "noreferrer",
290
- children: [
291
- image ? jsx("img", { class: "hono-decks-link-card-image", src: image, alt: title }) : "",
292
- jsx("span", {
293
- class: "hono-decks-link-card-body",
294
- children: [
295
- siteName ? jsx("span", { class: "hono-decks-link-card-site", children: siteName }) : "",
296
- jsx("span", { class: "hono-decks-link-card-title", children: title }),
297
- description ? jsx("span", { class: "hono-decks-link-card-description", children: description }) : "",
298
- jsx("span", { class: "hono-decks-link-card-label", children: label })
299
- ]
300
- })
301
- ]
302
- }) : [
303
- image ? jsx("img", { class: "hono-decks-link-card-image", src: image, alt: title }) : "",
304
- siteName ? jsx("span", { class: "hono-decks-link-card-site", children: siteName }) : "",
305
- jsx("span", { class: "hono-decks-link-card-title", children: title }),
306
- description ? jsx("span", { class: "hono-decks-link-card-description", children: description }) : ""
307
- ]
308
- });
309
- }
102
+ return value.replaceAll("&quot;", "\"").replaceAll("&#39;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
103
+ }
104
+ //#endregion
105
+ //#region src/renderer/jsx-renderer.ts
106
+ const builtInSlideComponents = defineSlideComponents({
107
+ Fire: (props) => {
108
+ if (props.order !== void 0) throw new Error("The Fire \"order\" prop is not supported. Fires reveal in source order.");
109
+ const effect = stringProp(props.effect);
110
+ const at = fireAtProp(props.at);
111
+ return jsx("div", {
112
+ "data-hono-decks-fire": true,
113
+ ...at ? { "data-fire-at": at } : {},
114
+ ...effect ? { "data-fire-effect": safeToken(effect).toLowerCase() } : {},
115
+ children: props.children
116
+ });
117
+ },
118
+ Hero: (props) => jsx("section", {
119
+ class: `mdx-hero${props.featured === true ? " is-featured" : ""}${props.image || props.src ? " has-image" : ""}`,
120
+ "data-component": "Hero",
121
+ children: [heroImage(props), jsx("div", {
122
+ class: "mdx-hero-copy",
123
+ children: [
124
+ typeof props.eyebrow === "string" && props.eyebrow ? jsx("p", {
125
+ class: "mdx-hero-eyebrow",
126
+ children: props.eyebrow
127
+ }) : "",
128
+ typeof props.title === "string" && props.title ? jsx("h1", { children: props.title }) : "",
129
+ typeof props.subtitle === "string" && props.subtitle ? jsx("p", {
130
+ class: "mdx-hero-subtitle",
131
+ children: props.subtitle
132
+ }) : typeof props.description === "string" && props.description ? jsx("p", {
133
+ class: "mdx-hero-subtitle",
134
+ children: props.description
135
+ }) : ""
136
+ ]
137
+ })]
138
+ }),
139
+ CodeBlock: (props) => {
140
+ const lang = stringProp(props.lang);
141
+ const filename = stringProp(props.filename ?? props.title);
142
+ const highlight = stringProp(props.highlight);
143
+ const code = codeBlockText(props.children);
144
+ const highlightedHtml = typeof props.highlightedHtml === "string" ? props.highlightedHtml : void 0;
145
+ return jsx("figure", {
146
+ class: "hono-decks-code-block",
147
+ ...lang ? { "data-lang": lang } : {},
148
+ ...filename ? { "data-filename": filename } : {},
149
+ ...highlight ? { "data-highlight": highlight } : {},
150
+ children: [filename ? jsx("figcaption", {
151
+ class: "hono-decks-code-caption",
152
+ children: filename
153
+ }) : "", highlightedHtml ? jsx("div", {
154
+ class: "hono-decks-code-highlight",
155
+ dangerouslySetInnerHTML: { __html: highlightedHtml }
156
+ }) : jsx("pre", { children: jsx("code", {
157
+ ...lang ? {
158
+ class: `language-${safeToken(lang)}`,
159
+ "data-lang": lang
160
+ } : {},
161
+ children: code
162
+ }) })]
163
+ });
164
+ },
165
+ EmbedFrame: (props) => {
166
+ const src = stringProp(props.src);
167
+ const provider = stringProp(props.provider)?.trim().toLowerCase();
168
+ const title = stringProp(props.title) ?? "Embedded content";
169
+ const aspectRatio = stringProp(props.aspectRatio) ?? "16 / 9";
170
+ const loading = stringProp(props.loading) ?? "lazy";
171
+ const allow = stringProp(props.allow) ?? "fullscreen; picture-in-picture";
172
+ const sandbox = props.sandbox === false ? void 0 : stringProp(props.sandbox) ?? "allow-scripts allow-same-origin allow-presentation allow-popups";
173
+ const referrerPolicy = stringProp(props.referrerPolicy ?? props.referrerpolicy) ?? "strict-origin-when-cross-origin";
174
+ const fallbackHref = stringProp(props.fallbackHref ?? props.fallbackUrl ?? props.href) ?? src;
175
+ const fallback = codeBlockText(props.children) || "Open embed";
176
+ const printPoster = provider === "youtube" ? youtubePrintPoster(src) : void 0;
177
+ return jsx("figure", {
178
+ class: "hono-decks-embed-frame",
179
+ "data-component": "EmbedFrame",
180
+ ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
181
+ children: [jsx("div", {
182
+ class: "hono-decks-embed-viewport",
183
+ style: `aspect-ratio:${safeAspectRatio(aspectRatio)}`,
184
+ children: [src ? jsx("iframe", {
185
+ src,
186
+ title,
187
+ loading,
188
+ referrerpolicy: referrerPolicy,
189
+ ...sandbox ? { sandbox } : {},
190
+ ...allow ? { allow } : {},
191
+ allowfullscreen: true
192
+ }) : "", fallbackHref ? jsx("a", {
193
+ class: "hono-decks-embed-print-fallback",
194
+ href: fallbackHref,
195
+ children: [printPoster ? jsx("img", {
196
+ class: "hono-decks-embed-print-poster",
197
+ src: printPoster,
198
+ alt: `${title} thumbnail`
199
+ }) : "", jsx("span", { children: fallback })]
200
+ }) : ""]
201
+ }), src ? jsx("figcaption", {
202
+ class: "hono-decks-embed-fallback",
203
+ children: jsx("a", {
204
+ href: fallbackHref,
205
+ target: "_blank",
206
+ rel: "noreferrer",
207
+ children: fallback
208
+ })
209
+ }) : ""]
210
+ });
211
+ },
212
+ SocialEmbed: (props) => {
213
+ const href = stringProp(props.href ?? props.url);
214
+ const provider = stringProp(props.provider ?? props.service);
215
+ const author = stringProp(props.author);
216
+ const label = stringProp(props.label) ?? (provider ? `Open on ${provider}` : "Open social post");
217
+ const quote = codeBlockText(props.children);
218
+ return jsx("figure", {
219
+ class: "hono-decks-social-embed",
220
+ "data-component": "SocialEmbed",
221
+ ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
222
+ children: jsx("blockquote", {
223
+ class: "hono-decks-social-card",
224
+ ...href ? { cite: href } : {},
225
+ children: [quote ? jsx("p", { children: quote }) : "", jsx("footer", { children: [author ? jsx("span", {
226
+ class: "hono-decks-social-author",
227
+ children: author
228
+ }) : "", href ? jsx("a", {
229
+ href,
230
+ target: "_blank",
231
+ rel: "noreferrer",
232
+ children: label
233
+ }) : ""] })]
234
+ })
235
+ });
236
+ },
237
+ TweetEmbed: (props) => {
238
+ const href = stringProp(props.href ?? props.url);
239
+ const label = stringProp(props.label) ?? "Open post on X";
240
+ return jsx("figure", {
241
+ class: "hono-decks-tweet-embed",
242
+ "data-component": "TweetEmbed",
243
+ children: [
244
+ jsx("blockquote", {
245
+ class: "twitter-tweet",
246
+ "data-dnt": "true",
247
+ children: href ? jsx("a", {
248
+ href,
249
+ target: "_blank",
250
+ rel: "noreferrer",
251
+ children: label
252
+ }) : label
253
+ }),
254
+ href ? jsx("script", {
255
+ async: true,
256
+ src: "https://platform.twitter.com/widgets.js",
257
+ charset: "utf-8"
258
+ }) : "",
259
+ href ? jsx("a", {
260
+ class: "hono-decks-tweet-print-fallback",
261
+ href,
262
+ children: label
263
+ }) : ""
264
+ ]
265
+ });
266
+ },
267
+ LinkCard: (props) => {
268
+ const href = stringProp(props.href ?? props.url);
269
+ const title = stringProp(props.title) ?? href ?? "Link";
270
+ const description = stringProp(props.description);
271
+ const image = stringProp(props.image ?? props.imageUrl);
272
+ const siteName = stringProp(props.siteName ?? props.site);
273
+ const label = codeBlockText(props.children) || "Open link";
274
+ return jsx("figure", {
275
+ class: "hono-decks-link-card",
276
+ "data-component": "LinkCard",
277
+ children: href ? jsx("a", {
278
+ class: "hono-decks-link-card-anchor",
279
+ href,
280
+ target: "_blank",
281
+ rel: "noreferrer",
282
+ children: [image ? jsx("img", {
283
+ class: "hono-decks-link-card-image",
284
+ src: image,
285
+ alt: title
286
+ }) : "", jsx("span", {
287
+ class: "hono-decks-link-card-body",
288
+ children: [
289
+ siteName ? jsx("span", {
290
+ class: "hono-decks-link-card-site",
291
+ children: siteName
292
+ }) : "",
293
+ jsx("span", {
294
+ class: "hono-decks-link-card-title",
295
+ children: title
296
+ }),
297
+ description ? jsx("span", {
298
+ class: "hono-decks-link-card-description",
299
+ children: description
300
+ }) : "",
301
+ jsx("span", {
302
+ class: "hono-decks-link-card-label",
303
+ children: label
304
+ })
305
+ ]
306
+ })]
307
+ }) : [
308
+ image ? jsx("img", {
309
+ class: "hono-decks-link-card-image",
310
+ src: image,
311
+ alt: title
312
+ }) : "",
313
+ siteName ? jsx("span", {
314
+ class: "hono-decks-link-card-site",
315
+ children: siteName
316
+ }) : "",
317
+ jsx("span", {
318
+ class: "hono-decks-link-card-title",
319
+ children: title
320
+ }),
321
+ description ? jsx("span", {
322
+ class: "hono-decks-link-card-description",
323
+ children: description
324
+ }) : ""
325
+ ]
326
+ });
327
+ }
310
328
  });
311
329
  function defineSlideComponents(input) {
312
- const registry = {};
313
- for (const [name, value] of Object.entries(input)) {
314
- registry[name] = typeof value === "function" ? { component: value } : value;
315
- }
316
- return registry;
330
+ const registry = {};
331
+ for (const [name, value] of Object.entries(input)) registry[name] = typeof value === "function" ? { component: value } : value;
332
+ return registry;
317
333
  }
318
334
  function createMdxComponents(registry, input = {}) {
319
- const components = {
320
- img: (props) => jsx("img", rewriteAssetProps(props, input.assets)),
321
- a: (props) => jsx("a", rewriteAssetProps(props, input.assets))
322
- };
323
- for (const [name, definition] of Object.entries(registry)) {
324
- components[name] = (props = {}) => renderRegisteredComponent(name, definition, props, props.children, input.assets);
325
- }
326
- return components;
335
+ const components = {
336
+ img: (props) => jsx("img", rewriteAssetProps(props, input.assets)),
337
+ a: (props) => jsx("a", rewriteAssetProps(props, input.assets))
338
+ };
339
+ for (const [name, definition] of Object.entries(registry)) components[name] = (props = {}) => renderRegisteredComponent(name, definition, props, props.children, input.assets);
340
+ return components;
327
341
  }
328
342
  function heroImage(props) {
329
- const image = typeof props.image === "string" ? props.image : typeof props.src === "string" ? props.src : void 0;
330
- if (!image) return "";
331
- const alt = typeof props.alt === "string" && props.alt ? props.alt : typeof props.title === "string" ? props.title : "Hero image";
332
- return jsx("img", { class: "mdx-hero-image", src: image, alt });
343
+ const image = typeof props.image === "string" ? props.image : typeof props.src === "string" ? props.src : void 0;
344
+ if (!image) return "";
345
+ return jsx("img", {
346
+ class: "mdx-hero-image",
347
+ src: image,
348
+ alt: typeof props.alt === "string" && props.alt ? props.alt : typeof props.title === "string" ? props.title : "Hero image"
349
+ });
333
350
  }
334
351
  function stringProp(value) {
335
- if (typeof value === "string") return value;
336
- if (typeof value === "number" || typeof value === "boolean") return String(value);
337
- return void 0;
352
+ if (typeof value === "string") return value;
353
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
338
354
  }
339
355
  function fireAtProp(value) {
340
- if (value === void 0 || value === null) return void 0;
341
- if (typeof value === "number") {
342
- if (Number.isInteger(value) && value >= 0) return String(value);
343
- throw new Error('The Fire "at" prop accepts a non-negative integer or a relative string such as "+1".');
344
- }
345
- if (typeof value === "string") {
346
- const at = value.trim();
347
- if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
348
- }
349
- throw new Error('The Fire "at" prop accepts a non-negative integer or a relative string such as "+1".');
356
+ if (value === void 0 || value === null) return void 0;
357
+ if (typeof value === "number") {
358
+ if (Number.isInteger(value) && value >= 0) return String(value);
359
+ throw new Error("The Fire \"at\" prop accepts a non-negative integer or a relative string such as \"+1\".");
360
+ }
361
+ if (typeof value === "string") {
362
+ const at = value.trim();
363
+ if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
364
+ }
365
+ throw new Error("The Fire \"at\" prop accepts a non-negative integer or a relative string such as \"+1\".");
350
366
  }
351
367
  function safeToken(value) {
352
- return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "text";
368
+ return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "text";
353
369
  }
354
370
  function safeAspectRatio(value) {
355
- return /^\s*\d+(\.\d+)?\s*(\/|:)\s*\d+(\.\d+)?\s*$/.test(value) ? value.replace(":", " / ") : "16 / 9";
371
+ return /^\s*\d+(\.\d+)?\s*(\/|:)\s*\d+(\.\d+)?\s*$/.test(value) ? value.replace(":", " / ") : "16 / 9";
356
372
  }
357
373
  function youtubePrintPoster(src) {
358
- if (!src) return void 0;
359
- try {
360
- const url = new URL(src);
361
- if (!["youtube.com", "www.youtube.com", "youtube-nocookie.com", "www.youtube-nocookie.com"].includes(url.hostname)) {
362
- return void 0;
363
- }
364
- const match = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]+)$/);
365
- return match ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : void 0;
366
- } catch {
367
- return void 0;
368
- }
374
+ if (!src) return void 0;
375
+ try {
376
+ const url = new URL(src);
377
+ if (![
378
+ "youtube.com",
379
+ "www.youtube.com",
380
+ "youtube-nocookie.com",
381
+ "www.youtube-nocookie.com"
382
+ ].includes(url.hostname)) return;
383
+ const match = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]+)$/);
384
+ return match ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : void 0;
385
+ } catch {
386
+ return;
387
+ }
369
388
  }
370
389
  function codeBlockText(value) {
371
- if (Array.isArray(value)) return value.map(codeBlockText).join("");
372
- if (value === null || value === void 0 || typeof value === "boolean") return "";
373
- if (typeof value === "string" || typeof value === "number") return String(value);
374
- if (isJsxValue(value)) return codeBlockText(value.props?.children);
375
- return String(value);
390
+ if (Array.isArray(value)) return value.map(codeBlockText).join("");
391
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
392
+ if (typeof value === "string" || typeof value === "number") return String(value);
393
+ if (isJsxValue(value)) return codeBlockText(value.props?.children);
394
+ return String(value);
376
395
  }
377
396
  function renderSlideNodes(nodes, input = {}) {
378
- return normalizeVoidElementSpacing(nodes.map((node) => String(renderSlideNode(node, input))).join(""));
397
+ return normalizeVoidElementSpacing(nodes.map((node) => String(renderSlideNode(node, input))).join(""));
379
398
  }
380
399
  async function renderSlideNodesAsync(nodes, input = {}) {
381
- const rendered = await Promise.all(nodes.map((node) => renderJsxValue(renderSlideNode(node, input))));
382
- return normalizeVoidElementSpacing(rendered.join(""));
400
+ return normalizeVoidElementSpacing((await Promise.all(nodes.map((node) => renderJsxValue(renderSlideNode(node, input))))).join(""));
383
401
  }
384
402
  function renderSlideNode(node, input) {
385
- switch (node.type) {
386
- case "text":
387
- return String(node.value);
388
- case "code":
389
- return jsx("pre", {
390
- children: jsx("code", {
391
- ...node.lang ? { "data-lang": node.lang } : {},
392
- children: node.value
393
- })
394
- });
395
- case "element":
396
- return jsx(node.tag, {
397
- ...rewriteAssetProps(node.props, input.assets),
398
- children: node.children.map((child) => renderSlideNode(child, input))
399
- });
400
- case "component":
401
- return renderComponentNode(node, input);
402
- }
403
+ switch (node.type) {
404
+ case "text": return String(node.value);
405
+ case "code": return jsx("pre", { children: jsx("code", {
406
+ ...node.lang ? { "data-lang": node.lang } : {},
407
+ children: node.value
408
+ }) });
409
+ case "element": return jsx(node.tag, {
410
+ ...rewriteAssetProps(node.props, input.assets),
411
+ children: node.children.map((child) => renderSlideNode(child, input))
412
+ });
413
+ case "component": return renderComponentNode(node, input);
414
+ }
403
415
  }
404
416
  function renderComponentNode(node, input) {
405
- const definition = input.components?.[node.name];
406
- if (!definition) return renderComponentPlaceholder(node);
407
- const children = node.children.map((child) => renderSlideNode(child, input));
408
- return renderRegisteredComponent(node.name, definition, node.props, children, input.assets);
417
+ const definition = input.components?.[node.name];
418
+ if (!definition) return renderComponentPlaceholder(node);
419
+ const children = node.children.map((child) => renderSlideNode(child, input));
420
+ return renderRegisteredComponent(node.name, definition, node.props, children, input.assets);
409
421
  }
410
422
  function renderRegisteredComponent(name, definition, props, children, assets = []) {
411
- const rewritten = rewriteAssetProps(props, assets);
412
- const componentProps = stripRuntimeProps(rewritten);
413
- const element = definition.component({ ...componentProps, children });
414
- if (!definition.client && rewritten.client !== true) return element;
415
- return jsx("div", {
416
- "data-hono-decks-island": definition.clientId ?? name,
417
- "data-hono-decks-props": JSON.stringify(serializableProps(name, componentProps)),
418
- children: element
419
- });
423
+ const rewritten = rewriteAssetProps(props, assets);
424
+ const componentProps = stripRuntimeProps(rewritten);
425
+ const element = definition.component({
426
+ ...componentProps,
427
+ children
428
+ });
429
+ if (!definition.client && rewritten.client !== true) return element;
430
+ return jsx("div", {
431
+ "data-hono-decks-island": definition.clientId ?? name,
432
+ "data-hono-decks-props": JSON.stringify(serializableProps(name, componentProps)),
433
+ children: element
434
+ });
420
435
  }
421
436
  async function renderJsxValue(value) {
422
- const resolved = value instanceof Promise ? await value : value;
423
- if (typeof resolved === "string") return resolved;
424
- if (resolved === null || resolved === void 0 || typeof resolved === "boolean") return "";
425
- if (typeof resolved === "number") return String(resolved);
426
- return await resolveCallback(resolved, HtmlEscapedCallbackPhase.Stringify, false, {});
437
+ const resolved = value instanceof Promise ? await value : value;
438
+ if (typeof resolved === "string") return resolved;
439
+ if (resolved === null || resolved === void 0 || typeof resolved === "boolean") return "";
440
+ if (typeof resolved === "number") return String(resolved);
441
+ return await resolveCallback(resolved, HtmlEscapedCallbackPhase.Stringify, false, {});
427
442
  }
428
443
  function renderComponentPlaceholder(node) {
429
- return jsx("div", {
430
- class: "mdx-component",
431
- "data-component": node.name,
432
- children: [
433
- jsx("strong", { children: `<${node.name} />` }),
434
- Object.keys(node.props).length > 0 ? jsx("dl", {
435
- children: Object.entries(node.props).map(
436
- ([key, value]) => jsx("div", {
437
- children: [jsx("dt", { children: key }), jsx("dd", { children: String(value) })]
438
- })
439
- )
440
- }) : ""
441
- ]
442
- });
444
+ return jsx("div", {
445
+ class: "mdx-component",
446
+ "data-component": node.name,
447
+ children: [jsx("strong", { children: `<${node.name} />` }), Object.keys(node.props).length > 0 ? jsx("dl", { children: Object.entries(node.props).map(([key, value]) => jsx("div", { children: [jsx("dt", { children: key }), jsx("dd", { children: String(value) })] })) }) : ""]
448
+ });
443
449
  }
444
450
  function stripRuntimeProps(props) {
445
- const clean = {};
446
- for (const [key, value] of Object.entries(props)) {
447
- if (key === "client" || key === "client:island" || key === "children" || value === void 0) continue;
448
- clean[key] = value;
449
- }
450
- return clean;
451
+ const clean = {};
452
+ for (const [key, value] of Object.entries(props)) {
453
+ if (key === "client" || key === "client:island" || key === "children" || value === void 0) continue;
454
+ clean[key] = value;
455
+ }
456
+ return clean;
451
457
  }
452
458
  function rewriteAssetProps(props, assets = []) {
453
- const rewritten = { ...props };
454
- for (const key of ["src", "href", "image", "background"]) {
455
- const value = rewritten[key];
456
- if (typeof value !== "string") continue;
457
- const asset = findAssetForHtmlUrl2(assets, value);
458
- if (asset) rewritten[key] = asset.publicPath;
459
- }
460
- return rewritten;
459
+ const rewritten = { ...props };
460
+ for (const key of [
461
+ "src",
462
+ "href",
463
+ "image",
464
+ "background"
465
+ ]) {
466
+ const value = rewritten[key];
467
+ if (typeof value !== "string") continue;
468
+ const asset = findAssetForHtmlUrl(assets, value);
469
+ if (asset) rewritten[key] = asset.publicPath;
470
+ }
471
+ return rewritten;
461
472
  }
462
473
  function serializableProps(componentName, props) {
463
- const result = {};
464
- for (const [key, value] of Object.entries(props)) {
465
- result[key] = serializeClientIslandProp(value, `${componentName}.${key}`);
466
- }
467
- return result;
474
+ const result = {};
475
+ for (const [key, value] of Object.entries(props)) result[key] = serializeClientIslandProp(value, `${componentName}.${key}`);
476
+ return result;
468
477
  }
469
478
  function serializeClientIslandProp(value, path) {
470
- if (value === null) return null;
471
- if (typeof value === "string" || typeof value === "boolean") return value;
472
- if (typeof value === "number") {
473
- if (!Number.isFinite(value)) {
474
- throw new Error(`Client island prop "${path}" must be JSON-serializable; non-finite numbers are not supported.`);
475
- }
476
- return value;
477
- }
478
- if (typeof value === "function") {
479
- throw new Error(`Client island prop "${path}" must be JSON-serializable; functions cannot be passed to the client.`);
480
- }
481
- if (value === void 0 || typeof value === "symbol" || typeof value === "bigint") {
482
- throw new Error(`Client island prop "${path}" must be JSON-serializable; ${typeof value} values are not supported.`);
483
- }
484
- if (isJsxValue(value)) {
485
- throw new Error(`Client island prop "${path}" must be JSON-serializable; JSX values cannot be passed to the client.`);
486
- }
487
- if (value instanceof Date) {
488
- throw new Error(`Client island prop "${path}" must be JSON-serializable; Date values must be converted to strings.`);
489
- }
490
- if (Array.isArray(value)) return value.map((item, index) => serializeClientIslandProp(item, `${path}[${index}]`));
491
- if (!isPlainObject(value)) {
492
- throw new Error(`Client island prop "${path}" must be JSON-serializable; class instances are not supported.`);
493
- }
494
- const result = {};
495
- for (const [key, item] of Object.entries(value)) {
496
- result[key] = serializeClientIslandProp(item, `${path}.${key}`);
497
- }
498
- return result;
479
+ if (value === null) return null;
480
+ if (typeof value === "string" || typeof value === "boolean") return value;
481
+ if (typeof value === "number") {
482
+ if (!Number.isFinite(value)) throw new Error(`Client island prop "${path}" must be JSON-serializable; non-finite numbers are not supported.`);
483
+ return value;
484
+ }
485
+ if (typeof value === "function") throw new Error(`Client island prop "${path}" must be JSON-serializable; functions cannot be passed to the client.`);
486
+ if (value === void 0 || typeof value === "symbol" || typeof value === "bigint") throw new Error(`Client island prop "${path}" must be JSON-serializable; ${typeof value} values are not supported.`);
487
+ if (isJsxValue(value)) throw new Error(`Client island prop "${path}" must be JSON-serializable; JSX values cannot be passed to the client.`);
488
+ if (value instanceof Date) throw new Error(`Client island prop "${path}" must be JSON-serializable; Date values must be converted to strings.`);
489
+ if (Array.isArray(value)) return value.map((item, index) => serializeClientIslandProp(item, `${path}[${index}]`));
490
+ if (!isPlainObject(value)) throw new Error(`Client island prop "${path}" must be JSON-serializable; class instances are not supported.`);
491
+ const result = {};
492
+ for (const [key, item] of Object.entries(value)) result[key] = serializeClientIslandProp(item, `${path}.${key}`);
493
+ return result;
499
494
  }
500
495
  function isJsxValue(value) {
501
- return typeof value === "object" && value !== null && "tag" in value && "props" in value && "type" in value;
496
+ return typeof value === "object" && value !== null && "tag" in value && "props" in value && "type" in value;
502
497
  }
503
498
  function isPlainObject(value) {
504
- if (typeof value !== "object" || value === null) return false;
505
- const prototype = Object.getPrototypeOf(value);
506
- return prototype === Object.prototype || prototype === null;
507
- }
508
- function findAssetForHtmlUrl2(assets, value) {
509
- const normalized = decodeURIComponent(value).replace(/^\.?\//, "");
510
- return assets.filter((asset) => asset.type === "local").find((asset) => {
511
- const assetPath = localAssetRelativePath2(asset.sourcePath);
512
- return normalized === assetPath || normalized === `assets/${assetPath}`;
513
- });
499
+ if (typeof value !== "object" || value === null) return false;
500
+ const prototype = Object.getPrototypeOf(value);
501
+ return prototype === Object.prototype || prototype === null;
514
502
  }
515
- function localAssetRelativePath2(sourcePath) {
516
- const marker = "/assets/";
517
- const normalized = sourcePath.replaceAll("\\", "/");
518
- const markerIndex = normalized.indexOf(marker);
519
- return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + marker.length);
503
+ function findAssetForHtmlUrl(assets, value) {
504
+ const normalized = decodeURIComponent(value).replace(/^\.?\//, "");
505
+ return assets.filter((asset) => asset.type === "local").find((asset) => {
506
+ const assetPath = localAssetRelativePath(asset.sourcePath);
507
+ return normalized === assetPath || normalized === `assets/${assetPath}`;
508
+ });
509
+ }
510
+ function localAssetRelativePath(sourcePath) {
511
+ const marker = "/assets/";
512
+ const normalized = sourcePath.replaceAll("\\", "/");
513
+ const markerIndex = normalized.indexOf(marker);
514
+ return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + 8);
520
515
  }
521
516
  function normalizeVoidElementSpacing(html) {
522
- return html.replace(/<(img|br|hr)([^>]*)\/>/g, "<$1$2 />");
517
+ return html.replace(/<(img|br|hr)([^<>]*)\/>/g, "<$1$2 />");
523
518
  }
524
-
525
- // src/renderer/control-icons.ts
526
- import { jsx as jsx2 } from "hono/jsx/jsx-runtime";
519
+ //#endregion
520
+ //#region src/renderer/control-icons.ts
527
521
  function renderControlIcon(name, className = "hono-decks-control-icon") {
528
- return jsx2("svg", {
529
- class: className,
530
- "data-hono-decks-control-icon": true,
531
- viewBox: "0 0 24 24",
532
- "aria-hidden": "true",
533
- focusable: "false",
534
- fill: "none",
535
- "stroke-width": "2",
536
- "stroke-linecap": "round",
537
- "stroke-linejoin": "round",
538
- children: controlIconPaths(name)
539
- });
522
+ return jsx("svg", {
523
+ class: className,
524
+ "data-hono-decks-control-icon": true,
525
+ viewBox: "0 0 24 24",
526
+ "aria-hidden": "true",
527
+ focusable: "false",
528
+ fill: "none",
529
+ "stroke-width": "2",
530
+ "stroke-linecap": "round",
531
+ "stroke-linejoin": "round",
532
+ children: controlIconPaths(name)
533
+ });
540
534
  }
541
535
  function renderControlIconHtml(name, className = "hono-decks-control-icon") {
542
- return `<svg class="${className}" data-hono-decks-control-icon viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${controlIconPathHtml(name)}</svg>`;
536
+ return `<svg class="${className}" data-hono-decks-control-icon viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${controlIconPathHtml(name)}</svg>`;
543
537
  }
544
538
  function controlIconLabel(name) {
545
- switch (name) {
546
- case "deck-list":
547
- return "Deck list";
548
- case "home":
549
- return "Home";
550
- case "viewer":
551
- return "Viewer";
552
- case "projection":
553
- return "Projection";
554
- case "presenter":
555
- return "Presenter";
556
- case "previous":
557
- return "Previous slide";
558
- case "next":
559
- return "Next slide";
560
- case "fullscreen":
561
- return "Toggle fullscreen";
562
- case "print":
563
- return "Print view";
564
- case "details":
565
- return "Details";
566
- case "export-pdf":
567
- return "Export PDF";
568
- case "export-png":
569
- return "Export PNG";
570
- }
539
+ switch (name) {
540
+ case "deck-list": return "Deck list";
541
+ case "home": return "Home";
542
+ case "viewer": return "Viewer";
543
+ case "projection": return "Projection";
544
+ case "presenter": return "Presenter";
545
+ case "previous": return "Previous slide";
546
+ case "next": return "Next slide";
547
+ case "fullscreen": return "Toggle fullscreen";
548
+ case "print": return "Print view";
549
+ case "details": return "Details";
550
+ case "export-pdf": return "Export PDF";
551
+ case "export-png": return "Export PNG";
552
+ }
571
553
  }
572
554
  function controlIconPathHtml(name) {
573
- switch (name) {
574
- case "deck-list":
575
- return '<path d="M4 5h16" /><path d="M4 12h16" /><path d="M4 19h16" />';
576
- case "home":
577
- return '<path d="M3 11l9-8 9 8" /><path d="M5 10v10h14V10" /><path d="M9 20v-6h6v6" />';
578
- case "viewer":
579
- return '<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" /><circle cx="12" cy="12" r="3" />';
580
- case "projection":
581
- return '<path d="M4 5h16v10H4z" /><path d="M8 19h8" /><path d="M12 15v4" />';
582
- case "presenter":
583
- return '<path d="M4 5h16v10H4z" /><path d="M8 21l4-6 4 6" /><path d="M9 9h6" />';
584
- case "previous":
585
- return '<path d="M15 6l-6 6 6 6" />';
586
- case "next":
587
- return '<path d="M9 6l6 6-6 6" />';
588
- case "fullscreen":
589
- return '<path d="M8 3H5a2 2 0 0 0-2 2v3" /><path d="M16 3h3a2 2 0 0 1 2 2v3" /><path d="M8 21H5a2 2 0 0 1-2-2v-3" /><path d="M16 21h3a2 2 0 0 0 2-2v-3" />';
590
- case "print":
591
- return '<path d="M6 9V3h12v6" /><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" /><path d="M6 14h12v7H6z" />';
592
- case "details":
593
- return '<circle cx="12" cy="12" r="9" /><path d="M12 11v5" /><path d="M12 8h.01" />';
594
- case "export-pdf":
595
- return '<path d="M14 3v4a2 2 0 0 0 2 2h4" /><path d="M5 3h9l6 6v12H5z" /><path d="M8 15h8" /><path d="M8 18h5" />';
596
- case "export-png":
597
- return '<rect x="4" y="5" width="16" height="14" rx="2" /><path d="M8 14l2.5-2.5L14 15l2-2 2 2" /><circle cx="9" cy="9" r="1" />';
598
- }
555
+ switch (name) {
556
+ case "deck-list": return "<path d=\"M4 5h16\" /><path d=\"M4 12h16\" /><path d=\"M4 19h16\" />";
557
+ case "home": return "<path d=\"M3 11l9-8 9 8\" /><path d=\"M5 10v10h14V10\" /><path d=\"M9 20v-6h6v6\" />";
558
+ case "viewer": return "<path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />";
559
+ case "projection": return "<path d=\"M4 5h16v10H4z\" /><path d=\"M8 19h8\" /><path d=\"M12 15v4\" />";
560
+ case "presenter": return "<path d=\"M4 5h16v10H4z\" /><path d=\"M8 21l4-6 4 6\" /><path d=\"M9 9h6\" />";
561
+ case "previous": return "<path d=\"M15 6l-6 6 6 6\" />";
562
+ case "next": return "<path d=\"M9 6l6 6-6 6\" />";
563
+ case "fullscreen": return "<path d=\"M8 3H5a2 2 0 0 0-2 2v3\" /><path d=\"M16 3h3a2 2 0 0 1 2 2v3\" /><path d=\"M8 21H5a2 2 0 0 1-2-2v-3\" /><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />";
564
+ case "print": return "<path d=\"M6 9V3h12v6\" /><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" /><path d=\"M6 14h12v7H6z\" />";
565
+ case "details": return "<circle cx=\"12\" cy=\"12\" r=\"9\" /><path d=\"M12 11v5\" /><path d=\"M12 8h.01\" />";
566
+ case "export-pdf": return "<path d=\"M14 3v4a2 2 0 0 0 2 2h4\" /><path d=\"M5 3h9l6 6v12H5z\" /><path d=\"M8 15h8\" /><path d=\"M8 18h5\" />";
567
+ case "export-png": return "<rect x=\"4\" y=\"5\" width=\"16\" height=\"14\" rx=\"2\" /><path d=\"M8 14l2.5-2.5L14 15l2-2 2 2\" /><circle cx=\"9\" cy=\"9\" r=\"1\" />";
568
+ }
599
569
  }
600
570
  function controlIconPaths(name) {
601
- switch (name) {
602
- case "deck-list":
603
- return [
604
- jsx2("path", { d: "M4 5h16" }),
605
- jsx2("path", { d: "M4 12h16" }),
606
- jsx2("path", { d: "M4 19h16" })
607
- ];
608
- case "home":
609
- return [
610
- jsx2("path", { d: "M3 11l9-8 9 8" }),
611
- jsx2("path", { d: "M5 10v10h14V10" }),
612
- jsx2("path", { d: "M9 20v-6h6v6" })
613
- ];
614
- case "viewer":
615
- return [
616
- jsx2("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" }),
617
- jsx2("circle", { cx: "12", cy: "12", r: "3" })
618
- ];
619
- case "projection":
620
- return [
621
- jsx2("path", { d: "M4 5h16v10H4z" }),
622
- jsx2("path", { d: "M8 19h8" }),
623
- jsx2("path", { d: "M12 15v4" })
624
- ];
625
- case "presenter":
626
- return [
627
- jsx2("path", { d: "M4 5h16v10H4z" }),
628
- jsx2("path", { d: "M8 21l4-6 4 6" }),
629
- jsx2("path", { d: "M9 9h6" })
630
- ];
631
- case "previous":
632
- return [jsx2("path", { d: "M15 6l-6 6 6 6" })];
633
- case "next":
634
- return [jsx2("path", { d: "M9 6l6 6-6 6" })];
635
- case "fullscreen":
636
- return [
637
- jsx2("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
638
- jsx2("path", { d: "M16 3h3a2 2 0 0 1 2 2v3" }),
639
- jsx2("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
640
- jsx2("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
641
- ];
642
- case "print":
643
- return [
644
- jsx2("path", { d: "M6 9V3h12v6" }),
645
- jsx2("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
646
- jsx2("path", { d: "M6 14h12v7H6z" })
647
- ];
648
- case "details":
649
- return [
650
- jsx2("circle", { cx: "12", cy: "12", r: "9" }),
651
- jsx2("path", { d: "M12 11v5" }),
652
- jsx2("path", { d: "M12 8h.01" })
653
- ];
654
- case "export-pdf":
655
- return [
656
- jsx2("path", { d: "M14 3v4a2 2 0 0 0 2 2h4" }),
657
- jsx2("path", { d: "M5 3h9l6 6v12H5z" }),
658
- jsx2("path", { d: "M8 15h8" }),
659
- jsx2("path", { d: "M8 18h5" })
660
- ];
661
- case "export-png":
662
- return [
663
- jsx2("rect", { x: "4", y: "5", width: "16", height: "14", rx: "2" }),
664
- jsx2("path", { d: "M8 14l2.5-2.5L14 15l2-2 2 2" }),
665
- jsx2("circle", { cx: "9", cy: "9", r: "1" })
666
- ];
667
- }
668
- }
669
-
670
- // src/server/document.ts
571
+ switch (name) {
572
+ case "deck-list": return [
573
+ jsx("path", { d: "M4 5h16" }),
574
+ jsx("path", { d: "M4 12h16" }),
575
+ jsx("path", { d: "M4 19h16" })
576
+ ];
577
+ case "home": return [
578
+ jsx("path", { d: "M3 11l9-8 9 8" }),
579
+ jsx("path", { d: "M5 10v10h14V10" }),
580
+ jsx("path", { d: "M9 20v-6h6v6" })
581
+ ];
582
+ case "viewer": return [jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" }), jsx("circle", {
583
+ cx: "12",
584
+ cy: "12",
585
+ r: "3"
586
+ })];
587
+ case "projection": return [
588
+ jsx("path", { d: "M4 5h16v10H4z" }),
589
+ jsx("path", { d: "M8 19h8" }),
590
+ jsx("path", { d: "M12 15v4" })
591
+ ];
592
+ case "presenter": return [
593
+ jsx("path", { d: "M4 5h16v10H4z" }),
594
+ jsx("path", { d: "M8 21l4-6 4 6" }),
595
+ jsx("path", { d: "M9 9h6" })
596
+ ];
597
+ case "previous": return [jsx("path", { d: "M15 6l-6 6 6 6" })];
598
+ case "next": return [jsx("path", { d: "M9 6l6 6-6 6" })];
599
+ case "fullscreen": return [
600
+ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
601
+ jsx("path", { d: "M16 3h3a2 2 0 0 1 2 2v3" }),
602
+ jsx("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
603
+ jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
604
+ ];
605
+ case "print": return [
606
+ jsx("path", { d: "M6 9V3h12v6" }),
607
+ jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
608
+ jsx("path", { d: "M6 14h12v7H6z" })
609
+ ];
610
+ case "details": return [
611
+ jsx("circle", {
612
+ cx: "12",
613
+ cy: "12",
614
+ r: "9"
615
+ }),
616
+ jsx("path", { d: "M12 11v5" }),
617
+ jsx("path", { d: "M12 8h.01" })
618
+ ];
619
+ case "export-pdf": return [
620
+ jsx("path", { d: "M14 3v4a2 2 0 0 0 2 2h4" }),
621
+ jsx("path", { d: "M5 3h9l6 6v12H5z" }),
622
+ jsx("path", { d: "M8 15h8" }),
623
+ jsx("path", { d: "M8 18h5" })
624
+ ];
625
+ case "export-png": return [
626
+ jsx("rect", {
627
+ x: "4",
628
+ y: "5",
629
+ width: "16",
630
+ height: "14",
631
+ rx: "2"
632
+ }),
633
+ jsx("path", { d: "M8 14l2.5-2.5L14 15l2-2 2 2" }),
634
+ jsx("circle", {
635
+ cx: "9",
636
+ cy: "9",
637
+ r: "1"
638
+ })
639
+ ];
640
+ }
641
+ }
642
+ //#endregion
643
+ //#region src/server/document.ts
671
644
  async function resolveDeckDocument(input, options, overrides) {
672
- const page = {
673
- lang: options?.lang,
674
- nonce: options?.nonce,
675
- head: options?.head,
676
- ...options?.surfaces?.[input.surface],
677
- ...definedDocumentOptions(overrides)
678
- };
679
- const langValue = await resolveDocumentValue(page.lang, input);
680
- const nonce = await resolveDocumentValue(page.nonce, input);
681
- const headValue = await resolveDocumentValue(page.head, input);
682
- return {
683
- lang: langValue?.trim() || "ja",
684
- ...nonce ? { nonce } : {},
685
- head: headValue === void 0 || headValue === null || headValue === false ? "" : await renderJsxValue(headValue)
686
- };
645
+ const page = {
646
+ lang: options?.lang,
647
+ nonce: options?.nonce,
648
+ head: options?.head,
649
+ ...options?.surfaces?.[input.surface],
650
+ ...definedDocumentOptions(overrides)
651
+ };
652
+ const langValue = await resolveDocumentValue(page.lang, input);
653
+ const nonce = await resolveDocumentValue(page.nonce, input);
654
+ const headValue = await resolveDocumentValue(page.head, input);
655
+ return {
656
+ lang: langValue?.trim() || "ja",
657
+ ...nonce ? { nonce } : {},
658
+ head: headValue === void 0 || headValue === null || headValue === false ? "" : await renderJsxValue(headValue)
659
+ };
687
660
  }
688
661
  function documentNonceAttribute(nonce) {
689
- return nonce ? ` nonce="${escapeHtml2(nonce)}"` : "";
662
+ return nonce ? ` nonce="${escapeHtml$7(nonce)}"` : "";
690
663
  }
691
664
  function definedDocumentOptions(options) {
692
- if (!options) return {};
693
- return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0));
665
+ if (!options) return {};
666
+ return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0));
694
667
  }
695
668
  async function resolveDocumentValue(value, input) {
696
- return typeof value === "function" ? await value(input) : await value;
669
+ return typeof value === "function" ? await value(input) : await value;
697
670
  }
698
- function escapeHtml2(value) {
699
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
671
+ function escapeHtml$7(value) {
672
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
700
673
  }
701
-
702
- // src/renderer/presentation-script.ts
674
+ //#endregion
675
+ //#region src/renderer/presentation-script.ts
703
676
  function renderLiveReloadScript(eventsPath, nonce) {
704
- return `<script${documentNonceAttribute(nonce)}>
677
+ return `<script${documentNonceAttribute(nonce)}>
705
678
  (() => {
706
679
  const eventsUrl = ${JSON.stringify(eventsPath)};
707
680
  const events = new EventSource(eventsUrl);
708
681
  events.addEventListener("deck:updated", () => location.reload());
709
682
  })();
710
- </script>`;
683
+ <\/script>`;
711
684
  }
712
685
  function renderClientEntryScript(clientEntry, nonce) {
713
- return `<script type="module" src="${escapeHtml3(clientEntry)}"${documentNonceAttribute(nonce)}></script>`;
686
+ return `<script type="module" src="${escapeHtml$6(clientEntry)}"${documentNonceAttribute(nonce)}><\/script>`;
714
687
  }
715
688
  function renderPresentationScript(nonce) {
716
- return `<script${documentNonceAttribute(nonce)}>
689
+ return `<script${documentNonceAttribute(nonce)}>
717
690
  (() => {
718
691
  const slides = Array.from(document.querySelectorAll(".slide"));
719
692
  const stage = document.querySelector("[data-hono-decks-stage]");
@@ -768,10 +741,10 @@ function renderPresentationScript(nonce) {
768
741
  const entries = slideFires(slide).map((fire) => {
769
742
  const rawAt = fire.getAttribute("data-fire-at");
770
743
  let position;
771
- if (rawAt !== null && /^d+$/.test(rawAt)) {
744
+ if (rawAt !== null && /^\d+$/.test(rawAt)) {
772
745
  position = Number(rawAt);
773
746
  } else {
774
- const offset = rawAt !== null && /^[+-]d+$/.test(rawAt) ? Number(rawAt) : 1;
747
+ const offset = rawAt !== null && /^[+-]\d+$/.test(rawAt) ? Number(rawAt) : 1;
775
748
  cursor += offset;
776
749
  position = cursor;
777
750
  }
@@ -1096,20 +1069,20 @@ function renderPresentationScript(nonce) {
1096
1069
  const initialState = readInitialState();
1097
1070
  show(initialState.index, initialState.stepIndex);
1098
1071
  })();
1099
- </script>`;
1072
+ <\/script>`;
1100
1073
  }
1101
- function escapeHtml3(value) {
1102
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1074
+ function escapeHtml$6(value) {
1075
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
1103
1076
  }
1104
-
1105
- // src/renderer/presentation-style.ts
1106
- var PRESENTATION_DESIGN_WIDTH = 1920;
1107
- var PRESENTATION_DESIGN_HEIGHT = 1080;
1077
+ //#endregion
1078
+ //#region src/renderer/presentation-style.ts
1079
+ const PRESENTATION_DESIGN_WIDTH = 1920;
1080
+ const PRESENTATION_DESIGN_HEIGHT = 1080;
1108
1081
  function presentationDesignCssVariables() {
1109
- return `--hono-decks-width:${PRESENTATION_DESIGN_WIDTH}px;--hono-decks-height:${PRESENTATION_DESIGN_HEIGHT}px`;
1082
+ return `--hono-decks-width:${PRESENTATION_DESIGN_WIDTH}px;--hono-decks-height:${PRESENTATION_DESIGN_HEIGHT}px`;
1110
1083
  }
1111
1084
  function basePresentationStyle() {
1112
- return `
1085
+ return `
1113
1086
  :root{color-scheme:dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:32px;${presentationDesignCssVariables()};--hono-decks-transition-duration:.24s;--hono-decks-transition-easing:ease;--hono-decks-color:#eef2ff;--hono-decks-muted-color:#cbd5e1;--hono-decks-accent-color:#8bd3ff;--hono-decks-border-color:rgba(148,163,184,.24);--hono-decks-inline-code-background:rgba(15,23,42,.72);--hono-decks-code-background:rgba(15,23,42,.78);--hono-decks-card-background:rgba(15,23,42,.78);--hono-decks-card-image-background:rgba(255,255,255,.08);--hono-decks-warning-background:rgba(255,193,7,.12);--hono-decks-warning-color:#ffe59b;color:var(--hono-decks-color)}
1114
1087
  html,body{margin:0;width:100%;height:100%;overflow:hidden}
1115
1088
  .hono-decks-stage{width:100vw;height:100vh;overflow:hidden;position:relative;display:grid;place-items:center}
@@ -1204,36 +1177,36 @@ body[data-hono-decks-print-preview] [data-hono-decks-fire]{visibility:visible!im
1204
1177
  @media print{.hono-decks-embed-viewport iframe,.hono-decks-tweet-embed iframe,.hono-decks-tweet-embed .twitter-tweet{display:none!important}.hono-decks-embed-print-fallback{display:grid;width:100%;height:100%;place-items:center;overflow:hidden;background:var(--hono-decks-card-background);color:var(--hono-decks-accent-color);text-decoration:none}.hono-decks-embed-print-fallback:has(.hono-decks-embed-print-poster){display:block}.hono-decks-embed-print-poster+span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.hono-decks-tweet-print-fallback{display:flex;min-height:12rem;align-items:center;justify-content:center;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);color:var(--hono-decks-accent-color);text-decoration:none}}
1205
1178
  @media (prefers-reduced-motion: reduce){*,*::before,*::after{scroll-behavior:auto!important;animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;transition-delay:0s!important}.slide[data-active-transition]{transform:none!important}}`;
1206
1179
  }
1207
-
1208
- // src/renderer/presentation-page.ts
1180
+ //#endregion
1181
+ //#region src/renderer/presentation-page.ts
1209
1182
  function presentationPageTitle(deck) {
1210
- return deck.meta.title ?? deck.slug;
1183
+ return deck.meta.title ?? deck.slug;
1211
1184
  }
1212
1185
  function renderCompiledDeckPage(input) {
1213
- const { deck } = input;
1214
- const document = input.document ?? { lang: "ja", head: "" };
1215
- const nonceAttribute = documentNonceAttribute(document.nonce);
1216
- const warnings = renderWarnings(deck);
1217
- const htmlAttrs = input.printPreview ? ' data-hono-decks-print-preview="true"' : "";
1218
- const bodyAttrs = [
1219
- input.printPreview ? 'data-hono-decks-print-preview="true"' : "",
1220
- input.speakerNotes === false ? 'data-hono-decks-projection="true"' : ""
1221
- ].filter(Boolean);
1222
- return `<!doctype html>
1223
- <html lang="${escapeHtml4(document.lang)}"${htmlAttrs}>
1186
+ const { deck } = input;
1187
+ const document = input.document ?? {
1188
+ lang: "ja",
1189
+ head: ""
1190
+ };
1191
+ const nonceAttribute = documentNonceAttribute(document.nonce);
1192
+ const warnings = renderWarnings(deck);
1193
+ const htmlAttrs = input.printPreview ? " data-hono-decks-print-preview=\"true\"" : "";
1194
+ const bodyAttrs = [input.printPreview ? "data-hono-decks-print-preview=\"true\"" : "", input.speakerNotes === false ? "data-hono-decks-projection=\"true\"" : ""].filter(Boolean);
1195
+ return `<!doctype html>
1196
+ <html lang="${escapeHtml$5(document.lang)}"${htmlAttrs}>
1224
1197
  <head>
1225
1198
  <meta charset="utf-8" />
1226
1199
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1227
- <title>${escapeHtml4(presentationPageTitle(deck))}</title>
1200
+ <title>${escapeHtml$5(presentationPageTitle(deck))}</title>
1228
1201
  <style${nonceAttribute}>${basePresentationStyle()}${deck.themeStyle ?? ""}${input.style ?? ""}</style>
1229
1202
  ${document.head}
1230
1203
  </head>
1231
1204
  <body${bodyAttrs.length ? ` ${bodyAttrs.join(" ")}` : ""}>
1232
1205
  ${warnings}
1233
1206
  ${renderCompiledDeck(deck, {
1234
- components: mergeComponentInputs(deck.componentRegistry, input.components),
1235
- speakerNotes: input.speakerNotes
1236
- })}
1207
+ components: mergeComponentInputs(deck.componentRegistry, input.components),
1208
+ speakerNotes: input.speakerNotes
1209
+ })}
1237
1210
  ${input.printPreview ? renderPrintPreviewScript(document.nonce) : renderPresentationScript(document.nonce)}
1238
1211
  ${!input.printPreview && input.liveReloadPath ? renderLiveReloadScript(input.liveReloadPath, document.nonce) : ""}
1239
1212
  ${!input.printPreview && input.clientEntry ? renderClientEntryScript(input.clientEntry, document.nonce) : ""}
@@ -1241,30 +1214,30 @@ function renderCompiledDeckPage(input) {
1241
1214
  </html>`;
1242
1215
  }
1243
1216
  async function renderCompiledDeckPageAsync(input) {
1244
- const { deck } = input;
1245
- const document = input.document ?? { lang: "ja", head: "" };
1246
- const nonceAttribute = documentNonceAttribute(document.nonce);
1247
- const warnings = renderWarnings(deck);
1248
- const htmlAttrs = input.printPreview ? ' data-hono-decks-print-preview="true"' : "";
1249
- const bodyAttrs = [
1250
- input.printPreview ? 'data-hono-decks-print-preview="true"' : "",
1251
- input.speakerNotes === false ? 'data-hono-decks-projection="true"' : ""
1252
- ].filter(Boolean);
1253
- return `<!doctype html>
1254
- <html lang="${escapeHtml4(document.lang)}"${htmlAttrs}>
1217
+ const { deck } = input;
1218
+ const document = input.document ?? {
1219
+ lang: "ja",
1220
+ head: ""
1221
+ };
1222
+ const nonceAttribute = documentNonceAttribute(document.nonce);
1223
+ const warnings = renderWarnings(deck);
1224
+ const htmlAttrs = input.printPreview ? " data-hono-decks-print-preview=\"true\"" : "";
1225
+ const bodyAttrs = [input.printPreview ? "data-hono-decks-print-preview=\"true\"" : "", input.speakerNotes === false ? "data-hono-decks-projection=\"true\"" : ""].filter(Boolean);
1226
+ return `<!doctype html>
1227
+ <html lang="${escapeHtml$5(document.lang)}"${htmlAttrs}>
1255
1228
  <head>
1256
1229
  <meta charset="utf-8" />
1257
1230
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1258
- <title>${escapeHtml4(presentationPageTitle(deck))}</title>
1231
+ <title>${escapeHtml$5(presentationPageTitle(deck))}</title>
1259
1232
  <style${nonceAttribute}>${basePresentationStyle()}${deck.themeStyle ?? ""}${input.style ?? ""}</style>
1260
1233
  ${document.head}
1261
1234
  </head>
1262
1235
  <body${bodyAttrs.length ? ` ${bodyAttrs.join(" ")}` : ""}>
1263
1236
  ${warnings}
1264
1237
  ${await renderCompiledDeckAsync(deck, {
1265
- components: mergeComponentInputs(deck.componentRegistry, input.components),
1266
- speakerNotes: input.speakerNotes
1267
- })}
1238
+ components: mergeComponentInputs(deck.componentRegistry, input.components),
1239
+ speakerNotes: input.speakerNotes
1240
+ })}
1268
1241
  ${input.printPreview ? renderPrintPreviewScript(document.nonce) : renderPresentationScript(document.nonce)}
1269
1242
  ${!input.printPreview && input.liveReloadPath ? renderLiveReloadScript(input.liveReloadPath, document.nonce) : ""}
1270
1243
  ${!input.printPreview && input.clientEntry ? renderClientEntryScript(input.clientEntry, document.nonce) : ""}
@@ -1272,27 +1245,31 @@ async function renderCompiledDeckPageAsync(input) {
1272
1245
  </html>`;
1273
1246
  }
1274
1247
  async function renderPresenterPageAsync(input) {
1275
- const { deck } = input;
1276
- const document = input.document ?? { lang: "ja", head: "" };
1277
- const nonceAttribute = documentNonceAttribute(document.nonce);
1278
- const components = mergeComponentInputs(deck.componentRegistry, input.components);
1279
- const mountPath = input.mountPath.replace(/\/$/, "") || "/";
1280
- const deckPath = `${mountPath === "/" ? "" : mountPath}/${encodeURIComponent(deck.slug)}`;
1281
- const stateQuery = input.presenterStateQuery ?? "";
1282
- const deckHref = `${deckPath}${stateQuery}`;
1283
- const projectionPath = `${deckPath}/presentation${stateQuery}`;
1284
- const previews = await Promise.all(
1285
- deck.slides.map(
1286
- (slide) => renderCompiledSlideAsync(slide, deck.assets, { components, deck, speakerNotes: false, slideState: "active" })
1287
- )
1288
- );
1289
- const notes = deck.slides.map((slide) => slide.notes ?? slide.meta.notes);
1290
- return `<!doctype html>
1291
- <html lang="${escapeHtml4(document.lang)}">
1248
+ const { deck } = input;
1249
+ const document = input.document ?? {
1250
+ lang: "ja",
1251
+ head: ""
1252
+ };
1253
+ const nonceAttribute = documentNonceAttribute(document.nonce);
1254
+ const components = mergeComponentInputs(deck.componentRegistry, input.components);
1255
+ const mountPath = input.mountPath.replace(/\/$/, "") || "/";
1256
+ const deckPath = `${mountPath === "/" ? "" : mountPath}/${encodeURIComponent(deck.slug)}`;
1257
+ const stateQuery = input.presenterStateQuery ?? "";
1258
+ const deckHref = `${deckPath}${stateQuery}`;
1259
+ const projectionPath = `${deckPath}/presentation${stateQuery}`;
1260
+ const previews = await Promise.all(deck.slides.map((slide) => renderCompiledSlideAsync(slide, deck.assets, {
1261
+ components,
1262
+ deck,
1263
+ speakerNotes: false,
1264
+ slideState: "active"
1265
+ })));
1266
+ const notes = deck.slides.map((slide) => slide.notes ?? slide.meta.notes);
1267
+ return `<!doctype html>
1268
+ <html lang="${escapeHtml$5(document.lang)}">
1292
1269
  <head>
1293
1270
  <meta charset="utf-8" />
1294
1271
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1295
- <title>${escapeHtml4(presentationPageTitle(deck))} - Presenter</title>
1272
+ <title>${escapeHtml$5(presentationPageTitle(deck))} - Presenter</title>
1296
1273
  <style${nonceAttribute}>${basePresenterStyle()}${deck.themeStyle ?? ""}${input.style ?? ""}</style>
1297
1274
  ${document.head}
1298
1275
  </head>
@@ -1300,31 +1277,27 @@ async function renderPresenterPageAsync(input) {
1300
1277
  <main class="hono-decks-presenter" data-hono-decks-presenter data-slide-index="0">
1301
1278
  <section class="hono-decks-presenter-current" data-hono-decks-presenter-current aria-label="Current slide">
1302
1279
  <nav class="hono-decks-presenter-controls" data-hono-decks-presenter-controls aria-label="Presenter controls">
1303
- <a href="${escapeHtml4(mountPath)}" title="Deck list" aria-label="Deck list">${renderControlIconHtml("home")}</a>
1304
- <a href="${escapeHtml4(deckHref)}" title="Viewer" aria-label="Viewer">${renderControlIconHtml("viewer")}</a>
1305
- <button type="button" data-action="openProjection" data-projection-url="${escapeHtml4(projectionPath)}" title="Projection" aria-label="Projection">${renderControlIconHtml("projection")}</button>
1280
+ <a href="${escapeHtml$5(mountPath)}" title="Deck list" aria-label="Deck list">${renderControlIconHtml("home")}</a>
1281
+ <a href="${escapeHtml$5(deckHref)}" title="Viewer" aria-label="Viewer">${renderControlIconHtml("viewer")}</a>
1282
+ <button type="button" data-action="openProjection" data-projection-url="${escapeHtml$5(projectionPath)}" title="Projection" aria-label="Projection">${renderControlIconHtml("projection")}</button>
1306
1283
  <button type="button" data-action="previous" title="Previous slide" aria-label="Previous slide">${renderControlIconHtml("previous")}</button>
1307
1284
  <span data-hono-decks-presenter-position>1 / ${deck.slides.length}</span>
1308
1285
  <button type="button" data-action="next" title="Next slide" aria-label="Next slide">${renderControlIconHtml("next")}</button>
1309
1286
  <span data-hono-decks-presenter-connection>Connected</span>
1310
1287
  </nav>
1311
- <iframe title="${escapeHtml4(presentationPageTitle(deck))}" src="${escapeHtml4(projectionPath)}"></iframe>
1288
+ <iframe title="${escapeHtml$5(presentationPageTitle(deck))}" src="${escapeHtml$5(projectionPath)}"></iframe>
1312
1289
  </section>
1313
1290
  <aside class="hono-decks-presenter-panel" aria-label="Presenter panel">
1314
1291
  <section class="hono-decks-presenter-next" data-hono-decks-presenter-next aria-label="Next slide preview">
1315
1292
  <h2>Next slide</h2>
1316
1293
  <div class="hono-decks-presenter-preview-list">
1317
- ${previews.map(
1318
- (preview, index) => `<div class="hono-decks-presenter-preview" data-hono-decks-presenter-preview data-slide-index="${index}"${index === 1 ? "" : " hidden"}>${preview}</div>`
1319
- ).join("\n")}
1294
+ ${previews.map((preview, index) => `<div class="hono-decks-presenter-preview" data-hono-decks-presenter-preview data-slide-index="${index}"${index === 1 ? "" : " hidden"}>${preview}</div>`).join("\n")}
1320
1295
  <p class="hono-decks-presenter-no-next" data-hono-decks-presenter-no-next${deck.slides.length > 1 ? " hidden" : ""}>No next slide</p>
1321
1296
  </div>
1322
1297
  </section>
1323
1298
  <section class="hono-decks-presenter-notes" data-hono-decks-presenter-notes aria-label="Speaker notes">
1324
1299
  <h2>Speaker notes</h2>
1325
- ${notes.map(
1326
- (note, index) => `<article data-hono-decks-presenter-note data-slide-index="${index}"${index === 0 ? "" : " hidden"}>${note ? escapeHtml4(note) : "<p>No speaker notes.</p>"}</article>`
1327
- ).join("\n")}
1300
+ ${notes.map((note, index) => `<article data-hono-decks-presenter-note data-slide-index="${index}"${index === 0 ? "" : " hidden"}>${note ? escapeHtml$5(note) : "<p>No speaker notes.</p>"}</article>`).join("\n")}
1328
1301
  </section>
1329
1302
  </aside>
1330
1303
  </main>
@@ -1333,10 +1306,10 @@ async function renderPresenterPageAsync(input) {
1333
1306
  </html>`;
1334
1307
  }
1335
1308
  function renderWarnings(deck) {
1336
- return deck.warnings.length ? `<aside class="hono-decks-warnings">${deck.warnings.map((warning) => `<p>${escapeHtml4(warning.message)}</p>`).join("")}</aside>` : "";
1309
+ return deck.warnings.length ? `<aside class="hono-decks-warnings">${deck.warnings.map((warning) => `<p>${escapeHtml$5(warning.message)}</p>`).join("")}</aside>` : "";
1337
1310
  }
1338
1311
  function renderPrintPreviewScript(nonce) {
1339
- return `<script${documentNonceAttribute(nonce)}>
1312
+ return `<script${documentNonceAttribute(nonce)}>
1340
1313
  (() => {
1341
1314
  const params = new URLSearchParams(window.location.search);
1342
1315
  if (params.get("autoprint") !== "1") return;
@@ -1344,15 +1317,18 @@ function renderPrintPreviewScript(nonce) {
1344
1317
  window.requestAnimationFrame(() => window.print());
1345
1318
  }, { once: true });
1346
1319
  })();
1347
- </script>`;
1320
+ <\/script>`;
1348
1321
  }
1349
1322
  function mergeComponentInputs(base, overrides) {
1350
- if (!base) return overrides;
1351
- if (!overrides) return base;
1352
- return { ...base, ...overrides };
1323
+ if (!base) return overrides;
1324
+ if (!overrides) return base;
1325
+ return {
1326
+ ...base,
1327
+ ...overrides
1328
+ };
1353
1329
  }
1354
1330
  function basePresenterStyle() {
1355
- return `${basePresentationStyle()}
1331
+ return `${basePresentationStyle()}
1356
1332
  body{margin:0;min-height:100vh;background:#050816;color:#eef2ff;font-family:Inter,ui-sans-serif,system-ui,sans-serif}
1357
1333
  .hono-decks-presenter{box-sizing:border-box;display:grid;grid-template-columns:minmax(0,2fr) minmax(320px,1fr);gap:16px;min-height:100vh;padding:16px}
1358
1334
  .hono-decks-presenter-current,.hono-decks-presenter-panel{min-width:0}
@@ -1374,7 +1350,7 @@ body:not([data-overview-mode]) .hono-decks-presenter-preview .slide{position:abs
1374
1350
  @media (max-width:900px){.hono-decks-presenter{grid-template-columns:1fr}.hono-decks-presenter-panel{grid-template-rows:auto auto}}`;
1375
1351
  }
1376
1352
  function renderPresenterScript(slideCount, nonce) {
1377
- return `<script${documentNonceAttribute(nonce)}>
1353
+ return `<script${documentNonceAttribute(nonce)}>
1378
1354
  (() => {
1379
1355
  const root = document.querySelector("[data-hono-decks-presenter]");
1380
1356
  const frame = document.querySelector("[data-hono-decks-presenter-current] iframe");
@@ -1485,273 +1461,311 @@ function renderPresenterScript(slideCount, nonce) {
1485
1461
  }
1486
1462
  });
1487
1463
  })();
1488
- </script>`;
1464
+ <\/script>`;
1489
1465
  }
1490
- function escapeHtml4(value) {
1491
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1466
+ function escapeHtml$5(value) {
1467
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
1492
1468
  }
1493
-
1494
- // src/renderer/compiled-render.ts
1469
+ //#endregion
1470
+ //#region src/renderer/compiled-render.ts
1495
1471
  function renderCompiledDeck(deck, input = {}) {
1496
- const components = normalizeComponents(input.components);
1497
- return `<main class="hono-decks-stage" data-hono-decks-stage data-deck-slug="${escapeHtml5(deck.slug)}"><div class="hono-decks-deck" data-hono-decks-deck>${deck.slides.map((slide) => renderCompiledSlide(slide, deck.assets, { components, deck, speakerNotes: input.speakerNotes })).join("\n")}</div></main>`;
1472
+ const components = normalizeComponents(input.components);
1473
+ return `<main class="hono-decks-stage" data-hono-decks-stage data-deck-slug="${escapeHtml$4(deck.slug)}"><div class="hono-decks-deck" data-hono-decks-deck>${deck.slides.map((slide) => renderCompiledSlide(slide, deck.assets, {
1474
+ components,
1475
+ deck,
1476
+ speakerNotes: input.speakerNotes
1477
+ })).join("\n")}</div></main>`;
1498
1478
  }
1499
1479
  async function renderCompiledDeckAsync(deck, input = {}) {
1500
- const components = normalizeComponents(input.components);
1501
- const slides = await Promise.all(
1502
- deck.slides.map(
1503
- (slide) => renderCompiledSlideAsync(slide, deck.assets, { components, deck, speakerNotes: input.speakerNotes })
1504
- )
1505
- );
1506
- return `<main class="hono-decks-stage" data-hono-decks-stage data-deck-slug="${escapeHtml5(deck.slug)}"><div class="hono-decks-deck" data-hono-decks-deck>${slides.join(
1507
- "\n"
1508
- )}</div></main>`;
1480
+ const components = normalizeComponents(input.components);
1481
+ const slides = await Promise.all(deck.slides.map((slide) => renderCompiledSlideAsync(slide, deck.assets, {
1482
+ components,
1483
+ deck,
1484
+ speakerNotes: input.speakerNotes
1485
+ })));
1486
+ return `<main class="hono-decks-stage" data-hono-decks-stage data-deck-slug="${escapeHtml$4(deck.slug)}"><div class="hono-decks-deck" data-hono-decks-deck>${slides.join("\n")}</div></main>`;
1509
1487
  }
1510
1488
  function renderCompiledSlide(slide, assets = [], input = {}) {
1511
- const components = normalizeComponents(input.components);
1512
- const slideState = input.slideState ?? "inactive";
1513
- const layout = slide.meta.layout ?? "default";
1514
- const classes = ["slide", `layout-${safeClass(layout)}`, slide.meta.className ? safeClass(slide.meta.className) : ""].filter(Boolean).join(" ");
1515
- const notes = slide.notes ?? slide.meta.notes;
1516
- const notesHtml = input.speakerNotes === false || !notes ? "" : `<aside class="speaker-notes" hidden>${escapeHtml5(notes)}</aside>`;
1517
- const html = slide.nodes?.length ? renderSlideNodes(slide.nodes, { components, assets }) : rewriteLocalAssetUrls(slide.html, assets);
1518
- const style = slideStyleAttribute(slide, assets);
1519
- const transition = slide.meta.transition ? ` data-transition="${escapeHtml5(safeClass(slide.meta.transition))}"` : "";
1520
- return `<section class="${classes}" data-slide-index="${slide.index}" data-slide-state="${slideState}"${slide.meta.title ? ` aria-label="${escapeHtml5(slide.meta.title)}"` : ""}${transition}${style}><div class="hono-decks-slide-content">${html}</div>${notesHtml}</section>`;
1489
+ const components = normalizeComponents(input.components);
1490
+ const slideState = input.slideState ?? "inactive";
1491
+ const classes = [
1492
+ "slide",
1493
+ `layout-${safeClass(slide.meta.layout ?? "default")}`,
1494
+ slide.meta.className ? safeClass(slide.meta.className) : ""
1495
+ ].filter(Boolean).join(" ");
1496
+ const notes = slide.notes ?? slide.meta.notes;
1497
+ const notesHtml = input.speakerNotes === false || !notes ? "" : `<aside class="speaker-notes" hidden>${escapeHtml$4(notes)}</aside>`;
1498
+ const html = slide.nodes?.length ? renderSlideNodes(slide.nodes, {
1499
+ components,
1500
+ assets
1501
+ }) : rewriteLocalAssetUrls(slide.html, assets);
1502
+ const style = slideStyleAttribute(slide, assets);
1503
+ const transition = slide.meta.transition ? ` data-transition="${escapeHtml$4(safeClass(slide.meta.transition))}"` : "";
1504
+ return `<section class="${classes}" data-slide-index="${slide.index}" data-slide-state="${slideState}"${slide.meta.title ? ` aria-label="${escapeHtml$4(slide.meta.title)}"` : ""}${transition}${style}><div class="hono-decks-slide-content">${html}</div>${notesHtml}</section>`;
1521
1505
  }
1522
1506
  async function renderCompiledSlideAsync(slide, assets = [], input = {}) {
1523
- const components = normalizeComponents(input.components);
1524
- const slideState = input.slideState ?? "inactive";
1525
- const layout = slide.meta.layout ?? "default";
1526
- const classes = ["slide", `layout-${safeClass(layout)}`, slide.meta.className ? safeClass(slide.meta.className) : ""].filter(Boolean).join(" ");
1527
- const notes = slide.notes ?? slide.meta.notes;
1528
- const notesHtml = input.speakerNotes === false || !notes ? "" : `<aside class="speaker-notes" hidden>${escapeHtml5(notes)}</aside>`;
1529
- const html = await renderSlideBodyAsync(slide, assets, { ...input, components });
1530
- const style = slideStyleAttribute(slide, assets);
1531
- const transition = slide.meta.transition ? ` data-transition="${escapeHtml5(safeClass(slide.meta.transition))}"` : "";
1532
- return `<section class="${classes}" data-slide-index="${slide.index}" data-slide-state="${slideState}"${slide.meta.title ? ` aria-label="${escapeHtml5(slide.meta.title)}"` : ""}${transition}${style}><div class="hono-decks-slide-content">${html}</div>${notesHtml}</section>`;
1507
+ const components = normalizeComponents(input.components);
1508
+ const slideState = input.slideState ?? "inactive";
1509
+ const classes = [
1510
+ "slide",
1511
+ `layout-${safeClass(slide.meta.layout ?? "default")}`,
1512
+ slide.meta.className ? safeClass(slide.meta.className) : ""
1513
+ ].filter(Boolean).join(" ");
1514
+ const notes = slide.notes ?? slide.meta.notes;
1515
+ const notesHtml = input.speakerNotes === false || !notes ? "" : `<aside class="speaker-notes" hidden>${escapeHtml$4(notes)}</aside>`;
1516
+ const html = await renderSlideBodyAsync(slide, assets, {
1517
+ ...input,
1518
+ components
1519
+ });
1520
+ const style = slideStyleAttribute(slide, assets);
1521
+ const transition = slide.meta.transition ? ` data-transition="${escapeHtml$4(safeClass(slide.meta.transition))}"` : "";
1522
+ return `<section class="${classes}" data-slide-index="${slide.index}" data-slide-state="${slideState}"${slide.meta.title ? ` aria-label="${escapeHtml$4(slide.meta.title)}"` : ""}${transition}${style}><div class="hono-decks-slide-content">${html}</div>${notesHtml}</section>`;
1533
1523
  }
1534
1524
  function slideStyleAttribute(slide, assets) {
1535
- const declarations = [];
1536
- if (slide.meta.background) declarations.push(backgroundStyle(slide.meta.background, assets));
1537
- if (slide.meta.transitionDuration) {
1538
- declarations.push(`--hono-decks-slide-transition-duration:${slide.meta.transitionDuration}`);
1539
- }
1540
- if (slide.meta.transitionEasing) {
1541
- declarations.push(`--hono-decks-slide-transition-easing:${slide.meta.transitionEasing}`);
1542
- }
1543
- return declarations.length ? ` style="${escapeHtml5(declarations.join(";"))}"` : "";
1525
+ const declarations = [];
1526
+ if (slide.meta.background) declarations.push(backgroundStyle(slide.meta.background, assets));
1527
+ if (slide.meta.transitionDuration) declarations.push(`--hono-decks-slide-transition-duration:${slide.meta.transitionDuration}`);
1528
+ if (slide.meta.transitionEasing) declarations.push(`--hono-decks-slide-transition-easing:${slide.meta.transitionEasing}`);
1529
+ return declarations.length ? ` style="${escapeHtml$4(declarations.join(";"))}"` : "";
1544
1530
  }
1545
1531
  async function renderSlideBodyAsync(slide, assets, input) {
1546
- try {
1547
- return slide.render ? await renderJsxValue(
1548
- slide.render({ components: createMdxComponents(input.components ?? builtInSlideComponents, { assets }) })
1549
- ) : slide.nodes?.length ? await renderSlideNodesAsync(slide.nodes, { components: input.components, assets }) : rewriteLocalAssetUrls(slide.html, assets);
1550
- } catch (error) {
1551
- if (error instanceof RenderError) throw error;
1552
- throw new RenderError(
1553
- `Render failed in ${input.deck?.sourcePath ?? "unknown deck"} slide ${slide.index + 1}: ${formatErrorMessage(error)}`,
1554
- "slide-render-error",
1555
- error
1556
- );
1557
- }
1532
+ try {
1533
+ return slide.render ? await renderJsxValue(slide.render({ components: createMdxComponents(input.components ?? builtInSlideComponents, { assets }) })) : slide.nodes?.length ? await renderSlideNodesAsync(slide.nodes, {
1534
+ components: input.components,
1535
+ assets
1536
+ }) : rewriteLocalAssetUrls(slide.html, assets);
1537
+ } catch (error) {
1538
+ if (error instanceof RenderError) throw error;
1539
+ throw new RenderError(`Render failed in ${input.deck?.sourcePath ?? "unknown deck"} slide ${slide.index + 1}: ${formatErrorMessage$1(error)}`, "slide-render-error", error);
1540
+ }
1558
1541
  }
1559
1542
  function normalizeComponents(components) {
1560
- return {
1561
- ...builtInSlideComponents,
1562
- ...components ? defineSlideComponents(components) : {}
1563
- };
1543
+ return {
1544
+ ...builtInSlideComponents,
1545
+ ...components ? defineSlideComponents(components) : {}
1546
+ };
1564
1547
  }
1565
1548
  function safeClass(value) {
1566
- return value.toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
1549
+ return value.toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
1567
1550
  }
1568
- function escapeHtml5(value) {
1569
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1551
+ function escapeHtml$4(value) {
1552
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
1570
1553
  }
1571
- function formatErrorMessage(error) {
1572
- if (error instanceof Error) return error.message;
1573
- return String(error);
1554
+ function formatErrorMessage$1(error) {
1555
+ if (error instanceof Error) return error.message;
1556
+ return String(error);
1574
1557
  }
1575
-
1576
- // src/server/client-entry.ts
1558
+ //#endregion
1559
+ //#region src/server/client-entry.ts
1577
1560
  function serveDecksClientEntry(clientEntry, options = {}) {
1578
- return (c) => {
1579
- const headers = {
1580
- "content-type": options.contentType ?? "text/javascript; charset=utf-8"
1581
- };
1582
- const cacheControl = options.cacheControl ?? "public, max-age=300";
1583
- if (cacheControl !== false) headers["cache-control"] = cacheControl;
1584
- return c.body(clientEntry, 200, headers);
1585
- };
1586
- }
1587
-
1588
- // src/server/path-utils.ts
1561
+ return (c) => {
1562
+ const headers = { "content-type": options.contentType ?? "text/javascript; charset=utf-8" };
1563
+ const cacheControl = options.cacheControl ?? "public, max-age=300";
1564
+ if (cacheControl !== false) headers["cache-control"] = cacheControl;
1565
+ return c.body(clientEntry, 200, headers);
1566
+ };
1567
+ }
1568
+ //#endregion
1569
+ //#region src/server/path-utils.ts
1589
1570
  function extractAssetPath(path, slug) {
1590
- const marker = `/${slug}/assets/`;
1591
- const markerIndex = path.indexOf(marker);
1592
- if (markerIndex === -1) return "";
1593
- return path.slice(markerIndex + marker.length);
1571
+ const marker = `/${slug}/assets/`;
1572
+ const markerIndex = path.indexOf(marker);
1573
+ if (markerIndex === -1) return "";
1574
+ return path.slice(markerIndex + marker.length);
1594
1575
  }
1595
1576
  function stripPathSuffix(path, suffix) {
1596
- return path.endsWith(suffix) ? path.slice(0, -suffix.length) : path;
1577
+ return path.endsWith(suffix) ? path.slice(0, -suffix.length) : path;
1597
1578
  }
1598
1579
  function inferMountPath(path, slug) {
1599
- const marker = `/${slug}`;
1600
- const markerIndex = path.indexOf(marker);
1601
- if (markerIndex === -1) return "";
1602
- return path.slice(0, markerIndex) || "/";
1580
+ const marker = `/${slug}`;
1581
+ const markerIndex = path.indexOf(marker);
1582
+ if (markerIndex === -1) return "";
1583
+ return path.slice(0, markerIndex) || "/";
1603
1584
  }
1604
1585
  function resolveGeneratedClientEntryUrl(options, mountPath) {
1605
- if (!options.clientEntryAsset) return void 0;
1606
- const basePath = mountPath === "/" ? "" : mountPath.replace(/\/$/, "");
1607
- return `${basePath}${normalizeClientEntryAssetPath(options.clientEntryAssetPath)}`;
1586
+ if (!options.clientEntryAsset) return void 0;
1587
+ return `${mountPath === "/" ? "" : mountPath.replace(/\/$/, "")}${normalizeClientEntryAssetPath(options.clientEntryAssetPath)}`;
1608
1588
  }
1609
1589
  function normalizeClientEntryAssetPath(path = "/_assets/client.js") {
1610
- const normalized = path.startsWith("/") ? path : `/${path}`;
1611
- return normalized.replace(/\/{2,}/g, "/");
1590
+ return (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
1612
1591
  }
1613
-
1614
- // src/server/paths.ts
1592
+ //#endregion
1593
+ //#region src/server/paths.ts
1594
+ /** Creates the canonical route map used by routers, controls, and app code. */
1615
1595
  function createDeckPaths(mountPath, slug) {
1616
- const mount = normalizeMountPath(mountPath);
1617
- const viewer = `${mount === "/" ? "" : mount}/${encodeURIComponent(slug)}`;
1618
- return {
1619
- viewer,
1620
- render: `${viewer}/render`,
1621
- print: `${viewer}/print`,
1622
- presentation: `${viewer}/presentation`,
1623
- presenter: `${viewer}/presenter`,
1624
- embed: `${viewer}/embed`,
1625
- exportPdf: `${viewer}/export.pdf`,
1626
- exportPng: `${viewer}/export.png`,
1627
- ogImage: `${viewer}/og.png`,
1628
- assets: `${viewer}/assets`
1629
- };
1630
- }
1596
+ const mount = normalizeMountPath(mountPath);
1597
+ const viewer = `${mount === "/" ? "" : mount}/${encodeURIComponent(slug)}`;
1598
+ return {
1599
+ viewer,
1600
+ render: `${viewer}/render`,
1601
+ print: `${viewer}/print`,
1602
+ presentation: `${viewer}/presentation`,
1603
+ presenter: `${viewer}/presenter`,
1604
+ embed: `${viewer}/embed`,
1605
+ exportPdf: `${viewer}/export.pdf`,
1606
+ exportPng: `${viewer}/export.png`,
1607
+ ogImage: `${viewer}/og.png`,
1608
+ assets: `${viewer}/assets`
1609
+ };
1610
+ }
1611
+ /** Normalizes a configured mount path to one leading slash and no trailing slash. */
1631
1612
  function normalizeMountPath(value) {
1632
- const trimmed = value.trim();
1633
- if (!trimmed || trimmed === "/") return "/";
1634
- return `/${trimmed.replace(/^\/+|\/+$/g, "")}`;
1635
- }
1636
-
1637
- // src/server/browser-export.ts
1613
+ const trimmed = value.trim();
1614
+ if (!trimmed || trimmed === "/") return "/";
1615
+ let start = 0;
1616
+ let end = trimmed.length;
1617
+ while (trimmed[start] === "/") start += 1;
1618
+ while (end > start && trimmed[end - 1] === "/") end -= 1;
1619
+ return `/${trimmed.slice(start, end)}`;
1620
+ }
1621
+ //#endregion
1622
+ //#region src/server/browser-export.ts
1638
1623
  async function renderDeckBrowserExport(c, options, format) {
1639
- const slug = c.req.param("slug");
1640
- if (!slug) return c.json({ error: "Deck not found", slug: "" }, 404);
1641
- const deck = await options.source.getCompiledDeck(c, slug);
1642
- if (!deck || options.dev !== true && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
1643
- const exportOptions = options.export;
1644
- if (!exportOptions) return c.json({ error: "Browser export not configured", slug, format }, 503);
1645
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/export.${format}`);
1646
- const resolverInput = {
1647
- c,
1648
- deck,
1649
- slug,
1650
- mountPath,
1651
- paths: createDeckPaths(mountPath, slug),
1652
- format
1653
- };
1654
- if (!await isBrowserExportAuthorized(exportOptions, resolverInput)) {
1655
- return c.json({ error: "Browser export not authorized", slug, format }, 403);
1656
- }
1657
- const browser = await exportOptions.browser(resolverInput);
1658
- if (!browser) return c.json({ error: "Browser export not configured", slug, format }, 503);
1659
- const printUrl = new URL(resolverInput.paths.print, c.req.url).toString();
1660
- const action = format === "pdf" ? "pdf" : "screenshot";
1661
- const response = await browser.quickAction(action, createBrowserRunExportRequest(exportOptions, printUrl, format));
1662
- const headers = new Headers(response.headers);
1663
- if (!headers.has("content-type")) headers.set("content-type", format === "pdf" ? "application/pdf" : "image/png");
1664
- headers.set("content-disposition", `attachment; filename="${exportFilename(exportOptions, deck, format)}"`);
1665
- return new Response(response.body, {
1666
- status: response.status,
1667
- statusText: response.statusText,
1668
- headers
1669
- });
1624
+ const slug = c.req.param("slug");
1625
+ if (!slug) return c.json({
1626
+ error: "Deck not found",
1627
+ slug: ""
1628
+ }, 404);
1629
+ const deck = await options.source.getCompiledDeck(c, slug);
1630
+ if (!deck || options.dev !== true && deck.meta.draft) return c.json({
1631
+ error: "Deck not found",
1632
+ slug
1633
+ }, 404);
1634
+ const exportOptions = options.export;
1635
+ if (!exportOptions) return c.json({
1636
+ error: "Browser export not configured",
1637
+ slug,
1638
+ format
1639
+ }, 503);
1640
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/export.${format}`);
1641
+ const resolverInput = {
1642
+ c,
1643
+ deck,
1644
+ slug,
1645
+ mountPath,
1646
+ paths: createDeckPaths(mountPath, slug),
1647
+ format
1648
+ };
1649
+ if (!await isBrowserExportAuthorized(exportOptions, resolverInput)) return c.json({
1650
+ error: "Browser export not authorized",
1651
+ slug,
1652
+ format
1653
+ }, 403);
1654
+ const browser = await exportOptions.browser(resolverInput);
1655
+ if (!browser) return c.json({
1656
+ error: "Browser export not configured",
1657
+ slug,
1658
+ format
1659
+ }, 503);
1660
+ const printUrl = new URL(resolverInput.paths.print, c.req.url).toString();
1661
+ const action = format === "pdf" ? "pdf" : "screenshot";
1662
+ const response = await browser.quickAction(action, createBrowserRunExportRequest(exportOptions, printUrl, format));
1663
+ const headers = new Headers(response.headers);
1664
+ if (!headers.has("content-type")) headers.set("content-type", format === "pdf" ? "application/pdf" : "image/png");
1665
+ headers.set("content-disposition", `attachment; filename="${exportFilename(exportOptions, deck, format)}"`);
1666
+ return new Response(response.body, {
1667
+ status: response.status,
1668
+ statusText: response.statusText,
1669
+ headers
1670
+ });
1670
1671
  }
1671
1672
  function isPdfExportEnabled(options) {
1672
- return Boolean(options && options.pdf !== void 0 && options.pdf !== false);
1673
+ return Boolean(options && options.pdf !== void 0 && options.pdf !== false);
1673
1674
  }
1674
1675
  function isPngExportEnabled(options) {
1675
- return Boolean(options && options.png !== void 0 && options.png !== false);
1676
+ return Boolean(options && options.png !== void 0 && options.png !== false);
1676
1677
  }
1677
1678
  async function resolveAuthorizedExportPaths(c, deck, mountPath, options) {
1678
- if (!options) return {};
1679
- const slug = deck.slug;
1680
- const paths = createDeckPaths(mountPath, slug);
1681
- const pdf = isPdfExportEnabled(options) && await isBrowserExportAuthorized(options, { c, deck, slug, mountPath, paths, format: "pdf" });
1682
- const png = isPngExportEnabled(options) && await isBrowserExportAuthorized(options, { c, deck, slug, mountPath, paths, format: "png" });
1683
- return { pdf, png };
1679
+ if (!options) return {};
1680
+ const slug = deck.slug;
1681
+ const paths = createDeckPaths(mountPath, slug);
1682
+ return {
1683
+ pdf: isPdfExportEnabled(options) && await isBrowserExportAuthorized(options, {
1684
+ c,
1685
+ deck,
1686
+ slug,
1687
+ mountPath,
1688
+ paths,
1689
+ format: "pdf"
1690
+ }),
1691
+ png: isPngExportEnabled(options) && await isBrowserExportAuthorized(options, {
1692
+ c,
1693
+ deck,
1694
+ slug,
1695
+ mountPath,
1696
+ paths,
1697
+ format: "png"
1698
+ })
1699
+ };
1684
1700
  }
1685
1701
  async function isBrowserExportAuthorized(options, input) {
1686
- return options.authorize ? options.authorize(input) : true;
1702
+ return options.authorize ? options.authorize(input) : true;
1687
1703
  }
1688
1704
  function createBrowserRunExportRequest(options, printUrl, format) {
1689
- if (format === "pdf") {
1690
- const request2 = exportOptionObject(options.pdf).request ?? {};
1691
- return {
1692
- ...request2,
1693
- url: printUrl,
1694
- gotoOptions: {
1695
- waitUntil: "networkidle2",
1696
- timeout: 45e3,
1697
- ...recordValue(request2.gotoOptions)
1698
- },
1699
- pdfOptions: {
1700
- format: "a4",
1701
- printBackground: true,
1702
- preferCSSPageSize: true,
1703
- ...recordValue(request2.pdfOptions)
1704
- }
1705
- };
1706
- }
1707
- const request = exportOptionObject(options.png).request ?? {};
1708
- return {
1709
- ...request,
1710
- url: printUrl,
1711
- gotoOptions: {
1712
- waitUntil: "networkidle2",
1713
- timeout: 45e3,
1714
- ...recordValue(request.gotoOptions)
1715
- },
1716
- viewport: {
1717
- width: 794,
1718
- height: 1123,
1719
- deviceScaleFactor: 2,
1720
- ...recordValue(request.viewport)
1721
- },
1722
- screenshotOptions: {
1723
- type: "png",
1724
- fullPage: true,
1725
- ...recordValue(request.screenshotOptions)
1726
- }
1727
- };
1705
+ if (format === "pdf") {
1706
+ const request = exportOptionObject(options.pdf).request ?? {};
1707
+ return {
1708
+ ...request,
1709
+ url: printUrl,
1710
+ gotoOptions: {
1711
+ waitUntil: "networkidle2",
1712
+ timeout: 45e3,
1713
+ ...recordValue(request.gotoOptions)
1714
+ },
1715
+ pdfOptions: {
1716
+ format: "a4",
1717
+ printBackground: true,
1718
+ preferCSSPageSize: true,
1719
+ ...recordValue(request.pdfOptions)
1720
+ }
1721
+ };
1722
+ }
1723
+ const request = exportOptionObject(options.png).request ?? {};
1724
+ return {
1725
+ ...request,
1726
+ url: printUrl,
1727
+ gotoOptions: {
1728
+ waitUntil: "networkidle2",
1729
+ timeout: 45e3,
1730
+ ...recordValue(request.gotoOptions)
1731
+ },
1732
+ viewport: {
1733
+ width: 794,
1734
+ height: 1123,
1735
+ deviceScaleFactor: 2,
1736
+ ...recordValue(request.viewport)
1737
+ },
1738
+ screenshotOptions: {
1739
+ type: "png",
1740
+ fullPage: true,
1741
+ ...recordValue(request.screenshotOptions)
1742
+ }
1743
+ };
1728
1744
  }
1729
1745
  function exportFilename(options, deck, format) {
1730
- const option = exportOptionObject(format === "pdf" ? options.pdf : options.png);
1731
- const name = typeof option.filename === "function" ? option.filename(deck) : option.filename;
1732
- return `${safeFilename(name ?? deck.meta.title ?? deck.slug)}.${format}`;
1746
+ const option = exportOptionObject(format === "pdf" ? options.pdf : options.png);
1747
+ return `${safeFilename$1((typeof option.filename === "function" ? option.filename(deck) : option.filename) ?? deck.meta.title ?? deck.slug)}.${format}`;
1733
1748
  }
1734
1749
  function exportOptionObject(option) {
1735
- return typeof option === "object" && option ? option : {};
1750
+ return typeof option === "object" && option ? option : {};
1736
1751
  }
1737
1752
  function recordValue(value) {
1738
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1739
- }
1740
- function safeFilename(value) {
1741
- const normalized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
1742
- return normalized || "deck";
1743
- }
1744
-
1745
- // src/server/viewer.ts
1746
- import { raw } from "hono/html";
1747
- import { jsx as jsx3 } from "hono/jsx/jsx-runtime";
1748
-
1749
- // src/server/viewer-script.ts
1750
- var VIEWER_STATE_MESSAGE_TYPE = "hono-decks:state";
1751
- var VIEWER_COMMAND_MESSAGE_TYPE = "hono-decks:command";
1753
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1754
+ }
1755
+ function safeFilename$1(value) {
1756
+ const sanitized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-");
1757
+ let start = 0;
1758
+ let end = sanitized.length;
1759
+ while (sanitized[start] === "-") start += 1;
1760
+ while (end > start && sanitized[end - 1] === "-") end -= 1;
1761
+ return sanitized.slice(start, end) || "deck";
1762
+ }
1763
+ //#endregion
1764
+ //#region src/server/viewer-script.ts
1765
+ const VIEWER_STATE_MESSAGE_TYPE = "hono-decks:state";
1766
+ const VIEWER_COMMAND_MESSAGE_TYPE = "hono-decks:command";
1752
1767
  function renderViewerScript(nonce) {
1753
- const nonceAttribute = nonce ? ` nonce="${escapeHtml6(nonce)}"` : "";
1754
- return `<script data-hono-decks-viewer-runtime${nonceAttribute}>
1768
+ return `<script data-hono-decks-viewer-runtime${nonce ? ` nonce="${escapeHtml$3(nonce)}"` : ""}>
1755
1769
  (() => {
1756
1770
  const roots = Array.from(document.querySelectorAll("[data-hono-decks-viewer]"));
1757
1771
  const runtime = window.__honoDecksViewerRuntime ??= { controllers: new Map(), globalInitialized: false };
@@ -1938,21 +1952,21 @@ function renderViewerScript(nonce) {
1938
1952
  });
1939
1953
  }
1940
1954
  })();
1941
- </script>`;
1955
+ <\/script>`;
1942
1956
  }
1943
- function escapeHtml6(value) {
1944
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1957
+ function escapeHtml$3(value) {
1958
+ return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1945
1959
  }
1946
-
1947
- // src/server/viewer-style.ts
1948
- var VIEWER_ASPECT_RATIO = "16/9";
1960
+ //#endregion
1961
+ //#region src/server/viewer-style.ts
1962
+ const VIEWER_ASPECT_RATIO = "16/9";
1949
1963
  function viewerViewportRule() {
1950
- return `.hono-decks-viewport{width:min(100%,calc((100vh - 58px) * 16 / 9));aspect-ratio:${VIEWER_ASPECT_RATIO};position:relative;overflow:hidden;touch-action:pan-y}
1964
+ return `.hono-decks-viewport{width:min(100%,calc((100vh - 58px) * 16 / 9));aspect-ratio:${VIEWER_ASPECT_RATIO};position:relative;overflow:hidden;touch-action:pan-y}
1951
1965
  @supports (height:100dvh){.hono-decks-viewport{width:min(100%,calc((100dvh - 58px) * 16 / 9))}}
1952
1966
  @supports (width:1cqw){.hono-decks-viewport{width:min(100cqw,calc(100cqh * 16 / 9));height:min(100cqh,calc(100cqw * 9 / 16))}}`;
1953
1967
  }
1954
1968
  function baseViewerStyle() {
1955
- return `
1969
+ return `
1956
1970
  :root{color-scheme:dark;background:#050816;color:#eef2ff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
1957
1971
  html,body{margin:0;width:100%;height:100%;min-height:100vh}
1958
1972
  @supports (height:100dvh){html,body{height:100dvh;min-height:100dvh}}
@@ -1987,8 +2001,8 @@ ${viewerViewportRule()}
1987
2001
  @media (prefers-reduced-motion: reduce){*,*::before,*::after{scroll-behavior:auto!important;animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}}`;
1988
2002
  }
1989
2003
  function embeddedViewerStyle() {
1990
- const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
1991
- return `${root}{color-scheme:dark;background:#050816;color:#eef2ff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;display:grid;gap:12px;width:100%;min-width:0;box-sizing:border-box}
2004
+ const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
2005
+ return `${root}{color-scheme:dark;background:#050816;color:#eef2ff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;display:grid;gap:12px;width:100%;min-width:0;box-sizing:border-box}
1992
2006
  ${root} *:focus-visible{outline:none}
1993
2007
  ${root} .hono-decks-viewer-shell{display:grid;grid-template-rows:minmax(0,1fr) auto;place-items:center;width:100%;min-width:0;min-height:0;box-sizing:border-box}
1994
2008
  ${root} .hono-decks-viewer-stage{display:grid;place-items:center;width:100%;min-width:0;min-height:0;container-type:inline-size}
@@ -2009,129 +2023,142 @@ ${root} .hono-decks-control-icon{width:16px;height:16px;flex:0 0 auto;stroke:cur
2009
2023
  ${root} .hono-decks-viewer-toc button{font:inherit}
2010
2024
  @media (prefers-reduced-motion: reduce){${root},${root} *{scroll-behavior:auto!important;animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}}`;
2011
2025
  }
2012
-
2013
- // src/server/viewer.ts
2026
+ //#endregion
2027
+ //#region src/server/viewer.ts
2014
2028
  async function createDeckViewerParts(input) {
2015
- const slug = input.deck.slug;
2016
- const title = input.deck.meta.title ?? slug;
2017
- const basePath = input.mountPath.replace(/\/$/, "");
2018
- const paths = createDeckPaths(basePath, slug);
2019
- const imagePath = await resolveOpenGraphImagePath(input.openGraph, { deck: input.deck, paths });
2020
- const renderUrl = `${paths.render}${input.viewerStateQuery ?? ""}`;
2021
- const slides = createDeckToc(input.deck);
2022
- const meta = {
2023
- title,
2024
- description: input.deck.meta.description,
2025
- paths,
2026
- availablePages: { print: input.availablePages?.print !== false },
2027
- availableExports: input.exportPaths ?? {},
2028
- ...imagePath ? { imagePath } : {}
2029
- };
2030
- const controlsInput = input.controls === false ? false : input.controls ?? {};
2031
- const controlsContext = { slug, title, mountPath: basePath, meta, slides };
2032
- const controls = controlsInput === false ? null : renderViewerControls(controlsInput, controlsContext);
2033
- return {
2034
- slug,
2035
- title,
2036
- renderUrl,
2037
- frame: {
2038
- content: renderViewerFrame({ title, renderUrl }),
2039
- html: renderViewerFrameHtml({ title, renderUrl })
2040
- },
2041
- controls: controls ? { content: controls, html: await renderJsxValue(controls) } : null,
2042
- toc: { content: renderViewerToc(slides), html: renderViewerTocHtml(slides) },
2043
- slides,
2044
- meta
2045
- };
2029
+ const slug = input.deck.slug;
2030
+ const title = input.deck.meta.title ?? slug;
2031
+ const basePath = input.mountPath.replace(/\/$/, "");
2032
+ const paths = createDeckPaths(basePath, slug);
2033
+ const imagePath = await resolveOpenGraphImagePath(input.openGraph, {
2034
+ deck: input.deck,
2035
+ paths
2036
+ });
2037
+ const renderUrl = `${paths.render}${input.viewerStateQuery ?? ""}`;
2038
+ const slides = createDeckToc(input.deck);
2039
+ const meta = {
2040
+ title,
2041
+ description: input.deck.meta.description,
2042
+ paths,
2043
+ availablePages: { print: input.availablePages?.print !== false },
2044
+ availableExports: input.exportPaths ?? {},
2045
+ ...imagePath ? { imagePath } : {}
2046
+ };
2047
+ const controlsInput = input.controls === false ? false : input.controls ?? {};
2048
+ const controls = controlsInput === false ? null : renderViewerControls(controlsInput, {
2049
+ slug,
2050
+ title,
2051
+ mountPath: basePath,
2052
+ meta,
2053
+ slides
2054
+ });
2055
+ return {
2056
+ slug,
2057
+ title,
2058
+ renderUrl,
2059
+ frame: {
2060
+ content: renderViewerFrame({
2061
+ title,
2062
+ renderUrl
2063
+ }),
2064
+ html: renderViewerFrameHtml({
2065
+ title,
2066
+ renderUrl
2067
+ })
2068
+ },
2069
+ controls: controls ? {
2070
+ content: controls,
2071
+ html: await renderJsxValue(controls)
2072
+ } : null,
2073
+ toc: {
2074
+ content: renderViewerToc(slides),
2075
+ html: renderViewerTocHtml(slides)
2076
+ },
2077
+ slides,
2078
+ meta
2079
+ };
2046
2080
  }
2047
2081
  async function createDeckViewerEmbed(input) {
2048
- const parts = await createDeckViewerParts(input);
2049
- const root = jsx3("section", {
2050
- class: ["hono-decks-embedded-viewer", input.className].filter(Boolean).join(" "),
2051
- "data-hono-decks-viewer": true,
2052
- "data-hono-decks-embed": true,
2053
- "data-deck-slug": parts.slug,
2054
- ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2055
- "aria-label": parts.title,
2056
- children: [
2057
- jsx3("div", {
2058
- class: "hono-decks-viewer-shell",
2059
- children: [parts.frame.content, parts.controls?.content]
2060
- }),
2061
- input.toc ? parts.toc.content : null
2062
- ]
2063
- });
2064
- const rootHtml = await renderJsxValue(root);
2065
- const nonceAttribute = input.nonce ? ` nonce="${escapeHtml7(input.nonce)}"` : "";
2066
- const embedHtml = `<style data-hono-decks-embed-style${nonceAttribute}>${embeddedViewerStyle()}${input.style ?? ""}</style>${rootHtml}${renderViewerScript(input.nonce)}`;
2067
- return {
2068
- ...parts,
2069
- embed: raw(embedHtml),
2070
- embedHtml
2071
- };
2082
+ const parts = await createDeckViewerParts(input);
2083
+ const rootHtml = await renderJsxValue(jsx("section", {
2084
+ class: ["hono-decks-embedded-viewer", input.className].filter(Boolean).join(" "),
2085
+ "data-hono-decks-viewer": true,
2086
+ "data-hono-decks-embed": true,
2087
+ "data-deck-slug": parts.slug,
2088
+ ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2089
+ "aria-label": parts.title,
2090
+ children: [jsx("div", {
2091
+ class: "hono-decks-viewer-shell",
2092
+ children: [parts.frame.content, parts.controls?.content]
2093
+ }), input.toc ? parts.toc.content : null]
2094
+ }));
2095
+ const embedHtml = `<style data-hono-decks-embed-style${input.nonce ? ` nonce="${escapeHtml$2(input.nonce)}"` : ""}>${embeddedViewerStyle()}${input.style ?? ""}</style>${rootHtml}${renderViewerScript(input.nonce)}`;
2096
+ return {
2097
+ ...parts,
2098
+ embed: raw(embedHtml),
2099
+ embedHtml
2100
+ };
2072
2101
  }
2073
2102
  async function renderDeckViewerPage(input) {
2074
- const parts = await createDeckViewerParts({
2075
- deck: input.deck,
2076
- mountPath: input.mountPath,
2077
- viewerStateQuery: input.viewerStateQuery,
2078
- controls: input.viewer?.controls,
2079
- openGraph: input.viewer?.openGraph,
2080
- availablePages: input.availablePages,
2081
- exportPaths: await resolveAuthorizedExportPaths(input.c, input.deck, input.mountPath, input.exportOptions)
2082
- });
2083
- const renderInput = {
2084
- ...parts,
2085
- c: input.c,
2086
- deck: input.deck,
2087
- mountPath: input.mountPath
2088
- };
2089
- const customContent = input.viewer?.render ? await input.viewer.render(renderInput) : void 0;
2090
- const content = jsx3("main", {
2091
- "data-hono-decks-viewer": true,
2092
- "data-deck-slug": parts.slug,
2093
- ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2094
- ...customContent ? { "aria-label": parts.title } : { "aria-labelledby": "hono-decks-viewer-title" },
2095
- children: customContent ?? [
2096
- jsx3("header", {
2097
- class: "hono-decks-viewer-header",
2098
- children: [
2099
- jsx3("h1", { id: "hono-decks-viewer-title", class: "hono-decks-viewer-title", children: parts.title }),
2100
- jsx3("p", {
2101
- class: "hono-decks-viewer-meta",
2102
- children: `${parts.slides.length} ${parts.slides.length === 1 ? "slide" : "slides"}`
2103
- })
2104
- ]
2105
- }),
2106
- jsx3("div", {
2107
- class: "hono-decks-viewer-shell",
2108
- children: [parts.frame.content, parts.controls?.content]
2109
- })
2110
- ]
2111
- });
2112
- const viewerHead = typeof input.viewer?.head === "function" ? await input.viewer.head(renderInput) : await input.viewer?.head;
2113
- const viewerLang = typeof input.viewer?.lang === "function" ? await input.viewer.lang(renderInput) : input.viewer?.lang;
2114
- const viewerNonce = typeof input.viewer?.nonce === "function" ? await input.viewer.nonce(renderInput) : input.viewer?.nonce;
2115
- const document = await resolveDeckDocument(
2116
- {
2117
- c: input.c,
2118
- surface: "viewer",
2119
- deck: input.deck,
2120
- slug: input.deck.slug,
2121
- mountPath: input.mountPath,
2122
- title: parts.title
2123
- },
2124
- input.document,
2125
- { head: viewerHead, lang: viewerLang, nonce: viewerNonce }
2126
- );
2127
- const nonceAttribute = documentNonceAttribute(document.nonce);
2128
- const socialHead = renderViewerSocialHead(input.c, parts.meta);
2129
- return `<!doctype html>
2130
- <html lang="${escapeHtml7(document.lang)}">
2103
+ const parts = await createDeckViewerParts({
2104
+ deck: input.deck,
2105
+ mountPath: input.mountPath,
2106
+ viewerStateQuery: input.viewerStateQuery,
2107
+ controls: input.viewer?.controls,
2108
+ openGraph: input.viewer?.openGraph,
2109
+ availablePages: input.availablePages,
2110
+ exportPaths: await resolveAuthorizedExportPaths(input.c, input.deck, input.mountPath, input.exportOptions)
2111
+ });
2112
+ const renderInput = {
2113
+ ...parts,
2114
+ c: input.c,
2115
+ deck: input.deck,
2116
+ mountPath: input.mountPath
2117
+ };
2118
+ const customContent = input.viewer?.render ? await input.viewer.render(renderInput) : void 0;
2119
+ const content = jsx("main", {
2120
+ "data-hono-decks-viewer": true,
2121
+ "data-deck-slug": parts.slug,
2122
+ ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2123
+ ...customContent ? { "aria-label": parts.title } : { "aria-labelledby": "hono-decks-viewer-title" },
2124
+ children: customContent ?? [jsx("header", {
2125
+ class: "hono-decks-viewer-header",
2126
+ children: [jsx("h1", {
2127
+ id: "hono-decks-viewer-title",
2128
+ class: "hono-decks-viewer-title",
2129
+ children: parts.title
2130
+ }), jsx("p", {
2131
+ class: "hono-decks-viewer-meta",
2132
+ children: `${parts.slides.length} ${parts.slides.length === 1 ? "slide" : "slides"}`
2133
+ })]
2134
+ }), jsx("div", {
2135
+ class: "hono-decks-viewer-shell",
2136
+ children: [parts.frame.content, parts.controls?.content]
2137
+ })]
2138
+ });
2139
+ const viewerHead = typeof input.viewer?.head === "function" ? await input.viewer.head(renderInput) : await input.viewer?.head;
2140
+ const viewerLang = typeof input.viewer?.lang === "function" ? await input.viewer.lang(renderInput) : input.viewer?.lang;
2141
+ const viewerNonce = typeof input.viewer?.nonce === "function" ? await input.viewer.nonce(renderInput) : input.viewer?.nonce;
2142
+ const document = await resolveDeckDocument({
2143
+ c: input.c,
2144
+ surface: "viewer",
2145
+ deck: input.deck,
2146
+ slug: input.deck.slug,
2147
+ mountPath: input.mountPath,
2148
+ title: parts.title
2149
+ }, input.document, {
2150
+ head: viewerHead,
2151
+ lang: viewerLang,
2152
+ nonce: viewerNonce
2153
+ });
2154
+ const nonceAttribute = documentNonceAttribute(document.nonce);
2155
+ const socialHead = renderViewerSocialHead(input.c, parts.meta);
2156
+ return `<!doctype html>
2157
+ <html lang="${escapeHtml$2(document.lang)}">
2131
2158
  <head>
2132
2159
  <meta charset="utf-8" />
2133
2160
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
2134
- <title>${escapeHtml7(parts.title)}</title>
2161
+ <title>${escapeHtml$2(parts.title)}</title>
2135
2162
  ${socialHead}
2136
2163
  <style${nonceAttribute}>${baseViewerStyle()}${input.viewer?.style ?? ""}</style>
2137
2164
  ${document.head}
@@ -2143,360 +2170,370 @@ async function renderDeckViewerPage(input) {
2143
2170
  </html>`;
2144
2171
  }
2145
2172
  async function resolveOpenGraphImagePath(options, input) {
2146
- if (!options) return void 0;
2147
- if (options === true) return input.paths.ogImage;
2148
- if (typeof options.imagePath === "function") return options.imagePath(input);
2149
- return options.imagePath ?? input.paths.ogImage;
2173
+ if (!options) return void 0;
2174
+ if (options === true) return input.paths.ogImage;
2175
+ if (typeof options.imagePath === "function") return options.imagePath(input);
2176
+ return options.imagePath ?? input.paths.ogImage;
2150
2177
  }
2151
2178
  function renderViewerSocialHead(c, meta) {
2152
- if (!meta.imagePath) return "";
2153
- const pageUrl = new URL(meta.paths.viewer, c.req.url).href;
2154
- const imageUrl = new URL(meta.imagePath, c.req.url).href;
2155
- const description = meta.description ? `
2156
- <meta name="description" content="${escapeHtml7(meta.description)}" />
2157
- <meta property="og:description" content="${escapeHtml7(meta.description)}" />
2158
- <meta name="twitter:description" content="${escapeHtml7(meta.description)}" />` : "";
2159
- return `<meta property="og:type" content="website" />
2160
- <meta property="og:title" content="${escapeHtml7(meta.title)}" />${description}
2161
- <meta property="og:url" content="${escapeHtml7(pageUrl)}" />
2162
- <meta property="og:image" content="${escapeHtml7(imageUrl)}" />
2179
+ if (!meta.imagePath) return "";
2180
+ const pageUrl = new URL(meta.paths.viewer, c.req.url).href;
2181
+ const imageUrl = new URL(meta.imagePath, c.req.url).href;
2182
+ const description = meta.description ? `\n <meta name="description" content="${escapeHtml$2(meta.description)}" />\n <meta property="og:description" content="${escapeHtml$2(meta.description)}" />\n <meta name="twitter:description" content="${escapeHtml$2(meta.description)}" />` : "";
2183
+ return `<meta property="og:type" content="website" />
2184
+ <meta property="og:title" content="${escapeHtml$2(meta.title)}" />${description}
2185
+ <meta property="og:url" content="${escapeHtml$2(pageUrl)}" />
2186
+ <meta property="og:image" content="${escapeHtml$2(imageUrl)}" />
2163
2187
  <meta property="og:image:width" content="1200" />
2164
2188
  <meta property="og:image:height" content="630" />
2165
- <meta property="og:image:alt" content="${escapeHtml7(meta.title)}" />
2189
+ <meta property="og:image:alt" content="${escapeHtml$2(meta.title)}" />
2166
2190
  <meta name="twitter:card" content="summary_large_image" />
2167
- <meta name="twitter:title" content="${escapeHtml7(meta.title)}" />
2168
- <meta name="twitter:image" content="${escapeHtml7(imageUrl)}" />`;
2191
+ <meta name="twitter:title" content="${escapeHtml$2(meta.title)}" />
2192
+ <meta name="twitter:image" content="${escapeHtml$2(imageUrl)}" />`;
2169
2193
  }
2170
2194
  function createDeckToc(deck) {
2171
- return deck.slides.map((slide) => ({
2172
- index: slide.index,
2173
- title: slide.meta.title,
2174
- label: slide.meta.title ?? `Slide ${slide.index + 1}`
2175
- }));
2195
+ return deck.slides.map((slide) => ({
2196
+ index: slide.index,
2197
+ title: slide.meta.title,
2198
+ label: slide.meta.title ?? `Slide ${slide.index + 1}`
2199
+ }));
2176
2200
  }
2177
2201
  function renderViewerFrame(input) {
2178
- return jsx3("div", {
2179
- class: "hono-decks-viewer-stage",
2180
- "data-hono-decks-frame": true,
2181
- children: jsx3("div", {
2182
- class: "hono-decks-viewport",
2183
- "data-viewer-viewport": true,
2184
- tabindex: "0",
2185
- children: [
2186
- jsx3("div", {
2187
- class: "hono-decks-frame-stage",
2188
- "data-viewer-stage": true,
2189
- children: jsx3("iframe", {
2190
- title: input.title,
2191
- src: input.renderUrl
2192
- })
2193
- }),
2194
- jsx3("button", {
2195
- class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous",
2196
- type: "button",
2197
- "data-viewer-navigation": "previous",
2198
- "aria-label": "Previous slide"
2199
- }),
2200
- jsx3("button", {
2201
- class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next",
2202
- type: "button",
2203
- "data-viewer-navigation": "next",
2204
- "aria-label": "Next slide"
2205
- })
2206
- ]
2207
- })
2208
- });
2202
+ return jsx("div", {
2203
+ class: "hono-decks-viewer-stage",
2204
+ "data-hono-decks-frame": true,
2205
+ children: jsx("div", {
2206
+ class: "hono-decks-viewport",
2207
+ "data-viewer-viewport": true,
2208
+ tabindex: "0",
2209
+ children: [
2210
+ jsx("div", {
2211
+ class: "hono-decks-frame-stage",
2212
+ "data-viewer-stage": true,
2213
+ children: jsx("iframe", {
2214
+ title: input.title,
2215
+ src: input.renderUrl
2216
+ })
2217
+ }),
2218
+ jsx("button", {
2219
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous",
2220
+ type: "button",
2221
+ "data-viewer-navigation": "previous",
2222
+ "aria-label": "Previous slide"
2223
+ }),
2224
+ jsx("button", {
2225
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next",
2226
+ type: "button",
2227
+ "data-viewer-navigation": "next",
2228
+ "aria-label": "Next slide"
2229
+ })
2230
+ ]
2231
+ })
2232
+ });
2209
2233
  }
2210
2234
  function renderViewerFrameHtml(input) {
2211
- return `<div class="hono-decks-viewer-stage" data-hono-decks-frame><div class="hono-decks-viewport" data-viewer-viewport tabindex="0"><div class="hono-decks-frame-stage" data-viewer-stage><iframe title="${escapeHtml7(input.title)}" src="${escapeHtml7(input.renderUrl)}"></iframe></div><button class="hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous" type="button" data-viewer-navigation="previous" aria-label="Previous slide"></button><button class="hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next" type="button" data-viewer-navigation="next" aria-label="Next slide"></button></div></div>`;
2235
+ return `<div class="hono-decks-viewer-stage" data-hono-decks-frame><div class="hono-decks-viewport" data-viewer-viewport tabindex="0"><div class="hono-decks-frame-stage" data-viewer-stage><iframe title="${escapeHtml$2(input.title)}" src="${escapeHtml$2(input.renderUrl)}"></iframe></div><button class="hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous" type="button" data-viewer-navigation="previous" aria-label="Previous slide"></button><button class="hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next" type="button" data-viewer-navigation="next" aria-label="Next slide"></button></div></div>`;
2212
2236
  }
2213
2237
  function renderViewerControls(options, context) {
2214
- return jsx3("nav", {
2215
- ...controlAttributes(options.attributes),
2216
- class: controlsClassName(options),
2217
- "data-hono-decks-viewer-controls": true,
2218
- "aria-label": options.ariaLabel ?? "Viewer controls",
2219
- children: resolveViewerControlItems(options, context).map((item) => renderViewerControlItem(item, options, context))
2220
- });
2238
+ return jsx("nav", {
2239
+ ...controlAttributes(options.attributes),
2240
+ class: controlsClassName(options),
2241
+ "data-hono-decks-viewer-controls": true,
2242
+ "aria-label": options.ariaLabel ?? "Viewer controls",
2243
+ children: resolveViewerControlItems(options, context).map((item) => renderViewerControlItem(item, options, context))
2244
+ });
2221
2245
  }
2222
2246
  function renderViewerToc(slides) {
2223
- return jsx3("nav", {
2224
- class: "hono-decks-viewer-toc",
2225
- "data-hono-decks-toc": true,
2226
- "aria-label": "Slide navigation",
2227
- children: jsx3("ol", {
2228
- children: slides.map(
2229
- (slide) => jsx3("li", {
2230
- children: jsx3("button", {
2231
- type: "button",
2232
- "data-action": "goTo",
2233
- "data-slide-index": String(slide.index),
2234
- children: slide.label
2235
- })
2236
- })
2237
- )
2238
- })
2239
- });
2247
+ return jsx("nav", {
2248
+ class: "hono-decks-viewer-toc",
2249
+ "data-hono-decks-toc": true,
2250
+ "aria-label": "Slide navigation",
2251
+ children: jsx("ol", { children: slides.map((slide) => jsx("li", { children: jsx("button", {
2252
+ type: "button",
2253
+ "data-action": "goTo",
2254
+ "data-slide-index": String(slide.index),
2255
+ children: slide.label
2256
+ }) })) })
2257
+ });
2240
2258
  }
2241
2259
  function renderViewerTocHtml(slides) {
2242
- const items = slides.map(
2243
- (slide) => `<li><button type="button" data-action="goTo" data-slide-index="${slide.index}">${escapeHtml7(slide.label)}</button></li>`
2244
- ).join("");
2245
- return `<nav class="hono-decks-viewer-toc" data-hono-decks-toc aria-label="Slide navigation"><ol>${items}</ol></nav>`;
2260
+ return `<nav class="hono-decks-viewer-toc" data-hono-decks-toc aria-label="Slide navigation"><ol>${slides.map((slide) => `<li><button type="button" data-action="goTo" data-slide-index="${slide.index}">${escapeHtml$2(slide.label)}</button></li>`).join("")}</ol></nav>`;
2246
2261
  }
2247
- function escapeHtml7(value) {
2248
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
2262
+ function escapeHtml$2(value) {
2263
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
2249
2264
  }
2250
- function safeFilename2(value) {
2251
- const normalized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
2252
- return normalized || "deck";
2265
+ function safeFilename(value) {
2266
+ return value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "deck";
2253
2267
  }
2254
2268
  function buildViewerControlDefaults(context) {
2255
- return {
2256
- back: { type: "default", key: "back" },
2257
- previous: { type: "default", key: "previous" },
2258
- position: { type: "default", key: "position" },
2259
- next: { type: "default", key: "next" },
2260
- fullscreen: { type: "default", key: "fullscreen" },
2261
- print: context.meta.availablePages.print ? { type: "default", key: "print" } : null,
2262
- exportPdf: context.meta.availableExports.pdf ? { type: "default", key: "exportPdf" } : null,
2263
- exportPng: context.meta.availableExports.png ? { type: "default", key: "exportPng" } : null
2264
- };
2269
+ return {
2270
+ back: {
2271
+ type: "default",
2272
+ key: "back"
2273
+ },
2274
+ previous: {
2275
+ type: "default",
2276
+ key: "previous"
2277
+ },
2278
+ position: {
2279
+ type: "default",
2280
+ key: "position"
2281
+ },
2282
+ next: {
2283
+ type: "default",
2284
+ key: "next"
2285
+ },
2286
+ fullscreen: {
2287
+ type: "default",
2288
+ key: "fullscreen"
2289
+ },
2290
+ print: context.meta.availablePages.print ? {
2291
+ type: "default",
2292
+ key: "print"
2293
+ } : null,
2294
+ exportPdf: context.meta.availableExports.pdf ? {
2295
+ type: "default",
2296
+ key: "exportPdf"
2297
+ } : null,
2298
+ exportPng: context.meta.availableExports.png ? {
2299
+ type: "default",
2300
+ key: "exportPng"
2301
+ } : null
2302
+ };
2265
2303
  }
2266
2304
  function resolveViewerControlItems(options, context) {
2267
- const defaults = buildViewerControlDefaults(context);
2268
- const baseItems = [
2269
- defaults.back,
2270
- defaults.previous,
2271
- defaults.position,
2272
- defaults.next,
2273
- defaults.fullscreen,
2274
- defaults.print,
2275
- defaults.exportPdf,
2276
- defaults.exportPng
2277
- ];
2278
- const items = typeof options.items === "function" ? options.items(defaults, context) : options.items ?? [
2279
- ...resolveViewerControlSlotItems(options.before, context),
2280
- ...filterHiddenDefaultItems(baseItems, options.hidden),
2281
- ...resolveViewerControlSlotItems(options.after, context)
2282
- ];
2283
- return items.filter((item) => Boolean(item)).map((item) => applyViewerControlLabels(item, options.labels));
2305
+ const defaults = buildViewerControlDefaults(context);
2306
+ const baseItems = [
2307
+ defaults.back,
2308
+ defaults.previous,
2309
+ defaults.position,
2310
+ defaults.next,
2311
+ defaults.fullscreen,
2312
+ defaults.print,
2313
+ defaults.exportPdf,
2314
+ defaults.exportPng
2315
+ ];
2316
+ return (typeof options.items === "function" ? options.items(defaults, context) : options.items ?? [
2317
+ ...resolveViewerControlSlotItems(options.before, context),
2318
+ ...filterHiddenDefaultItems(baseItems, options.hidden),
2319
+ ...resolveViewerControlSlotItems(options.after, context)
2320
+ ]).filter((item) => Boolean(item)).map((item) => applyViewerControlLabels(item, options.labels));
2284
2321
  }
2285
2322
  function renderViewerControlItem(item, options, context) {
2286
- const renderDefault = () => renderBaseViewerControlItem(item, options, context);
2287
- return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
2323
+ const renderDefault = () => renderBaseViewerControlItem(item, options, context);
2324
+ return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
2288
2325
  }
2289
2326
  function renderBaseViewerControlItem(item, options, context) {
2290
- if (item.type === "link") {
2291
- const ariaLabel = item.icon ? String(item.attributes?.["aria-label"] ?? item.label) : void 0;
2292
- return jsx3("a", {
2293
- ...linkAttributes(item.attributes),
2294
- ...safeHref(item.href) ? { href: safeHref(item.href) } : {},
2295
- ...item.download ? { download: item.download } : {},
2296
- ...classAttribute(options.itemClassName, item.className),
2297
- ...item.icon ? { "aria-label": ariaLabel, title: ariaLabel } : {},
2298
- children: item.icon ? renderControlIcon(item.icon) : item.label
2299
- });
2300
- }
2301
- if (item.type === "render") {
2302
- return item.render({ context });
2303
- }
2304
- return renderDefaultViewerControlItem(item, options, context);
2327
+ if (item.type === "link") {
2328
+ const ariaLabel = item.icon ? String(item.attributes?.["aria-label"] ?? item.label) : void 0;
2329
+ return jsx("a", {
2330
+ ...linkAttributes(item.attributes),
2331
+ ...safeHref(item.href) ? { href: safeHref(item.href) } : {},
2332
+ ...item.download ? { download: item.download } : {},
2333
+ ...classAttribute(options.itemClassName, item.className),
2334
+ ...item.icon ? {
2335
+ "aria-label": ariaLabel,
2336
+ title: ariaLabel
2337
+ } : {},
2338
+ children: item.icon ? renderControlIcon(item.icon) : item.label
2339
+ });
2340
+ }
2341
+ if (item.type === "render") return item.render({ context });
2342
+ return renderDefaultViewerControlItem(item, options, context);
2305
2343
  }
2306
2344
  function renderDefaultViewerControlItem(item, options, context) {
2307
- const itemProps = {
2308
- ...linkAttributes(item.attributes),
2309
- ...classAttribute(options.itemClassName, item.className)
2310
- };
2311
- switch (item.key) {
2312
- case "back":
2313
- return jsx3("a", {
2314
- ...itemProps,
2315
- href: context.mountPath,
2316
- "data-hono-decks-back-link": true,
2317
- ...item.label === void 0 ? { "aria-label": controlIconLabel("deck-list"), title: controlIconLabel("deck-list") } : {},
2318
- children: item.label ?? renderControlIcon("deck-list")
2319
- });
2320
- case "previous":
2321
- return renderIconButton("previous", item, itemProps);
2322
- case "position":
2323
- return jsx3("span", {
2324
- ...itemProps,
2325
- "data-slide-position": true,
2326
- "data-hono-decks-position": true,
2327
- children: item.label ?? "1 / ?"
2328
- });
2329
- case "next":
2330
- return renderIconButton("next", item, itemProps);
2331
- case "fullscreen":
2332
- return renderIconButton("fullscreen", item, itemProps);
2333
- case "print":
2334
- return jsx3("a", {
2335
- ...itemProps,
2336
- href: context.meta.paths.print,
2337
- "data-hono-decks-print": true,
2338
- ...item.label === void 0 ? { "aria-label": controlIconLabel("print"), title: controlIconLabel("print") } : {},
2339
- children: item.label ?? renderControlIcon("print")
2340
- });
2341
- case "exportPdf":
2342
- return jsx3("a", {
2343
- ...itemProps,
2344
- href: context.meta.paths.exportPdf,
2345
- download: `${safeFilename2(context.meta.title)}.pdf`,
2346
- "data-hono-decks-export": "pdf",
2347
- ...item.label === void 0 ? { "aria-label": controlIconLabel("export-pdf"), title: controlIconLabel("export-pdf") } : {},
2348
- children: item.label ?? renderControlIcon("export-pdf")
2349
- });
2350
- case "exportPng":
2351
- return jsx3("a", {
2352
- ...itemProps,
2353
- href: context.meta.paths.exportPng,
2354
- download: `${safeFilename2(context.meta.title)}.png`,
2355
- "data-hono-decks-export": "png",
2356
- ...item.label === void 0 ? { "aria-label": controlIconLabel("export-png"), title: controlIconLabel("export-png") } : {},
2357
- children: item.label ?? renderControlIcon("export-png")
2358
- });
2359
- }
2345
+ const itemProps = {
2346
+ ...linkAttributes(item.attributes),
2347
+ ...classAttribute(options.itemClassName, item.className)
2348
+ };
2349
+ switch (item.key) {
2350
+ case "back": return jsx("a", {
2351
+ ...itemProps,
2352
+ href: context.mountPath,
2353
+ "data-hono-decks-back-link": true,
2354
+ ...item.label === void 0 ? {
2355
+ "aria-label": controlIconLabel("deck-list"),
2356
+ title: controlIconLabel("deck-list")
2357
+ } : {},
2358
+ children: item.label ?? renderControlIcon("deck-list")
2359
+ });
2360
+ case "previous": return renderIconButton("previous", item, itemProps);
2361
+ case "position": return jsx("span", {
2362
+ ...itemProps,
2363
+ "data-slide-position": true,
2364
+ "data-hono-decks-position": true,
2365
+ children: item.label ?? "1 / ?"
2366
+ });
2367
+ case "next": return renderIconButton("next", item, itemProps);
2368
+ case "fullscreen": return renderIconButton("fullscreen", item, itemProps);
2369
+ case "print": return jsx("a", {
2370
+ ...itemProps,
2371
+ href: context.meta.paths.print,
2372
+ "data-hono-decks-print": true,
2373
+ ...item.label === void 0 ? {
2374
+ "aria-label": controlIconLabel("print"),
2375
+ title: controlIconLabel("print")
2376
+ } : {},
2377
+ children: item.label ?? renderControlIcon("print")
2378
+ });
2379
+ case "exportPdf": return jsx("a", {
2380
+ ...itemProps,
2381
+ href: context.meta.paths.exportPdf,
2382
+ download: `${safeFilename(context.meta.title)}.pdf`,
2383
+ "data-hono-decks-export": "pdf",
2384
+ ...item.label === void 0 ? {
2385
+ "aria-label": controlIconLabel("export-pdf"),
2386
+ title: controlIconLabel("export-pdf")
2387
+ } : {},
2388
+ children: item.label ?? renderControlIcon("export-pdf")
2389
+ });
2390
+ case "exportPng": return jsx("a", {
2391
+ ...itemProps,
2392
+ href: context.meta.paths.exportPng,
2393
+ download: `${safeFilename(context.meta.title)}.png`,
2394
+ "data-hono-decks-export": "png",
2395
+ ...item.label === void 0 ? {
2396
+ "aria-label": controlIconLabel("export-png"),
2397
+ title: controlIconLabel("export-png")
2398
+ } : {},
2399
+ children: item.label ?? renderControlIcon("export-png")
2400
+ });
2401
+ }
2360
2402
  }
2361
2403
  function renderIconButton(action, item, itemProps) {
2362
- const iconName = controlIconNameForKey(action);
2363
- const label = controlIconLabel(iconName);
2364
- return jsx3("button", {
2365
- ...itemProps,
2366
- type: "button",
2367
- "data-action": action,
2368
- "data-hono-decks-navigation-control": action,
2369
- ...item.label === void 0 ? { "aria-label": label, title: label } : {},
2370
- children: item.label ?? renderControlIcon(iconName)
2371
- });
2404
+ const iconName = controlIconNameForKey(action);
2405
+ const label = controlIconLabel(iconName);
2406
+ return jsx("button", {
2407
+ ...itemProps,
2408
+ type: "button",
2409
+ "data-action": action,
2410
+ "data-hono-decks-navigation-control": action,
2411
+ ...item.label === void 0 ? {
2412
+ "aria-label": label,
2413
+ title: label
2414
+ } : {},
2415
+ children: item.label ?? renderControlIcon(iconName)
2416
+ });
2372
2417
  }
2373
2418
  function controlIconNameForKey(key) {
2374
- switch (key) {
2375
- case "back":
2376
- return "deck-list";
2377
- case "previous":
2378
- return "previous";
2379
- case "next":
2380
- return "next";
2381
- case "fullscreen":
2382
- return "fullscreen";
2383
- case "print":
2384
- return "print";
2385
- case "exportPdf":
2386
- return "export-pdf";
2387
- case "exportPng":
2388
- return "export-png";
2389
- case "position":
2390
- return "viewer";
2391
- }
2419
+ switch (key) {
2420
+ case "back": return "deck-list";
2421
+ case "previous": return "previous";
2422
+ case "next": return "next";
2423
+ case "fullscreen": return "fullscreen";
2424
+ case "print": return "print";
2425
+ case "exportPdf": return "export-pdf";
2426
+ case "exportPng": return "export-png";
2427
+ case "position": return "viewer";
2428
+ }
2392
2429
  }
2393
2430
  function controlsClassName(options) {
2394
- return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
2431
+ return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
2395
2432
  }
2396
2433
  function resolveViewerControlSlotItems(items, context) {
2397
- return typeof items === "function" ? items(context) : items ?? [];
2434
+ return typeof items === "function" ? items(context) : items ?? [];
2398
2435
  }
2399
2436
  function filterHiddenDefaultItems(items, hidden) {
2400
- if (!hidden?.length) return items;
2401
- const hiddenKeys = new Set(hidden);
2402
- return items.filter((item) => !item || item.type !== "default" || !hiddenKeys.has(item.key));
2437
+ if (!hidden?.length) return items;
2438
+ const hiddenKeys = new Set(hidden);
2439
+ return items.filter((item) => !item || item.type !== "default" || !hiddenKeys.has(item.key));
2403
2440
  }
2404
2441
  function applyViewerControlLabels(item, labels) {
2405
- if (item.type !== "default" || item.label !== void 0) return item;
2406
- const label = labels?.[item.key];
2407
- return label === void 0 ? item : { ...item, label };
2442
+ if (item.type !== "default" || item.label !== void 0) return item;
2443
+ const label = labels?.[item.key];
2444
+ return label === void 0 ? item : {
2445
+ ...item,
2446
+ label
2447
+ };
2408
2448
  }
2409
2449
  function linkAttributes(attributes) {
2410
- const props = {};
2411
- for (const [name, value] of Object.entries(attributes ?? {})) {
2412
- if (!isSafeAttributeName(name) || value === false || value === void 0) continue;
2413
- props[name] = value;
2414
- }
2415
- return props;
2450
+ const props = {};
2451
+ for (const [name, value] of Object.entries(attributes ?? {})) {
2452
+ if (!isSafeAttributeName(name) || value === false || value === void 0) continue;
2453
+ props[name] = value;
2454
+ }
2455
+ return props;
2416
2456
  }
2417
2457
  function controlAttributes(attributes) {
2418
- const props = linkAttributes(attributes);
2419
- delete props.class;
2420
- delete props["aria-label"];
2421
- delete props["data-hono-decks-viewer-controls"];
2422
- return props;
2458
+ const props = linkAttributes(attributes);
2459
+ delete props.class;
2460
+ delete props["aria-label"];
2461
+ delete props["data-hono-decks-viewer-controls"];
2462
+ return props;
2423
2463
  }
2424
2464
  function classAttribute(...classNames) {
2425
- const className = classNames.filter(Boolean).join(" ");
2426
- return className ? { class: className } : {};
2465
+ const className = classNames.filter(Boolean).join(" ");
2466
+ return className ? { class: className } : {};
2427
2467
  }
2428
2468
  function isSafeAttributeName(value) {
2429
- return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
2469
+ return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
2430
2470
  }
2431
2471
  function safeHref(value) {
2432
- const trimmed = value.trim();
2433
- if (trimmed.length === 0) return void 0;
2434
- if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return value;
2435
- if (/^(javascript:|data:|vbscript:)/i.test(trimmed)) return void 0;
2436
- if (/^[^/?#.]+:/i.test(trimmed)) return void 0;
2437
- return value;
2438
- }
2439
-
2440
- // src/server/external-embed.ts
2472
+ const trimmed = value.trim();
2473
+ if (trimmed.length === 0) return void 0;
2474
+ if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return value;
2475
+ if (/^(javascript:|data:|vbscript:)/i.test(trimmed)) return void 0;
2476
+ if (/^[^/?#.]+:/i.test(trimmed)) return void 0;
2477
+ return value;
2478
+ }
2479
+ //#endregion
2480
+ //#region src/server/external-embed.ts
2441
2481
  async function renderDeckExternalEmbedResponse(input) {
2442
- const { c, deck, slug, mountPath } = input.context;
2443
- const enabled = input.options.enabled;
2444
- if (enabled === false || typeof enabled === "function" && !await enabled(input.context)) {
2445
- return c.json({ error: "Embed route not found", slug }, 404);
2446
- }
2447
- const document = await resolveDeckDocument(
2448
- {
2449
- c,
2450
- surface: "embed",
2451
- deck,
2452
- slug,
2453
- mountPath,
2454
- title: deck.meta.title ?? slug
2455
- },
2456
- input.document,
2457
- input.options.document
2458
- );
2459
- const viewerOptions = typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer;
2460
- const viewer = await createDeckViewerEmbed({
2461
- deck,
2462
- mountPath,
2463
- ...viewerOptions,
2464
- availablePages: input.availablePages,
2465
- nonce: document.nonce
2466
- });
2467
- const renderInput = { ...input.context, viewer, document };
2468
- const body = input.options.render ? await input.options.render(renderInput) : viewer.embed;
2469
- const html = await renderExternalEmbedDocument({
2470
- title: deck.meta.title ?? slug,
2471
- body: await renderJsxValue(body),
2472
- document,
2473
- pageStyle: input.options.pageStyle,
2474
- robots: input.options.robots
2475
- });
2476
- const frameAncestorsValue = typeof input.options.frameAncestors === "function" ? await input.options.frameAncestors(input.context) : input.options.frameAncestors;
2477
- const response = c.html(html);
2478
- const headers = new Headers(response.headers);
2479
- headers.delete("x-frame-options");
2480
- headers.set(
2481
- "content-security-policy",
2482
- withFrameAncestors(headers.get("content-security-policy"), normalizeFrameAncestors(frameAncestorsValue))
2483
- );
2484
- return new Response(response.body, {
2485
- status: response.status,
2486
- statusText: response.statusText,
2487
- headers
2488
- });
2482
+ const { c, deck, slug, mountPath } = input.context;
2483
+ const enabled = input.options.enabled;
2484
+ if (enabled === false || typeof enabled === "function" && !await enabled(input.context)) return c.json({
2485
+ error: "Embed route not found",
2486
+ slug
2487
+ }, 404);
2488
+ const document = await resolveDeckDocument({
2489
+ c,
2490
+ surface: "embed",
2491
+ deck,
2492
+ slug,
2493
+ mountPath,
2494
+ title: deck.meta.title ?? slug
2495
+ }, input.document, input.options.document);
2496
+ const viewer = await createDeckViewerEmbed({
2497
+ deck,
2498
+ mountPath,
2499
+ ...typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer,
2500
+ availablePages: input.availablePages,
2501
+ nonce: document.nonce
2502
+ });
2503
+ const renderInput = {
2504
+ ...input.context,
2505
+ viewer,
2506
+ document
2507
+ };
2508
+ const body = input.options.render ? await input.options.render(renderInput) : viewer.embed;
2509
+ const html = await renderExternalEmbedDocument({
2510
+ title: deck.meta.title ?? slug,
2511
+ body: await renderJsxValue(body),
2512
+ document,
2513
+ pageStyle: input.options.pageStyle,
2514
+ robots: input.options.robots
2515
+ });
2516
+ const frameAncestorsValue = typeof input.options.frameAncestors === "function" ? await input.options.frameAncestors(input.context) : input.options.frameAncestors;
2517
+ const response = c.html(html);
2518
+ const headers = new Headers(response.headers);
2519
+ headers.delete("x-frame-options");
2520
+ headers.set("content-security-policy", withFrameAncestors(headers.get("content-security-policy"), normalizeFrameAncestors(frameAncestorsValue)));
2521
+ return new Response(response.body, {
2522
+ status: response.status,
2523
+ statusText: response.statusText,
2524
+ headers
2525
+ });
2489
2526
  }
2490
2527
  async function renderExternalEmbedDocument(input) {
2491
- const nonceAttribute = documentNonceAttribute(input.document.nonce);
2492
- const robots = input.robots === false ? "" : `<meta name="robots" content="${escapeHtml8(input.robots ?? "noindex")}"/>`;
2493
- return `<!doctype html>
2494
- <html lang="${escapeHtml8(input.document.lang)}" data-hono-decks-external-embed-document>
2528
+ const nonceAttribute = documentNonceAttribute(input.document.nonce);
2529
+ const robots = input.robots === false ? "" : `<meta name="robots" content="${escapeHtml$1(input.robots ?? "noindex")}"/>`;
2530
+ return `<!doctype html>
2531
+ <html lang="${escapeHtml$1(input.document.lang)}" data-hono-decks-external-embed-document>
2495
2532
  <head>
2496
2533
  <meta charset="utf-8" />
2497
2534
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
2498
2535
  ${robots}
2499
- <title>${escapeHtml8(input.title)}</title>
2536
+ <title>${escapeHtml$1(input.title)}</title>
2500
2537
  <style id="hono-decks-external-embed-css"${nonceAttribute}>${externalEmbedPageStyle()}${input.pageStyle ?? ""}</style>
2501
2538
  ${input.document.head}
2502
2539
  </head>
@@ -2504,384 +2541,444 @@ async function renderExternalEmbedDocument(input) {
2504
2541
  </html>`;
2505
2542
  }
2506
2543
  function normalizeFrameAncestors(value) {
2507
- const values = Array.isArray(value) ? value : value?.split(/[\s,]+/) ?? [];
2508
- const ancestors = /* @__PURE__ */ new Set(["'self'"]);
2509
- for (const candidate of values) {
2510
- const token = candidate.trim();
2511
- if (!token || token === "'self'") continue;
2512
- if (token === "*") return ["*"];
2513
- if (token === "'none'") return ["'none'"];
2514
- try {
2515
- const url = new URL(token);
2516
- if ((url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password) {
2517
- ancestors.add(url.origin);
2518
- }
2519
- } catch {
2520
- }
2521
- }
2522
- return [...ancestors];
2544
+ const values = Array.isArray(value) ? value : value?.split(/[\s,]+/) ?? [];
2545
+ const ancestors = /* @__PURE__ */ new Set(["'self'"]);
2546
+ for (const candidate of values) {
2547
+ const token = candidate.trim();
2548
+ if (!token || token === "'self'") continue;
2549
+ if (token === "*") return ["*"];
2550
+ if (token === "'none'") return ["'none'"];
2551
+ try {
2552
+ const url = new URL(token);
2553
+ if ((url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password) ancestors.add(url.origin);
2554
+ } catch {}
2555
+ }
2556
+ return [...ancestors];
2523
2557
  }
2524
2558
  function withFrameAncestors(currentPolicy, ancestors) {
2525
- const directives = (currentPolicy ?? "").split(";").map((directive) => directive.trim()).filter((directive) => directive && !/^frame-ancestors(?:\s|$)/i.test(directive));
2526
- directives.push(`frame-ancestors ${ancestors.join(" ")}`);
2527
- return directives.join("; ");
2559
+ const directives = (currentPolicy ?? "").split(";").map((directive) => directive.trim()).filter((directive) => directive && !/^frame-ancestors(?:\s|$)/i.test(directive));
2560
+ directives.push(`frame-ancestors ${ancestors.join(" ")}`);
2561
+ return directives.join("; ");
2528
2562
  }
2529
2563
  function externalEmbedPageStyle() {
2530
- return `:root{color-scheme:dark;background:#050816}html,body{width:100%;height:100%;margin:0;overflow:hidden}body{min-height:100vh}@supports (height:100dvh){body{min-height:100dvh}}.hono-decks-embedded-viewer{width:100%;height:100%;min-height:0}.hono-decks-embedded-viewer .hono-decks-viewer-shell{position:relative;height:100%;grid-template-rows:minmax(0,1fr)}.hono-decks-embedded-viewer .hono-decks-viewport{width:min(100%,calc(100vh * 16 / 9));max-height:100%}@supports (height:100dvh){.hono-decks-embedded-viewer .hono-decks-viewport{width:min(100%,calc(100dvh * 16 / 9))}}.hono-decks-embedded-viewer .hono-decks-viewer-controls{position:absolute;right:max(10px,env(safe-area-inset-right,0px));bottom:max(10px,env(safe-area-inset-bottom,0px));z-index:4}`;
2564
+ return `:root{color-scheme:dark;background:#050816}html,body{width:100%;height:100%;margin:0;overflow:hidden}body{min-height:100vh}@supports (height:100dvh){body{min-height:100dvh}}.hono-decks-embedded-viewer{width:100%;height:100%;min-height:0}.hono-decks-embedded-viewer .hono-decks-viewer-shell{position:relative;height:100%;grid-template-rows:minmax(0,1fr)}.hono-decks-embedded-viewer .hono-decks-viewport{width:min(100%,calc(100vh * 16 / 9));max-height:100%}@supports (height:100dvh){.hono-decks-embedded-viewer .hono-decks-viewport{width:min(100%,calc(100dvh * 16 / 9))}}.hono-decks-embedded-viewer .hono-decks-viewer-controls{position:absolute;right:max(10px,env(safe-area-inset-right,0px));bottom:max(10px,env(safe-area-inset-bottom,0px));z-index:4}`;
2531
2565
  }
2532
- function escapeHtml8(value) {
2533
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2566
+ function escapeHtml$1(value) {
2567
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
2534
2568
  }
2535
-
2536
- // src/server/router.ts
2569
+ //#endregion
2570
+ //#region src/server/router.ts
2537
2571
  function decksRouter(options) {
2538
- const router = new Hono();
2539
- if (options.clientEntryAsset) {
2540
- router.get(normalizeClientEntryAssetPath(options.clientEntryAssetPath), serveDecksClientEntry(options.clientEntryAsset));
2541
- }
2542
- for (const extension of options.extensions ?? []) {
2543
- router.route(extension.path, extension.router);
2544
- }
2545
- router.get("/", async (c) => {
2546
- const dev = await isDevEnabled(c, options);
2547
- const decks = (await options.source.listDecks(c)).filter((deck) => dev || !deck.draft);
2548
- const mountPath = c.req.path.replace(/\/$/, "");
2549
- const pageInput = { c, surface: "index", mountPath, dev, decks };
2550
- if (!await isPageSurfaceEnabled(options, pageInput)) {
2551
- return c.json({ error: "Deck index not found" }, 404);
2552
- }
2553
- const indexOptions = options.pages?.index === false ? void 0 : options.pages?.index;
2554
- const title = await resolveIndexTitle(indexOptions?.title, pageInput);
2555
- const document = await resolveRouterDocument(c, options, "index", mountPath, void 0, title);
2556
- const defaultContentHtml = renderDeckIndexContent(decks, mountPath);
2557
- const defaultContent = raw2(defaultContentHtml);
2558
- const content = indexOptions?.render ? await renderJsxValue(indexOptions.render({ ...pageInput, title, document, defaultContent })) : defaultContentHtml;
2559
- return c.html(renderDeckIndex(content, title, document));
2560
- });
2561
- router.get("/:slug/assets/*", async (c) => {
2562
- const slug = c.req.param("slug");
2563
- const assetPath = extractAssetPath(c.req.path, slug);
2564
- const deck = await options.source.getCompiledDeck(c, slug);
2565
- if (!deck || !await isDevEnabled(c, options) && deck.meta.draft) {
2566
- return c.json({ error: "Asset not found", slug, assetPath }, 404);
2567
- }
2568
- const response = await options.source.getAsset?.(c, slug, assetPath);
2569
- if (!response) return c.json({ error: "Asset not found", slug, assetPath }, 404);
2570
- return response;
2571
- });
2572
- router.get("/:slug/render", async (c) => {
2573
- const slug = c.req.param("slug");
2574
- const deck = await options.source.getCompiledDeck(c, slug);
2575
- const dev = await isDevEnabled(c, options);
2576
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2577
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/render`);
2578
- if (!await isPageSurfaceEnabled(options, { c, surface: "render", mountPath, dev, deck, slug })) {
2579
- return c.json({ error: "Deck route not found", slug, surface: "render" }, 404);
2580
- }
2581
- const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2582
- try {
2583
- const document = await resolveRouterDocument(c, options, "render", mountPath, deck);
2584
- return c.html(
2585
- await renderCompiledDeckPageAsync({
2586
- deck,
2587
- mountPath,
2588
- style: options.style,
2589
- components: options.components,
2590
- clientEntry,
2591
- liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2592
- document
2593
- })
2594
- );
2595
- } catch (error) {
2596
- const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage2(error)}`;
2597
- return c.text(message, 500);
2598
- }
2599
- });
2600
- router.get("/:slug/print", async (c) => {
2601
- const slug = c.req.param("slug");
2602
- const deck = await options.source.getCompiledDeck(c, slug);
2603
- const dev = await isDevEnabled(c, options);
2604
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2605
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/print`);
2606
- if (!await isPageSurfaceEnabled(options, { c, surface: "print", mountPath, dev, deck, slug })) {
2607
- return c.json({ error: "Deck route not found", slug, surface: "print" }, 404);
2608
- }
2609
- try {
2610
- const document = await resolveRouterDocument(c, options, "print", mountPath, deck);
2611
- return c.html(
2612
- await renderCompiledDeckPageAsync({
2613
- deck,
2614
- mountPath,
2615
- style: options.style,
2616
- components: options.components,
2617
- printPreview: true,
2618
- document
2619
- })
2620
- );
2621
- } catch (error) {
2622
- const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage2(error)}`;
2623
- return c.text(message, 500);
2624
- }
2625
- });
2626
- if (isPdfExportEnabled(options.export)) {
2627
- router.get(
2628
- "/:slug/export.pdf",
2629
- async (c) => renderDeckBrowserExport(c, { ...options, dev: await isDevEnabled(c, options) }, "pdf")
2630
- );
2631
- }
2632
- if (isPngExportEnabled(options.export)) {
2633
- router.get(
2634
- "/:slug/export.png",
2635
- async (c) => renderDeckBrowserExport(c, { ...options, dev: await isDevEnabled(c, options) }, "png")
2636
- );
2637
- }
2638
- const embedOptions = options.embed;
2639
- if (embedOptions) {
2640
- router.get("/:slug/embed", async (c) => {
2641
- const slug = c.req.param("slug");
2642
- const deck = await options.source.getCompiledDeck(c, slug);
2643
- const dev = await isDevEnabled(c, options);
2644
- if (!deck || !dev && deck.meta.draft) {
2645
- return c.json({ error: "Deck not found", slug }, 404);
2646
- }
2647
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/embed`);
2648
- const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2649
- return renderDeckExternalEmbedResponse({
2650
- context: { c, deck, slug, mountPath },
2651
- options: embedOptions,
2652
- document: options.document,
2653
- availablePages
2654
- });
2655
- });
2656
- }
2657
- router.get("/:slug/presentation", async (c) => {
2658
- const slug = c.req.param("slug");
2659
- const deck = await options.source.getCompiledDeck(c, slug);
2660
- const dev = await isDevEnabled(c, options);
2661
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2662
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/presentation`);
2663
- if (!await isPageSurfaceEnabled(options, { c, surface: "presentation", mountPath, dev, deck, slug })) {
2664
- return c.json({ error: "Deck route not found", slug, surface: "presentation" }, 404);
2665
- }
2666
- const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2667
- try {
2668
- const document = await resolveRouterDocument(c, options, "presentation", mountPath, deck);
2669
- return c.html(
2670
- await renderCompiledDeckPageAsync({
2671
- deck,
2672
- mountPath,
2673
- style: options.style,
2674
- components: options.components,
2675
- clientEntry,
2676
- speakerNotes: false,
2677
- liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2678
- document
2679
- })
2680
- );
2681
- } catch (error) {
2682
- const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage2(error)}`;
2683
- return c.text(message, 500);
2684
- }
2685
- });
2686
- router.get("/:slug/presenter", async (c) => {
2687
- const slug = c.req.param("slug");
2688
- const deck = await options.source.getCompiledDeck(c, slug);
2689
- const dev = await isDevEnabled(c, options);
2690
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2691
- const mountPath = stripPathSuffix(c.req.path, `/${slug}/presenter`);
2692
- if (!await isPageSurfaceEnabled(options, { c, surface: "presenter", mountPath, dev, deck, slug })) {
2693
- return c.json({ error: "Presenter route not found", slug }, 404);
2694
- }
2695
- if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) {
2696
- return c.json({ error: "Presenter route not found", slug }, 404);
2697
- }
2698
- try {
2699
- const document = await resolveRouterDocument(c, options, "presenter", mountPath, deck);
2700
- return c.html(
2701
- await renderPresenterPageAsync({
2702
- deck,
2703
- mountPath,
2704
- presenterStateQuery: paginationQueryFromRequest(c),
2705
- style: options.style,
2706
- components: options.components,
2707
- document
2708
- })
2709
- );
2710
- } catch (error) {
2711
- const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage2(error)}`;
2712
- return c.text(message, 500);
2713
- }
2714
- });
2715
- router.get("/:slug", async (c) => {
2716
- const slug = c.req.param("slug");
2717
- const deck = await options.source.getCompiledDeck(c, slug);
2718
- const dev = await isDevEnabled(c, options);
2719
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2720
- const mountPath = stripPathSuffix(c.req.path, `/${slug}`);
2721
- if (!await isPageSurfaceEnabled(options, { c, surface: "viewer", mountPath, dev, deck, slug })) {
2722
- return c.json({ error: "Deck route not found", slug, surface: "viewer" }, 404);
2723
- }
2724
- const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2725
- return c.html(
2726
- await renderDeckViewerPage({
2727
- c,
2728
- deck,
2729
- mountPath,
2730
- viewerStateQuery: paginationQueryFromRequest(c),
2731
- viewer: {
2732
- ...options.viewer,
2733
- controls: await resolveViewerControls(c, options, deck, slug, mountPath)
2734
- },
2735
- availablePages,
2736
- exportOptions: options.export,
2737
- document: options.document
2738
- })
2739
- );
2740
- });
2741
- return router;
2572
+ const router = new Hono();
2573
+ if (options.clientEntryAsset) router.get(normalizeClientEntryAssetPath(options.clientEntryAssetPath), serveDecksClientEntry(options.clientEntryAsset));
2574
+ for (const extension of options.extensions ?? []) router.route(extension.path, extension.router);
2575
+ router.get("/", async (c) => {
2576
+ const dev = await isDevEnabled(c, options);
2577
+ const decks = (await options.source.listDecks(c)).filter((deck) => dev || !deck.draft);
2578
+ const mountPath = c.req.path.replace(/\/$/, "");
2579
+ const pageInput = {
2580
+ c,
2581
+ surface: "index",
2582
+ mountPath,
2583
+ dev,
2584
+ decks
2585
+ };
2586
+ if (!await isPageSurfaceEnabled(options, pageInput)) return c.json({ error: "Deck index not found" }, 404);
2587
+ const indexOptions = options.pages?.index === false ? void 0 : options.pages?.index;
2588
+ const title = await resolveIndexTitle(indexOptions?.title, pageInput);
2589
+ const document = await resolveRouterDocument(c, options, "index", mountPath, void 0, title);
2590
+ const defaultContentHtml = renderDeckIndexContent(decks, mountPath);
2591
+ const defaultContent = raw(defaultContentHtml);
2592
+ const content = indexOptions?.render ? await renderJsxValue(indexOptions.render({
2593
+ ...pageInput,
2594
+ title,
2595
+ document,
2596
+ defaultContent
2597
+ })) : defaultContentHtml;
2598
+ return c.html(renderDeckIndex(content, title, document));
2599
+ });
2600
+ router.get("/:slug/assets/*", async (c) => {
2601
+ const slug = c.req.param("slug");
2602
+ const assetPath = extractAssetPath(c.req.path, slug);
2603
+ const deck = await options.source.getCompiledDeck(c, slug);
2604
+ if (!deck || !await isDevEnabled(c, options) && deck.meta.draft) return c.json({
2605
+ error: "Asset not found",
2606
+ slug,
2607
+ assetPath
2608
+ }, 404);
2609
+ const response = await options.source.getAsset?.(c, slug, assetPath);
2610
+ if (!response) return c.json({
2611
+ error: "Asset not found",
2612
+ slug,
2613
+ assetPath
2614
+ }, 404);
2615
+ return response;
2616
+ });
2617
+ router.get("/:slug/render", async (c) => {
2618
+ const slug = c.req.param("slug");
2619
+ const deck = await options.source.getCompiledDeck(c, slug);
2620
+ const dev = await isDevEnabled(c, options);
2621
+ if (!deck || !dev && deck.meta.draft) return c.json({
2622
+ error: "Deck not found",
2623
+ slug
2624
+ }, 404);
2625
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/render`);
2626
+ if (!await isPageSurfaceEnabled(options, {
2627
+ c,
2628
+ surface: "render",
2629
+ mountPath,
2630
+ dev,
2631
+ deck,
2632
+ slug
2633
+ })) return c.json({
2634
+ error: "Deck route not found",
2635
+ slug,
2636
+ surface: "render"
2637
+ }, 404);
2638
+ const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2639
+ try {
2640
+ const document = await resolveRouterDocument(c, options, "render", mountPath, deck);
2641
+ return c.html(await renderCompiledDeckPageAsync({
2642
+ deck,
2643
+ mountPath,
2644
+ style: options.style,
2645
+ components: options.components,
2646
+ clientEntry,
2647
+ liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2648
+ document
2649
+ }));
2650
+ } catch (error) {
2651
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2652
+ return c.text(message, 500);
2653
+ }
2654
+ });
2655
+ router.get("/:slug/print", async (c) => {
2656
+ const slug = c.req.param("slug");
2657
+ const deck = await options.source.getCompiledDeck(c, slug);
2658
+ const dev = await isDevEnabled(c, options);
2659
+ if (!deck || !dev && deck.meta.draft) return c.json({
2660
+ error: "Deck not found",
2661
+ slug
2662
+ }, 404);
2663
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/print`);
2664
+ if (!await isPageSurfaceEnabled(options, {
2665
+ c,
2666
+ surface: "print",
2667
+ mountPath,
2668
+ dev,
2669
+ deck,
2670
+ slug
2671
+ })) return c.json({
2672
+ error: "Deck route not found",
2673
+ slug,
2674
+ surface: "print"
2675
+ }, 404);
2676
+ try {
2677
+ const document = await resolveRouterDocument(c, options, "print", mountPath, deck);
2678
+ return c.html(await renderCompiledDeckPageAsync({
2679
+ deck,
2680
+ mountPath,
2681
+ style: options.style,
2682
+ components: options.components,
2683
+ printPreview: true,
2684
+ document
2685
+ }));
2686
+ } catch (error) {
2687
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2688
+ return c.text(message, 500);
2689
+ }
2690
+ });
2691
+ if (isPdfExportEnabled(options.export)) router.get("/:slug/export.pdf", async (c) => renderDeckBrowserExport(c, {
2692
+ ...options,
2693
+ dev: await isDevEnabled(c, options)
2694
+ }, "pdf"));
2695
+ if (isPngExportEnabled(options.export)) router.get("/:slug/export.png", async (c) => renderDeckBrowserExport(c, {
2696
+ ...options,
2697
+ dev: await isDevEnabled(c, options)
2698
+ }, "png"));
2699
+ const embedOptions = options.embed;
2700
+ if (embedOptions) router.get("/:slug/embed", async (c) => {
2701
+ const slug = c.req.param("slug");
2702
+ const deck = await options.source.getCompiledDeck(c, slug);
2703
+ const dev = await isDevEnabled(c, options);
2704
+ if (!deck || !dev && deck.meta.draft) return c.json({
2705
+ error: "Deck not found",
2706
+ slug
2707
+ }, 404);
2708
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/embed`);
2709
+ const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2710
+ return renderDeckExternalEmbedResponse({
2711
+ context: {
2712
+ c,
2713
+ deck,
2714
+ slug,
2715
+ mountPath
2716
+ },
2717
+ options: embedOptions,
2718
+ document: options.document,
2719
+ availablePages
2720
+ });
2721
+ });
2722
+ router.get("/:slug/presentation", async (c) => {
2723
+ const slug = c.req.param("slug");
2724
+ const deck = await options.source.getCompiledDeck(c, slug);
2725
+ const dev = await isDevEnabled(c, options);
2726
+ if (!deck || !dev && deck.meta.draft) return c.json({
2727
+ error: "Deck not found",
2728
+ slug
2729
+ }, 404);
2730
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/presentation`);
2731
+ if (!await isPageSurfaceEnabled(options, {
2732
+ c,
2733
+ surface: "presentation",
2734
+ mountPath,
2735
+ dev,
2736
+ deck,
2737
+ slug
2738
+ })) return c.json({
2739
+ error: "Deck route not found",
2740
+ slug,
2741
+ surface: "presentation"
2742
+ }, 404);
2743
+ const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2744
+ try {
2745
+ const document = await resolveRouterDocument(c, options, "presentation", mountPath, deck);
2746
+ return c.html(await renderCompiledDeckPageAsync({
2747
+ deck,
2748
+ mountPath,
2749
+ style: options.style,
2750
+ components: options.components,
2751
+ clientEntry,
2752
+ speakerNotes: false,
2753
+ liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2754
+ document
2755
+ }));
2756
+ } catch (error) {
2757
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2758
+ return c.text(message, 500);
2759
+ }
2760
+ });
2761
+ router.get("/:slug/presenter", async (c) => {
2762
+ const slug = c.req.param("slug");
2763
+ const deck = await options.source.getCompiledDeck(c, slug);
2764
+ const dev = await isDevEnabled(c, options);
2765
+ if (!deck || !dev && deck.meta.draft) return c.json({
2766
+ error: "Deck not found",
2767
+ slug
2768
+ }, 404);
2769
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/presenter`);
2770
+ if (!await isPageSurfaceEnabled(options, {
2771
+ c,
2772
+ surface: "presenter",
2773
+ mountPath,
2774
+ dev,
2775
+ deck,
2776
+ slug
2777
+ })) return c.json({
2778
+ error: "Presenter route not found",
2779
+ slug
2780
+ }, 404);
2781
+ if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) return c.json({
2782
+ error: "Presenter route not found",
2783
+ slug
2784
+ }, 404);
2785
+ try {
2786
+ const document = await resolveRouterDocument(c, options, "presenter", mountPath, deck);
2787
+ return c.html(await renderPresenterPageAsync({
2788
+ deck,
2789
+ mountPath,
2790
+ presenterStateQuery: paginationQueryFromRequest(c),
2791
+ style: options.style,
2792
+ components: options.components,
2793
+ document
2794
+ }));
2795
+ } catch (error) {
2796
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2797
+ return c.text(message, 500);
2798
+ }
2799
+ });
2800
+ router.get("/:slug", async (c) => {
2801
+ const slug = c.req.param("slug");
2802
+ const deck = await options.source.getCompiledDeck(c, slug);
2803
+ const dev = await isDevEnabled(c, options);
2804
+ if (!deck || !dev && deck.meta.draft) return c.json({
2805
+ error: "Deck not found",
2806
+ slug
2807
+ }, 404);
2808
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}`);
2809
+ if (!await isPageSurfaceEnabled(options, {
2810
+ c,
2811
+ surface: "viewer",
2812
+ mountPath,
2813
+ dev,
2814
+ deck,
2815
+ slug
2816
+ })) return c.json({
2817
+ error: "Deck route not found",
2818
+ slug,
2819
+ surface: "viewer"
2820
+ }, 404);
2821
+ const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2822
+ return c.html(await renderDeckViewerPage({
2823
+ c,
2824
+ deck,
2825
+ mountPath,
2826
+ viewerStateQuery: paginationQueryFromRequest(c),
2827
+ viewer: {
2828
+ ...options.viewer,
2829
+ controls: await resolveViewerControls(c, options, deck, slug, mountPath)
2830
+ },
2831
+ availablePages,
2832
+ exportOptions: options.export,
2833
+ document: options.document
2834
+ }));
2835
+ });
2836
+ return router;
2742
2837
  }
2743
2838
  function paginationQueryFromRequest(c) {
2744
- const source = new URL(c.req.url);
2745
- const params = new URLSearchParams();
2746
- const slide = positiveIntegerParam(source.searchParams.get("slide"));
2747
- const step = nonNegativeIntegerParam(source.searchParams.get("step"));
2748
- if (slide !== void 0) params.set("slide", String(slide));
2749
- if (step !== void 0) params.set("step", String(step));
2750
- const query = params.toString();
2751
- return query ? `?${query}` : "";
2839
+ const source = new URL(c.req.url);
2840
+ const params = new URLSearchParams();
2841
+ const slide = positiveIntegerParam(source.searchParams.get("slide"));
2842
+ const step = nonNegativeIntegerParam(source.searchParams.get("step"));
2843
+ if (slide !== void 0) params.set("slide", String(slide));
2844
+ if (step !== void 0) params.set("step", String(step));
2845
+ const query = params.toString();
2846
+ return query ? `?${query}` : "";
2752
2847
  }
2753
2848
  function positiveIntegerParam(value) {
2754
- if (value === null) return void 0;
2755
- const parsed = Number(value);
2756
- return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
2849
+ if (value === null) return void 0;
2850
+ const parsed = Number(value);
2851
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
2757
2852
  }
2758
2853
  function nonNegativeIntegerParam(value) {
2759
- if (value === null) return void 0;
2760
- const parsed = Number(value);
2761
- return Number.isInteger(parsed) && parsed >= 0 ? parsed : void 0;
2854
+ if (value === null) return void 0;
2855
+ const parsed = Number(value);
2856
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : void 0;
2762
2857
  }
2763
2858
  function deckContext(options) {
2764
- return async (c, next) => {
2765
- const slug = c.req.param("slug");
2766
- if (!slug) return c.json({ error: "Deck not found", slug: "" }, 404);
2767
- const sourceContext = c;
2768
- const deck = await options.source.getCompiledDeck(sourceContext, slug);
2769
- const dev = await isDevEnabled(sourceContext, options);
2770
- if (!deck || !dev && deck.meta.draft) return c.json({ error: "Deck not found", slug }, 404);
2771
- const mountPath = options.mountPath ?? inferMountPath(c.req.path, slug);
2772
- const availablePages = await resolveViewerAvailablePages(sourceContext, options, deck, slug, mountPath, dev);
2773
- const viewer = await createDeckViewerParts({
2774
- deck,
2775
- mountPath,
2776
- controls: options.viewer?.controls,
2777
- openGraph: options.viewer?.openGraph,
2778
- availablePages
2779
- });
2780
- c.set("deck", deck);
2781
- c.set("deckViewer", viewer);
2782
- c.set("deckToc", viewer.slides);
2783
- c.set("deckMeta", viewer.meta);
2784
- await next();
2785
- };
2859
+ return async (c, next) => {
2860
+ const slug = c.req.param("slug");
2861
+ if (!slug) return c.json({
2862
+ error: "Deck not found",
2863
+ slug: ""
2864
+ }, 404);
2865
+ const sourceContext = c;
2866
+ const deck = await options.source.getCompiledDeck(sourceContext, slug);
2867
+ const dev = await isDevEnabled(sourceContext, options);
2868
+ if (!deck || !dev && deck.meta.draft) return c.json({
2869
+ error: "Deck not found",
2870
+ slug
2871
+ }, 404);
2872
+ const mountPath = options.mountPath ?? inferMountPath(c.req.path, slug);
2873
+ const availablePages = await resolveViewerAvailablePages(sourceContext, options, deck, slug, mountPath, dev);
2874
+ const viewer = await createDeckViewerParts({
2875
+ deck,
2876
+ mountPath,
2877
+ controls: options.viewer?.controls,
2878
+ openGraph: options.viewer?.openGraph,
2879
+ availablePages
2880
+ });
2881
+ c.set("deck", deck);
2882
+ c.set("deckViewer", viewer);
2883
+ c.set("deckToc", viewer.slides);
2884
+ c.set("deckMeta", viewer.meta);
2885
+ await next();
2886
+ };
2786
2887
  }
2787
2888
  async function resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev) {
2788
- return {
2789
- print: await isPageSurfaceEnabled(options, { c, surface: "print", mountPath, dev, deck, slug })
2790
- };
2889
+ return { print: await isPageSurfaceEnabled(options, {
2890
+ c,
2891
+ surface: "print",
2892
+ mountPath,
2893
+ dev,
2894
+ deck,
2895
+ slug
2896
+ }) };
2791
2897
  }
2792
2898
  async function isDevEnabled(c, options) {
2793
- if (typeof options.dev === "function") return Boolean(await options.dev({ c }));
2794
- if (typeof options.dev === "boolean") return options.dev;
2795
- return inferToolchainDevMode();
2899
+ if (typeof options.dev === "function") return Boolean(await options.dev({ c }));
2900
+ if (typeof options.dev === "boolean") return options.dev;
2901
+ return inferToolchainDevMode();
2796
2902
  }
2797
2903
  function inferToolchainDevMode() {
2798
- try {
2799
- return process.env.NODE_ENV === "development";
2800
- } catch {
2801
- return false;
2802
- }
2904
+ try {
2905
+ return process.env.NODE_ENV === "development";
2906
+ } catch {
2907
+ return false;
2908
+ }
2803
2909
  }
2804
2910
  async function resolveViewerControls(c, options, deck, slug, mountPath) {
2805
- const controls = options.viewer?.controls;
2806
- if (controls === false) return false;
2807
- const presenterControl = await resolvePresenterViewerControl(c, options, deck, slug, mountPath);
2808
- if (!presenterControl) return controls;
2809
- const nextControls = controls ? { ...controls } : {};
2810
- if (nextControls.items) return nextControls;
2811
- const placement = presenterControl.placement ?? "after";
2812
- const item = presenterControl.item;
2813
- if (placement === "before") {
2814
- nextControls.before = mergeControlSlotItems(nextControls.before, item, "before");
2815
- } else {
2816
- nextControls.after = mergeControlSlotItems(nextControls.after, item, "after");
2817
- }
2818
- return nextControls;
2911
+ const controls = options.viewer?.controls;
2912
+ if (controls === false) return false;
2913
+ const presenterControl = await resolvePresenterViewerControl(c, options, deck, slug, mountPath);
2914
+ if (!presenterControl) return controls;
2915
+ const nextControls = controls ? { ...controls } : {};
2916
+ if (nextControls.items) return nextControls;
2917
+ const placement = presenterControl.placement ?? "after";
2918
+ const item = presenterControl.item;
2919
+ if (placement === "before") nextControls.before = mergeControlSlotItems(nextControls.before, item, "before");
2920
+ else nextControls.after = mergeControlSlotItems(nextControls.after, item, "after");
2921
+ return nextControls;
2819
2922
  }
2820
2923
  async function resolvePresenterViewerControl(c, options, deck, slug, mountPath) {
2821
- const presenter = options.presenter;
2822
- if (!presenter || !presenter.viewerControl) return null;
2823
- if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) return null;
2824
- const control = presenter.viewerControl === true ? {} : presenter.viewerControl;
2825
- const presenterPath = createDeckPaths(mountPath, slug).presenter;
2826
- return {
2827
- item: {
2828
- type: "link",
2829
- key: control.key ?? "presenter",
2830
- href: presenterPath,
2831
- label: control.label ?? "Presenter",
2832
- icon: control.icon ?? "presenter",
2833
- className: control.className,
2834
- attributes: control.attributes
2835
- },
2836
- placement: control.placement ?? "after"
2837
- };
2924
+ const presenter = options.presenter;
2925
+ if (!presenter || !presenter.viewerControl) return null;
2926
+ if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) return null;
2927
+ const control = presenter.viewerControl === true ? {} : presenter.viewerControl;
2928
+ const presenterPath = createDeckPaths(mountPath, slug).presenter;
2929
+ return {
2930
+ item: {
2931
+ type: "link",
2932
+ key: control.key ?? "presenter",
2933
+ href: presenterPath,
2934
+ label: control.label ?? "Presenter",
2935
+ icon: control.icon ?? "presenter",
2936
+ className: control.className,
2937
+ attributes: control.attributes
2938
+ },
2939
+ placement: control.placement ?? "after"
2940
+ };
2838
2941
  }
2839
2942
  function mergeControlSlotItems(slot, item, placement) {
2840
- if (!slot) return [item];
2841
- if (Array.isArray(slot)) return placement === "before" ? [item, ...slot] : [...slot, item];
2842
- return (context) => {
2843
- const items = slot(context);
2844
- return placement === "before" ? [item, ...items] : [...items, item];
2845
- };
2943
+ if (!slot) return [item];
2944
+ if (Array.isArray(slot)) return placement === "before" ? [item, ...slot] : [...slot, item];
2945
+ return (context) => {
2946
+ const items = slot(context);
2947
+ return placement === "before" ? [item, ...items] : [...items, item];
2948
+ };
2846
2949
  }
2847
2950
  async function isPresenterEnabled(c, options, deck, slug, mountPath) {
2848
- const presenter = options.presenter;
2849
- if (presenter === false) return false;
2850
- const enabled = presenter?.enabled;
2851
- if (enabled === void 0) return true;
2852
- if (typeof enabled === "boolean") return enabled;
2853
- return Boolean(
2854
- await enabled({
2855
- c,
2856
- deck,
2857
- slug,
2858
- mountPath: mountPath.replace(/\/$/, ""),
2859
- dev: await isDevEnabled(c, options),
2860
- presenterPath: createDeckPaths(mountPath, slug).presenter,
2861
- presentationPath: createDeckPaths(mountPath, slug).presentation
2862
- })
2863
- );
2951
+ const presenter = options.presenter;
2952
+ if (presenter === false) return false;
2953
+ const enabled = presenter?.enabled;
2954
+ if (enabled === void 0) return true;
2955
+ if (typeof enabled === "boolean") return enabled;
2956
+ return Boolean(await enabled({
2957
+ c,
2958
+ deck,
2959
+ slug,
2960
+ mountPath: mountPath.replace(/\/$/, ""),
2961
+ dev: await isDevEnabled(c, options),
2962
+ presenterPath: createDeckPaths(mountPath, slug).presenter,
2963
+ presentationPath: createDeckPaths(mountPath, slug).presentation
2964
+ }));
2864
2965
  }
2865
2966
  function renderDeckIndexContent(decks, mountPath) {
2866
- const basePath = mountPath.replace(/\/$/, "");
2867
- const items = decks.map((deck) => {
2868
- const href = `${basePath}/${encodeURIComponent(deck.slug)}`;
2869
- const title = escapeHtml9(deck.title ?? deck.slug);
2870
- const description = deck.description ? `<p>${escapeHtml9(deck.description)}</p>` : "";
2871
- return `<li><a href="${href}">${title}</a>${description}</li>`;
2872
- }).join("");
2873
- return `<main>
2967
+ const basePath = mountPath.replace(/\/$/, "");
2968
+ return `<main>
2874
2969
  <h1>Hono Decks</h1>
2875
- <ul>${items}</ul>
2970
+ <ul>${decks.map((deck) => {
2971
+ return `<li><a href="${`${basePath}/${encodeURIComponent(deck.slug)}`}">${escapeHtml(deck.title ?? deck.slug)}</a>${deck.description ? `<p>${escapeHtml(deck.description)}</p>` : ""}</li>`;
2972
+ }).join("")}</ul>
2876
2973
  </main>`;
2877
2974
  }
2878
2975
  function renderDeckIndex(content, title, document) {
2879
- return `<!doctype html>
2880
- <html lang="${escapeHtml9(document.lang)}">
2976
+ return `<!doctype html>
2977
+ <html lang="${escapeHtml(document.lang)}">
2881
2978
  <head>
2882
2979
  <meta charset="utf-8" />
2883
2980
  <meta name="viewport" content="width=device-width, initial-scale=1" />
2884
- <title>${escapeHtml9(title)}</title>
2981
+ <title>${escapeHtml(title)}</title>
2885
2982
  ${document.head}
2886
2983
  </head>
2887
2984
  <body>
@@ -2890,273 +2987,300 @@ function renderDeckIndex(content, title, document) {
2890
2987
  </html>`;
2891
2988
  }
2892
2989
  async function resolveIndexTitle(title, input) {
2893
- const resolved = typeof title === "function" ? await title(input) : title;
2894
- return resolved?.trim() || "Hono Decks";
2990
+ return (typeof title === "function" ? await title(input) : title)?.trim() || "Hono Decks";
2895
2991
  }
2896
2992
  async function isPageSurfaceEnabled(options, input) {
2897
- const configured = input.surface === "index" ? options.pages?.index === false ? false : options.pages?.index?.enabled : options.pages?.[input.surface];
2898
- if (typeof configured === "function") return Boolean(await configured(input));
2899
- return configured !== false;
2993
+ const configured = input.surface === "index" ? options.pages?.index === false ? false : options.pages?.index?.enabled : options.pages?.[input.surface];
2994
+ if (typeof configured === "function") return Boolean(await configured(input));
2995
+ return configured !== false;
2900
2996
  }
2901
2997
  async function resolveRouterDocument(c, options, surface, mountPath, deck, titleOverride) {
2902
- const title = titleOverride ?? deck?.meta.title ?? deck?.slug ?? "Hono Decks";
2903
- return resolveDeckDocument(
2904
- {
2905
- c,
2906
- surface,
2907
- deck,
2908
- slug: deck?.slug,
2909
- mountPath,
2910
- title: surface === "presenter" ? `${title} - Presenter` : title
2911
- },
2912
- options.document
2913
- );
2914
- }
2915
- function escapeHtml9(value) {
2916
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
2917
- }
2918
- function formatErrorMessage2(error) {
2919
- if (error instanceof Error) return error.message;
2920
- return String(error);
2921
- }
2922
-
2923
- // src/server/define-decks.ts
2924
- function defineDecks(options) {
2925
- const manifest = Array.isArray(options.decks) ? { decks: options.decks } : options.manifest;
2926
- if (!manifest) throw new Error("defineDecks requires either decks or manifest.");
2927
- const source = manifestDeckSource(manifest);
2928
- const baseOptions = {
2929
- ...options,
2930
- source
2931
- };
2932
- return {
2933
- source,
2934
- router(overrides = {}) {
2935
- const nextSource = overrides.source ?? source;
2936
- return decksRouter(mergeDecksRouterOptions(baseOptions, { ...overrides, source: nextSource }));
2937
- }
2938
- };
2998
+ const title = titleOverride ?? deck?.meta.title ?? deck?.slug ?? "Hono Decks";
2999
+ return resolveDeckDocument({
3000
+ c,
3001
+ surface,
3002
+ deck,
3003
+ slug: deck?.slug,
3004
+ mountPath,
3005
+ title: surface === "presenter" ? `${title} - Presenter` : title
3006
+ }, options.document);
3007
+ }
3008
+ function escapeHtml(value) {
3009
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
2939
3010
  }
3011
+ function formatErrorMessage(error) {
3012
+ if (error instanceof Error) return error.message;
3013
+ return String(error);
3014
+ }
3015
+ //#endregion
3016
+ //#region src/server/define-decks.ts
3017
+ function defineDecks(options) {
3018
+ const manifest = Array.isArray(options.decks) ? { decks: options.decks } : options.manifest;
3019
+ if (!manifest) throw new Error("defineDecks requires either decks or manifest.");
3020
+ const source = manifestDeckSource(manifest);
3021
+ const baseOptions = {
3022
+ ...options,
3023
+ source
3024
+ };
3025
+ return {
3026
+ source,
3027
+ router(overrides = {}) {
3028
+ const nextSource = overrides.source ?? source;
3029
+ return decksRouter(mergeDecksRouterOptions(baseOptions, {
3030
+ ...overrides,
3031
+ source: nextSource
3032
+ }));
3033
+ }
3034
+ };
3035
+ }
3036
+ /** Defines the single configuration consumed by the CLI and runtime. */
2940
3037
  function defineDecksConfig(config) {
2941
- return config;
3038
+ return config;
2942
3039
  }
3040
+ /** Applies app configuration to generated decks and returns the primary runtime API. */
2943
3041
  function configureDecks(defined, config) {
2944
- const mountPath = normalizeMountPath(config.mountPath);
2945
- const source = config.source?.(defined.source) ?? defined.source;
2946
- const configuredRouter = mergeDecksRouterOptions(
2947
- { source, ...config.router },
2948
- { source }
2949
- );
2950
- return {
2951
- mountPath,
2952
- source,
2953
- router(overrides = {}) {
2954
- return defined.router(mergeDecksRouterOptions(configuredRouter, overrides));
2955
- },
2956
- context(overrides = {}) {
2957
- const merged = mergeDecksRouterOptions(configuredRouter, overrides);
2958
- return deckContext({
2959
- source,
2960
- mountPath,
2961
- dev: merged.dev,
2962
- pages: merged.pages,
2963
- viewer: merged.viewer
2964
- });
2965
- },
2966
- paths(slug) {
2967
- return createDeckPaths(mountPath, slug);
2968
- }
2969
- };
3042
+ const mountPath = normalizeMountPath(config.mountPath);
3043
+ const source = config.source?.(defined.source) ?? defined.source;
3044
+ const configuredRouter = mergeDecksRouterOptions({
3045
+ source,
3046
+ ...config.router
3047
+ }, { source });
3048
+ return {
3049
+ mountPath,
3050
+ source,
3051
+ router(overrides = {}) {
3052
+ return defined.router(mergeDecksRouterOptions(configuredRouter, overrides));
3053
+ },
3054
+ context(overrides = {}) {
3055
+ const merged = mergeDecksRouterOptions(configuredRouter, overrides);
3056
+ return deckContext({
3057
+ source,
3058
+ mountPath,
3059
+ dev: merged.dev,
3060
+ pages: merged.pages,
3061
+ viewer: merged.viewer
3062
+ });
3063
+ },
3064
+ paths(slug) {
3065
+ return createDeckPaths(mountPath, slug);
3066
+ }
3067
+ };
2970
3068
  }
2971
3069
  function mergeDecksRouterOptions(base, overrides) {
2972
- const viewer = mergeViewerOptions(base.viewer, overrides.viewer);
2973
- const presenter = mergePresenterOptions(base.presenter, overrides.presenter);
2974
- const document = mergeDocumentOptions(base.document, overrides.document);
2975
- const pages = mergePagesOptions(base.pages, overrides.pages);
2976
- const embed = mergeExternalEmbedOptions(base.embed, overrides.embed);
2977
- const exportOptions = overrides.export === false ? false : base.export === false ? overrides.export : base.export || overrides.export ? { ...base.export, ...overrides.export } : void 0;
2978
- const components = base.components || overrides.components ? { ...base.components, ...overrides.components } : void 0;
2979
- return {
2980
- ...base,
2981
- ...overrides,
2982
- ...viewer === void 0 ? {} : { viewer },
2983
- ...presenter === void 0 ? {} : { presenter },
2984
- ...document === void 0 ? {} : { document },
2985
- ...pages === void 0 ? {} : { pages },
2986
- ...embed === void 0 ? {} : { embed },
2987
- ...exportOptions === void 0 ? {} : { export: exportOptions },
2988
- ...components === void 0 ? {} : { components },
2989
- source: overrides.source ?? base.source
2990
- };
3070
+ const viewer = mergeViewerOptions(base.viewer, overrides.viewer);
3071
+ const presenter = mergePresenterOptions(base.presenter, overrides.presenter);
3072
+ const document = mergeDocumentOptions(base.document, overrides.document);
3073
+ const pages = mergePagesOptions(base.pages, overrides.pages);
3074
+ const embed = mergeExternalEmbedOptions(base.embed, overrides.embed);
3075
+ const exportOptions = overrides.export === false ? false : base.export === false ? overrides.export : base.export || overrides.export ? {
3076
+ ...base.export,
3077
+ ...overrides.export
3078
+ } : void 0;
3079
+ const components = base.components || overrides.components ? {
3080
+ ...base.components,
3081
+ ...overrides.components
3082
+ } : void 0;
3083
+ return {
3084
+ ...base,
3085
+ ...overrides,
3086
+ ...viewer === void 0 ? {} : { viewer },
3087
+ ...presenter === void 0 ? {} : { presenter },
3088
+ ...document === void 0 ? {} : { document },
3089
+ ...pages === void 0 ? {} : { pages },
3090
+ ...embed === void 0 ? {} : { embed },
3091
+ ...exportOptions === void 0 ? {} : { export: exportOptions },
3092
+ ...components === void 0 ? {} : { components },
3093
+ source: overrides.source ?? base.source
3094
+ };
2991
3095
  }
2992
3096
  function mergePagesOptions(base, override) {
2993
- if (!base) return override;
2994
- if (!override) return base;
2995
- const index = override.index === false ? false : override.index === void 0 ? base.index : base.index === false || base.index === void 0 ? override.index : { ...base.index, ...override.index };
2996
- return {
2997
- ...base,
2998
- ...override,
2999
- ...index === void 0 ? {} : { index }
3000
- };
3097
+ if (!base) return override;
3098
+ if (!override) return base;
3099
+ const index = override.index === false ? false : override.index === void 0 ? base.index : base.index === false || base.index === void 0 ? override.index : {
3100
+ ...base.index,
3101
+ ...override.index
3102
+ };
3103
+ return {
3104
+ ...base,
3105
+ ...override,
3106
+ ...index === void 0 ? {} : { index }
3107
+ };
3001
3108
  }
3002
3109
  function mergeExternalEmbedOptions(base, override) {
3003
- if (override === false) return false;
3004
- if (override === void 0) return base;
3005
- if (base === false || base === void 0) return override;
3006
- const viewer = typeof base.viewer === "object" && typeof override.viewer === "object" ? { ...base.viewer, ...override.viewer } : override.viewer ?? base.viewer;
3007
- const document = base.document || override.document ? { ...base.document, ...override.document } : void 0;
3008
- return {
3009
- ...base,
3010
- ...override,
3011
- ...viewer === void 0 ? {} : { viewer },
3012
- ...document === void 0 ? {} : { document }
3013
- };
3110
+ if (override === false) return false;
3111
+ if (override === void 0) return base;
3112
+ if (base === false || base === void 0) return override;
3113
+ const viewer = typeof base.viewer === "object" && typeof override.viewer === "object" ? {
3114
+ ...base.viewer,
3115
+ ...override.viewer
3116
+ } : override.viewer ?? base.viewer;
3117
+ const document = base.document || override.document ? {
3118
+ ...base.document,
3119
+ ...override.document
3120
+ } : void 0;
3121
+ return {
3122
+ ...base,
3123
+ ...override,
3124
+ ...viewer === void 0 ? {} : { viewer },
3125
+ ...document === void 0 ? {} : { document }
3126
+ };
3014
3127
  }
3015
3128
  function mergeDocumentOptions(base, override) {
3016
- if (!base) return override;
3017
- if (!override) return base;
3018
- return {
3019
- ...base,
3020
- ...override,
3021
- surfaces: base.surfaces || override.surfaces ? { ...base.surfaces, ...override.surfaces } : void 0
3022
- };
3129
+ if (!base) return override;
3130
+ if (!override) return base;
3131
+ return {
3132
+ ...base,
3133
+ ...override,
3134
+ surfaces: base.surfaces || override.surfaces ? {
3135
+ ...base.surfaces,
3136
+ ...override.surfaces
3137
+ } : void 0
3138
+ };
3023
3139
  }
3024
3140
  function mergeViewerOptions(base, override) {
3025
- if (!base) return override;
3026
- if (!override) return base;
3027
- const controls = mergeControls(base.controls, override.controls);
3028
- const openGraph = mergeOpenGraph(base.openGraph, override.openGraph);
3029
- return {
3030
- ...base,
3031
- ...override,
3032
- ...controls === void 0 ? {} : { controls },
3033
- ...openGraph === void 0 ? {} : { openGraph }
3034
- };
3141
+ if (!base) return override;
3142
+ if (!override) return base;
3143
+ const controls = mergeControls(base.controls, override.controls);
3144
+ const openGraph = mergeOpenGraph(base.openGraph, override.openGraph);
3145
+ return {
3146
+ ...base,
3147
+ ...override,
3148
+ ...controls === void 0 ? {} : { controls },
3149
+ ...openGraph === void 0 ? {} : { openGraph }
3150
+ };
3035
3151
  }
3036
3152
  function mergeOpenGraph(base, override) {
3037
- if (override === void 0) return base;
3038
- if (base === void 0 || typeof base === "boolean" || typeof override === "boolean") return override;
3039
- return { ...base, ...override };
3153
+ if (override === void 0) return base;
3154
+ if (base === void 0 || typeof base === "boolean" || typeof override === "boolean") return override;
3155
+ return {
3156
+ ...base,
3157
+ ...override
3158
+ };
3040
3159
  }
3041
3160
  function mergeControls(base, override) {
3042
- if (override === void 0) return base;
3043
- if (base === void 0 || base === false || override === false) return override;
3044
- return {
3045
- ...base,
3046
- ...override,
3047
- attributes: base.attributes || override.attributes ? { ...base.attributes, ...override.attributes } : void 0,
3048
- labels: base.labels || override.labels ? { ...base.labels, ...override.labels } : void 0,
3049
- hidden: base.hidden || override.hidden ? [.../* @__PURE__ */ new Set([...base.hidden ?? [], ...override.hidden ?? []])] : void 0,
3050
- before: mergeControlSlots(base.before, override.before),
3051
- after: mergeControlSlots(base.after, override.after)
3052
- };
3161
+ if (override === void 0) return base;
3162
+ if (base === void 0 || base === false || override === false) return override;
3163
+ return {
3164
+ ...base,
3165
+ ...override,
3166
+ attributes: base.attributes || override.attributes ? {
3167
+ ...base.attributes,
3168
+ ...override.attributes
3169
+ } : void 0,
3170
+ labels: base.labels || override.labels ? {
3171
+ ...base.labels,
3172
+ ...override.labels
3173
+ } : void 0,
3174
+ hidden: base.hidden || override.hidden ? [.../* @__PURE__ */ new Set([...base.hidden ?? [], ...override.hidden ?? []])] : void 0,
3175
+ before: mergeControlSlots(base.before, override.before),
3176
+ after: mergeControlSlots(base.after, override.after)
3177
+ };
3053
3178
  }
3054
3179
  function mergeControlSlots(base, override) {
3055
- if (override === void 0) return base;
3056
- if (base === void 0) return override;
3057
- if (Array.isArray(base) && Array.isArray(override)) return [...base, ...override];
3058
- return override;
3180
+ if (override === void 0) return base;
3181
+ if (base === void 0) return override;
3182
+ if (Array.isArray(base) && Array.isArray(override)) return [...base, ...override];
3183
+ return override;
3059
3184
  }
3060
3185
  function mergePresenterOptions(base, override) {
3061
- if (override === void 0) return base;
3062
- if (override === false || base === false || base === void 0) return override;
3063
- const viewerControl = typeof base.viewerControl === "object" && typeof override.viewerControl === "object" ? { ...base.viewerControl, ...override.viewerControl } : override.viewerControl ?? base.viewerControl;
3064
- return { ...base, ...override, viewerControl };
3065
- }
3066
-
3067
- // src/source/r2-assets.ts
3186
+ if (override === void 0) return base;
3187
+ if (override === false || base === false || base === void 0) return override;
3188
+ const viewerControl = typeof base.viewerControl === "object" && typeof override.viewerControl === "object" ? {
3189
+ ...base.viewerControl,
3190
+ ...override.viewerControl
3191
+ } : override.viewerControl ?? base.viewerControl;
3192
+ return {
3193
+ ...base,
3194
+ ...override,
3195
+ viewerControl
3196
+ };
3197
+ }
3198
+ //#endregion
3199
+ //#region src/source/r2-assets.ts
3068
3200
  function withR2Assets(source, options) {
3069
- return {
3070
- async listDecks(c) {
3071
- return source.listDecks(c);
3072
- },
3073
- async getCompiledDeck(c, slug) {
3074
- return source.getCompiledDeck(c, slug);
3075
- },
3076
- async getAsset(c, slug, assetPath) {
3077
- const deck = await source.getCompiledDeck(c, slug);
3078
- const asset = deck ? findRoutableAsset(deck.assets, slug, assetPath) : void 0;
3079
- const bucket = asset ? await resolveBucket(options.bucket, c) : void 0;
3080
- if (deck && asset && bucket) {
3081
- const object = await bucket.get(resolveR2Key({ deck, asset, slug, assetPath }, options));
3082
- if (object) {
3083
- const response = await responseFromR2Object(object, { deck, asset, slug, assetPath }, options);
3084
- if (response) return response;
3085
- }
3086
- }
3087
- return source.getAsset?.(c, slug, assetPath) ?? null;
3088
- }
3089
- };
3201
+ return {
3202
+ async listDecks(c) {
3203
+ return source.listDecks(c);
3204
+ },
3205
+ async getCompiledDeck(c, slug) {
3206
+ return source.getCompiledDeck(c, slug);
3207
+ },
3208
+ async getAsset(c, slug, assetPath) {
3209
+ const deck = await source.getCompiledDeck(c, slug);
3210
+ const asset = deck ? findRoutableAsset(deck.assets, slug, assetPath) : void 0;
3211
+ const bucket = asset ? await resolveBucket(options.bucket, c) : void 0;
3212
+ if (deck && asset && bucket) {
3213
+ const object = await bucket.get(resolveR2Key({
3214
+ deck,
3215
+ asset,
3216
+ slug,
3217
+ assetPath
3218
+ }, options));
3219
+ if (object) {
3220
+ const response = await responseFromR2Object(object, {
3221
+ deck,
3222
+ asset,
3223
+ slug,
3224
+ assetPath
3225
+ }, options);
3226
+ if (response) return response;
3227
+ }
3228
+ }
3229
+ return source.getAsset?.(c, slug, assetPath) ?? null;
3230
+ }
3231
+ };
3090
3232
  }
3091
3233
  async function resolveBucket(bucket, c) {
3092
- return typeof bucket === "function" ? bucket(c) : bucket;
3234
+ return typeof bucket === "function" ? bucket(c) : bucket;
3093
3235
  }
3094
3236
  function findRoutableAsset(assets, slug, assetPath) {
3095
- const normalized = assetPath.replace(/^\/+/, "");
3096
- return assets.find((asset) => {
3097
- if (asset.type !== "local" && asset.type !== "r2") return false;
3098
- const suffix = `/${slug}/assets/${normalized}`;
3099
- return asset.publicPath.endsWith(suffix);
3100
- });
3237
+ const normalized = stripLeadingSlashes(assetPath);
3238
+ return assets.find((asset) => {
3239
+ if (asset.type !== "local" && asset.type !== "r2") return false;
3240
+ const suffix = `/${slug}/assets/${normalized}`;
3241
+ return asset.publicPath.endsWith(suffix);
3242
+ });
3101
3243
  }
3102
3244
  function resolveR2Key(input, options) {
3103
- if (options.key) return options.key(input);
3104
- if (input.asset.r2Key) return input.asset.r2Key;
3105
- const key = input.asset.sourcePath || `${input.slug}/assets/${input.assetPath}`;
3106
- const prefix = options.keyPrefix?.replace(/\/+$/, "");
3107
- return prefix ? `${prefix}/${key.replace(/^\/+/, "")}` : key.replace(/^\/+/, "");
3245
+ if (options.key) return options.key(input);
3246
+ if (input.asset.r2Key) return input.asset.r2Key;
3247
+ const key = input.asset.sourcePath || `${input.slug}/assets/${input.assetPath}`;
3248
+ const prefix = options.keyPrefix ? stripTrailingSlashes(options.keyPrefix) : void 0;
3249
+ const normalizedKey = stripLeadingSlashes(key);
3250
+ return prefix ? `${prefix}/${normalizedKey}` : normalizedKey;
3251
+ }
3252
+ function stripLeadingSlashes(value) {
3253
+ let index = 0;
3254
+ while (value[index] === "/") index += 1;
3255
+ return value.slice(index);
3256
+ }
3257
+ function stripTrailingSlashes(value) {
3258
+ let end = value.length;
3259
+ while (end > 0 && value[end - 1] === "/") end -= 1;
3260
+ return value.slice(0, end);
3108
3261
  }
3109
3262
  async function responseFromR2Object(object, input, options) {
3110
- const body = object.body ?? (object.arrayBuffer ? await object.arrayBuffer() : void 0);
3111
- if (body == null) return null;
3112
- const headers = new Headers();
3113
- object.writeHttpMetadata?.(headers);
3114
- applyHttpMetadata(headers, object.httpMetadata);
3115
- if (!headers.has("content-type") && input.asset.contentType) headers.set("content-type", input.asset.contentType);
3116
- const cacheControl = resolveCacheControl(input, options);
3117
- if (cacheControl) {
3118
- headers.set("cache-control", cacheControl);
3119
- } else if (cacheControl !== false && input.asset.cacheControl && !headers.has("cache-control")) {
3120
- headers.set("cache-control", input.asset.cacheControl);
3121
- }
3122
- return new Response(body, { headers });
3263
+ const body = object.body ?? (object.arrayBuffer ? await object.arrayBuffer() : void 0);
3264
+ if (body == null) return null;
3265
+ const headers = new Headers();
3266
+ object.writeHttpMetadata?.(headers);
3267
+ applyHttpMetadata(headers, object.httpMetadata);
3268
+ if (!headers.has("content-type") && input.asset.contentType) headers.set("content-type", input.asset.contentType);
3269
+ const cacheControl = resolveCacheControl(input, options);
3270
+ if (cacheControl) headers.set("cache-control", cacheControl);
3271
+ else if (cacheControl !== false && input.asset.cacheControl && !headers.has("cache-control")) headers.set("cache-control", input.asset.cacheControl);
3272
+ return new Response(body, { headers });
3123
3273
  }
3124
3274
  function applyHttpMetadata(headers, metadata) {
3125
- if (!metadata) return;
3126
- if (metadata.contentType && !headers.has("content-type")) headers.set("content-type", metadata.contentType);
3127
- if (metadata.cacheControl && !headers.has("cache-control")) headers.set("cache-control", metadata.cacheControl);
3128
- if (metadata.contentDisposition && !headers.has("content-disposition")) {
3129
- headers.set("content-disposition", metadata.contentDisposition);
3130
- }
3131
- if (metadata.contentEncoding && !headers.has("content-encoding")) headers.set("content-encoding", metadata.contentEncoding);
3132
- if (metadata.contentLanguage && !headers.has("content-language")) headers.set("content-language", metadata.contentLanguage);
3275
+ if (!metadata) return;
3276
+ if (metadata.contentType && !headers.has("content-type")) headers.set("content-type", metadata.contentType);
3277
+ if (metadata.cacheControl && !headers.has("cache-control")) headers.set("cache-control", metadata.cacheControl);
3278
+ if (metadata.contentDisposition && !headers.has("content-disposition")) headers.set("content-disposition", metadata.contentDisposition);
3279
+ if (metadata.contentEncoding && !headers.has("content-encoding")) headers.set("content-encoding", metadata.contentEncoding);
3280
+ if (metadata.contentLanguage && !headers.has("content-language")) headers.set("content-language", metadata.contentLanguage);
3133
3281
  }
3134
3282
  function resolveCacheControl(input, options) {
3135
- return typeof options.cacheControl === "function" ? options.cacheControl(input) : options.cacheControl;
3136
- }
3137
- export {
3138
- SLIDE_TRANSITIONS,
3139
- builtInSlideComponents,
3140
- configureDecks,
3141
- controlIconLabel,
3142
- createDeckPaths,
3143
- createDeckViewerEmbed,
3144
- createDeckViewerParts,
3145
- deckContext,
3146
- decksRouter,
3147
- defineDecks,
3148
- defineDecksConfig,
3149
- defineSlideComponents,
3150
- manifestDeckSource,
3151
- mergeDecksRouterOptions,
3152
- renderCompiledDeck,
3153
- renderCompiledDeckAsync,
3154
- renderCompiledDeckPage,
3155
- renderCompiledDeckPageAsync,
3156
- renderCompiledSlide,
3157
- renderCompiledSlideAsync,
3158
- renderControlIcon,
3159
- renderControlIconHtml,
3160
- serveDecksClientEntry,
3161
- withR2Assets
3162
- };
3283
+ return typeof options.cacheControl === "function" ? options.cacheControl(input) : options.cacheControl;
3284
+ }
3285
+ //#endregion
3286
+ export { SLIDE_TRANSITIONS, builtInSlideComponents, configureDecks, controlIconLabel, createDeckPaths, createDeckViewerEmbed, createDeckViewerParts, deckContext, decksRouter, defineDecks, defineDecksConfig, defineSlideComponents, manifestDeckSource, mergeDecksRouterOptions, renderCompiledDeck, renderCompiledDeckAsync, renderCompiledDeckPage, renderCompiledDeckPageAsync, renderCompiledSlide, renderCompiledSlideAsync, renderControlIcon, renderControlIconHtml, serveDecksClientEntry, withR2Assets };