hono-decks 0.2.0 → 0.2.1

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,302 @@ 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, "")}`;
1613
+ const trimmed = value.trim();
1614
+ if (!trimmed || trimmed === "/") return "/";
1615
+ return `/${trimmed.replace(/^\/+|\/+$/g, "")}`;
1635
1616
  }
1636
-
1637
- // src/server/browser-export.ts
1617
+ //#endregion
1618
+ //#region src/server/browser-export.ts
1638
1619
  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
- });
1620
+ const slug = c.req.param("slug");
1621
+ if (!slug) return c.json({
1622
+ error: "Deck not found",
1623
+ slug: ""
1624
+ }, 404);
1625
+ const deck = await options.source.getCompiledDeck(c, slug);
1626
+ if (!deck || options.dev !== true && deck.meta.draft) return c.json({
1627
+ error: "Deck not found",
1628
+ slug
1629
+ }, 404);
1630
+ const exportOptions = options.export;
1631
+ if (!exportOptions) return c.json({
1632
+ error: "Browser export not configured",
1633
+ slug,
1634
+ format
1635
+ }, 503);
1636
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/export.${format}`);
1637
+ const resolverInput = {
1638
+ c,
1639
+ deck,
1640
+ slug,
1641
+ mountPath,
1642
+ paths: createDeckPaths(mountPath, slug),
1643
+ format
1644
+ };
1645
+ if (!await isBrowserExportAuthorized(exportOptions, resolverInput)) return c.json({
1646
+ error: "Browser export not authorized",
1647
+ slug,
1648
+ format
1649
+ }, 403);
1650
+ const browser = await exportOptions.browser(resolverInput);
1651
+ if (!browser) return c.json({
1652
+ error: "Browser export not configured",
1653
+ slug,
1654
+ format
1655
+ }, 503);
1656
+ const printUrl = new URL(resolverInput.paths.print, c.req.url).toString();
1657
+ const action = format === "pdf" ? "pdf" : "screenshot";
1658
+ const response = await browser.quickAction(action, createBrowserRunExportRequest(exportOptions, printUrl, format));
1659
+ const headers = new Headers(response.headers);
1660
+ if (!headers.has("content-type")) headers.set("content-type", format === "pdf" ? "application/pdf" : "image/png");
1661
+ headers.set("content-disposition", `attachment; filename="${exportFilename(exportOptions, deck, format)}"`);
1662
+ return new Response(response.body, {
1663
+ status: response.status,
1664
+ statusText: response.statusText,
1665
+ headers
1666
+ });
1670
1667
  }
1671
1668
  function isPdfExportEnabled(options) {
1672
- return Boolean(options && options.pdf !== void 0 && options.pdf !== false);
1669
+ return Boolean(options && options.pdf !== void 0 && options.pdf !== false);
1673
1670
  }
1674
1671
  function isPngExportEnabled(options) {
1675
- return Boolean(options && options.png !== void 0 && options.png !== false);
1672
+ return Boolean(options && options.png !== void 0 && options.png !== false);
1676
1673
  }
1677
1674
  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 };
1675
+ if (!options) return {};
1676
+ const slug = deck.slug;
1677
+ const paths = createDeckPaths(mountPath, slug);
1678
+ return {
1679
+ pdf: isPdfExportEnabled(options) && await isBrowserExportAuthorized(options, {
1680
+ c,
1681
+ deck,
1682
+ slug,
1683
+ mountPath,
1684
+ paths,
1685
+ format: "pdf"
1686
+ }),
1687
+ png: isPngExportEnabled(options) && await isBrowserExportAuthorized(options, {
1688
+ c,
1689
+ deck,
1690
+ slug,
1691
+ mountPath,
1692
+ paths,
1693
+ format: "png"
1694
+ })
1695
+ };
1684
1696
  }
1685
1697
  async function isBrowserExportAuthorized(options, input) {
1686
- return options.authorize ? options.authorize(input) : true;
1698
+ return options.authorize ? options.authorize(input) : true;
1687
1699
  }
1688
1700
  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
- };
1701
+ if (format === "pdf") {
1702
+ const request = exportOptionObject(options.pdf).request ?? {};
1703
+ return {
1704
+ ...request,
1705
+ url: printUrl,
1706
+ gotoOptions: {
1707
+ waitUntil: "networkidle2",
1708
+ timeout: 45e3,
1709
+ ...recordValue(request.gotoOptions)
1710
+ },
1711
+ pdfOptions: {
1712
+ format: "a4",
1713
+ printBackground: true,
1714
+ preferCSSPageSize: true,
1715
+ ...recordValue(request.pdfOptions)
1716
+ }
1717
+ };
1718
+ }
1719
+ const request = exportOptionObject(options.png).request ?? {};
1720
+ return {
1721
+ ...request,
1722
+ url: printUrl,
1723
+ gotoOptions: {
1724
+ waitUntil: "networkidle2",
1725
+ timeout: 45e3,
1726
+ ...recordValue(request.gotoOptions)
1727
+ },
1728
+ viewport: {
1729
+ width: 794,
1730
+ height: 1123,
1731
+ deviceScaleFactor: 2,
1732
+ ...recordValue(request.viewport)
1733
+ },
1734
+ screenshotOptions: {
1735
+ type: "png",
1736
+ fullPage: true,
1737
+ ...recordValue(request.screenshotOptions)
1738
+ }
1739
+ };
1728
1740
  }
1729
1741
  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}`;
1742
+ const option = exportOptionObject(format === "pdf" ? options.pdf : options.png);
1743
+ return `${safeFilename$1((typeof option.filename === "function" ? option.filename(deck) : option.filename) ?? deck.meta.title ?? deck.slug)}.${format}`;
1733
1744
  }
1734
1745
  function exportOptionObject(option) {
1735
- return typeof option === "object" && option ? option : {};
1746
+ return typeof option === "object" && option ? option : {};
1736
1747
  }
1737
1748
  function recordValue(value) {
1738
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1749
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1739
1750
  }
1740
- function safeFilename(value) {
1741
- const normalized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
1742
- return normalized || "deck";
1751
+ function safeFilename$1(value) {
1752
+ return value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "deck";
1743
1753
  }
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";
1754
+ //#endregion
1755
+ //#region src/server/viewer-script.ts
1756
+ const VIEWER_STATE_MESSAGE_TYPE = "hono-decks:state";
1757
+ const VIEWER_COMMAND_MESSAGE_TYPE = "hono-decks:command";
1752
1758
  function renderViewerScript(nonce) {
1753
- const nonceAttribute = nonce ? ` nonce="${escapeHtml6(nonce)}"` : "";
1754
- return `<script data-hono-decks-viewer-runtime${nonceAttribute}>
1759
+ return `<script data-hono-decks-viewer-runtime${nonce ? ` nonce="${escapeHtml$3(nonce)}"` : ""}>
1755
1760
  (() => {
1756
1761
  const roots = Array.from(document.querySelectorAll("[data-hono-decks-viewer]"));
1757
1762
  const runtime = window.__honoDecksViewerRuntime ??= { controllers: new Map(), globalInitialized: false };
@@ -1938,21 +1943,21 @@ function renderViewerScript(nonce) {
1938
1943
  });
1939
1944
  }
1940
1945
  })();
1941
- </script>`;
1946
+ <\/script>`;
1942
1947
  }
1943
- function escapeHtml6(value) {
1944
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1948
+ function escapeHtml$3(value) {
1949
+ return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1945
1950
  }
1946
-
1947
- // src/server/viewer-style.ts
1948
- var VIEWER_ASPECT_RATIO = "16/9";
1951
+ //#endregion
1952
+ //#region src/server/viewer-style.ts
1953
+ const VIEWER_ASPECT_RATIO = "16/9";
1949
1954
  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}
1955
+ 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
1956
  @supports (height:100dvh){.hono-decks-viewport{width:min(100%,calc((100dvh - 58px) * 16 / 9))}}
1952
1957
  @supports (width:1cqw){.hono-decks-viewport{width:min(100cqw,calc(100cqh * 16 / 9));height:min(100cqh,calc(100cqw * 9 / 16))}}`;
1953
1958
  }
1954
1959
  function baseViewerStyle() {
1955
- return `
1960
+ return `
1956
1961
  :root{color-scheme:dark;background:#050816;color:#eef2ff;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
1957
1962
  html,body{margin:0;width:100%;height:100%;min-height:100vh}
1958
1963
  @supports (height:100dvh){html,body{height:100dvh;min-height:100dvh}}
@@ -1987,8 +1992,8 @@ ${viewerViewportRule()}
1987
1992
  @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
1993
  }
1989
1994
  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}
1995
+ const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
1996
+ 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
1997
  ${root} *:focus-visible{outline:none}
1993
1998
  ${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
1999
  ${root} .hono-decks-viewer-stage{display:grid;place-items:center;width:100%;min-width:0;min-height:0;container-type:inline-size}
@@ -2009,129 +2014,142 @@ ${root} .hono-decks-control-icon{width:16px;height:16px;flex:0 0 auto;stroke:cur
2009
2014
  ${root} .hono-decks-viewer-toc button{font:inherit}
2010
2015
  @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
2016
  }
2012
-
2013
- // src/server/viewer.ts
2017
+ //#endregion
2018
+ //#region src/server/viewer.ts
2014
2019
  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
- };
2020
+ const slug = input.deck.slug;
2021
+ const title = input.deck.meta.title ?? slug;
2022
+ const basePath = input.mountPath.replace(/\/$/, "");
2023
+ const paths = createDeckPaths(basePath, slug);
2024
+ const imagePath = await resolveOpenGraphImagePath(input.openGraph, {
2025
+ deck: input.deck,
2026
+ paths
2027
+ });
2028
+ const renderUrl = `${paths.render}${input.viewerStateQuery ?? ""}`;
2029
+ const slides = createDeckToc(input.deck);
2030
+ const meta = {
2031
+ title,
2032
+ description: input.deck.meta.description,
2033
+ paths,
2034
+ availablePages: { print: input.availablePages?.print !== false },
2035
+ availableExports: input.exportPaths ?? {},
2036
+ ...imagePath ? { imagePath } : {}
2037
+ };
2038
+ const controlsInput = input.controls === false ? false : input.controls ?? {};
2039
+ const controls = controlsInput === false ? null : renderViewerControls(controlsInput, {
2040
+ slug,
2041
+ title,
2042
+ mountPath: basePath,
2043
+ meta,
2044
+ slides
2045
+ });
2046
+ return {
2047
+ slug,
2048
+ title,
2049
+ renderUrl,
2050
+ frame: {
2051
+ content: renderViewerFrame({
2052
+ title,
2053
+ renderUrl
2054
+ }),
2055
+ html: renderViewerFrameHtml({
2056
+ title,
2057
+ renderUrl
2058
+ })
2059
+ },
2060
+ controls: controls ? {
2061
+ content: controls,
2062
+ html: await renderJsxValue(controls)
2063
+ } : null,
2064
+ toc: {
2065
+ content: renderViewerToc(slides),
2066
+ html: renderViewerTocHtml(slides)
2067
+ },
2068
+ slides,
2069
+ meta
2070
+ };
2046
2071
  }
2047
2072
  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
- };
2073
+ const parts = await createDeckViewerParts(input);
2074
+ const rootHtml = await renderJsxValue(jsx("section", {
2075
+ class: ["hono-decks-embedded-viewer", input.className].filter(Boolean).join(" "),
2076
+ "data-hono-decks-viewer": true,
2077
+ "data-hono-decks-embed": true,
2078
+ "data-deck-slug": parts.slug,
2079
+ ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2080
+ "aria-label": parts.title,
2081
+ children: [jsx("div", {
2082
+ class: "hono-decks-viewer-shell",
2083
+ children: [parts.frame.content, parts.controls?.content]
2084
+ }), input.toc ? parts.toc.content : null]
2085
+ }));
2086
+ const embedHtml = `<style data-hono-decks-embed-style${input.nonce ? ` nonce="${escapeHtml$2(input.nonce)}"` : ""}>${embeddedViewerStyle()}${input.style ?? ""}</style>${rootHtml}${renderViewerScript(input.nonce)}`;
2087
+ return {
2088
+ ...parts,
2089
+ embed: raw(embedHtml),
2090
+ embedHtml
2091
+ };
2072
2092
  }
2073
2093
  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)}">
2094
+ const parts = await createDeckViewerParts({
2095
+ deck: input.deck,
2096
+ mountPath: input.mountPath,
2097
+ viewerStateQuery: input.viewerStateQuery,
2098
+ controls: input.viewer?.controls,
2099
+ openGraph: input.viewer?.openGraph,
2100
+ availablePages: input.availablePages,
2101
+ exportPaths: await resolveAuthorizedExportPaths(input.c, input.deck, input.mountPath, input.exportOptions)
2102
+ });
2103
+ const renderInput = {
2104
+ ...parts,
2105
+ c: input.c,
2106
+ deck: input.deck,
2107
+ mountPath: input.mountPath
2108
+ };
2109
+ const customContent = input.viewer?.render ? await input.viewer.render(renderInput) : void 0;
2110
+ const content = jsx("main", {
2111
+ "data-hono-decks-viewer": true,
2112
+ "data-deck-slug": parts.slug,
2113
+ ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
2114
+ ...customContent ? { "aria-label": parts.title } : { "aria-labelledby": "hono-decks-viewer-title" },
2115
+ children: customContent ?? [jsx("header", {
2116
+ class: "hono-decks-viewer-header",
2117
+ children: [jsx("h1", {
2118
+ id: "hono-decks-viewer-title",
2119
+ class: "hono-decks-viewer-title",
2120
+ children: parts.title
2121
+ }), jsx("p", {
2122
+ class: "hono-decks-viewer-meta",
2123
+ children: `${parts.slides.length} ${parts.slides.length === 1 ? "slide" : "slides"}`
2124
+ })]
2125
+ }), jsx("div", {
2126
+ class: "hono-decks-viewer-shell",
2127
+ children: [parts.frame.content, parts.controls?.content]
2128
+ })]
2129
+ });
2130
+ const viewerHead = typeof input.viewer?.head === "function" ? await input.viewer.head(renderInput) : await input.viewer?.head;
2131
+ const viewerLang = typeof input.viewer?.lang === "function" ? await input.viewer.lang(renderInput) : input.viewer?.lang;
2132
+ const viewerNonce = typeof input.viewer?.nonce === "function" ? await input.viewer.nonce(renderInput) : input.viewer?.nonce;
2133
+ const document = await resolveDeckDocument({
2134
+ c: input.c,
2135
+ surface: "viewer",
2136
+ deck: input.deck,
2137
+ slug: input.deck.slug,
2138
+ mountPath: input.mountPath,
2139
+ title: parts.title
2140
+ }, input.document, {
2141
+ head: viewerHead,
2142
+ lang: viewerLang,
2143
+ nonce: viewerNonce
2144
+ });
2145
+ const nonceAttribute = documentNonceAttribute(document.nonce);
2146
+ const socialHead = renderViewerSocialHead(input.c, parts.meta);
2147
+ return `<!doctype html>
2148
+ <html lang="${escapeHtml$2(document.lang)}">
2131
2149
  <head>
2132
2150
  <meta charset="utf-8" />
2133
2151
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
2134
- <title>${escapeHtml7(parts.title)}</title>
2152
+ <title>${escapeHtml$2(parts.title)}</title>
2135
2153
  ${socialHead}
2136
2154
  <style${nonceAttribute}>${baseViewerStyle()}${input.viewer?.style ?? ""}</style>
2137
2155
  ${document.head}
@@ -2143,360 +2161,370 @@ async function renderDeckViewerPage(input) {
2143
2161
  </html>`;
2144
2162
  }
2145
2163
  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;
2164
+ if (!options) return void 0;
2165
+ if (options === true) return input.paths.ogImage;
2166
+ if (typeof options.imagePath === "function") return options.imagePath(input);
2167
+ return options.imagePath ?? input.paths.ogImage;
2150
2168
  }
2151
2169
  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)}" />
2170
+ if (!meta.imagePath) return "";
2171
+ const pageUrl = new URL(meta.paths.viewer, c.req.url).href;
2172
+ const imageUrl = new URL(meta.imagePath, c.req.url).href;
2173
+ 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)}" />` : "";
2174
+ return `<meta property="og:type" content="website" />
2175
+ <meta property="og:title" content="${escapeHtml$2(meta.title)}" />${description}
2176
+ <meta property="og:url" content="${escapeHtml$2(pageUrl)}" />
2177
+ <meta property="og:image" content="${escapeHtml$2(imageUrl)}" />
2163
2178
  <meta property="og:image:width" content="1200" />
2164
2179
  <meta property="og:image:height" content="630" />
2165
- <meta property="og:image:alt" content="${escapeHtml7(meta.title)}" />
2180
+ <meta property="og:image:alt" content="${escapeHtml$2(meta.title)}" />
2166
2181
  <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)}" />`;
2182
+ <meta name="twitter:title" content="${escapeHtml$2(meta.title)}" />
2183
+ <meta name="twitter:image" content="${escapeHtml$2(imageUrl)}" />`;
2169
2184
  }
2170
2185
  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
- }));
2186
+ return deck.slides.map((slide) => ({
2187
+ index: slide.index,
2188
+ title: slide.meta.title,
2189
+ label: slide.meta.title ?? `Slide ${slide.index + 1}`
2190
+ }));
2176
2191
  }
2177
2192
  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
- });
2193
+ return jsx("div", {
2194
+ class: "hono-decks-viewer-stage",
2195
+ "data-hono-decks-frame": true,
2196
+ children: jsx("div", {
2197
+ class: "hono-decks-viewport",
2198
+ "data-viewer-viewport": true,
2199
+ tabindex: "0",
2200
+ children: [
2201
+ jsx("div", {
2202
+ class: "hono-decks-frame-stage",
2203
+ "data-viewer-stage": true,
2204
+ children: jsx("iframe", {
2205
+ title: input.title,
2206
+ src: input.renderUrl
2207
+ })
2208
+ }),
2209
+ jsx("button", {
2210
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous",
2211
+ type: "button",
2212
+ "data-viewer-navigation": "previous",
2213
+ "aria-label": "Previous slide"
2214
+ }),
2215
+ jsx("button", {
2216
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next",
2217
+ type: "button",
2218
+ "data-viewer-navigation": "next",
2219
+ "aria-label": "Next slide"
2220
+ })
2221
+ ]
2222
+ })
2223
+ });
2209
2224
  }
2210
2225
  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>`;
2226
+ 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
2227
  }
2213
2228
  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
- });
2229
+ return jsx("nav", {
2230
+ ...controlAttributes(options.attributes),
2231
+ class: controlsClassName(options),
2232
+ "data-hono-decks-viewer-controls": true,
2233
+ "aria-label": options.ariaLabel ?? "Viewer controls",
2234
+ children: resolveViewerControlItems(options, context).map((item) => renderViewerControlItem(item, options, context))
2235
+ });
2221
2236
  }
2222
2237
  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
- });
2238
+ return jsx("nav", {
2239
+ class: "hono-decks-viewer-toc",
2240
+ "data-hono-decks-toc": true,
2241
+ "aria-label": "Slide navigation",
2242
+ children: jsx("ol", { children: slides.map((slide) => jsx("li", { children: jsx("button", {
2243
+ type: "button",
2244
+ "data-action": "goTo",
2245
+ "data-slide-index": String(slide.index),
2246
+ children: slide.label
2247
+ }) })) })
2248
+ });
2240
2249
  }
2241
2250
  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>`;
2251
+ 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
2252
  }
2247
- function escapeHtml7(value) {
2248
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
2253
+ function escapeHtml$2(value) {
2254
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
2249
2255
  }
2250
- function safeFilename2(value) {
2251
- const normalized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
2252
- return normalized || "deck";
2256
+ function safeFilename(value) {
2257
+ return value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "deck";
2253
2258
  }
2254
2259
  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
- };
2260
+ return {
2261
+ back: {
2262
+ type: "default",
2263
+ key: "back"
2264
+ },
2265
+ previous: {
2266
+ type: "default",
2267
+ key: "previous"
2268
+ },
2269
+ position: {
2270
+ type: "default",
2271
+ key: "position"
2272
+ },
2273
+ next: {
2274
+ type: "default",
2275
+ key: "next"
2276
+ },
2277
+ fullscreen: {
2278
+ type: "default",
2279
+ key: "fullscreen"
2280
+ },
2281
+ print: context.meta.availablePages.print ? {
2282
+ type: "default",
2283
+ key: "print"
2284
+ } : null,
2285
+ exportPdf: context.meta.availableExports.pdf ? {
2286
+ type: "default",
2287
+ key: "exportPdf"
2288
+ } : null,
2289
+ exportPng: context.meta.availableExports.png ? {
2290
+ type: "default",
2291
+ key: "exportPng"
2292
+ } : null
2293
+ };
2265
2294
  }
2266
2295
  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));
2296
+ const defaults = buildViewerControlDefaults(context);
2297
+ const baseItems = [
2298
+ defaults.back,
2299
+ defaults.previous,
2300
+ defaults.position,
2301
+ defaults.next,
2302
+ defaults.fullscreen,
2303
+ defaults.print,
2304
+ defaults.exportPdf,
2305
+ defaults.exportPng
2306
+ ];
2307
+ return (typeof options.items === "function" ? options.items(defaults, context) : options.items ?? [
2308
+ ...resolveViewerControlSlotItems(options.before, context),
2309
+ ...filterHiddenDefaultItems(baseItems, options.hidden),
2310
+ ...resolveViewerControlSlotItems(options.after, context)
2311
+ ]).filter((item) => Boolean(item)).map((item) => applyViewerControlLabels(item, options.labels));
2284
2312
  }
2285
2313
  function renderViewerControlItem(item, options, context) {
2286
- const renderDefault = () => renderBaseViewerControlItem(item, options, context);
2287
- return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
2314
+ const renderDefault = () => renderBaseViewerControlItem(item, options, context);
2315
+ return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
2288
2316
  }
2289
2317
  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);
2318
+ if (item.type === "link") {
2319
+ const ariaLabel = item.icon ? String(item.attributes?.["aria-label"] ?? item.label) : void 0;
2320
+ return jsx("a", {
2321
+ ...linkAttributes(item.attributes),
2322
+ ...safeHref(item.href) ? { href: safeHref(item.href) } : {},
2323
+ ...item.download ? { download: item.download } : {},
2324
+ ...classAttribute(options.itemClassName, item.className),
2325
+ ...item.icon ? {
2326
+ "aria-label": ariaLabel,
2327
+ title: ariaLabel
2328
+ } : {},
2329
+ children: item.icon ? renderControlIcon(item.icon) : item.label
2330
+ });
2331
+ }
2332
+ if (item.type === "render") return item.render({ context });
2333
+ return renderDefaultViewerControlItem(item, options, context);
2305
2334
  }
2306
2335
  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
- }
2336
+ const itemProps = {
2337
+ ...linkAttributes(item.attributes),
2338
+ ...classAttribute(options.itemClassName, item.className)
2339
+ };
2340
+ switch (item.key) {
2341
+ case "back": return jsx("a", {
2342
+ ...itemProps,
2343
+ href: context.mountPath,
2344
+ "data-hono-decks-back-link": true,
2345
+ ...item.label === void 0 ? {
2346
+ "aria-label": controlIconLabel("deck-list"),
2347
+ title: controlIconLabel("deck-list")
2348
+ } : {},
2349
+ children: item.label ?? renderControlIcon("deck-list")
2350
+ });
2351
+ case "previous": return renderIconButton("previous", item, itemProps);
2352
+ case "position": return jsx("span", {
2353
+ ...itemProps,
2354
+ "data-slide-position": true,
2355
+ "data-hono-decks-position": true,
2356
+ children: item.label ?? "1 / ?"
2357
+ });
2358
+ case "next": return renderIconButton("next", item, itemProps);
2359
+ case "fullscreen": return renderIconButton("fullscreen", item, itemProps);
2360
+ case "print": return jsx("a", {
2361
+ ...itemProps,
2362
+ href: context.meta.paths.print,
2363
+ "data-hono-decks-print": true,
2364
+ ...item.label === void 0 ? {
2365
+ "aria-label": controlIconLabel("print"),
2366
+ title: controlIconLabel("print")
2367
+ } : {},
2368
+ children: item.label ?? renderControlIcon("print")
2369
+ });
2370
+ case "exportPdf": return jsx("a", {
2371
+ ...itemProps,
2372
+ href: context.meta.paths.exportPdf,
2373
+ download: `${safeFilename(context.meta.title)}.pdf`,
2374
+ "data-hono-decks-export": "pdf",
2375
+ ...item.label === void 0 ? {
2376
+ "aria-label": controlIconLabel("export-pdf"),
2377
+ title: controlIconLabel("export-pdf")
2378
+ } : {},
2379
+ children: item.label ?? renderControlIcon("export-pdf")
2380
+ });
2381
+ case "exportPng": return jsx("a", {
2382
+ ...itemProps,
2383
+ href: context.meta.paths.exportPng,
2384
+ download: `${safeFilename(context.meta.title)}.png`,
2385
+ "data-hono-decks-export": "png",
2386
+ ...item.label === void 0 ? {
2387
+ "aria-label": controlIconLabel("export-png"),
2388
+ title: controlIconLabel("export-png")
2389
+ } : {},
2390
+ children: item.label ?? renderControlIcon("export-png")
2391
+ });
2392
+ }
2360
2393
  }
2361
2394
  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
- });
2395
+ const iconName = controlIconNameForKey(action);
2396
+ const label = controlIconLabel(iconName);
2397
+ return jsx("button", {
2398
+ ...itemProps,
2399
+ type: "button",
2400
+ "data-action": action,
2401
+ "data-hono-decks-navigation-control": action,
2402
+ ...item.label === void 0 ? {
2403
+ "aria-label": label,
2404
+ title: label
2405
+ } : {},
2406
+ children: item.label ?? renderControlIcon(iconName)
2407
+ });
2372
2408
  }
2373
2409
  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
- }
2410
+ switch (key) {
2411
+ case "back": return "deck-list";
2412
+ case "previous": return "previous";
2413
+ case "next": return "next";
2414
+ case "fullscreen": return "fullscreen";
2415
+ case "print": return "print";
2416
+ case "exportPdf": return "export-pdf";
2417
+ case "exportPng": return "export-png";
2418
+ case "position": return "viewer";
2419
+ }
2392
2420
  }
2393
2421
  function controlsClassName(options) {
2394
- return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
2422
+ return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
2395
2423
  }
2396
2424
  function resolveViewerControlSlotItems(items, context) {
2397
- return typeof items === "function" ? items(context) : items ?? [];
2425
+ return typeof items === "function" ? items(context) : items ?? [];
2398
2426
  }
2399
2427
  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));
2428
+ if (!hidden?.length) return items;
2429
+ const hiddenKeys = new Set(hidden);
2430
+ return items.filter((item) => !item || item.type !== "default" || !hiddenKeys.has(item.key));
2403
2431
  }
2404
2432
  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 };
2433
+ if (item.type !== "default" || item.label !== void 0) return item;
2434
+ const label = labels?.[item.key];
2435
+ return label === void 0 ? item : {
2436
+ ...item,
2437
+ label
2438
+ };
2408
2439
  }
2409
2440
  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;
2441
+ const props = {};
2442
+ for (const [name, value] of Object.entries(attributes ?? {})) {
2443
+ if (!isSafeAttributeName(name) || value === false || value === void 0) continue;
2444
+ props[name] = value;
2445
+ }
2446
+ return props;
2416
2447
  }
2417
2448
  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;
2449
+ const props = linkAttributes(attributes);
2450
+ delete props.class;
2451
+ delete props["aria-label"];
2452
+ delete props["data-hono-decks-viewer-controls"];
2453
+ return props;
2423
2454
  }
2424
2455
  function classAttribute(...classNames) {
2425
- const className = classNames.filter(Boolean).join(" ");
2426
- return className ? { class: className } : {};
2456
+ const className = classNames.filter(Boolean).join(" ");
2457
+ return className ? { class: className } : {};
2427
2458
  }
2428
2459
  function isSafeAttributeName(value) {
2429
- return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
2460
+ return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
2430
2461
  }
2431
2462
  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
2463
+ const trimmed = value.trim();
2464
+ if (trimmed.length === 0) return void 0;
2465
+ if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return value;
2466
+ if (/^(javascript:|data:|vbscript:)/i.test(trimmed)) return void 0;
2467
+ if (/^[^/?#.]+:/i.test(trimmed)) return void 0;
2468
+ return value;
2469
+ }
2470
+ //#endregion
2471
+ //#region src/server/external-embed.ts
2441
2472
  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
- });
2473
+ const { c, deck, slug, mountPath } = input.context;
2474
+ const enabled = input.options.enabled;
2475
+ if (enabled === false || typeof enabled === "function" && !await enabled(input.context)) return c.json({
2476
+ error: "Embed route not found",
2477
+ slug
2478
+ }, 404);
2479
+ const document = await resolveDeckDocument({
2480
+ c,
2481
+ surface: "embed",
2482
+ deck,
2483
+ slug,
2484
+ mountPath,
2485
+ title: deck.meta.title ?? slug
2486
+ }, input.document, input.options.document);
2487
+ const viewer = await createDeckViewerEmbed({
2488
+ deck,
2489
+ mountPath,
2490
+ ...typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer,
2491
+ availablePages: input.availablePages,
2492
+ nonce: document.nonce
2493
+ });
2494
+ const renderInput = {
2495
+ ...input.context,
2496
+ viewer,
2497
+ document
2498
+ };
2499
+ const body = input.options.render ? await input.options.render(renderInput) : viewer.embed;
2500
+ const html = await renderExternalEmbedDocument({
2501
+ title: deck.meta.title ?? slug,
2502
+ body: await renderJsxValue(body),
2503
+ document,
2504
+ pageStyle: input.options.pageStyle,
2505
+ robots: input.options.robots
2506
+ });
2507
+ const frameAncestorsValue = typeof input.options.frameAncestors === "function" ? await input.options.frameAncestors(input.context) : input.options.frameAncestors;
2508
+ const response = c.html(html);
2509
+ const headers = new Headers(response.headers);
2510
+ headers.delete("x-frame-options");
2511
+ headers.set("content-security-policy", withFrameAncestors(headers.get("content-security-policy"), normalizeFrameAncestors(frameAncestorsValue)));
2512
+ return new Response(response.body, {
2513
+ status: response.status,
2514
+ statusText: response.statusText,
2515
+ headers
2516
+ });
2489
2517
  }
2490
2518
  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>
2519
+ const nonceAttribute = documentNonceAttribute(input.document.nonce);
2520
+ const robots = input.robots === false ? "" : `<meta name="robots" content="${escapeHtml$1(input.robots ?? "noindex")}"/>`;
2521
+ return `<!doctype html>
2522
+ <html lang="${escapeHtml$1(input.document.lang)}" data-hono-decks-external-embed-document>
2495
2523
  <head>
2496
2524
  <meta charset="utf-8" />
2497
2525
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
2498
2526
  ${robots}
2499
- <title>${escapeHtml8(input.title)}</title>
2527
+ <title>${escapeHtml$1(input.title)}</title>
2500
2528
  <style id="hono-decks-external-embed-css"${nonceAttribute}>${externalEmbedPageStyle()}${input.pageStyle ?? ""}</style>
2501
2529
  ${input.document.head}
2502
2530
  </head>
@@ -2504,384 +2532,444 @@ async function renderExternalEmbedDocument(input) {
2504
2532
  </html>`;
2505
2533
  }
2506
2534
  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];
2535
+ const values = Array.isArray(value) ? value : value?.split(/[\s,]+/) ?? [];
2536
+ const ancestors = /* @__PURE__ */ new Set(["'self'"]);
2537
+ for (const candidate of values) {
2538
+ const token = candidate.trim();
2539
+ if (!token || token === "'self'") continue;
2540
+ if (token === "*") return ["*"];
2541
+ if (token === "'none'") return ["'none'"];
2542
+ try {
2543
+ const url = new URL(token);
2544
+ if ((url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password) ancestors.add(url.origin);
2545
+ } catch {}
2546
+ }
2547
+ return [...ancestors];
2523
2548
  }
2524
2549
  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("; ");
2550
+ const directives = (currentPolicy ?? "").split(";").map((directive) => directive.trim()).filter((directive) => directive && !/^frame-ancestors(?:\s|$)/i.test(directive));
2551
+ directives.push(`frame-ancestors ${ancestors.join(" ")}`);
2552
+ return directives.join("; ");
2528
2553
  }
2529
2554
  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}`;
2555
+ 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
2556
  }
2532
- function escapeHtml8(value) {
2533
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2557
+ function escapeHtml$1(value) {
2558
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
2534
2559
  }
2535
-
2536
- // src/server/router.ts
2560
+ //#endregion
2561
+ //#region src/server/router.ts
2537
2562
  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;
2563
+ const router = new Hono();
2564
+ if (options.clientEntryAsset) router.get(normalizeClientEntryAssetPath(options.clientEntryAssetPath), serveDecksClientEntry(options.clientEntryAsset));
2565
+ for (const extension of options.extensions ?? []) router.route(extension.path, extension.router);
2566
+ router.get("/", async (c) => {
2567
+ const dev = await isDevEnabled(c, options);
2568
+ const decks = (await options.source.listDecks(c)).filter((deck) => dev || !deck.draft);
2569
+ const mountPath = c.req.path.replace(/\/$/, "");
2570
+ const pageInput = {
2571
+ c,
2572
+ surface: "index",
2573
+ mountPath,
2574
+ dev,
2575
+ decks
2576
+ };
2577
+ if (!await isPageSurfaceEnabled(options, pageInput)) return c.json({ error: "Deck index not found" }, 404);
2578
+ const indexOptions = options.pages?.index === false ? void 0 : options.pages?.index;
2579
+ const title = await resolveIndexTitle(indexOptions?.title, pageInput);
2580
+ const document = await resolveRouterDocument(c, options, "index", mountPath, void 0, title);
2581
+ const defaultContentHtml = renderDeckIndexContent(decks, mountPath);
2582
+ const defaultContent = raw(defaultContentHtml);
2583
+ const content = indexOptions?.render ? await renderJsxValue(indexOptions.render({
2584
+ ...pageInput,
2585
+ title,
2586
+ document,
2587
+ defaultContent
2588
+ })) : defaultContentHtml;
2589
+ return c.html(renderDeckIndex(content, title, document));
2590
+ });
2591
+ router.get("/:slug/assets/*", async (c) => {
2592
+ const slug = c.req.param("slug");
2593
+ const assetPath = extractAssetPath(c.req.path, slug);
2594
+ const deck = await options.source.getCompiledDeck(c, slug);
2595
+ if (!deck || !await isDevEnabled(c, options) && deck.meta.draft) return c.json({
2596
+ error: "Asset not found",
2597
+ slug,
2598
+ assetPath
2599
+ }, 404);
2600
+ const response = await options.source.getAsset?.(c, slug, assetPath);
2601
+ if (!response) return c.json({
2602
+ error: "Asset not found",
2603
+ slug,
2604
+ assetPath
2605
+ }, 404);
2606
+ return response;
2607
+ });
2608
+ router.get("/:slug/render", async (c) => {
2609
+ const slug = c.req.param("slug");
2610
+ const deck = await options.source.getCompiledDeck(c, slug);
2611
+ const dev = await isDevEnabled(c, options);
2612
+ if (!deck || !dev && deck.meta.draft) return c.json({
2613
+ error: "Deck not found",
2614
+ slug
2615
+ }, 404);
2616
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/render`);
2617
+ if (!await isPageSurfaceEnabled(options, {
2618
+ c,
2619
+ surface: "render",
2620
+ mountPath,
2621
+ dev,
2622
+ deck,
2623
+ slug
2624
+ })) return c.json({
2625
+ error: "Deck route not found",
2626
+ slug,
2627
+ surface: "render"
2628
+ }, 404);
2629
+ const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2630
+ try {
2631
+ const document = await resolveRouterDocument(c, options, "render", mountPath, deck);
2632
+ return c.html(await renderCompiledDeckPageAsync({
2633
+ deck,
2634
+ mountPath,
2635
+ style: options.style,
2636
+ components: options.components,
2637
+ clientEntry,
2638
+ liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2639
+ document
2640
+ }));
2641
+ } catch (error) {
2642
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2643
+ return c.text(message, 500);
2644
+ }
2645
+ });
2646
+ router.get("/:slug/print", async (c) => {
2647
+ const slug = c.req.param("slug");
2648
+ const deck = await options.source.getCompiledDeck(c, slug);
2649
+ const dev = await isDevEnabled(c, options);
2650
+ if (!deck || !dev && deck.meta.draft) return c.json({
2651
+ error: "Deck not found",
2652
+ slug
2653
+ }, 404);
2654
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/print`);
2655
+ if (!await isPageSurfaceEnabled(options, {
2656
+ c,
2657
+ surface: "print",
2658
+ mountPath,
2659
+ dev,
2660
+ deck,
2661
+ slug
2662
+ })) return c.json({
2663
+ error: "Deck route not found",
2664
+ slug,
2665
+ surface: "print"
2666
+ }, 404);
2667
+ try {
2668
+ const document = await resolveRouterDocument(c, options, "print", mountPath, deck);
2669
+ return c.html(await renderCompiledDeckPageAsync({
2670
+ deck,
2671
+ mountPath,
2672
+ style: options.style,
2673
+ components: options.components,
2674
+ printPreview: true,
2675
+ document
2676
+ }));
2677
+ } catch (error) {
2678
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2679
+ return c.text(message, 500);
2680
+ }
2681
+ });
2682
+ if (isPdfExportEnabled(options.export)) router.get("/:slug/export.pdf", async (c) => renderDeckBrowserExport(c, {
2683
+ ...options,
2684
+ dev: await isDevEnabled(c, options)
2685
+ }, "pdf"));
2686
+ if (isPngExportEnabled(options.export)) router.get("/:slug/export.png", async (c) => renderDeckBrowserExport(c, {
2687
+ ...options,
2688
+ dev: await isDevEnabled(c, options)
2689
+ }, "png"));
2690
+ const embedOptions = options.embed;
2691
+ if (embedOptions) router.get("/:slug/embed", async (c) => {
2692
+ const slug = c.req.param("slug");
2693
+ const deck = await options.source.getCompiledDeck(c, slug);
2694
+ const dev = await isDevEnabled(c, options);
2695
+ if (!deck || !dev && deck.meta.draft) return c.json({
2696
+ error: "Deck not found",
2697
+ slug
2698
+ }, 404);
2699
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/embed`);
2700
+ const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2701
+ return renderDeckExternalEmbedResponse({
2702
+ context: {
2703
+ c,
2704
+ deck,
2705
+ slug,
2706
+ mountPath
2707
+ },
2708
+ options: embedOptions,
2709
+ document: options.document,
2710
+ availablePages
2711
+ });
2712
+ });
2713
+ router.get("/:slug/presentation", async (c) => {
2714
+ const slug = c.req.param("slug");
2715
+ const deck = await options.source.getCompiledDeck(c, slug);
2716
+ const dev = await isDevEnabled(c, options);
2717
+ if (!deck || !dev && deck.meta.draft) return c.json({
2718
+ error: "Deck not found",
2719
+ slug
2720
+ }, 404);
2721
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/presentation`);
2722
+ if (!await isPageSurfaceEnabled(options, {
2723
+ c,
2724
+ surface: "presentation",
2725
+ mountPath,
2726
+ dev,
2727
+ deck,
2728
+ slug
2729
+ })) return c.json({
2730
+ error: "Deck route not found",
2731
+ slug,
2732
+ surface: "presentation"
2733
+ }, 404);
2734
+ const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
2735
+ try {
2736
+ const document = await resolveRouterDocument(c, options, "presentation", mountPath, deck);
2737
+ return c.html(await renderCompiledDeckPageAsync({
2738
+ deck,
2739
+ mountPath,
2740
+ style: options.style,
2741
+ components: options.components,
2742
+ clientEntry,
2743
+ speakerNotes: false,
2744
+ liveReloadPath: dev ? options.liveReloadPath?.(slug, mountPath) : void 0,
2745
+ document
2746
+ }));
2747
+ } catch (error) {
2748
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2749
+ return c.text(message, 500);
2750
+ }
2751
+ });
2752
+ router.get("/:slug/presenter", async (c) => {
2753
+ const slug = c.req.param("slug");
2754
+ const deck = await options.source.getCompiledDeck(c, slug);
2755
+ const dev = await isDevEnabled(c, options);
2756
+ if (!deck || !dev && deck.meta.draft) return c.json({
2757
+ error: "Deck not found",
2758
+ slug
2759
+ }, 404);
2760
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}/presenter`);
2761
+ if (!await isPageSurfaceEnabled(options, {
2762
+ c,
2763
+ surface: "presenter",
2764
+ mountPath,
2765
+ dev,
2766
+ deck,
2767
+ slug
2768
+ })) return c.json({
2769
+ error: "Presenter route not found",
2770
+ slug
2771
+ }, 404);
2772
+ if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) return c.json({
2773
+ error: "Presenter route not found",
2774
+ slug
2775
+ }, 404);
2776
+ try {
2777
+ const document = await resolveRouterDocument(c, options, "presenter", mountPath, deck);
2778
+ return c.html(await renderPresenterPageAsync({
2779
+ deck,
2780
+ mountPath,
2781
+ presenterStateQuery: paginationQueryFromRequest(c),
2782
+ style: options.style,
2783
+ components: options.components,
2784
+ document
2785
+ }));
2786
+ } catch (error) {
2787
+ const message = error instanceof RenderError ? error.message : `Render failed in ${deck.sourcePath}: ${formatErrorMessage(error)}`;
2788
+ return c.text(message, 500);
2789
+ }
2790
+ });
2791
+ router.get("/:slug", async (c) => {
2792
+ const slug = c.req.param("slug");
2793
+ const deck = await options.source.getCompiledDeck(c, slug);
2794
+ const dev = await isDevEnabled(c, options);
2795
+ if (!deck || !dev && deck.meta.draft) return c.json({
2796
+ error: "Deck not found",
2797
+ slug
2798
+ }, 404);
2799
+ const mountPath = stripPathSuffix(c.req.path, `/${slug}`);
2800
+ if (!await isPageSurfaceEnabled(options, {
2801
+ c,
2802
+ surface: "viewer",
2803
+ mountPath,
2804
+ dev,
2805
+ deck,
2806
+ slug
2807
+ })) return c.json({
2808
+ error: "Deck route not found",
2809
+ slug,
2810
+ surface: "viewer"
2811
+ }, 404);
2812
+ const availablePages = await resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev);
2813
+ return c.html(await renderDeckViewerPage({
2814
+ c,
2815
+ deck,
2816
+ mountPath,
2817
+ viewerStateQuery: paginationQueryFromRequest(c),
2818
+ viewer: {
2819
+ ...options.viewer,
2820
+ controls: await resolveViewerControls(c, options, deck, slug, mountPath)
2821
+ },
2822
+ availablePages,
2823
+ exportOptions: options.export,
2824
+ document: options.document
2825
+ }));
2826
+ });
2827
+ return router;
2742
2828
  }
2743
2829
  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}` : "";
2830
+ const source = new URL(c.req.url);
2831
+ const params = new URLSearchParams();
2832
+ const slide = positiveIntegerParam(source.searchParams.get("slide"));
2833
+ const step = nonNegativeIntegerParam(source.searchParams.get("step"));
2834
+ if (slide !== void 0) params.set("slide", String(slide));
2835
+ if (step !== void 0) params.set("step", String(step));
2836
+ const query = params.toString();
2837
+ return query ? `?${query}` : "";
2752
2838
  }
2753
2839
  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;
2840
+ if (value === null) return void 0;
2841
+ const parsed = Number(value);
2842
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
2757
2843
  }
2758
2844
  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;
2845
+ if (value === null) return void 0;
2846
+ const parsed = Number(value);
2847
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : void 0;
2762
2848
  }
2763
2849
  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
- };
2850
+ return async (c, next) => {
2851
+ const slug = c.req.param("slug");
2852
+ if (!slug) return c.json({
2853
+ error: "Deck not found",
2854
+ slug: ""
2855
+ }, 404);
2856
+ const sourceContext = c;
2857
+ const deck = await options.source.getCompiledDeck(sourceContext, slug);
2858
+ const dev = await isDevEnabled(sourceContext, options);
2859
+ if (!deck || !dev && deck.meta.draft) return c.json({
2860
+ error: "Deck not found",
2861
+ slug
2862
+ }, 404);
2863
+ const mountPath = options.mountPath ?? inferMountPath(c.req.path, slug);
2864
+ const availablePages = await resolveViewerAvailablePages(sourceContext, options, deck, slug, mountPath, dev);
2865
+ const viewer = await createDeckViewerParts({
2866
+ deck,
2867
+ mountPath,
2868
+ controls: options.viewer?.controls,
2869
+ openGraph: options.viewer?.openGraph,
2870
+ availablePages
2871
+ });
2872
+ c.set("deck", deck);
2873
+ c.set("deckViewer", viewer);
2874
+ c.set("deckToc", viewer.slides);
2875
+ c.set("deckMeta", viewer.meta);
2876
+ await next();
2877
+ };
2786
2878
  }
2787
2879
  async function resolveViewerAvailablePages(c, options, deck, slug, mountPath, dev) {
2788
- return {
2789
- print: await isPageSurfaceEnabled(options, { c, surface: "print", mountPath, dev, deck, slug })
2790
- };
2880
+ return { print: await isPageSurfaceEnabled(options, {
2881
+ c,
2882
+ surface: "print",
2883
+ mountPath,
2884
+ dev,
2885
+ deck,
2886
+ slug
2887
+ }) };
2791
2888
  }
2792
2889
  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();
2890
+ if (typeof options.dev === "function") return Boolean(await options.dev({ c }));
2891
+ if (typeof options.dev === "boolean") return options.dev;
2892
+ return inferToolchainDevMode();
2796
2893
  }
2797
2894
  function inferToolchainDevMode() {
2798
- try {
2799
- return process.env.NODE_ENV === "development";
2800
- } catch {
2801
- return false;
2802
- }
2895
+ try {
2896
+ return process.env.NODE_ENV === "development";
2897
+ } catch {
2898
+ return false;
2899
+ }
2803
2900
  }
2804
2901
  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;
2902
+ const controls = options.viewer?.controls;
2903
+ if (controls === false) return false;
2904
+ const presenterControl = await resolvePresenterViewerControl(c, options, deck, slug, mountPath);
2905
+ if (!presenterControl) return controls;
2906
+ const nextControls = controls ? { ...controls } : {};
2907
+ if (nextControls.items) return nextControls;
2908
+ const placement = presenterControl.placement ?? "after";
2909
+ const item = presenterControl.item;
2910
+ if (placement === "before") nextControls.before = mergeControlSlotItems(nextControls.before, item, "before");
2911
+ else nextControls.after = mergeControlSlotItems(nextControls.after, item, "after");
2912
+ return nextControls;
2819
2913
  }
2820
2914
  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
- };
2915
+ const presenter = options.presenter;
2916
+ if (!presenter || !presenter.viewerControl) return null;
2917
+ if (!await isPresenterEnabled(c, options, deck, slug, mountPath)) return null;
2918
+ const control = presenter.viewerControl === true ? {} : presenter.viewerControl;
2919
+ const presenterPath = createDeckPaths(mountPath, slug).presenter;
2920
+ return {
2921
+ item: {
2922
+ type: "link",
2923
+ key: control.key ?? "presenter",
2924
+ href: presenterPath,
2925
+ label: control.label ?? "Presenter",
2926
+ icon: control.icon ?? "presenter",
2927
+ className: control.className,
2928
+ attributes: control.attributes
2929
+ },
2930
+ placement: control.placement ?? "after"
2931
+ };
2838
2932
  }
2839
2933
  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
- };
2934
+ if (!slot) return [item];
2935
+ if (Array.isArray(slot)) return placement === "before" ? [item, ...slot] : [...slot, item];
2936
+ return (context) => {
2937
+ const items = slot(context);
2938
+ return placement === "before" ? [item, ...items] : [...items, item];
2939
+ };
2846
2940
  }
2847
2941
  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
- );
2942
+ const presenter = options.presenter;
2943
+ if (presenter === false) return false;
2944
+ const enabled = presenter?.enabled;
2945
+ if (enabled === void 0) return true;
2946
+ if (typeof enabled === "boolean") return enabled;
2947
+ return Boolean(await enabled({
2948
+ c,
2949
+ deck,
2950
+ slug,
2951
+ mountPath: mountPath.replace(/\/$/, ""),
2952
+ dev: await isDevEnabled(c, options),
2953
+ presenterPath: createDeckPaths(mountPath, slug).presenter,
2954
+ presentationPath: createDeckPaths(mountPath, slug).presentation
2955
+ }));
2864
2956
  }
2865
2957
  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>
2958
+ const basePath = mountPath.replace(/\/$/, "");
2959
+ return `<main>
2874
2960
  <h1>Hono Decks</h1>
2875
- <ul>${items}</ul>
2961
+ <ul>${decks.map((deck) => {
2962
+ return `<li><a href="${`${basePath}/${encodeURIComponent(deck.slug)}`}">${escapeHtml(deck.title ?? deck.slug)}</a>${deck.description ? `<p>${escapeHtml(deck.description)}</p>` : ""}</li>`;
2963
+ }).join("")}</ul>
2876
2964
  </main>`;
2877
2965
  }
2878
2966
  function renderDeckIndex(content, title, document) {
2879
- return `<!doctype html>
2880
- <html lang="${escapeHtml9(document.lang)}">
2967
+ return `<!doctype html>
2968
+ <html lang="${escapeHtml(document.lang)}">
2881
2969
  <head>
2882
2970
  <meta charset="utf-8" />
2883
2971
  <meta name="viewport" content="width=device-width, initial-scale=1" />
2884
- <title>${escapeHtml9(title)}</title>
2972
+ <title>${escapeHtml(title)}</title>
2885
2973
  ${document.head}
2886
2974
  </head>
2887
2975
  <body>
@@ -2890,273 +2978,289 @@ function renderDeckIndex(content, title, document) {
2890
2978
  </html>`;
2891
2979
  }
2892
2980
  async function resolveIndexTitle(title, input) {
2893
- const resolved = typeof title === "function" ? await title(input) : title;
2894
- return resolved?.trim() || "Hono Decks";
2981
+ return (typeof title === "function" ? await title(input) : title)?.trim() || "Hono Decks";
2895
2982
  }
2896
2983
  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;
2984
+ const configured = input.surface === "index" ? options.pages?.index === false ? false : options.pages?.index?.enabled : options.pages?.[input.surface];
2985
+ if (typeof configured === "function") return Boolean(await configured(input));
2986
+ return configured !== false;
2900
2987
  }
2901
2988
  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
- };
2989
+ const title = titleOverride ?? deck?.meta.title ?? deck?.slug ?? "Hono Decks";
2990
+ return resolveDeckDocument({
2991
+ c,
2992
+ surface,
2993
+ deck,
2994
+ slug: deck?.slug,
2995
+ mountPath,
2996
+ title: surface === "presenter" ? `${title} - Presenter` : title
2997
+ }, options.document);
2998
+ }
2999
+ function escapeHtml(value) {
3000
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
2939
3001
  }
3002
+ function formatErrorMessage(error) {
3003
+ if (error instanceof Error) return error.message;
3004
+ return String(error);
3005
+ }
3006
+ //#endregion
3007
+ //#region src/server/define-decks.ts
3008
+ function defineDecks(options) {
3009
+ const manifest = Array.isArray(options.decks) ? { decks: options.decks } : options.manifest;
3010
+ if (!manifest) throw new Error("defineDecks requires either decks or manifest.");
3011
+ const source = manifestDeckSource(manifest);
3012
+ const baseOptions = {
3013
+ ...options,
3014
+ source
3015
+ };
3016
+ return {
3017
+ source,
3018
+ router(overrides = {}) {
3019
+ const nextSource = overrides.source ?? source;
3020
+ return decksRouter(mergeDecksRouterOptions(baseOptions, {
3021
+ ...overrides,
3022
+ source: nextSource
3023
+ }));
3024
+ }
3025
+ };
3026
+ }
3027
+ /** Defines the single configuration consumed by the CLI and runtime. */
2940
3028
  function defineDecksConfig(config) {
2941
- return config;
3029
+ return config;
2942
3030
  }
3031
+ /** Applies app configuration to generated decks and returns the primary runtime API. */
2943
3032
  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
- };
3033
+ const mountPath = normalizeMountPath(config.mountPath);
3034
+ const source = config.source?.(defined.source) ?? defined.source;
3035
+ const configuredRouter = mergeDecksRouterOptions({
3036
+ source,
3037
+ ...config.router
3038
+ }, { source });
3039
+ return {
3040
+ mountPath,
3041
+ source,
3042
+ router(overrides = {}) {
3043
+ return defined.router(mergeDecksRouterOptions(configuredRouter, overrides));
3044
+ },
3045
+ context(overrides = {}) {
3046
+ const merged = mergeDecksRouterOptions(configuredRouter, overrides);
3047
+ return deckContext({
3048
+ source,
3049
+ mountPath,
3050
+ dev: merged.dev,
3051
+ pages: merged.pages,
3052
+ viewer: merged.viewer
3053
+ });
3054
+ },
3055
+ paths(slug) {
3056
+ return createDeckPaths(mountPath, slug);
3057
+ }
3058
+ };
2970
3059
  }
2971
3060
  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
- };
3061
+ const viewer = mergeViewerOptions(base.viewer, overrides.viewer);
3062
+ const presenter = mergePresenterOptions(base.presenter, overrides.presenter);
3063
+ const document = mergeDocumentOptions(base.document, overrides.document);
3064
+ const pages = mergePagesOptions(base.pages, overrides.pages);
3065
+ const embed = mergeExternalEmbedOptions(base.embed, overrides.embed);
3066
+ const exportOptions = overrides.export === false ? false : base.export === false ? overrides.export : base.export || overrides.export ? {
3067
+ ...base.export,
3068
+ ...overrides.export
3069
+ } : void 0;
3070
+ const components = base.components || overrides.components ? {
3071
+ ...base.components,
3072
+ ...overrides.components
3073
+ } : void 0;
3074
+ return {
3075
+ ...base,
3076
+ ...overrides,
3077
+ ...viewer === void 0 ? {} : { viewer },
3078
+ ...presenter === void 0 ? {} : { presenter },
3079
+ ...document === void 0 ? {} : { document },
3080
+ ...pages === void 0 ? {} : { pages },
3081
+ ...embed === void 0 ? {} : { embed },
3082
+ ...exportOptions === void 0 ? {} : { export: exportOptions },
3083
+ ...components === void 0 ? {} : { components },
3084
+ source: overrides.source ?? base.source
3085
+ };
2991
3086
  }
2992
3087
  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
- };
3088
+ if (!base) return override;
3089
+ if (!override) return base;
3090
+ const index = override.index === false ? false : override.index === void 0 ? base.index : base.index === false || base.index === void 0 ? override.index : {
3091
+ ...base.index,
3092
+ ...override.index
3093
+ };
3094
+ return {
3095
+ ...base,
3096
+ ...override,
3097
+ ...index === void 0 ? {} : { index }
3098
+ };
3001
3099
  }
3002
3100
  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
- };
3101
+ if (override === false) return false;
3102
+ if (override === void 0) return base;
3103
+ if (base === false || base === void 0) return override;
3104
+ const viewer = typeof base.viewer === "object" && typeof override.viewer === "object" ? {
3105
+ ...base.viewer,
3106
+ ...override.viewer
3107
+ } : override.viewer ?? base.viewer;
3108
+ const document = base.document || override.document ? {
3109
+ ...base.document,
3110
+ ...override.document
3111
+ } : void 0;
3112
+ return {
3113
+ ...base,
3114
+ ...override,
3115
+ ...viewer === void 0 ? {} : { viewer },
3116
+ ...document === void 0 ? {} : { document }
3117
+ };
3014
3118
  }
3015
3119
  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
- };
3120
+ if (!base) return override;
3121
+ if (!override) return base;
3122
+ return {
3123
+ ...base,
3124
+ ...override,
3125
+ surfaces: base.surfaces || override.surfaces ? {
3126
+ ...base.surfaces,
3127
+ ...override.surfaces
3128
+ } : void 0
3129
+ };
3023
3130
  }
3024
3131
  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
- };
3132
+ if (!base) return override;
3133
+ if (!override) return base;
3134
+ const controls = mergeControls(base.controls, override.controls);
3135
+ const openGraph = mergeOpenGraph(base.openGraph, override.openGraph);
3136
+ return {
3137
+ ...base,
3138
+ ...override,
3139
+ ...controls === void 0 ? {} : { controls },
3140
+ ...openGraph === void 0 ? {} : { openGraph }
3141
+ };
3035
3142
  }
3036
3143
  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 };
3144
+ if (override === void 0) return base;
3145
+ if (base === void 0 || typeof base === "boolean" || typeof override === "boolean") return override;
3146
+ return {
3147
+ ...base,
3148
+ ...override
3149
+ };
3040
3150
  }
3041
3151
  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
- };
3152
+ if (override === void 0) return base;
3153
+ if (base === void 0 || base === false || override === false) return override;
3154
+ return {
3155
+ ...base,
3156
+ ...override,
3157
+ attributes: base.attributes || override.attributes ? {
3158
+ ...base.attributes,
3159
+ ...override.attributes
3160
+ } : void 0,
3161
+ labels: base.labels || override.labels ? {
3162
+ ...base.labels,
3163
+ ...override.labels
3164
+ } : void 0,
3165
+ hidden: base.hidden || override.hidden ? [.../* @__PURE__ */ new Set([...base.hidden ?? [], ...override.hidden ?? []])] : void 0,
3166
+ before: mergeControlSlots(base.before, override.before),
3167
+ after: mergeControlSlots(base.after, override.after)
3168
+ };
3053
3169
  }
3054
3170
  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;
3171
+ if (override === void 0) return base;
3172
+ if (base === void 0) return override;
3173
+ if (Array.isArray(base) && Array.isArray(override)) return [...base, ...override];
3174
+ return override;
3059
3175
  }
3060
3176
  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
3177
+ if (override === void 0) return base;
3178
+ if (override === false || base === false || base === void 0) return override;
3179
+ const viewerControl = typeof base.viewerControl === "object" && typeof override.viewerControl === "object" ? {
3180
+ ...base.viewerControl,
3181
+ ...override.viewerControl
3182
+ } : override.viewerControl ?? base.viewerControl;
3183
+ return {
3184
+ ...base,
3185
+ ...override,
3186
+ viewerControl
3187
+ };
3188
+ }
3189
+ //#endregion
3190
+ //#region src/source/r2-assets.ts
3068
3191
  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
- };
3192
+ return {
3193
+ async listDecks(c) {
3194
+ return source.listDecks(c);
3195
+ },
3196
+ async getCompiledDeck(c, slug) {
3197
+ return source.getCompiledDeck(c, slug);
3198
+ },
3199
+ async getAsset(c, slug, assetPath) {
3200
+ const deck = await source.getCompiledDeck(c, slug);
3201
+ const asset = deck ? findRoutableAsset(deck.assets, slug, assetPath) : void 0;
3202
+ const bucket = asset ? await resolveBucket(options.bucket, c) : void 0;
3203
+ if (deck && asset && bucket) {
3204
+ const object = await bucket.get(resolveR2Key({
3205
+ deck,
3206
+ asset,
3207
+ slug,
3208
+ assetPath
3209
+ }, options));
3210
+ if (object) {
3211
+ const response = await responseFromR2Object(object, {
3212
+ deck,
3213
+ asset,
3214
+ slug,
3215
+ assetPath
3216
+ }, options);
3217
+ if (response) return response;
3218
+ }
3219
+ }
3220
+ return source.getAsset?.(c, slug, assetPath) ?? null;
3221
+ }
3222
+ };
3090
3223
  }
3091
3224
  async function resolveBucket(bucket, c) {
3092
- return typeof bucket === "function" ? bucket(c) : bucket;
3225
+ return typeof bucket === "function" ? bucket(c) : bucket;
3093
3226
  }
3094
3227
  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
- });
3228
+ const normalized = assetPath.replace(/^\/+/, "");
3229
+ return assets.find((asset) => {
3230
+ if (asset.type !== "local" && asset.type !== "r2") return false;
3231
+ const suffix = `/${slug}/assets/${normalized}`;
3232
+ return asset.publicPath.endsWith(suffix);
3233
+ });
3101
3234
  }
3102
3235
  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(/^\/+/, "");
3236
+ if (options.key) return options.key(input);
3237
+ if (input.asset.r2Key) return input.asset.r2Key;
3238
+ const key = input.asset.sourcePath || `${input.slug}/assets/${input.assetPath}`;
3239
+ const prefix = options.keyPrefix?.replace(/\/+$/, "");
3240
+ return prefix ? `${prefix}/${key.replace(/^\/+/, "")}` : key.replace(/^\/+/, "");
3108
3241
  }
3109
3242
  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 });
3243
+ const body = object.body ?? (object.arrayBuffer ? await object.arrayBuffer() : void 0);
3244
+ if (body == null) return null;
3245
+ const headers = new Headers();
3246
+ object.writeHttpMetadata?.(headers);
3247
+ applyHttpMetadata(headers, object.httpMetadata);
3248
+ if (!headers.has("content-type") && input.asset.contentType) headers.set("content-type", input.asset.contentType);
3249
+ const cacheControl = resolveCacheControl(input, options);
3250
+ if (cacheControl) headers.set("cache-control", cacheControl);
3251
+ else if (cacheControl !== false && input.asset.cacheControl && !headers.has("cache-control")) headers.set("cache-control", input.asset.cacheControl);
3252
+ return new Response(body, { headers });
3123
3253
  }
3124
3254
  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);
3255
+ if (!metadata) return;
3256
+ if (metadata.contentType && !headers.has("content-type")) headers.set("content-type", metadata.contentType);
3257
+ if (metadata.cacheControl && !headers.has("cache-control")) headers.set("cache-control", metadata.cacheControl);
3258
+ if (metadata.contentDisposition && !headers.has("content-disposition")) headers.set("content-disposition", metadata.contentDisposition);
3259
+ if (metadata.contentEncoding && !headers.has("content-encoding")) headers.set("content-encoding", metadata.contentEncoding);
3260
+ if (metadata.contentLanguage && !headers.has("content-language")) headers.set("content-language", metadata.contentLanguage);
3133
3261
  }
3134
3262
  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
- };
3263
+ return typeof options.cacheControl === "function" ? options.cacheControl(input) : options.cacheControl;
3264
+ }
3265
+ //#endregion
3266
+ 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 };