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/mod.js CHANGED
@@ -1,437 +1,444 @@
1
- // src/server/router.ts
2
- import { Hono } from "hono";
3
- import { raw as raw2 } from "hono/html";
4
-
5
- // src/deck/model.ts
6
- var SLIDE_TRANSITIONS = [
7
- "none",
8
- "fade",
9
- "fade-out",
10
- "slide-left",
11
- "slide-right",
12
- "slide-up",
13
- "slide-down",
14
- "view-transition"
15
- ];
16
-
17
- // src/renderer/jsx-renderer.ts
1
+ import { raw } from "hono/html";
18
2
  import { jsx } from "hono/jsx/jsx-runtime";
19
3
  import { HtmlEscapedCallbackPhase, resolveCallback } from "hono/utils/html";
20
- var builtInSlideComponents = defineSlideComponents({
21
- Fire: (props) => {
22
- if (props.order !== void 0) {
23
- throw new Error('The Fire "order" prop is not supported. Fires reveal in source order.');
24
- }
25
- const effect = stringProp(props.effect);
26
- const at = fireAtProp(props.at);
27
- return jsx("div", {
28
- "data-hono-decks-fire": true,
29
- ...at ? { "data-fire-at": at } : {},
30
- ...effect ? { "data-fire-effect": safeToken(effect).toLowerCase() } : {},
31
- children: props.children
32
- });
33
- },
34
- Hero: (props) => jsx("section", {
35
- class: `mdx-hero${props.featured === true ? " is-featured" : ""}${props.image || props.src ? " has-image" : ""}`,
36
- "data-component": "Hero",
37
- children: [
38
- heroImage(props),
39
- jsx("div", {
40
- class: "mdx-hero-copy",
41
- children: [
42
- typeof props.eyebrow === "string" && props.eyebrow ? jsx("p", { class: "mdx-hero-eyebrow", children: props.eyebrow }) : "",
43
- typeof props.title === "string" && props.title ? jsx("h1", { children: props.title }) : "",
44
- 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 }) : ""
45
- ]
46
- })
47
- ]
48
- }),
49
- CodeBlock: (props) => {
50
- const lang = stringProp(props.lang);
51
- const filename = stringProp(props.filename ?? props.title);
52
- const highlight = stringProp(props.highlight);
53
- const code = codeBlockText(props.children);
54
- const highlightedHtml = typeof props.highlightedHtml === "string" ? props.highlightedHtml : void 0;
55
- return jsx("figure", {
56
- class: "hono-decks-code-block",
57
- ...lang ? { "data-lang": lang } : {},
58
- ...filename ? { "data-filename": filename } : {},
59
- ...highlight ? { "data-highlight": highlight } : {},
60
- children: [
61
- filename ? jsx("figcaption", { class: "hono-decks-code-caption", children: filename }) : "",
62
- highlightedHtml ? jsx("div", {
63
- class: "hono-decks-code-highlight",
64
- dangerouslySetInnerHTML: { __html: highlightedHtml }
65
- }) : jsx("pre", {
66
- children: jsx("code", {
67
- ...lang ? { class: `language-${safeToken(lang)}`, "data-lang": lang } : {},
68
- children: code
69
- })
70
- })
71
- ]
72
- });
73
- },
74
- EmbedFrame: (props) => {
75
- const src = stringProp(props.src);
76
- const provider = stringProp(props.provider)?.trim().toLowerCase();
77
- const title = stringProp(props.title) ?? "Embedded content";
78
- const aspectRatio = stringProp(props.aspectRatio) ?? "16 / 9";
79
- const loading = stringProp(props.loading) ?? "lazy";
80
- const allow = stringProp(props.allow) ?? "fullscreen; picture-in-picture";
81
- const sandbox = props.sandbox === false ? void 0 : stringProp(props.sandbox) ?? "allow-scripts allow-same-origin allow-presentation allow-popups";
82
- const referrerPolicy = stringProp(props.referrerPolicy ?? props.referrerpolicy) ?? "strict-origin-when-cross-origin";
83
- const fallbackHref = stringProp(props.fallbackHref ?? props.fallbackUrl ?? props.href) ?? src;
84
- const fallback = codeBlockText(props.children) || "Open embed";
85
- const printPoster = provider === "youtube" ? youtubePrintPoster(src) : void 0;
86
- return jsx("figure", {
87
- class: "hono-decks-embed-frame",
88
- "data-component": "EmbedFrame",
89
- ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
90
- children: [
91
- jsx("div", {
92
- class: "hono-decks-embed-viewport",
93
- style: `aspect-ratio:${safeAspectRatio(aspectRatio)}`,
94
- children: [
95
- src ? jsx("iframe", {
96
- src,
97
- title,
98
- loading,
99
- referrerpolicy: referrerPolicy,
100
- ...sandbox ? { sandbox } : {},
101
- ...allow ? { allow } : {},
102
- allowfullscreen: true
103
- }) : "",
104
- fallbackHref ? jsx("a", {
105
- class: "hono-decks-embed-print-fallback",
106
- href: fallbackHref,
107
- children: [
108
- printPoster ? jsx("img", {
109
- class: "hono-decks-embed-print-poster",
110
- src: printPoster,
111
- alt: `${title} thumbnail`
112
- }) : "",
113
- jsx("span", { children: fallback })
114
- ]
115
- }) : ""
116
- ]
117
- }),
118
- src ? jsx("figcaption", {
119
- class: "hono-decks-embed-fallback",
120
- children: jsx("a", { href: fallbackHref, target: "_blank", rel: "noreferrer", children: fallback })
121
- }) : ""
122
- ]
123
- });
124
- },
125
- SocialEmbed: (props) => {
126
- const href = stringProp(props.href ?? props.url);
127
- const provider = stringProp(props.provider ?? props.service);
128
- const author = stringProp(props.author);
129
- const label = stringProp(props.label) ?? (provider ? `Open on ${provider}` : "Open social post");
130
- const quote = codeBlockText(props.children);
131
- return jsx("figure", {
132
- class: "hono-decks-social-embed",
133
- "data-component": "SocialEmbed",
134
- ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
135
- children: jsx("blockquote", {
136
- class: "hono-decks-social-card",
137
- ...href ? { cite: href } : {},
138
- children: [
139
- quote ? jsx("p", { children: quote }) : "",
140
- jsx("footer", {
141
- children: [
142
- author ? jsx("span", { class: "hono-decks-social-author", children: author }) : "",
143
- href ? jsx("a", {
144
- href,
145
- target: "_blank",
146
- rel: "noreferrer",
147
- children: label
148
- }) : ""
149
- ]
150
- })
151
- ]
152
- })
153
- });
154
- },
155
- TweetEmbed: (props) => {
156
- const href = stringProp(props.href ?? props.url);
157
- const label = stringProp(props.label) ?? "Open post on X";
158
- return jsx("figure", {
159
- class: "hono-decks-tweet-embed",
160
- "data-component": "TweetEmbed",
161
- children: [
162
- jsx("blockquote", {
163
- class: "twitter-tweet",
164
- "data-dnt": "true",
165
- children: href ? jsx("a", {
166
- href,
167
- target: "_blank",
168
- rel: "noreferrer",
169
- children: label
170
- }) : label
171
- }),
172
- href ? jsx("script", {
173
- async: true,
174
- src: "https://platform.twitter.com/widgets.js",
175
- charset: "utf-8"
176
- }) : "",
177
- href ? jsx("a", {
178
- class: "hono-decks-tweet-print-fallback",
179
- href,
180
- children: label
181
- }) : ""
182
- ]
183
- });
184
- },
185
- LinkCard: (props) => {
186
- const href = stringProp(props.href ?? props.url);
187
- const title = stringProp(props.title) ?? href ?? "Link";
188
- const description = stringProp(props.description);
189
- const image = stringProp(props.image ?? props.imageUrl);
190
- const siteName = stringProp(props.siteName ?? props.site);
191
- const label = codeBlockText(props.children) || "Open link";
192
- return jsx("figure", {
193
- class: "hono-decks-link-card",
194
- "data-component": "LinkCard",
195
- children: href ? jsx("a", {
196
- class: "hono-decks-link-card-anchor",
197
- href,
198
- target: "_blank",
199
- rel: "noreferrer",
200
- children: [
201
- image ? jsx("img", { class: "hono-decks-link-card-image", src: image, alt: title }) : "",
202
- jsx("span", {
203
- class: "hono-decks-link-card-body",
204
- children: [
205
- siteName ? jsx("span", { class: "hono-decks-link-card-site", children: siteName }) : "",
206
- jsx("span", { class: "hono-decks-link-card-title", children: title }),
207
- description ? jsx("span", { class: "hono-decks-link-card-description", children: description }) : "",
208
- jsx("span", { class: "hono-decks-link-card-label", children: label })
209
- ]
210
- })
211
- ]
212
- }) : [
213
- image ? jsx("img", { class: "hono-decks-link-card-image", src: image, alt: title }) : "",
214
- siteName ? jsx("span", { class: "hono-decks-link-card-site", children: siteName }) : "",
215
- jsx("span", { class: "hono-decks-link-card-title", children: title }),
216
- description ? jsx("span", { class: "hono-decks-link-card-description", children: description }) : ""
217
- ]
218
- });
219
- }
4
+ //#region src/deck/model.ts
5
+ const SLIDE_TRANSITIONS = [
6
+ "none",
7
+ "fade",
8
+ "fade-out",
9
+ "slide-left",
10
+ "slide-right",
11
+ "slide-up",
12
+ "slide-down",
13
+ "view-transition"
14
+ ];
15
+ defineSlideComponents({
16
+ Fire: (props) => {
17
+ if (props.order !== void 0) throw new Error("The Fire \"order\" prop is not supported. Fires reveal in source order.");
18
+ const effect = stringProp(props.effect);
19
+ const at = fireAtProp(props.at);
20
+ return jsx("div", {
21
+ "data-hono-decks-fire": true,
22
+ ...at ? { "data-fire-at": at } : {},
23
+ ...effect ? { "data-fire-effect": safeToken(effect).toLowerCase() } : {},
24
+ children: props.children
25
+ });
26
+ },
27
+ Hero: (props) => jsx("section", {
28
+ class: `mdx-hero${props.featured === true ? " is-featured" : ""}${props.image || props.src ? " has-image" : ""}`,
29
+ "data-component": "Hero",
30
+ children: [heroImage(props), jsx("div", {
31
+ class: "mdx-hero-copy",
32
+ children: [
33
+ typeof props.eyebrow === "string" && props.eyebrow ? jsx("p", {
34
+ class: "mdx-hero-eyebrow",
35
+ children: props.eyebrow
36
+ }) : "",
37
+ typeof props.title === "string" && props.title ? jsx("h1", { children: props.title }) : "",
38
+ typeof props.subtitle === "string" && props.subtitle ? jsx("p", {
39
+ class: "mdx-hero-subtitle",
40
+ children: props.subtitle
41
+ }) : typeof props.description === "string" && props.description ? jsx("p", {
42
+ class: "mdx-hero-subtitle",
43
+ children: props.description
44
+ }) : ""
45
+ ]
46
+ })]
47
+ }),
48
+ CodeBlock: (props) => {
49
+ const lang = stringProp(props.lang);
50
+ const filename = stringProp(props.filename ?? props.title);
51
+ const highlight = stringProp(props.highlight);
52
+ const code = codeBlockText(props.children);
53
+ const highlightedHtml = typeof props.highlightedHtml === "string" ? props.highlightedHtml : void 0;
54
+ return jsx("figure", {
55
+ class: "hono-decks-code-block",
56
+ ...lang ? { "data-lang": lang } : {},
57
+ ...filename ? { "data-filename": filename } : {},
58
+ ...highlight ? { "data-highlight": highlight } : {},
59
+ children: [filename ? jsx("figcaption", {
60
+ class: "hono-decks-code-caption",
61
+ children: filename
62
+ }) : "", highlightedHtml ? jsx("div", {
63
+ class: "hono-decks-code-highlight",
64
+ dangerouslySetInnerHTML: { __html: highlightedHtml }
65
+ }) : jsx("pre", { children: jsx("code", {
66
+ ...lang ? {
67
+ class: `language-${safeToken(lang)}`,
68
+ "data-lang": lang
69
+ } : {},
70
+ children: code
71
+ }) })]
72
+ });
73
+ },
74
+ EmbedFrame: (props) => {
75
+ const src = stringProp(props.src);
76
+ const provider = stringProp(props.provider)?.trim().toLowerCase();
77
+ const title = stringProp(props.title) ?? "Embedded content";
78
+ const aspectRatio = stringProp(props.aspectRatio) ?? "16 / 9";
79
+ const loading = stringProp(props.loading) ?? "lazy";
80
+ const allow = stringProp(props.allow) ?? "fullscreen; picture-in-picture";
81
+ const sandbox = props.sandbox === false ? void 0 : stringProp(props.sandbox) ?? "allow-scripts allow-same-origin allow-presentation allow-popups";
82
+ const referrerPolicy = stringProp(props.referrerPolicy ?? props.referrerpolicy) ?? "strict-origin-when-cross-origin";
83
+ const fallbackHref = stringProp(props.fallbackHref ?? props.fallbackUrl ?? props.href) ?? src;
84
+ const fallback = codeBlockText(props.children) || "Open embed";
85
+ const printPoster = provider === "youtube" ? youtubePrintPoster(src) : void 0;
86
+ return jsx("figure", {
87
+ class: "hono-decks-embed-frame",
88
+ "data-component": "EmbedFrame",
89
+ ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
90
+ children: [jsx("div", {
91
+ class: "hono-decks-embed-viewport",
92
+ style: `aspect-ratio:${safeAspectRatio(aspectRatio)}`,
93
+ children: [src ? jsx("iframe", {
94
+ src,
95
+ title,
96
+ loading,
97
+ referrerpolicy: referrerPolicy,
98
+ ...sandbox ? { sandbox } : {},
99
+ ...allow ? { allow } : {},
100
+ allowfullscreen: true
101
+ }) : "", fallbackHref ? jsx("a", {
102
+ class: "hono-decks-embed-print-fallback",
103
+ href: fallbackHref,
104
+ children: [printPoster ? jsx("img", {
105
+ class: "hono-decks-embed-print-poster",
106
+ src: printPoster,
107
+ alt: `${title} thumbnail`
108
+ }) : "", jsx("span", { children: fallback })]
109
+ }) : ""]
110
+ }), src ? jsx("figcaption", {
111
+ class: "hono-decks-embed-fallback",
112
+ children: jsx("a", {
113
+ href: fallbackHref,
114
+ target: "_blank",
115
+ rel: "noreferrer",
116
+ children: fallback
117
+ })
118
+ }) : ""]
119
+ });
120
+ },
121
+ SocialEmbed: (props) => {
122
+ const href = stringProp(props.href ?? props.url);
123
+ const provider = stringProp(props.provider ?? props.service);
124
+ const author = stringProp(props.author);
125
+ const label = stringProp(props.label) ?? (provider ? `Open on ${provider}` : "Open social post");
126
+ const quote = codeBlockText(props.children);
127
+ return jsx("figure", {
128
+ class: "hono-decks-social-embed",
129
+ "data-component": "SocialEmbed",
130
+ ...provider ? { "data-provider": safeToken(provider).toLowerCase() } : {},
131
+ children: jsx("blockquote", {
132
+ class: "hono-decks-social-card",
133
+ ...href ? { cite: href } : {},
134
+ children: [quote ? jsx("p", { children: quote }) : "", jsx("footer", { children: [author ? jsx("span", {
135
+ class: "hono-decks-social-author",
136
+ children: author
137
+ }) : "", href ? jsx("a", {
138
+ href,
139
+ target: "_blank",
140
+ rel: "noreferrer",
141
+ children: label
142
+ }) : ""] })]
143
+ })
144
+ });
145
+ },
146
+ TweetEmbed: (props) => {
147
+ const href = stringProp(props.href ?? props.url);
148
+ const label = stringProp(props.label) ?? "Open post on X";
149
+ return jsx("figure", {
150
+ class: "hono-decks-tweet-embed",
151
+ "data-component": "TweetEmbed",
152
+ children: [
153
+ jsx("blockquote", {
154
+ class: "twitter-tweet",
155
+ "data-dnt": "true",
156
+ children: href ? jsx("a", {
157
+ href,
158
+ target: "_blank",
159
+ rel: "noreferrer",
160
+ children: label
161
+ }) : label
162
+ }),
163
+ href ? jsx("script", {
164
+ async: true,
165
+ src: "https://platform.twitter.com/widgets.js",
166
+ charset: "utf-8"
167
+ }) : "",
168
+ href ? jsx("a", {
169
+ class: "hono-decks-tweet-print-fallback",
170
+ href,
171
+ children: label
172
+ }) : ""
173
+ ]
174
+ });
175
+ },
176
+ LinkCard: (props) => {
177
+ const href = stringProp(props.href ?? props.url);
178
+ const title = stringProp(props.title) ?? href ?? "Link";
179
+ const description = stringProp(props.description);
180
+ const image = stringProp(props.image ?? props.imageUrl);
181
+ const siteName = stringProp(props.siteName ?? props.site);
182
+ const label = codeBlockText(props.children) || "Open link";
183
+ return jsx("figure", {
184
+ class: "hono-decks-link-card",
185
+ "data-component": "LinkCard",
186
+ children: href ? jsx("a", {
187
+ class: "hono-decks-link-card-anchor",
188
+ href,
189
+ target: "_blank",
190
+ rel: "noreferrer",
191
+ children: [image ? jsx("img", {
192
+ class: "hono-decks-link-card-image",
193
+ src: image,
194
+ alt: title
195
+ }) : "", jsx("span", {
196
+ class: "hono-decks-link-card-body",
197
+ children: [
198
+ siteName ? jsx("span", {
199
+ class: "hono-decks-link-card-site",
200
+ children: siteName
201
+ }) : "",
202
+ jsx("span", {
203
+ class: "hono-decks-link-card-title",
204
+ children: title
205
+ }),
206
+ description ? jsx("span", {
207
+ class: "hono-decks-link-card-description",
208
+ children: description
209
+ }) : "",
210
+ jsx("span", {
211
+ class: "hono-decks-link-card-label",
212
+ children: label
213
+ })
214
+ ]
215
+ })]
216
+ }) : [
217
+ image ? jsx("img", {
218
+ class: "hono-decks-link-card-image",
219
+ src: image,
220
+ alt: title
221
+ }) : "",
222
+ siteName ? jsx("span", {
223
+ class: "hono-decks-link-card-site",
224
+ children: siteName
225
+ }) : "",
226
+ jsx("span", {
227
+ class: "hono-decks-link-card-title",
228
+ children: title
229
+ }),
230
+ description ? jsx("span", {
231
+ class: "hono-decks-link-card-description",
232
+ children: description
233
+ }) : ""
234
+ ]
235
+ });
236
+ }
220
237
  });
221
238
  function defineSlideComponents(input) {
222
- const registry = {};
223
- for (const [name, value] of Object.entries(input)) {
224
- registry[name] = typeof value === "function" ? { component: value } : value;
225
- }
226
- return registry;
239
+ const registry = {};
240
+ for (const [name, value] of Object.entries(input)) registry[name] = typeof value === "function" ? { component: value } : value;
241
+ return registry;
227
242
  }
228
243
  function heroImage(props) {
229
- const image = typeof props.image === "string" ? props.image : typeof props.src === "string" ? props.src : void 0;
230
- if (!image) return "";
231
- const alt = typeof props.alt === "string" && props.alt ? props.alt : typeof props.title === "string" ? props.title : "Hero image";
232
- return jsx("img", { class: "mdx-hero-image", src: image, alt });
244
+ const image = typeof props.image === "string" ? props.image : typeof props.src === "string" ? props.src : void 0;
245
+ if (!image) return "";
246
+ return jsx("img", {
247
+ class: "mdx-hero-image",
248
+ src: image,
249
+ alt: typeof props.alt === "string" && props.alt ? props.alt : typeof props.title === "string" ? props.title : "Hero image"
250
+ });
233
251
  }
234
252
  function stringProp(value) {
235
- if (typeof value === "string") return value;
236
- if (typeof value === "number" || typeof value === "boolean") return String(value);
237
- return void 0;
253
+ if (typeof value === "string") return value;
254
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
238
255
  }
239
256
  function fireAtProp(value) {
240
- if (value === void 0 || value === null) return void 0;
241
- if (typeof value === "number") {
242
- if (Number.isInteger(value) && value >= 0) return String(value);
243
- throw new Error('The Fire "at" prop accepts a non-negative integer or a relative string such as "+1".');
244
- }
245
- if (typeof value === "string") {
246
- const at = value.trim();
247
- if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
248
- }
249
- throw new Error('The Fire "at" prop accepts a non-negative integer or a relative string such as "+1".');
257
+ if (value === void 0 || value === null) return void 0;
258
+ if (typeof value === "number") {
259
+ if (Number.isInteger(value) && value >= 0) return String(value);
260
+ throw new Error("The Fire \"at\" prop accepts a non-negative integer or a relative string such as \"+1\".");
261
+ }
262
+ if (typeof value === "string") {
263
+ const at = value.trim();
264
+ if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
265
+ }
266
+ throw new Error("The Fire \"at\" prop accepts a non-negative integer or a relative string such as \"+1\".");
250
267
  }
251
268
  function safeToken(value) {
252
- return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "text";
269
+ return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "text";
253
270
  }
254
271
  function safeAspectRatio(value) {
255
- return /^\s*\d+(\.\d+)?\s*(\/|:)\s*\d+(\.\d+)?\s*$/.test(value) ? value.replace(":", " / ") : "16 / 9";
272
+ return /^\s*\d+(\.\d+)?\s*(\/|:)\s*\d+(\.\d+)?\s*$/.test(value) ? value.replace(":", " / ") : "16 / 9";
256
273
  }
257
274
  function youtubePrintPoster(src) {
258
- if (!src) return void 0;
259
- try {
260
- const url = new URL(src);
261
- if (!["youtube.com", "www.youtube.com", "youtube-nocookie.com", "www.youtube-nocookie.com"].includes(url.hostname)) {
262
- return void 0;
263
- }
264
- const match = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]+)$/);
265
- return match ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : void 0;
266
- } catch {
267
- return void 0;
268
- }
275
+ if (!src) return void 0;
276
+ try {
277
+ const url = new URL(src);
278
+ if (![
279
+ "youtube.com",
280
+ "www.youtube.com",
281
+ "youtube-nocookie.com",
282
+ "www.youtube-nocookie.com"
283
+ ].includes(url.hostname)) return;
284
+ const match = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]+)$/);
285
+ return match ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : void 0;
286
+ } catch {
287
+ return;
288
+ }
269
289
  }
270
290
  function codeBlockText(value) {
271
- if (Array.isArray(value)) return value.map(codeBlockText).join("");
272
- if (value === null || value === void 0 || typeof value === "boolean") return "";
273
- if (typeof value === "string" || typeof value === "number") return String(value);
274
- if (isJsxValue(value)) return codeBlockText(value.props?.children);
275
- return String(value);
291
+ if (Array.isArray(value)) return value.map(codeBlockText).join("");
292
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
293
+ if (typeof value === "string" || typeof value === "number") return String(value);
294
+ if (isJsxValue(value)) return codeBlockText(value.props?.children);
295
+ return String(value);
276
296
  }
277
297
  async function renderJsxValue(value) {
278
- const resolved = value instanceof Promise ? await value : value;
279
- if (typeof resolved === "string") return resolved;
280
- if (resolved === null || resolved === void 0 || typeof resolved === "boolean") return "";
281
- if (typeof resolved === "number") return String(resolved);
282
- return await resolveCallback(resolved, HtmlEscapedCallbackPhase.Stringify, false, {});
298
+ const resolved = value instanceof Promise ? await value : value;
299
+ if (typeof resolved === "string") return resolved;
300
+ if (resolved === null || resolved === void 0 || typeof resolved === "boolean") return "";
301
+ if (typeof resolved === "number") return String(resolved);
302
+ return await resolveCallback(resolved, HtmlEscapedCallbackPhase.Stringify, false, {});
283
303
  }
284
304
  function isJsxValue(value) {
285
- return typeof value === "object" && value !== null && "tag" in value && "props" in value && "type" in value;
305
+ return typeof value === "object" && value !== null && "tag" in value && "props" in value && "type" in value;
286
306
  }
287
-
288
- // src/renderer/control-icons.ts
289
- import { jsx as jsx2 } from "hono/jsx/jsx-runtime";
307
+ //#endregion
308
+ //#region src/renderer/control-icons.ts
290
309
  function renderControlIcon(name, className = "hono-decks-control-icon") {
291
- return jsx2("svg", {
292
- class: className,
293
- "data-hono-decks-control-icon": true,
294
- viewBox: "0 0 24 24",
295
- "aria-hidden": "true",
296
- focusable: "false",
297
- fill: "none",
298
- "stroke-width": "2",
299
- "stroke-linecap": "round",
300
- "stroke-linejoin": "round",
301
- children: controlIconPaths(name)
302
- });
310
+ return jsx("svg", {
311
+ class: className,
312
+ "data-hono-decks-control-icon": true,
313
+ viewBox: "0 0 24 24",
314
+ "aria-hidden": "true",
315
+ focusable: "false",
316
+ fill: "none",
317
+ "stroke-width": "2",
318
+ "stroke-linecap": "round",
319
+ "stroke-linejoin": "round",
320
+ children: controlIconPaths(name)
321
+ });
303
322
  }
304
323
  function controlIconLabel(name) {
305
- switch (name) {
306
- case "deck-list":
307
- return "Deck list";
308
- case "home":
309
- return "Home";
310
- case "viewer":
311
- return "Viewer";
312
- case "projection":
313
- return "Projection";
314
- case "presenter":
315
- return "Presenter";
316
- case "previous":
317
- return "Previous slide";
318
- case "next":
319
- return "Next slide";
320
- case "fullscreen":
321
- return "Toggle fullscreen";
322
- case "print":
323
- return "Print view";
324
- case "details":
325
- return "Details";
326
- case "export-pdf":
327
- return "Export PDF";
328
- case "export-png":
329
- return "Export PNG";
330
- }
324
+ switch (name) {
325
+ case "deck-list": return "Deck list";
326
+ case "home": return "Home";
327
+ case "viewer": return "Viewer";
328
+ case "projection": return "Projection";
329
+ case "presenter": return "Presenter";
330
+ case "previous": return "Previous slide";
331
+ case "next": return "Next slide";
332
+ case "fullscreen": return "Toggle fullscreen";
333
+ case "print": return "Print view";
334
+ case "details": return "Details";
335
+ case "export-pdf": return "Export PDF";
336
+ case "export-png": return "Export PNG";
337
+ }
331
338
  }
332
339
  function controlIconPaths(name) {
333
- switch (name) {
334
- case "deck-list":
335
- return [
336
- jsx2("path", { d: "M4 5h16" }),
337
- jsx2("path", { d: "M4 12h16" }),
338
- jsx2("path", { d: "M4 19h16" })
339
- ];
340
- case "home":
341
- return [
342
- jsx2("path", { d: "M3 11l9-8 9 8" }),
343
- jsx2("path", { d: "M5 10v10h14V10" }),
344
- jsx2("path", { d: "M9 20v-6h6v6" })
345
- ];
346
- case "viewer":
347
- return [
348
- jsx2("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" }),
349
- jsx2("circle", { cx: "12", cy: "12", r: "3" })
350
- ];
351
- case "projection":
352
- return [
353
- jsx2("path", { d: "M4 5h16v10H4z" }),
354
- jsx2("path", { d: "M8 19h8" }),
355
- jsx2("path", { d: "M12 15v4" })
356
- ];
357
- case "presenter":
358
- return [
359
- jsx2("path", { d: "M4 5h16v10H4z" }),
360
- jsx2("path", { d: "M8 21l4-6 4 6" }),
361
- jsx2("path", { d: "M9 9h6" })
362
- ];
363
- case "previous":
364
- return [jsx2("path", { d: "M15 6l-6 6 6 6" })];
365
- case "next":
366
- return [jsx2("path", { d: "M9 6l6 6-6 6" })];
367
- case "fullscreen":
368
- return [
369
- jsx2("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
370
- jsx2("path", { d: "M16 3h3a2 2 0 0 1 2 2v3" }),
371
- jsx2("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
372
- jsx2("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
373
- ];
374
- case "print":
375
- return [
376
- jsx2("path", { d: "M6 9V3h12v6" }),
377
- 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" }),
378
- jsx2("path", { d: "M6 14h12v7H6z" })
379
- ];
380
- case "details":
381
- return [
382
- jsx2("circle", { cx: "12", cy: "12", r: "9" }),
383
- jsx2("path", { d: "M12 11v5" }),
384
- jsx2("path", { d: "M12 8h.01" })
385
- ];
386
- case "export-pdf":
387
- return [
388
- jsx2("path", { d: "M14 3v4a2 2 0 0 0 2 2h4" }),
389
- jsx2("path", { d: "M5 3h9l6 6v12H5z" }),
390
- jsx2("path", { d: "M8 15h8" }),
391
- jsx2("path", { d: "M8 18h5" })
392
- ];
393
- case "export-png":
394
- return [
395
- jsx2("rect", { x: "4", y: "5", width: "16", height: "14", rx: "2" }),
396
- jsx2("path", { d: "M8 14l2.5-2.5L14 15l2-2 2 2" }),
397
- jsx2("circle", { cx: "9", cy: "9", r: "1" })
398
- ];
399
- }
340
+ switch (name) {
341
+ case "deck-list": return [
342
+ jsx("path", { d: "M4 5h16" }),
343
+ jsx("path", { d: "M4 12h16" }),
344
+ jsx("path", { d: "M4 19h16" })
345
+ ];
346
+ case "home": return [
347
+ jsx("path", { d: "M3 11l9-8 9 8" }),
348
+ jsx("path", { d: "M5 10v10h14V10" }),
349
+ jsx("path", { d: "M9 20v-6h6v6" })
350
+ ];
351
+ 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", {
352
+ cx: "12",
353
+ cy: "12",
354
+ r: "3"
355
+ })];
356
+ case "projection": return [
357
+ jsx("path", { d: "M4 5h16v10H4z" }),
358
+ jsx("path", { d: "M8 19h8" }),
359
+ jsx("path", { d: "M12 15v4" })
360
+ ];
361
+ case "presenter": return [
362
+ jsx("path", { d: "M4 5h16v10H4z" }),
363
+ jsx("path", { d: "M8 21l4-6 4 6" }),
364
+ jsx("path", { d: "M9 9h6" })
365
+ ];
366
+ case "previous": return [jsx("path", { d: "M15 6l-6 6 6 6" })];
367
+ case "next": return [jsx("path", { d: "M9 6l6 6-6 6" })];
368
+ case "fullscreen": return [
369
+ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
370
+ jsx("path", { d: "M16 3h3a2 2 0 0 1 2 2v3" }),
371
+ jsx("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
372
+ jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
373
+ ];
374
+ case "print": return [
375
+ jsx("path", { d: "M6 9V3h12v6" }),
376
+ 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" }),
377
+ jsx("path", { d: "M6 14h12v7H6z" })
378
+ ];
379
+ case "details": return [
380
+ jsx("circle", {
381
+ cx: "12",
382
+ cy: "12",
383
+ r: "9"
384
+ }),
385
+ jsx("path", { d: "M12 11v5" }),
386
+ jsx("path", { d: "M12 8h.01" })
387
+ ];
388
+ case "export-pdf": return [
389
+ jsx("path", { d: "M14 3v4a2 2 0 0 0 2 2h4" }),
390
+ jsx("path", { d: "M5 3h9l6 6v12H5z" }),
391
+ jsx("path", { d: "M8 15h8" }),
392
+ jsx("path", { d: "M8 18h5" })
393
+ ];
394
+ case "export-png": return [
395
+ jsx("rect", {
396
+ x: "4",
397
+ y: "5",
398
+ width: "16",
399
+ height: "14",
400
+ rx: "2"
401
+ }),
402
+ jsx("path", { d: "M8 14l2.5-2.5L14 15l2-2 2 2" }),
403
+ jsx("circle", {
404
+ cx: "9",
405
+ cy: "9",
406
+ r: "1"
407
+ })
408
+ ];
409
+ }
400
410
  }
401
-
402
- // src/server/paths.ts
411
+ //#endregion
412
+ //#region src/server/paths.ts
413
+ /** Creates the canonical route map used by routers, controls, and app code. */
403
414
  function createDeckPaths(mountPath, slug) {
404
- const mount = normalizeMountPath(mountPath);
405
- const viewer = `${mount === "/" ? "" : mount}/${encodeURIComponent(slug)}`;
406
- return {
407
- viewer,
408
- render: `${viewer}/render`,
409
- print: `${viewer}/print`,
410
- presentation: `${viewer}/presentation`,
411
- presenter: `${viewer}/presenter`,
412
- embed: `${viewer}/embed`,
413
- exportPdf: `${viewer}/export.pdf`,
414
- exportPng: `${viewer}/export.png`,
415
- ogImage: `${viewer}/og.png`,
416
- assets: `${viewer}/assets`
417
- };
415
+ const mount = normalizeMountPath(mountPath);
416
+ const viewer = `${mount === "/" ? "" : mount}/${encodeURIComponent(slug)}`;
417
+ return {
418
+ viewer,
419
+ render: `${viewer}/render`,
420
+ print: `${viewer}/print`,
421
+ presentation: `${viewer}/presentation`,
422
+ presenter: `${viewer}/presenter`,
423
+ embed: `${viewer}/embed`,
424
+ exportPdf: `${viewer}/export.pdf`,
425
+ exportPng: `${viewer}/export.png`,
426
+ ogImage: `${viewer}/og.png`,
427
+ assets: `${viewer}/assets`
428
+ };
418
429
  }
430
+ /** Normalizes a configured mount path to one leading slash and no trailing slash. */
419
431
  function normalizeMountPath(value) {
420
- const trimmed = value.trim();
421
- if (!trimmed || trimmed === "/") return "/";
422
- return `/${trimmed.replace(/^\/+|\/+$/g, "")}`;
432
+ const trimmed = value.trim();
433
+ if (!trimmed || trimmed === "/") return "/";
434
+ return `/${trimmed.replace(/^\/+|\/+$/g, "")}`;
423
435
  }
424
-
425
- // src/server/viewer.ts
426
- import { raw } from "hono/html";
427
- import { jsx as jsx3 } from "hono/jsx/jsx-runtime";
428
-
429
- // src/server/viewer-script.ts
430
- var VIEWER_STATE_MESSAGE_TYPE = "hono-decks:state";
431
- var VIEWER_COMMAND_MESSAGE_TYPE = "hono-decks:command";
436
+ //#endregion
437
+ //#region src/server/viewer-script.ts
438
+ const VIEWER_STATE_MESSAGE_TYPE = "hono-decks:state";
439
+ const VIEWER_COMMAND_MESSAGE_TYPE = "hono-decks:command";
432
440
  function renderViewerScript(nonce) {
433
- const nonceAttribute = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
434
- return `<script data-hono-decks-viewer-runtime${nonceAttribute}>
441
+ return `<script data-hono-decks-viewer-runtime${nonce ? ` nonce="${escapeHtml$1(nonce)}"` : ""}>
435
442
  (() => {
436
443
  const roots = Array.from(document.querySelectorAll("[data-hono-decks-viewer]"));
437
444
  const runtime = window.__honoDecksViewerRuntime ??= { controllers: new Map(), globalInitialized: false };
@@ -618,17 +625,17 @@ function renderViewerScript(nonce) {
618
625
  });
619
626
  }
620
627
  })();
621
- </script>`;
628
+ <\/script>`;
622
629
  }
623
- function escapeHtml(value) {
624
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
630
+ function escapeHtml$1(value) {
631
+ return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
625
632
  }
626
-
627
- // src/server/viewer-style.ts
628
- var VIEWER_ASPECT_RATIO = "16/9";
633
+ //#endregion
634
+ //#region src/server/viewer-style.ts
635
+ const VIEWER_ASPECT_RATIO = "16/9";
629
636
  function embeddedViewerStyle() {
630
- const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
631
- 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}
637
+ const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
638
+ 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}
632
639
  ${root} *:focus-visible{outline:none}
633
640
  ${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}
634
641
  ${root} .hono-decks-viewer-stage{display:grid;place-items:center;width:100%;min-width:0;min-height:0;container-type:inline-size}
@@ -649,423 +656,454 @@ ${root} .hono-decks-control-icon{width:16px;height:16px;flex:0 0 auto;stroke:cur
649
656
  ${root} .hono-decks-viewer-toc button{font:inherit}
650
657
  @media (prefers-reduced-motion: reduce){${root},${root} *{scroll-behavior:auto!important;animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}}`;
651
658
  }
652
-
653
- // src/server/viewer.ts
659
+ //#endregion
660
+ //#region src/server/viewer.ts
654
661
  async function createDeckViewerParts(input) {
655
- const slug = input.deck.slug;
656
- const title = input.deck.meta.title ?? slug;
657
- const basePath = input.mountPath.replace(/\/$/, "");
658
- const paths = createDeckPaths(basePath, slug);
659
- const imagePath = await resolveOpenGraphImagePath(input.openGraph, { deck: input.deck, paths });
660
- const renderUrl = `${paths.render}${input.viewerStateQuery ?? ""}`;
661
- const slides = createDeckToc(input.deck);
662
- const meta = {
663
- title,
664
- description: input.deck.meta.description,
665
- paths,
666
- availablePages: { print: input.availablePages?.print !== false },
667
- availableExports: input.exportPaths ?? {},
668
- ...imagePath ? { imagePath } : {}
669
- };
670
- const controlsInput = input.controls === false ? false : input.controls ?? {};
671
- const controlsContext = { slug, title, mountPath: basePath, meta, slides };
672
- const controls = controlsInput === false ? null : renderViewerControls(controlsInput, controlsContext);
673
- return {
674
- slug,
675
- title,
676
- renderUrl,
677
- frame: {
678
- content: renderViewerFrame({ title, renderUrl }),
679
- html: renderViewerFrameHtml({ title, renderUrl })
680
- },
681
- controls: controls ? { content: controls, html: await renderJsxValue(controls) } : null,
682
- toc: { content: renderViewerToc(slides), html: renderViewerTocHtml(slides) },
683
- slides,
684
- meta
685
- };
662
+ const slug = input.deck.slug;
663
+ const title = input.deck.meta.title ?? slug;
664
+ const basePath = input.mountPath.replace(/\/$/, "");
665
+ const paths = createDeckPaths(basePath, slug);
666
+ const imagePath = await resolveOpenGraphImagePath(input.openGraph, {
667
+ deck: input.deck,
668
+ paths
669
+ });
670
+ const renderUrl = `${paths.render}${input.viewerStateQuery ?? ""}`;
671
+ const slides = createDeckToc(input.deck);
672
+ const meta = {
673
+ title,
674
+ description: input.deck.meta.description,
675
+ paths,
676
+ availablePages: { print: input.availablePages?.print !== false },
677
+ availableExports: input.exportPaths ?? {},
678
+ ...imagePath ? { imagePath } : {}
679
+ };
680
+ const controlsInput = input.controls === false ? false : input.controls ?? {};
681
+ const controls = controlsInput === false ? null : renderViewerControls(controlsInput, {
682
+ slug,
683
+ title,
684
+ mountPath: basePath,
685
+ meta,
686
+ slides
687
+ });
688
+ return {
689
+ slug,
690
+ title,
691
+ renderUrl,
692
+ frame: {
693
+ content: renderViewerFrame({
694
+ title,
695
+ renderUrl
696
+ }),
697
+ html: renderViewerFrameHtml({
698
+ title,
699
+ renderUrl
700
+ })
701
+ },
702
+ controls: controls ? {
703
+ content: controls,
704
+ html: await renderJsxValue(controls)
705
+ } : null,
706
+ toc: {
707
+ content: renderViewerToc(slides),
708
+ html: renderViewerTocHtml(slides)
709
+ },
710
+ slides,
711
+ meta
712
+ };
686
713
  }
687
714
  async function createDeckViewerEmbed(input) {
688
- const parts = await createDeckViewerParts(input);
689
- const root = jsx3("section", {
690
- class: ["hono-decks-embedded-viewer", input.className].filter(Boolean).join(" "),
691
- "data-hono-decks-viewer": true,
692
- "data-hono-decks-embed": true,
693
- "data-deck-slug": parts.slug,
694
- ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
695
- "aria-label": parts.title,
696
- children: [
697
- jsx3("div", {
698
- class: "hono-decks-viewer-shell",
699
- children: [parts.frame.content, parts.controls?.content]
700
- }),
701
- input.toc ? parts.toc.content : null
702
- ]
703
- });
704
- const rootHtml = await renderJsxValue(root);
705
- const nonceAttribute = input.nonce ? ` nonce="${escapeHtml2(input.nonce)}"` : "";
706
- const embedHtml = `<style data-hono-decks-embed-style${nonceAttribute}>${embeddedViewerStyle()}${input.style ?? ""}</style>${rootHtml}${renderViewerScript(input.nonce)}`;
707
- return {
708
- ...parts,
709
- embed: raw(embedHtml),
710
- embedHtml
711
- };
715
+ const parts = await createDeckViewerParts(input);
716
+ const rootHtml = await renderJsxValue(jsx("section", {
717
+ class: ["hono-decks-embedded-viewer", input.className].filter(Boolean).join(" "),
718
+ "data-hono-decks-viewer": true,
719
+ "data-hono-decks-embed": true,
720
+ "data-deck-slug": parts.slug,
721
+ ...parts.meta.availablePages.print ? { "data-hono-decks-print-path": parts.meta.paths.print } : {},
722
+ "aria-label": parts.title,
723
+ children: [jsx("div", {
724
+ class: "hono-decks-viewer-shell",
725
+ children: [parts.frame.content, parts.controls?.content]
726
+ }), input.toc ? parts.toc.content : null]
727
+ }));
728
+ const embedHtml = `<style data-hono-decks-embed-style${input.nonce ? ` nonce="${escapeHtml(input.nonce)}"` : ""}>${embeddedViewerStyle()}${input.style ?? ""}</style>${rootHtml}${renderViewerScript(input.nonce)}`;
729
+ return {
730
+ ...parts,
731
+ embed: raw(embedHtml),
732
+ embedHtml
733
+ };
712
734
  }
713
735
  async function resolveOpenGraphImagePath(options, input) {
714
- if (!options) return void 0;
715
- if (options === true) return input.paths.ogImage;
716
- if (typeof options.imagePath === "function") return options.imagePath(input);
717
- return options.imagePath ?? input.paths.ogImage;
736
+ if (!options) return void 0;
737
+ if (options === true) return input.paths.ogImage;
738
+ if (typeof options.imagePath === "function") return options.imagePath(input);
739
+ return options.imagePath ?? input.paths.ogImage;
718
740
  }
719
741
  function createDeckToc(deck) {
720
- return deck.slides.map((slide) => ({
721
- index: slide.index,
722
- title: slide.meta.title,
723
- label: slide.meta.title ?? `Slide ${slide.index + 1}`
724
- }));
742
+ return deck.slides.map((slide) => ({
743
+ index: slide.index,
744
+ title: slide.meta.title,
745
+ label: slide.meta.title ?? `Slide ${slide.index + 1}`
746
+ }));
725
747
  }
726
748
  function renderViewerFrame(input) {
727
- return jsx3("div", {
728
- class: "hono-decks-viewer-stage",
729
- "data-hono-decks-frame": true,
730
- children: jsx3("div", {
731
- class: "hono-decks-viewport",
732
- "data-viewer-viewport": true,
733
- tabindex: "0",
734
- children: [
735
- jsx3("div", {
736
- class: "hono-decks-frame-stage",
737
- "data-viewer-stage": true,
738
- children: jsx3("iframe", {
739
- title: input.title,
740
- src: input.renderUrl
741
- })
742
- }),
743
- jsx3("button", {
744
- class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous",
745
- type: "button",
746
- "data-viewer-navigation": "previous",
747
- "aria-label": "Previous slide"
748
- }),
749
- jsx3("button", {
750
- class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next",
751
- type: "button",
752
- "data-viewer-navigation": "next",
753
- "aria-label": "Next slide"
754
- })
755
- ]
756
- })
757
- });
749
+ return jsx("div", {
750
+ class: "hono-decks-viewer-stage",
751
+ "data-hono-decks-frame": true,
752
+ children: jsx("div", {
753
+ class: "hono-decks-viewport",
754
+ "data-viewer-viewport": true,
755
+ tabindex: "0",
756
+ children: [
757
+ jsx("div", {
758
+ class: "hono-decks-frame-stage",
759
+ "data-viewer-stage": true,
760
+ children: jsx("iframe", {
761
+ title: input.title,
762
+ src: input.renderUrl
763
+ })
764
+ }),
765
+ jsx("button", {
766
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-previous",
767
+ type: "button",
768
+ "data-viewer-navigation": "previous",
769
+ "aria-label": "Previous slide"
770
+ }),
771
+ jsx("button", {
772
+ class: "hono-decks-viewer-navigation-layer hono-decks-viewer-navigation-next",
773
+ type: "button",
774
+ "data-viewer-navigation": "next",
775
+ "aria-label": "Next slide"
776
+ })
777
+ ]
778
+ })
779
+ });
758
780
  }
759
781
  function renderViewerFrameHtml(input) {
760
- 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="${escapeHtml2(input.title)}" src="${escapeHtml2(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>`;
782
+ 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(input.title)}" src="${escapeHtml(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>`;
761
783
  }
762
784
  function renderViewerControls(options, context) {
763
- return jsx3("nav", {
764
- ...controlAttributes(options.attributes),
765
- class: controlsClassName(options),
766
- "data-hono-decks-viewer-controls": true,
767
- "aria-label": options.ariaLabel ?? "Viewer controls",
768
- children: resolveViewerControlItems(options, context).map((item) => renderViewerControlItem(item, options, context))
769
- });
785
+ return jsx("nav", {
786
+ ...controlAttributes(options.attributes),
787
+ class: controlsClassName(options),
788
+ "data-hono-decks-viewer-controls": true,
789
+ "aria-label": options.ariaLabel ?? "Viewer controls",
790
+ children: resolveViewerControlItems(options, context).map((item) => renderViewerControlItem(item, options, context))
791
+ });
770
792
  }
771
793
  function renderViewerToc(slides) {
772
- return jsx3("nav", {
773
- class: "hono-decks-viewer-toc",
774
- "data-hono-decks-toc": true,
775
- "aria-label": "Slide navigation",
776
- children: jsx3("ol", {
777
- children: slides.map(
778
- (slide) => jsx3("li", {
779
- children: jsx3("button", {
780
- type: "button",
781
- "data-action": "goTo",
782
- "data-slide-index": String(slide.index),
783
- children: slide.label
784
- })
785
- })
786
- )
787
- })
788
- });
794
+ return jsx("nav", {
795
+ class: "hono-decks-viewer-toc",
796
+ "data-hono-decks-toc": true,
797
+ "aria-label": "Slide navigation",
798
+ children: jsx("ol", { children: slides.map((slide) => jsx("li", { children: jsx("button", {
799
+ type: "button",
800
+ "data-action": "goTo",
801
+ "data-slide-index": String(slide.index),
802
+ children: slide.label
803
+ }) })) })
804
+ });
789
805
  }
790
806
  function renderViewerTocHtml(slides) {
791
- const items = slides.map(
792
- (slide) => `<li><button type="button" data-action="goTo" data-slide-index="${slide.index}">${escapeHtml2(slide.label)}</button></li>`
793
- ).join("");
794
- return `<nav class="hono-decks-viewer-toc" data-hono-decks-toc aria-label="Slide navigation"><ol>${items}</ol></nav>`;
807
+ 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(slide.label)}</button></li>`).join("")}</ol></nav>`;
795
808
  }
796
- function escapeHtml2(value) {
797
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
809
+ function escapeHtml(value) {
810
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
798
811
  }
799
812
  function safeFilename(value) {
800
- const normalized = value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
801
- return normalized || "deck";
813
+ return value.trim().replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "deck";
802
814
  }
803
815
  function buildViewerControlDefaults(context) {
804
- return {
805
- back: { type: "default", key: "back" },
806
- previous: { type: "default", key: "previous" },
807
- position: { type: "default", key: "position" },
808
- next: { type: "default", key: "next" },
809
- fullscreen: { type: "default", key: "fullscreen" },
810
- print: context.meta.availablePages.print ? { type: "default", key: "print" } : null,
811
- exportPdf: context.meta.availableExports.pdf ? { type: "default", key: "exportPdf" } : null,
812
- exportPng: context.meta.availableExports.png ? { type: "default", key: "exportPng" } : null
813
- };
816
+ return {
817
+ back: {
818
+ type: "default",
819
+ key: "back"
820
+ },
821
+ previous: {
822
+ type: "default",
823
+ key: "previous"
824
+ },
825
+ position: {
826
+ type: "default",
827
+ key: "position"
828
+ },
829
+ next: {
830
+ type: "default",
831
+ key: "next"
832
+ },
833
+ fullscreen: {
834
+ type: "default",
835
+ key: "fullscreen"
836
+ },
837
+ print: context.meta.availablePages.print ? {
838
+ type: "default",
839
+ key: "print"
840
+ } : null,
841
+ exportPdf: context.meta.availableExports.pdf ? {
842
+ type: "default",
843
+ key: "exportPdf"
844
+ } : null,
845
+ exportPng: context.meta.availableExports.png ? {
846
+ type: "default",
847
+ key: "exportPng"
848
+ } : null
849
+ };
814
850
  }
815
851
  function resolveViewerControlItems(options, context) {
816
- const defaults = buildViewerControlDefaults(context);
817
- const baseItems = [
818
- defaults.back,
819
- defaults.previous,
820
- defaults.position,
821
- defaults.next,
822
- defaults.fullscreen,
823
- defaults.print,
824
- defaults.exportPdf,
825
- defaults.exportPng
826
- ];
827
- const items = typeof options.items === "function" ? options.items(defaults, context) : options.items ?? [
828
- ...resolveViewerControlSlotItems(options.before, context),
829
- ...filterHiddenDefaultItems(baseItems, options.hidden),
830
- ...resolveViewerControlSlotItems(options.after, context)
831
- ];
832
- return items.filter((item) => Boolean(item)).map((item) => applyViewerControlLabels(item, options.labels));
852
+ const defaults = buildViewerControlDefaults(context);
853
+ const baseItems = [
854
+ defaults.back,
855
+ defaults.previous,
856
+ defaults.position,
857
+ defaults.next,
858
+ defaults.fullscreen,
859
+ defaults.print,
860
+ defaults.exportPdf,
861
+ defaults.exportPng
862
+ ];
863
+ return (typeof options.items === "function" ? options.items(defaults, context) : options.items ?? [
864
+ ...resolveViewerControlSlotItems(options.before, context),
865
+ ...filterHiddenDefaultItems(baseItems, options.hidden),
866
+ ...resolveViewerControlSlotItems(options.after, context)
867
+ ]).filter((item) => Boolean(item)).map((item) => applyViewerControlLabels(item, options.labels));
833
868
  }
834
869
  function renderViewerControlItem(item, options, context) {
835
- const renderDefault = () => renderBaseViewerControlItem(item, options, context);
836
- return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
870
+ const renderDefault = () => renderBaseViewerControlItem(item, options, context);
871
+ return options.renderItem?.(item, context, renderDefault) ?? renderDefault();
837
872
  }
838
873
  function renderBaseViewerControlItem(item, options, context) {
839
- if (item.type === "link") {
840
- const ariaLabel = item.icon ? String(item.attributes?.["aria-label"] ?? item.label) : void 0;
841
- return jsx3("a", {
842
- ...linkAttributes(item.attributes),
843
- ...safeHref(item.href) ? { href: safeHref(item.href) } : {},
844
- ...item.download ? { download: item.download } : {},
845
- ...classAttribute(options.itemClassName, item.className),
846
- ...item.icon ? { "aria-label": ariaLabel, title: ariaLabel } : {},
847
- children: item.icon ? renderControlIcon(item.icon) : item.label
848
- });
849
- }
850
- if (item.type === "render") {
851
- return item.render({ context });
852
- }
853
- return renderDefaultViewerControlItem(item, options, context);
874
+ if (item.type === "link") {
875
+ const ariaLabel = item.icon ? String(item.attributes?.["aria-label"] ?? item.label) : void 0;
876
+ return jsx("a", {
877
+ ...linkAttributes(item.attributes),
878
+ ...safeHref(item.href) ? { href: safeHref(item.href) } : {},
879
+ ...item.download ? { download: item.download } : {},
880
+ ...classAttribute(options.itemClassName, item.className),
881
+ ...item.icon ? {
882
+ "aria-label": ariaLabel,
883
+ title: ariaLabel
884
+ } : {},
885
+ children: item.icon ? renderControlIcon(item.icon) : item.label
886
+ });
887
+ }
888
+ if (item.type === "render") return item.render({ context });
889
+ return renderDefaultViewerControlItem(item, options, context);
854
890
  }
855
891
  function renderDefaultViewerControlItem(item, options, context) {
856
- const itemProps = {
857
- ...linkAttributes(item.attributes),
858
- ...classAttribute(options.itemClassName, item.className)
859
- };
860
- switch (item.key) {
861
- case "back":
862
- return jsx3("a", {
863
- ...itemProps,
864
- href: context.mountPath,
865
- "data-hono-decks-back-link": true,
866
- ...item.label === void 0 ? { "aria-label": controlIconLabel("deck-list"), title: controlIconLabel("deck-list") } : {},
867
- children: item.label ?? renderControlIcon("deck-list")
868
- });
869
- case "previous":
870
- return renderIconButton("previous", item, itemProps);
871
- case "position":
872
- return jsx3("span", {
873
- ...itemProps,
874
- "data-slide-position": true,
875
- "data-hono-decks-position": true,
876
- children: item.label ?? "1 / ?"
877
- });
878
- case "next":
879
- return renderIconButton("next", item, itemProps);
880
- case "fullscreen":
881
- return renderIconButton("fullscreen", item, itemProps);
882
- case "print":
883
- return jsx3("a", {
884
- ...itemProps,
885
- href: context.meta.paths.print,
886
- "data-hono-decks-print": true,
887
- ...item.label === void 0 ? { "aria-label": controlIconLabel("print"), title: controlIconLabel("print") } : {},
888
- children: item.label ?? renderControlIcon("print")
889
- });
890
- case "exportPdf":
891
- return jsx3("a", {
892
- ...itemProps,
893
- href: context.meta.paths.exportPdf,
894
- download: `${safeFilename(context.meta.title)}.pdf`,
895
- "data-hono-decks-export": "pdf",
896
- ...item.label === void 0 ? { "aria-label": controlIconLabel("export-pdf"), title: controlIconLabel("export-pdf") } : {},
897
- children: item.label ?? renderControlIcon("export-pdf")
898
- });
899
- case "exportPng":
900
- return jsx3("a", {
901
- ...itemProps,
902
- href: context.meta.paths.exportPng,
903
- download: `${safeFilename(context.meta.title)}.png`,
904
- "data-hono-decks-export": "png",
905
- ...item.label === void 0 ? { "aria-label": controlIconLabel("export-png"), title: controlIconLabel("export-png") } : {},
906
- children: item.label ?? renderControlIcon("export-png")
907
- });
908
- }
892
+ const itemProps = {
893
+ ...linkAttributes(item.attributes),
894
+ ...classAttribute(options.itemClassName, item.className)
895
+ };
896
+ switch (item.key) {
897
+ case "back": return jsx("a", {
898
+ ...itemProps,
899
+ href: context.mountPath,
900
+ "data-hono-decks-back-link": true,
901
+ ...item.label === void 0 ? {
902
+ "aria-label": controlIconLabel("deck-list"),
903
+ title: controlIconLabel("deck-list")
904
+ } : {},
905
+ children: item.label ?? renderControlIcon("deck-list")
906
+ });
907
+ case "previous": return renderIconButton("previous", item, itemProps);
908
+ case "position": return jsx("span", {
909
+ ...itemProps,
910
+ "data-slide-position": true,
911
+ "data-hono-decks-position": true,
912
+ children: item.label ?? "1 / ?"
913
+ });
914
+ case "next": return renderIconButton("next", item, itemProps);
915
+ case "fullscreen": return renderIconButton("fullscreen", item, itemProps);
916
+ case "print": return jsx("a", {
917
+ ...itemProps,
918
+ href: context.meta.paths.print,
919
+ "data-hono-decks-print": true,
920
+ ...item.label === void 0 ? {
921
+ "aria-label": controlIconLabel("print"),
922
+ title: controlIconLabel("print")
923
+ } : {},
924
+ children: item.label ?? renderControlIcon("print")
925
+ });
926
+ case "exportPdf": return jsx("a", {
927
+ ...itemProps,
928
+ href: context.meta.paths.exportPdf,
929
+ download: `${safeFilename(context.meta.title)}.pdf`,
930
+ "data-hono-decks-export": "pdf",
931
+ ...item.label === void 0 ? {
932
+ "aria-label": controlIconLabel("export-pdf"),
933
+ title: controlIconLabel("export-pdf")
934
+ } : {},
935
+ children: item.label ?? renderControlIcon("export-pdf")
936
+ });
937
+ case "exportPng": return jsx("a", {
938
+ ...itemProps,
939
+ href: context.meta.paths.exportPng,
940
+ download: `${safeFilename(context.meta.title)}.png`,
941
+ "data-hono-decks-export": "png",
942
+ ...item.label === void 0 ? {
943
+ "aria-label": controlIconLabel("export-png"),
944
+ title: controlIconLabel("export-png")
945
+ } : {},
946
+ children: item.label ?? renderControlIcon("export-png")
947
+ });
948
+ }
909
949
  }
910
950
  function renderIconButton(action, item, itemProps) {
911
- const iconName = controlIconNameForKey(action);
912
- const label = controlIconLabel(iconName);
913
- return jsx3("button", {
914
- ...itemProps,
915
- type: "button",
916
- "data-action": action,
917
- "data-hono-decks-navigation-control": action,
918
- ...item.label === void 0 ? { "aria-label": label, title: label } : {},
919
- children: item.label ?? renderControlIcon(iconName)
920
- });
951
+ const iconName = controlIconNameForKey(action);
952
+ const label = controlIconLabel(iconName);
953
+ return jsx("button", {
954
+ ...itemProps,
955
+ type: "button",
956
+ "data-action": action,
957
+ "data-hono-decks-navigation-control": action,
958
+ ...item.label === void 0 ? {
959
+ "aria-label": label,
960
+ title: label
961
+ } : {},
962
+ children: item.label ?? renderControlIcon(iconName)
963
+ });
921
964
  }
922
965
  function controlIconNameForKey(key) {
923
- switch (key) {
924
- case "back":
925
- return "deck-list";
926
- case "previous":
927
- return "previous";
928
- case "next":
929
- return "next";
930
- case "fullscreen":
931
- return "fullscreen";
932
- case "print":
933
- return "print";
934
- case "exportPdf":
935
- return "export-pdf";
936
- case "exportPng":
937
- return "export-png";
938
- case "position":
939
- return "viewer";
940
- }
966
+ switch (key) {
967
+ case "back": return "deck-list";
968
+ case "previous": return "previous";
969
+ case "next": return "next";
970
+ case "fullscreen": return "fullscreen";
971
+ case "print": return "print";
972
+ case "exportPdf": return "export-pdf";
973
+ case "exportPng": return "export-png";
974
+ case "position": return "viewer";
975
+ }
941
976
  }
942
977
  function controlsClassName(options) {
943
- return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
978
+ return ["hono-decks-viewer-controls", options.className].filter(Boolean).join(" ");
944
979
  }
945
980
  function resolveViewerControlSlotItems(items, context) {
946
- return typeof items === "function" ? items(context) : items ?? [];
981
+ return typeof items === "function" ? items(context) : items ?? [];
947
982
  }
948
983
  function filterHiddenDefaultItems(items, hidden) {
949
- if (!hidden?.length) return items;
950
- const hiddenKeys = new Set(hidden);
951
- return items.filter((item) => !item || item.type !== "default" || !hiddenKeys.has(item.key));
984
+ if (!hidden?.length) return items;
985
+ const hiddenKeys = new Set(hidden);
986
+ return items.filter((item) => !item || item.type !== "default" || !hiddenKeys.has(item.key));
952
987
  }
953
988
  function applyViewerControlLabels(item, labels) {
954
- if (item.type !== "default" || item.label !== void 0) return item;
955
- const label = labels?.[item.key];
956
- return label === void 0 ? item : { ...item, label };
989
+ if (item.type !== "default" || item.label !== void 0) return item;
990
+ const label = labels?.[item.key];
991
+ return label === void 0 ? item : {
992
+ ...item,
993
+ label
994
+ };
957
995
  }
958
996
  function linkAttributes(attributes) {
959
- const props = {};
960
- for (const [name, value] of Object.entries(attributes ?? {})) {
961
- if (!isSafeAttributeName(name) || value === false || value === void 0) continue;
962
- props[name] = value;
963
- }
964
- return props;
997
+ const props = {};
998
+ for (const [name, value] of Object.entries(attributes ?? {})) {
999
+ if (!isSafeAttributeName(name) || value === false || value === void 0) continue;
1000
+ props[name] = value;
1001
+ }
1002
+ return props;
965
1003
  }
966
1004
  function controlAttributes(attributes) {
967
- const props = linkAttributes(attributes);
968
- delete props.class;
969
- delete props["aria-label"];
970
- delete props["data-hono-decks-viewer-controls"];
971
- return props;
1005
+ const props = linkAttributes(attributes);
1006
+ delete props.class;
1007
+ delete props["aria-label"];
1008
+ delete props["data-hono-decks-viewer-controls"];
1009
+ return props;
972
1010
  }
973
1011
  function classAttribute(...classNames) {
974
- const className = classNames.filter(Boolean).join(" ");
975
- return className ? { class: className } : {};
1012
+ const className = classNames.filter(Boolean).join(" ");
1013
+ return className ? { class: className } : {};
976
1014
  }
977
1015
  function isSafeAttributeName(value) {
978
- return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
1016
+ return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(value) && !/^on/i.test(value);
979
1017
  }
980
1018
  function safeHref(value) {
981
- const trimmed = value.trim();
982
- if (trimmed.length === 0) return void 0;
983
- if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return value;
984
- if (/^(javascript:|data:|vbscript:)/i.test(trimmed)) return void 0;
985
- if (/^[^/?#.]+:/i.test(trimmed)) return void 0;
986
- return value;
1019
+ const trimmed = value.trim();
1020
+ if (trimmed.length === 0) return void 0;
1021
+ if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return value;
1022
+ if (/^(javascript:|data:|vbscript:)/i.test(trimmed)) return void 0;
1023
+ if (/^[^/?#.]+:/i.test(trimmed)) return void 0;
1024
+ return value;
987
1025
  }
988
-
989
- // src/server/define-decks.ts
1026
+ //#endregion
1027
+ //#region src/server/define-decks.ts
1028
+ /** Defines the single configuration consumed by the CLI and runtime. */
990
1029
  function defineDecksConfig(config) {
991
- return config;
1030
+ return config;
992
1031
  }
993
-
994
- // src/source/r2-assets.ts
1032
+ //#endregion
1033
+ //#region src/source/r2-assets.ts
995
1034
  function withR2Assets(source, options) {
996
- return {
997
- async listDecks(c) {
998
- return source.listDecks(c);
999
- },
1000
- async getCompiledDeck(c, slug) {
1001
- return source.getCompiledDeck(c, slug);
1002
- },
1003
- async getAsset(c, slug, assetPath) {
1004
- const deck = await source.getCompiledDeck(c, slug);
1005
- const asset = deck ? findRoutableAsset(deck.assets, slug, assetPath) : void 0;
1006
- const bucket = asset ? await resolveBucket(options.bucket, c) : void 0;
1007
- if (deck && asset && bucket) {
1008
- const object = await bucket.get(resolveR2Key({ deck, asset, slug, assetPath }, options));
1009
- if (object) {
1010
- const response = await responseFromR2Object(object, { deck, asset, slug, assetPath }, options);
1011
- if (response) return response;
1012
- }
1013
- }
1014
- return source.getAsset?.(c, slug, assetPath) ?? null;
1015
- }
1016
- };
1035
+ return {
1036
+ async listDecks(c) {
1037
+ return source.listDecks(c);
1038
+ },
1039
+ async getCompiledDeck(c, slug) {
1040
+ return source.getCompiledDeck(c, slug);
1041
+ },
1042
+ async getAsset(c, slug, assetPath) {
1043
+ const deck = await source.getCompiledDeck(c, slug);
1044
+ const asset = deck ? findRoutableAsset(deck.assets, slug, assetPath) : void 0;
1045
+ const bucket = asset ? await resolveBucket(options.bucket, c) : void 0;
1046
+ if (deck && asset && bucket) {
1047
+ const object = await bucket.get(resolveR2Key({
1048
+ deck,
1049
+ asset,
1050
+ slug,
1051
+ assetPath
1052
+ }, options));
1053
+ if (object) {
1054
+ const response = await responseFromR2Object(object, {
1055
+ deck,
1056
+ asset,
1057
+ slug,
1058
+ assetPath
1059
+ }, options);
1060
+ if (response) return response;
1061
+ }
1062
+ }
1063
+ return source.getAsset?.(c, slug, assetPath) ?? null;
1064
+ }
1065
+ };
1017
1066
  }
1018
1067
  async function resolveBucket(bucket, c) {
1019
- return typeof bucket === "function" ? bucket(c) : bucket;
1068
+ return typeof bucket === "function" ? bucket(c) : bucket;
1020
1069
  }
1021
1070
  function findRoutableAsset(assets, slug, assetPath) {
1022
- const normalized = assetPath.replace(/^\/+/, "");
1023
- return assets.find((asset) => {
1024
- if (asset.type !== "local" && asset.type !== "r2") return false;
1025
- const suffix = `/${slug}/assets/${normalized}`;
1026
- return asset.publicPath.endsWith(suffix);
1027
- });
1071
+ const normalized = assetPath.replace(/^\/+/, "");
1072
+ return assets.find((asset) => {
1073
+ if (asset.type !== "local" && asset.type !== "r2") return false;
1074
+ const suffix = `/${slug}/assets/${normalized}`;
1075
+ return asset.publicPath.endsWith(suffix);
1076
+ });
1028
1077
  }
1029
1078
  function resolveR2Key(input, options) {
1030
- if (options.key) return options.key(input);
1031
- if (input.asset.r2Key) return input.asset.r2Key;
1032
- const key = input.asset.sourcePath || `${input.slug}/assets/${input.assetPath}`;
1033
- const prefix = options.keyPrefix?.replace(/\/+$/, "");
1034
- return prefix ? `${prefix}/${key.replace(/^\/+/, "")}` : key.replace(/^\/+/, "");
1079
+ if (options.key) return options.key(input);
1080
+ if (input.asset.r2Key) return input.asset.r2Key;
1081
+ const key = input.asset.sourcePath || `${input.slug}/assets/${input.assetPath}`;
1082
+ const prefix = options.keyPrefix?.replace(/\/+$/, "");
1083
+ return prefix ? `${prefix}/${key.replace(/^\/+/, "")}` : key.replace(/^\/+/, "");
1035
1084
  }
1036
1085
  async function responseFromR2Object(object, input, options) {
1037
- const body = object.body ?? (object.arrayBuffer ? await object.arrayBuffer() : void 0);
1038
- if (body == null) return null;
1039
- const headers = new Headers();
1040
- object.writeHttpMetadata?.(headers);
1041
- applyHttpMetadata(headers, object.httpMetadata);
1042
- if (!headers.has("content-type") && input.asset.contentType) headers.set("content-type", input.asset.contentType);
1043
- const cacheControl = resolveCacheControl(input, options);
1044
- if (cacheControl) {
1045
- headers.set("cache-control", cacheControl);
1046
- } else if (cacheControl !== false && input.asset.cacheControl && !headers.has("cache-control")) {
1047
- headers.set("cache-control", input.asset.cacheControl);
1048
- }
1049
- return new Response(body, { headers });
1086
+ const body = object.body ?? (object.arrayBuffer ? await object.arrayBuffer() : void 0);
1087
+ if (body == null) return null;
1088
+ const headers = new Headers();
1089
+ object.writeHttpMetadata?.(headers);
1090
+ applyHttpMetadata(headers, object.httpMetadata);
1091
+ if (!headers.has("content-type") && input.asset.contentType) headers.set("content-type", input.asset.contentType);
1092
+ const cacheControl = resolveCacheControl(input, options);
1093
+ if (cacheControl) headers.set("cache-control", cacheControl);
1094
+ else if (cacheControl !== false && input.asset.cacheControl && !headers.has("cache-control")) headers.set("cache-control", input.asset.cacheControl);
1095
+ return new Response(body, { headers });
1050
1096
  }
1051
1097
  function applyHttpMetadata(headers, metadata) {
1052
- if (!metadata) return;
1053
- if (metadata.contentType && !headers.has("content-type")) headers.set("content-type", metadata.contentType);
1054
- if (metadata.cacheControl && !headers.has("cache-control")) headers.set("cache-control", metadata.cacheControl);
1055
- if (metadata.contentDisposition && !headers.has("content-disposition")) {
1056
- headers.set("content-disposition", metadata.contentDisposition);
1057
- }
1058
- if (metadata.contentEncoding && !headers.has("content-encoding")) headers.set("content-encoding", metadata.contentEncoding);
1059
- if (metadata.contentLanguage && !headers.has("content-language")) headers.set("content-language", metadata.contentLanguage);
1098
+ if (!metadata) return;
1099
+ if (metadata.contentType && !headers.has("content-type")) headers.set("content-type", metadata.contentType);
1100
+ if (metadata.cacheControl && !headers.has("cache-control")) headers.set("cache-control", metadata.cacheControl);
1101
+ if (metadata.contentDisposition && !headers.has("content-disposition")) headers.set("content-disposition", metadata.contentDisposition);
1102
+ if (metadata.contentEncoding && !headers.has("content-encoding")) headers.set("content-encoding", metadata.contentEncoding);
1103
+ if (metadata.contentLanguage && !headers.has("content-language")) headers.set("content-language", metadata.contentLanguage);
1060
1104
  }
1061
1105
  function resolveCacheControl(input, options) {
1062
- return typeof options.cacheControl === "function" ? options.cacheControl(input) : options.cacheControl;
1106
+ return typeof options.cacheControl === "function" ? options.cacheControl(input) : options.cacheControl;
1063
1107
  }
1064
- export {
1065
- SLIDE_TRANSITIONS,
1066
- createDeckPaths,
1067
- createDeckViewerEmbed,
1068
- defineDecksConfig,
1069
- defineSlideComponents,
1070
- withR2Assets
1071
- };
1108
+ //#endregion
1109
+ export { SLIDE_TRANSITIONS, createDeckPaths, createDeckViewerEmbed, defineDecksConfig, defineSlideComponents, withR2Assets };