hono-decks 1.0.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -1,4 +1,5 @@
1
- import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
1
+ import { createRequire } from "node:module";
2
+ import { mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
2
3
  import { join, relative, resolve } from "node:path";
3
4
  import { unified } from "unified";
4
5
  import remarkGfm from "remark-gfm";
@@ -7,8 +8,8 @@ import remarkParse from "remark-parse";
7
8
  import { compile } from "@mdx-js/mdx";
8
9
  import remarkDirective from "remark-directive";
9
10
  import { codeToHtml } from "shiki";
11
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
12
  import { existsSync, watch } from "node:fs";
11
- import { fileURLToPath } from "node:url";
12
13
  import { build } from "esbuild";
13
14
  import { lookup } from "node:dns/promises";
14
15
  import { isIP } from "node:net";
@@ -17,23 +18,23 @@ import { raw } from "hono/html";
17
18
  import { jsx } from "hono/jsx/jsx-runtime";
18
19
  import { HtmlEscapedCallbackPhase, resolveCallback } from "hono/utils/html";
19
20
  //#region src/routing/file-routing.ts
20
- function resolveDeckFiles(paths, root = "decks") {
21
+ function resolveDeckFiles(paths, root = "decks", options = {}) {
21
22
  const normalizedRoot = normalizePath$3(root).replace(/\/$/, "");
22
23
  const decks = /* @__PURE__ */ new Map();
23
24
  const assetsBySlug = /* @__PURE__ */ new Map();
24
25
  for (const inputPath of paths.map(normalizePath$3).sort()) {
25
26
  assertInsideRoot(inputPath, normalizedRoot);
26
27
  const segments = inputPath.slice(normalizedRoot.length + 1).split("/");
27
- if (segments.length === 1 && segments[0].endsWith(".mdx")) {
28
+ if (segments.length === 1 && (segments[0].endsWith(".mdx") || options.markdown && segments[0].endsWith(".md"))) {
28
29
  addDeck(decks, {
29
- slug: segments[0].replace(/\.mdx$/, ""),
30
+ slug: segments[0].replace(/\.mdx?$/, ""),
30
31
  sourcePath: inputPath,
31
32
  kind: "single-file",
32
33
  assetPaths: []
33
34
  });
34
35
  continue;
35
36
  }
36
- if (segments.length === 2 && segments[1] === "deck.mdx") {
37
+ if (segments.length === 2 && (segments[1] === "deck.mdx" || options.markdown && segments[1] === "deck.md")) {
37
38
  addDeck(decks, {
38
39
  slug: segments[0],
39
40
  sourcePath: inputPath,
@@ -51,7 +52,7 @@ function resolveDeckFiles(paths, root = "decks") {
51
52
  if (deck?.kind === "directory") deck.assetPaths = assetPaths;
52
53
  continue;
53
54
  }
54
- if (inputPath.endsWith(".mdx")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
55
+ if (inputPath.endsWith(".mdx") || options.markdown && inputPath.endsWith(".md")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
55
56
  }
56
57
  return [...decks.values()].map((deck) => ({
57
58
  ...deck,
@@ -1184,8 +1185,8 @@ import { configureDecks, defineDecks } from ${JSON.stringify(DECKS_ADVANCED_ENTR
1184
1185
  import type { ConfiguredDecks, DecksConfig } from ${runtimeEntry};
1185
1186
  import type { Env } from "hono";
1186
1187
  import { decksClientEntry } from "./client-entry";
1187
- ${input.decks.flatMap((deck) => deck.slideModules.map((slide, index) => `import ${slideImportName(deck.deck.slug, index)} from ${JSON.stringify(slide.importPath)};`)).join("\n")}
1188
- ${input.decks.filter((deck) => deck.componentModulePath).map((deck) => `import * as ${componentImportName(deck.deck.slug)} from ${JSON.stringify(deck.componentModulePath)};`).join("\n")}
1188
+ ${input.decks.flatMap((deck, deckIndex) => deck.slideModules.map((slide, index) => `import ${slideImportName(deckIndex, index)} from ${JSON.stringify(slide.importPath)};`)).join("\n")}
1189
+ ${input.decks.flatMap((deck, deckIndex) => deck.componentModulePath ? [`import * as ${componentImportName(deckIndex)} from ${JSON.stringify(deck.componentModulePath)};`] : []).join("\n")}
1189
1190
 
1190
1191
  function withClientComponentIds(module, clientIds) {
1191
1192
  const registry = {};
@@ -1205,7 +1206,7 @@ function withClientComponentIds(module, clientIds) {
1205
1206
  const generatedDecks = defineDecks({
1206
1207
  clientEntryAsset: decksClientEntry,
1207
1208
  decks: [
1208
- ${input.decks.map(emitDeckObject).join(",\n")}
1209
+ ${input.decks.map((deck, deckIndex) => emitDeckObject(deck, deckIndex)).join(",\n")}
1209
1210
  ]
1210
1211
  });
1211
1212
 
@@ -1218,15 +1219,16 @@ function definedDecksFor<E extends Env>() {
1218
1219
  }
1219
1220
  `;
1220
1221
  }
1221
- function emitDeckObject(deck) {
1222
+ function emitDeckObject(deck, deckIndex) {
1222
1223
  return ` {
1223
1224
  slug: ${JSON.stringify(deck.deck.slug)},
1224
1225
  sourcePath: ${JSON.stringify(deck.deck.sourcePath)},
1225
1226
  kind: ${JSON.stringify(deck.deck.kind)},
1226
1227
  meta: ${serializeValue(deck.deck.meta, 3)},
1228
+ ${typeof deck.deck.clientEntryRequired === "boolean" ? ` clientEntryRequired: ${deck.deck.clientEntryRequired},\n` : ""}
1227
1229
  ${deck.deck.themeStyle ? ` "themeStyle": ${serializeValue(deck.deck.themeStyle, 3)},\n` : ""}${deck.deck.themeSourcePath ? ` "themeSourcePath": ${JSON.stringify(deck.deck.themeSourcePath)},\n` : ""}
1228
1230
  assets: ${serializeValue(deck.deck.assets, 3)},
1229
- componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(deck.deck.slug)}, ${serializeValue(deck.clientComponentIds ?? {}, 3)})` : "{}"},
1231
+ componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(deckIndex)}, ${serializeValue(deck.clientComponentIds ?? {}, 3)})` : "{}"},
1230
1232
  warnings: ${serializeValue(deck.deck.warnings, 3)},
1231
1233
  slides: [
1232
1234
  ${deck.deck.slides.map((slide) => ` {
@@ -1235,19 +1237,16 @@ ${deck.deck.slides.map((slide) => ` {
1235
1237
  html: "",
1236
1238
  components: [],
1237
1239
  notes: ${serializeValue(slide.notes, 5)},
1238
- render: ${slideImportName(deck.deck.slug, slide.index)}
1240
+ render: ${slideImportName(deckIndex, slide.index)}
1239
1241
  }`).join(",\n")}
1240
1242
  ]
1241
1243
  }`;
1242
1244
  }
1243
- function slideImportName(slug, index) {
1244
- return `Slide_${safeIdentifier$1(slug)}_${index}`;
1245
+ function slideImportName(deckIndex, slideIndex) {
1246
+ return `Slide_${deckIndex}_${slideIndex}`;
1245
1247
  }
1246
- function componentImportName(slug) {
1247
- return `Components_${safeIdentifier$1(slug)}`;
1248
- }
1249
- function safeIdentifier$1(value) {
1250
- return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
1248
+ function componentImportName(deckIndex) {
1249
+ return `Components_${deckIndex}`;
1251
1250
  }
1252
1251
  function serializeValue(value, depth) {
1253
1252
  const indent = " ".repeat(depth);
@@ -1699,6 +1698,191 @@ function extractDeckPrelude(source) {
1699
1698
  };
1700
1699
  }
1701
1700
  //#endregion
1701
+ //#region src/node/markdown-decks.ts
1702
+ async function compileMarkdownDecks(input, adapter) {
1703
+ const markdownEntries = input.decks.filter((entry) => entry.sourcePath.endsWith(".md"));
1704
+ const mdx = await compileMdxModuleDecks({
1705
+ ...input,
1706
+ decks: input.decks.filter((entry) => !entry.sourcePath.endsWith(".md"))
1707
+ });
1708
+ if (!markdownEntries.length) return mdx;
1709
+ const decks = [...mdx.decks];
1710
+ const clients = /* @__PURE__ */ new Set();
1711
+ const markdownIslands = /* @__PURE__ */ new Map();
1712
+ for (const entry of markdownEntries) try {
1713
+ const source = await input.readText(entry.sourcePath);
1714
+ const parsed = adapter.read(source);
1715
+ if (input.clientComponentIds?.[entry.slug] && Object.keys(input.clientComponentIds[entry.slug]).length) throw new Error("Void Markdown slides support static components only; use MDX for hono-decks client components.");
1716
+ const warnings = [];
1717
+ const meta = toDeckFrontmatter(parsed.frontmatter, warnings);
1718
+ addUnknownFrontmatterWarnings(warnings, meta.meta, "deck");
1719
+ const assets = [...await buildAssetRefs(entry.slug, entry.assetPaths, input), ...buildExternalAssetRefs([
1720
+ ...collectMarkdownAssetCandidates(parsed.content),
1721
+ ...collectFrontmatterAssetCandidates(meta.assets),
1722
+ ...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
1723
+ ])];
1724
+ const candidates = [
1725
+ ...collectMarkdownAssetCandidates(parsed.content),
1726
+ ...collectFrontmatterAssetCandidates(parsed.frontmatter.assets),
1727
+ ...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
1728
+ ];
1729
+ if (entry.kind === "single-file" && candidates.some(isLocalRelativeAssetCandidate)) throw new Error("Single-file Markdown decks cannot reference local relative assets; use a directory deck.");
1730
+ let clientEntryRequired = false;
1731
+ const slideModules = [];
1732
+ const slides = [];
1733
+ for (const [index, slideSource] of splitMarkdownSlides(parsed.content).entries()) {
1734
+ const prelude = parsed.script ? `<script>\n${parsed.script}\n<\/script>\n` : "";
1735
+ const slide = adapter.read(prelude + slideSource);
1736
+ const path = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
1737
+ const rewritten = rewriteAssetUrls(`${prelude}---\n${JSON.stringify({
1738
+ ...parsed.frontmatter,
1739
+ ...slide.frontmatter
1740
+ })}\n---\n${slide.content}`, assets);
1741
+ const result = await adapter.compile(rewritten, { sourcePath: resolve(input.cwd ?? process.cwd(), entry.sourcePath) });
1742
+ clientEntryRequired ||= Boolean(result.clientCode || result.islands?.length);
1743
+ if (result.clientCode) clients.add(result.clientCode);
1744
+ for (const island of result.islands ?? []) markdownIslands.set(island.id, island);
1745
+ const slideMeta = toSlideFrontmatter(slide.frontmatter, warnings, {
1746
+ slideIndex: index,
1747
+ fallbackTitle: result.headings[0]?.text,
1748
+ fallbackTransition: meta.transition,
1749
+ fallbackTransitionDuration: meta.transitionDuration,
1750
+ fallbackTransitionEasing: meta.transitionEasing
1751
+ });
1752
+ addUnknownFrontmatterWarnings(warnings, slideMeta.meta, "slide", index);
1753
+ slideModules.push({
1754
+ path,
1755
+ importPath: `./decks/${entry.slug}/slide-${index}`,
1756
+ code: `// @ts-nocheck\n${rewriteRelativeMdxImports(result.code, dirname$1(entry.sourcePath), dirname$1(path))}`
1757
+ });
1758
+ slides.push({
1759
+ index,
1760
+ meta: slideMeta,
1761
+ html: "",
1762
+ components: [],
1763
+ notes: slideMeta.notes
1764
+ });
1765
+ }
1766
+ addExternalAssetWarnings(warnings, assets);
1767
+ const themeStyle = input.themeStyles?.[entry.slug];
1768
+ decks.push({
1769
+ deck: {
1770
+ slug: entry.slug,
1771
+ sourcePath: entry.sourcePath,
1772
+ kind: entry.kind,
1773
+ clientEntryRequired,
1774
+ meta,
1775
+ slides,
1776
+ assets,
1777
+ warnings,
1778
+ themeStyle: themeStyle?.style,
1779
+ themeSourcePath: themeStyle?.sourcePath
1780
+ },
1781
+ slideModules
1782
+ });
1783
+ } catch (error) {
1784
+ throw new CompileError(`Void Markdown compile failed in ${entry.sourcePath}: ${error instanceof Error ? error.message : String(error)}`, "void-markdown-compile-error");
1785
+ }
1786
+ decks.sort((a, b) => a.deck.slug.localeCompare(b.deck.slug));
1787
+ return {
1788
+ decks,
1789
+ routerModule: emitModuleDecksRouter({ decks }),
1790
+ clientCode: [...clients].join("\n"),
1791
+ clientModules: [adapter.client([...markdownIslands.values()])].filter(Boolean),
1792
+ markdownIslands: [...markdownIslands.values()]
1793
+ };
1794
+ }
1795
+ /** Protect fenced code from the deck separator parser. */
1796
+ function splitMarkdownSlides(source) {
1797
+ const fences = [];
1798
+ let prefix = "HONO_DECKS_FENCE_";
1799
+ while (source.includes(prefix)) prefix += "_";
1800
+ const lines = source.replace(/\r\n/g, "\n").split("\n");
1801
+ const protectedLines = [];
1802
+ for (let index = 0; index < lines.length; index++) {
1803
+ const open = /^ {0,3}(`{3,}|~{3,})/.exec(lines[index]);
1804
+ if (/^[ \t]+---[ \t]*$/.test(lines[index])) {
1805
+ protectedLines.push(`${prefix}${fences.push(lines[index]) - 1}_END`);
1806
+ continue;
1807
+ }
1808
+ if (!open) {
1809
+ protectedLines.push(lines[index]);
1810
+ continue;
1811
+ }
1812
+ const block = [lines[index]];
1813
+ const close = new RegExp(`^ {0,3}${open[1][0]}{${open[1].length},}[ \\t]*$`);
1814
+ let closed = false;
1815
+ while (++index < lines.length) {
1816
+ block.push(lines[index]);
1817
+ if (close.test(lines[index])) {
1818
+ closed = true;
1819
+ break;
1820
+ }
1821
+ }
1822
+ if (!closed) throw new Error("Markdown code fence is not closed.");
1823
+ protectedLines.push(`${prefix}${fences.push(block.join("\n")) - 1}_END`);
1824
+ }
1825
+ return splitSlideSources(protectedLines.join("\n")).map((slide) => slide.replace(new RegExp(`${prefix}(\\d+)_END`, "g"), (_, id) => fences[Number(id)]));
1826
+ }
1827
+ //#endregion
1828
+ //#region src/adapters/hono-markdown.ts
1829
+ /** Node-only bridge. Existing Void configs retain their original compiler/runtime pair. */
1830
+ async function createHonoMarkdownAdapter(cwd, legacy = false, options) {
1831
+ const require = createRequire(resolve(cwd, "package.json"));
1832
+ const entry = legacy ? "void-plugin-hono-jsx/markdown/compiler" : "@ts-76/hono-markdown/compiler";
1833
+ let compiler;
1834
+ try {
1835
+ compiler = await import(pathToFileURL(require.resolve(entry)).href);
1836
+ } catch (cause) {
1837
+ throw new Error(`Hono Markdown requires ${legacy ? "its public /markdown/compiler API" : "@ts-76/hono-markdown/compiler"} and Markdown peers installed in the app.`, { cause });
1838
+ }
1839
+ const read = compiler.readMarkdownModule ?? compiler.readStaticMarkdown;
1840
+ const compile = compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown;
1841
+ if (typeof read !== "function" || typeof compile !== "function") throw new Error("Missing standalone compiler API");
1842
+ const runtime = entry.startsWith("void-plugin") ? "void-plugin-hono-jsx/markdown/client" : "@ts-76/hono-markdown/client";
1843
+ return {
1844
+ read,
1845
+ compile: (source, input) => compile(source, {
1846
+ ...input,
1847
+ options
1848
+ }),
1849
+ client: (islands) => emitMarkdownClient(islands, runtime)
1850
+ };
1851
+ }
1852
+ function emitMarkdownClient(islands, runtime) {
1853
+ if (!islands.length) return "";
1854
+ const literal = (value) => JSON.stringify(value).replace(/</g, "\\u003c");
1855
+ return `import { hydrateMarkdownIslands } from ${literal(runtime)};
1856
+ hydrateMarkdownIslands({ ${islands.map((island) => `${literal(island.id)}: () => import(${literal(island.modulePath)})`).join(",")} }, {
1857
+ ready(element, start) {
1858
+ const slide = element.closest('section[data-slide-state]');
1859
+ if (!slide || slide.dataset.slideState === 'active') { start(); return; }
1860
+ const observer = new MutationObserver(() => {
1861
+ if (slide.dataset.slideState === 'active') { observer.disconnect(); start(); }
1862
+ });
1863
+ observer.observe(slide, { attributes: true, attributeFilter: ['data-slide-state'] });
1864
+ }
1865
+ });`;
1866
+ }
1867
+ //#endregion
1868
+ //#region src/node/markdown-adapter.ts
1869
+ async function loadMarkdownAdapter(selection, cwd) {
1870
+ if (selection === "void") return createHonoMarkdownAdapter(cwd, true);
1871
+ if (selection.adapter === "hono-decks/adapters/hono-markdown") return createHonoMarkdownAdapter(cwd, false, selection.options);
1872
+ const module = await import(pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(selection.adapter)).href);
1873
+ if (typeof module.createMarkdownAdapter !== "function") throw new Error("Markdown adapter must export createMarkdownAdapter.");
1874
+ const adapter = await module.createMarkdownAdapter({
1875
+ cwd,
1876
+ options: selection.options
1877
+ });
1878
+ if (![
1879
+ adapter?.read,
1880
+ adapter?.compile,
1881
+ adapter?.client
1882
+ ].every((value) => typeof value === "function")) throw new Error("Markdown adapter must provide read, compile and client functions.");
1883
+ return adapter;
1884
+ }
1885
+ //#endregion
1702
1886
  //#region src/generator/component-registry.ts
1703
1887
  function applyDeckComponentRegistry(input) {
1704
1888
  const components = input.components.map((component) => ({
@@ -1809,10 +1993,12 @@ async function emitClientEntryModule(input) {
1809
1993
  registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
1810
1994
  }
1811
1995
  }
1812
- if (registrations.length === 0) return "export const decksClientEntry = \"\";\n";
1813
- const output = (await build({
1996
+ const clientModules = input.clientModules ?? [];
1997
+ if (registrations.length === 0 && clientModules.length === 0) return `export const decksClientEntry = ${JSON.stringify(input.extraCode ?? "")};\n`;
1998
+ const result = await build({
1814
1999
  stdin: {
1815
- contents: `import { hydrateSlideIslands } from "hono-decks/client";
2000
+ contents: `${clientModules.join("\n")}
2001
+ import { hydrateSlideIslands } from "hono-decks/client";
1816
2002
  ${imports.join("\n")}
1817
2003
 
1818
2004
  hydrateSlideIslands({
@@ -1827,6 +2013,15 @@ hydrateSlideIslands({
1827
2013
  },
1828
2014
  bundle: true,
1829
2015
  write: false,
2016
+ outdir: join(input.cwd, ".hono-decks-client"),
2017
+ loader: {
2018
+ ".svg": "dataurl",
2019
+ ".png": "dataurl",
2020
+ ".jpg": "dataurl",
2021
+ ".webp": "dataurl",
2022
+ ".woff2": "dataurl",
2023
+ ".woff": "dataurl"
2024
+ },
1830
2025
  format: "esm",
1831
2026
  platform: "browser",
1832
2027
  target: "es2022",
@@ -1836,9 +2031,12 @@ hydrateSlideIslands({
1836
2031
  alias: { "hono-decks/client": resolveClientRuntimeEntry() },
1837
2032
  sourcemap: false,
1838
2033
  minify: false
1839
- })).outputFiles[0];
2034
+ });
2035
+ const output = result.outputFiles.find((file) => file.path.endsWith(".js"));
2036
+ const css = result.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
2037
+ input.onCss?.(css);
1840
2038
  if (!output) throw new Error("Client entry did not produce output.");
1841
- return `export const decksClientEntry = ${JSON.stringify(output.text)};\n`;
2039
+ return `export const decksClientEntry = ${JSON.stringify(output.text + (input.extraCode ? "\n" + input.extraCode : ""))};\n`;
1842
2040
  }
1843
2041
  function resolveClientRuntimeEntry() {
1844
2042
  const built = fileURLToPath(new URL("./client.js", import.meta.url));
@@ -1966,6 +2164,63 @@ function slugForDeckPath(path, root) {
1966
2164
  if (segments.length >= 3 && segments[1] === "assets") return segments[0];
1967
2165
  }
1968
2166
  //#endregion
2167
+ //#region src/node/file-transaction.ts
2168
+ /** Stage every file before publishing; restore the previous files on an I/O error.
2169
+ * Individual renames are atomic. This is rollback on failure, not a cross-process
2170
+ * or crash-atomic filesystem transaction. Failed rollback retains recovery files.
2171
+ */
2172
+ async function publishGeneratedFiles(files, renameFile = rename) {
2173
+ const staged = [];
2174
+ const published = [];
2175
+ let keepRecovery = false;
2176
+ try {
2177
+ if (new Set(files.map((file) => file.path)).size !== files.length) throw new Error("Generated output paths must be unique.");
2178
+ for (const file of files) {
2179
+ const parent = dirname(file.path);
2180
+ await mkdir(parent, { recursive: true });
2181
+ const dir = await mkdtemp(join(parent, ".hono-decks-"));
2182
+ const entry = {
2183
+ path: file.path,
2184
+ dir,
2185
+ next: join(dir, "next")
2186
+ };
2187
+ staged.push(entry);
2188
+ await writeFile(entry.next, file.content, "utf8");
2189
+ let previous;
2190
+ try {
2191
+ previous = await readFile(file.path);
2192
+ } catch (error) {
2193
+ if (error.code === "ENOENT") continue;
2194
+ throw error;
2195
+ }
2196
+ entry.backup = join(dir, "previous");
2197
+ await writeFile(entry.backup, previous);
2198
+ }
2199
+ for (const entry of staged) {
2200
+ await renameFile(entry.next, entry.path);
2201
+ published.push(entry);
2202
+ }
2203
+ } catch (error) {
2204
+ const failures = [];
2205
+ for (const entry of published.reverse()) try {
2206
+ if (entry.backup) await renameFile(entry.backup, entry.path);
2207
+ else await rm(entry.path, { force: true });
2208
+ } catch (failure) {
2209
+ failures.push(failure);
2210
+ }
2211
+ if (failures.length) {
2212
+ keepRecovery = true;
2213
+ throw new AggregateError([error, ...failures], `Generated output rollback failed. Recovery files remain in: ${staged.map((entry) => entry.dir).join(", ")}`);
2214
+ }
2215
+ throw error;
2216
+ } finally {
2217
+ if (!keepRecovery) await Promise.allSettled(staged.map((entry) => rm(entry.dir, {
2218
+ recursive: true,
2219
+ force: true
2220
+ })));
2221
+ }
2222
+ }
2223
+ //#endregion
1969
2224
  //#region src/node/ogp.ts
1970
2225
  const MAX_OGP_BYTES = 256 * 1024;
1971
2226
  const MAX_OGP_REDIRECTS = 3;
@@ -2152,10 +2407,16 @@ function isHttpUrl(value) {
2152
2407
  //#endregion
2153
2408
  //#region src/node/compile-decks.ts
2154
2409
  async function compileDecks(input) {
2410
+ const compiled = await compileDeckModules(input);
2411
+ await publishGeneratedFiles([...compiled.modules, ...compiled.cacheFile ? [compiled.cacheFile] : []]);
2412
+ return compiled.result;
2413
+ }
2414
+ /** Shared by file output and Vite's in-memory module graph. Does not write files. */
2415
+ async function compileDeckModules(input) {
2155
2416
  const out = normalizeRelativePath(input.out, "Output directory");
2156
2417
  const root = normalizeDeckRoot(input.root);
2157
2418
  const paths = await listFiles(input.cwd, join(input.cwd, root));
2158
- const resolved = resolveDeckFiles(paths, root);
2419
+ const resolved = resolveDeckFiles(paths, root, { markdown: Boolean(input.markdown) });
2159
2420
  const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
2160
2421
  const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
2161
2422
  return path ? [[deck.slug, path]] : [];
@@ -2183,7 +2444,9 @@ async function compileDecks(input) {
2183
2444
  refresh: input.refreshOgp,
2184
2445
  resolveOgp: input.resolveOgp ?? resolveOgpMetadata
2185
2446
  });
2186
- const generated = await compileMdxModuleDecks({
2447
+ const adapter = input.markdown && resolved.some((entry) => entry.sourcePath.endsWith(".md")) ? await loadMarkdownAdapter(input.markdown, input.cwd) : void 0;
2448
+ const generated = await (adapter ? (value) => compileMarkdownDecks(value, adapter) : compileMdxModuleDecks)({
2449
+ cwd: input.cwd,
2187
2450
  root,
2188
2451
  outDir: out,
2189
2452
  mountPath: input.mountPath,
@@ -2195,20 +2458,57 @@ async function compileDecks(input) {
2195
2458
  readText: (path) => readFile(join(input.cwd, path), "utf8"),
2196
2459
  readBinary: (path) => readFile(join(input.cwd, path))
2197
2460
  });
2198
- for (const deck of generated.decks) for (const slide of deck.slideModules) await writeTextFile(join(input.cwd, slide.path), slide.code);
2199
- await writeTextFile(join(input.cwd, out, "decks.ts"), generated.routerModule);
2200
- await writeTextFile(join(input.cwd, out, "client-entry.ts"), await emitClientEntryModule({
2461
+ let clientCss = "";
2462
+ const clientEntry = await emitClientEntryModule({
2201
2463
  cwd: input.cwd,
2202
2464
  clientEntries: clientEntryPaths,
2203
- clientComponentIds
2204
- }));
2205
- await ogpCache.write();
2206
- return { decks: generated.decks.map((deck) => deck.deck) };
2465
+ clientComponentIds,
2466
+ extraCode: generated.clientCode,
2467
+ clientModules: generated.clientModules,
2468
+ onCss: (css) => {
2469
+ clientCss = css;
2470
+ }
2471
+ });
2472
+ if (clientCss) {
2473
+ for (const deck of generated.decks) if (deck.deck.clientEntryRequired || Object.keys(deck.clientComponentIds ?? {}).length) deck.deck.themeStyle = [deck.deck.themeStyle, clientCss].filter(Boolean).join("\n");
2474
+ generated.routerModule = emitModuleDecksRouter({ decks: generated.decks });
2475
+ }
2476
+ const files = generated.decks.flatMap((deck) => deck.slideModules.map((slide) => ({
2477
+ path: join(input.cwd, slide.path),
2478
+ content: slide.code
2479
+ })));
2480
+ files.push({
2481
+ path: join(input.cwd, out, "decks.ts"),
2482
+ content: generated.routerModule
2483
+ }, {
2484
+ path: join(input.cwd, out, "client-entry.ts"),
2485
+ content: clientEntry
2486
+ });
2487
+ return {
2488
+ modules: files,
2489
+ cacheFile: ogpCache.output(),
2490
+ result: {
2491
+ outDir: out,
2492
+ routerModulePath: `${out}/decks.ts`,
2493
+ decks: generated.decks.map(({ deck, slideModules }) => {
2494
+ const { slides, componentRegistry: _componentRegistry, ...metadata } = deck;
2495
+ return {
2496
+ ...metadata,
2497
+ slides: slides.map((slide, index) => ({
2498
+ index: slide.index,
2499
+ meta: slide.meta,
2500
+ notes: slide.notes,
2501
+ modulePath: slideModules[index].path
2502
+ }))
2503
+ };
2504
+ })
2505
+ }
2506
+ };
2207
2507
  }
2208
2508
  async function createOgpCacheResolver(input) {
2209
2509
  if (!input.cacheFile) return {
2210
2510
  resolveOgp: input.resolveOgp,
2211
- write: async () => void 0
2511
+ output: () => void 0
2212
2512
  };
2213
2513
  const cachePath = join(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
2214
2514
  const cache = await readOgpCache(cachePath);
@@ -2224,9 +2524,11 @@ async function createOgpCacheResolver(input) {
2224
2524
  }
2225
2525
  return metadata;
2226
2526
  },
2227
- async write() {
2228
- if (!dirty) return;
2229
- await writeTextFile(cachePath, `${JSON.stringify(cache, null, 2)}\n`);
2527
+ output() {
2528
+ return dirty ? {
2529
+ path: cachePath,
2530
+ content: `${JSON.stringify(cache, null, 2)}\n`
2531
+ } : void 0;
2230
2532
  }
2231
2533
  };
2232
2534
  }
@@ -2288,10 +2590,6 @@ async function buildDeckManifestFromPaths(input) {
2288
2590
  readBinary: (path) => readFile(join(input.cwd, path))
2289
2591
  });
2290
2592
  }
2291
- async function writeTextFile(path, contents) {
2292
- await mkdir(dirname(path), { recursive: true });
2293
- await writeFile(path, contents, "utf8");
2294
- }
2295
2593
  function findComponentModule(paths, root, slug) {
2296
2594
  const base = `${normalizePath(root).replace(/\/$/, "")}/${slug}/components/index`;
2297
2595
  return paths.find((path) => {
@@ -3087,6 +3385,108 @@ function controlIconPaths(name) {
3087
3385
  }
3088
3386
  }
3089
3387
  //#endregion
3388
+ //#region src/renderer/presentation-style.ts
3389
+ const PRESENTATION_DESIGN_WIDTH = 1920;
3390
+ const PRESENTATION_DESIGN_HEIGHT = 1080;
3391
+ function presentationDesignCssVariables() {
3392
+ return `--hono-decks-width:${PRESENTATION_DESIGN_WIDTH}px;--hono-decks-height:${PRESENTATION_DESIGN_HEIGHT}px`;
3393
+ }
3394
+ function basePresentationStyle() {
3395
+ return `
3396
+ :root{color-scheme:light;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-background:#fff;--hono-decks-color:#172033;--hono-decks-muted-color:#475569;--hono-decks-accent-color:#0369a1;--hono-decks-border-color:#cbd5e1;--hono-decks-inline-code-background:#f1f5f9;--hono-decks-code-background:#f1f5f9;--hono-decks-card-background:#f1f5f9;--hono-decks-card-image-background:#e2e8f0;--hono-decks-warning-background:#fef3c7;--hono-decks-warning-color:#854d0e;background:var(--hono-decks-background);color:var(--hono-decks-color)}
3397
+ html,body{margin:0;width:100%;height:100%;overflow:hidden}
3398
+ .hono-decks-stage{width:100vw;height:100vh;overflow:hidden;position:relative;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);place-items:center}
3399
+ .hono-decks-deck{display:grid;gap:1rem;width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;transform-origin:center}
3400
+ .slide{box-sizing:border-box;aspect-ratio:16/9;background:var(--hono-decks-background);color:var(--hono-decks-color);padding:1.8rem;overflow:hidden}
3401
+ .hono-decks-slide-content{width:100%;height:100%}
3402
+ .slide.layout-cover,.slide.layout-statement{display:flex;flex-direction:column;justify-content:center}
3403
+ .slide.layout-cover>.hono-decks-slide-content,.slide.layout-statement>.hono-decks-slide-content{display:flex;flex-direction:column;justify-content:center}
3404
+ .slide code{font-family:"SFMono-Regular","Cascadia Code","Liberation Mono",Menlo,Consolas,monospace;font-size:.9em;line-height:1.45}
3405
+ .slide :not(pre)>code{border-radius:6px;background:var(--hono-decks-inline-code-background);padding:.12em .34em}
3406
+ .slide pre{max-width:100%;overflow:auto;box-sizing:border-box;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-code-background);padding:1rem;tab-size:2;white-space:pre}
3407
+ .slide pre code{display:block;min-width:max-content;background:transparent;padding:0}
3408
+ .slide table{width:100%;margin:1rem 0;border-collapse:collapse}
3409
+ .slide th,.slide td{border:1px solid var(--hono-decks-border-color);padding:.4em .75em;text-align:left}
3410
+ .slide th{background:var(--hono-decks-card-background);font-weight:700}
3411
+ .slide del{color:var(--hono-decks-muted-color)}
3412
+ .slide li.task-list-item{list-style:none;margin-left:-1.4em}
3413
+ .slide li.task-list-item input[type=checkbox]{margin-right:.5em;vertical-align:middle}
3414
+ .hono-decks-code-block{margin:1rem 0;max-width:100%}
3415
+ .hono-decks-code-caption{display:inline-flex;margin:0 0 .4rem;border:1px solid var(--hono-decks-border-color);border-radius:6px;padding:.2rem .5rem;background:var(--hono-decks-inline-code-background);color:var(--hono-decks-muted-color);font-size:.82rem}
3416
+ .hono-decks-embed-frame{margin:1rem 0;max-width:100%}
3417
+ .hono-decks-embed-viewport{width:min(100%,72rem);overflow:hidden}
3418
+ .hono-decks-embed-viewport iframe{display:block;width:100%;height:100%;border:0}
3419
+ .hono-decks-embed-print-fallback{display:none}
3420
+ .hono-decks-embed-print-poster{display:block;width:100%;height:100%;object-fit:cover}
3421
+ .hono-decks-embed-fallback{margin:.45rem 0 0;color:var(--hono-decks-muted-color);font-size:.84rem}
3422
+ .hono-decks-embed-fallback a{color:inherit}
3423
+ .hono-decks-social-embed{margin:1rem 0;max-width:min(100%,42rem)}
3424
+ .hono-decks-social-card{margin:0;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem}
3425
+ .hono-decks-social-card p{margin:0 0 .75rem;line-height:1.55}
3426
+ .hono-decks-social-card footer{display:flex;flex-wrap:wrap;gap:.65rem;align-items:center;color:var(--hono-decks-muted-color);font-size:.9rem}
3427
+ .hono-decks-social-card a{color:inherit}
3428
+ .hono-decks-tweet-embed{margin:1rem 0;max-width:min(100%,42rem)}
3429
+ .hono-decks-tweet-embed .twitter-tweet{margin:0;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem}
3430
+ .hono-decks-tweet-embed .twitter-tweet a{color:inherit}
3431
+ .hono-decks-tweet-print-fallback{display:none}
3432
+ .hono-decks-link-card{margin:1rem 0;max-width:min(100%,42rem)}
3433
+ .hono-decks-link-card-anchor{display:grid;grid-template-columns:minmax(9rem,32%) minmax(0,1fr);gap:.75rem;align-items:stretch;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem;color:inherit;text-decoration:none}
3434
+ .hono-decks-link-card-body{display:grid;gap:.35rem;min-width:0}
3435
+ .hono-decks-link-card-image{width:100%;height:100%;max-height:10rem;aspect-ratio:16/9;object-fit:cover;border-radius:6px;background:var(--hono-decks-card-image-background)}
3436
+ .hono-decks-link-card-site{color:var(--hono-decks-accent-color);font-size:.8rem;text-transform:uppercase}
3437
+ .hono-decks-link-card-title{font-weight:700}
3438
+ .hono-decks-link-card-description{color:var(--hono-decks-muted-color);line-height:1.45}
3439
+ .hono-decks-link-card-label{color:var(--hono-decks-accent-color);font-size:.88rem}
3440
+ .mdx-hero{height:100%;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,42%);gap:1.8rem;align-items:center}
3441
+ .mdx-hero:not(.has-image){grid-template-columns:1fr}
3442
+ .mdx-hero-copy{min-width:0}
3443
+ .mdx-hero-eyebrow{margin:0 0 .75rem;color:var(--hono-decks-accent-color);text-transform:uppercase;font-size:.85rem;letter-spacing:0}
3444
+ .mdx-hero h1{margin:0;font-size:3rem;line-height:1.02}
3445
+ .mdx-hero-subtitle{margin:1rem 0 0;font-size:1.125rem;line-height:1.45;color:var(--hono-decks-muted-color)}
3446
+ .mdx-hero-image{width:100%;height:auto;max-height:756px;object-fit:contain;border-radius:8px}
3447
+ [data-hono-decks-fire]{--fire-duration:.18s;--fire-easing:ease;--fire-opacity:0;--fire-transform:translateY(.35rem);--fire-filter:none;visibility:visible;opacity:1;transform:none;filter:none;transition:visibility 0s linear 0s,opacity var(--fire-duration) var(--fire-easing),transform var(--fire-duration) var(--fire-easing),filter var(--fire-duration) var(--fire-easing)}
3448
+ [data-hono-decks-fire][data-fire-hidden]{visibility:hidden;opacity:var(--fire-opacity);transform:var(--fire-transform);filter:var(--fire-filter);transition-delay:var(--fire-duration),0s,0s,0s}
3449
+ [data-fire-effect=none]{--fire-duration:0s;--fire-transform:none}
3450
+ [data-fire-effect=fade]{--fire-transform:none}
3451
+ [data-fire-effect=fade-up]{--fire-transform:translateY(.85rem)}
3452
+ [data-fire-effect=scale]{--fire-transform:scale(.96)}
3453
+ body:not([data-overview-mode]) .hono-decks-deck{position:relative}
3454
+ body:not([data-overview-mode]) .slide{position:absolute;inset:0;width:100%;height:100%}
3455
+ .slide[data-active-transition]{transition:opacity var(--hono-decks-active-transition-duration,var(--hono-decks-slide-transition-duration,var(--hono-decks-transition-duration))) var(--hono-decks-active-transition-easing,var(--hono-decks-slide-transition-easing,var(--hono-decks-transition-easing))),transform var(--hono-decks-active-transition-duration,var(--hono-decks-slide-transition-duration,var(--hono-decks-transition-duration))) var(--hono-decks-active-transition-easing,var(--hono-decks-slide-transition-easing,var(--hono-decks-transition-easing)));will-change:opacity,transform}
3456
+ .slide[data-slide-state="inactive"]{visibility:hidden;pointer-events:none}
3457
+ .slide[data-slide-state="active"]{visibility:visible;opacity:1;transform:translate3d(0,0,0) scale(1)}
3458
+ .slide[data-active-transition="fade"][data-slide-state="entering"],.slide[data-active-transition="fade"][data-slide-state="leaving"],.slide[data-active-transition="view-transition"][data-slide-state="entering"],.slide[data-active-transition="view-transition"][data-slide-state="leaving"]{opacity:0}
3459
+ .slide[data-active-transition="fade-out"][data-slide-state="entering"]{opacity:1}
3460
+ .slide[data-active-transition="fade-out"][data-slide-state="leaving"]{opacity:0}
3461
+ .slide[data-active-transition="slide-left"][data-slide-direction="forward"][data-slide-state="entering"],.slide[data-active-transition="slide-right"][data-slide-direction="backward"][data-slide-state="leaving"]{transform:translate3d(100%,0,0)}
3462
+ .slide[data-active-transition="slide-left"][data-slide-direction="forward"][data-slide-state="leaving"],.slide[data-active-transition="slide-right"][data-slide-direction="backward"][data-slide-state="entering"]{transform:translate3d(-100%,0,0)}
3463
+ .slide[data-active-transition="slide-left"][data-slide-direction="backward"][data-slide-state="entering"],.slide[data-active-transition="slide-right"][data-slide-direction="forward"][data-slide-state="leaving"]{transform:translate3d(-100%,0,0)}
3464
+ .slide[data-active-transition="slide-left"][data-slide-direction="backward"][data-slide-state="leaving"],.slide[data-active-transition="slide-right"][data-slide-direction="forward"][data-slide-state="entering"]{transform:translate3d(100%,0,0)}
3465
+ .slide[data-active-transition="slide-up"][data-slide-direction="forward"][data-slide-state="entering"],.slide[data-active-transition="slide-down"][data-slide-direction="backward"][data-slide-state="leaving"]{transform:translate3d(0,100%,0)}
3466
+ .slide[data-active-transition="slide-up"][data-slide-direction="forward"][data-slide-state="leaving"],.slide[data-active-transition="slide-down"][data-slide-direction="backward"][data-slide-state="entering"]{transform:translate3d(0,-100%,0)}
3467
+ .slide[data-active-transition="slide-up"][data-slide-direction="backward"][data-slide-state="entering"],.slide[data-active-transition="slide-down"][data-slide-direction="forward"][data-slide-state="leaving"]{transform:translate3d(0,-100%,0)}
3468
+ .slide[data-active-transition="slide-up"][data-slide-direction="backward"][data-slide-state="leaving"],.slide[data-active-transition="slide-down"][data-slide-direction="forward"][data-slide-state="entering"]{transform:translate3d(0,100%,0)}
3469
+ body:not([data-overview-mode]) .slide[hidden]{display:none}
3470
+ body[data-overview-mode] .hono-decks-deck{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
3471
+ body[data-overview-mode] .slide{cursor:pointer}
3472
+ body[data-presenter-mode] .speaker-notes{display:block;margin-top:1rem;padding:.75rem;border-radius:8px;background:var(--hono-decks-card-image-background)}
3473
+ .hono-decks-warnings{margin:1rem;padding:.75rem;border-radius:14px;background:var(--hono-decks-warning-background);color:var(--hono-decks-warning-color)}
3474
+ @media screen{html[data-hono-decks-print-preview]{width:auto;height:auto;min-height:100%;overflow:visible}
3475
+ body[data-hono-decks-print-preview]{min-height:100vh;overflow:auto;color-scheme:light;color:#000;--hono-decks-print-gap:6mm;--hono-decks-print-slot-height:80mm;--hono-decks-print-scale:.28}
3476
+ body[data-hono-decks-print-preview] .hono-decks-stage{display:block;width:auto;height:auto;min-height:100vh;overflow:visible;padding:12mm 0;box-sizing:border-box}
3477
+ body[data-hono-decks-print-preview] .hono-decks-deck{display:grid;grid-template-columns:1fr;grid-auto-rows:var(--hono-decks-print-slot-height);gap:var(--hono-decks-print-gap);width:calc(var(--hono-decks-print-slot-height) * 16 / 9);max-width:calc(100vw - 24px);height:auto;margin:0 auto;transform:none!important}
3478
+ body[data-hono-decks-print-preview] .slide{position:static;width:100%;max-width:100%;height:var(--hono-decks-print-slot-height);aspect-ratio:16/9;justify-self:center;align-self:center;padding:0;box-shadow:0 2px 10px rgba(15,23,42,.16);transition:none!important;transform:none!important}
3479
+ body[data-hono-decks-print-preview] .slide.layout-cover,body[data-hono-decks-print-preview] .slide.layout-statement{display:block}
3480
+ body[data-hono-decks-print-preview] .hono-decks-slide-content{width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;padding:1.8rem;transform:scale(var(--hono-decks-print-scale));transform-origin:left top;overflow:hidden}
3481
+ body[data-hono-decks-print-preview]:not([data-overview-mode]) .slide[hidden]{display:block!important}
3482
+ body[data-hono-decks-print-preview] .slide[data-slide-state]{visibility:visible!important;opacity:1!important;transform:none!important}
3483
+ body[data-hono-decks-print-preview] [data-hono-decks-fire]{visibility:visible!important;opacity:1!important;transform:none!important;filter:none!important}}
3484
+ @page{size:A4 portrait;margin:12mm}
3485
+ @media print{:root{color-scheme:light;color:#000;--hono-decks-print-gap:6mm;--hono-decks-print-slot-height:80mm;--hono-decks-print-scale:.28}html,body{width:auto;height:auto;overflow:visible}.hono-decks-stage{display:block;width:auto;height:auto;overflow:visible}.hono-decks-deck{display:grid;grid-template-columns:1fr;grid-auto-rows:var(--hono-decks-print-slot-height);gap:var(--hono-decks-print-gap);width:calc(var(--hono-decks-print-slot-height) * 16 / 9);height:auto;margin:0 auto;transform:none!important}body:not([data-overview-mode]) .slide{position:relative}.slide{position:relative;width:100%;max-width:100%;height:var(--hono-decks-print-slot-height);aspect-ratio:16/9;justify-self:center;align-self:center;padding:0;page-break-after:auto;break-after:auto;break-inside:avoid;box-shadow:none;transition:none!important;transform:none!important}.slide.layout-cover,.slide.layout-statement{display:block}.hono-decks-slide-content{position:absolute;inset:0 auto auto 0;width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;padding:1.8rem;zoom:var(--hono-decks-print-scale);overflow:hidden}.slide:nth-of-type(3n):not(:last-child){page-break-after:always;break-after:page}body:not([data-overview-mode]) .slide[hidden]{display:block!important}.slide[data-slide-state]{visibility:visible!important;opacity:1!important;transform:none!important}[data-hono-decks-fire]{visibility:visible!important;opacity:1!important;transform:none!important;filter:none!important}}
3486
+ @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}}
3487
+ @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}}`;
3488
+ }
3489
+ //#endregion
3090
3490
  //#region src/server/document.ts
3091
3491
  async function resolveDeckDocument(input, options, overrides) {
3092
3492
  const page = {
@@ -3138,8 +3538,8 @@ function renderPresentationScript(nonce) {
3138
3538
  const slides = Array.from(document.querySelectorAll(".slide"));
3139
3539
  const stage = document.querySelector("[data-hono-decks-stage]");
3140
3540
  const deck = document.querySelector("[data-hono-decks-deck]");
3141
- const DESIGN_WIDTH = 1920;
3142
- const DESIGN_HEIGHT = 1080;
3541
+ const DESIGN_WIDTH = ${PRESENTATION_DESIGN_WIDTH};
3542
+ const DESIGN_HEIGHT = ${PRESENTATION_DESIGN_HEIGHT};
3143
3543
  let index = 0;
3144
3544
  let previousIndex = 0;
3145
3545
  let stepIndex = 0;
@@ -3524,6 +3924,10 @@ function renderPresentationScript(nonce) {
3524
3924
  });
3525
3925
 
3526
3926
  document.addEventListener("keydown", (event) => {
3927
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return;
3928
+ const target = event.target;
3929
+ if (target instanceof HTMLElement && (target.isContentEditable || target.closest("input,textarea,select,button,a[href],[role=textbox],[role=button],[role=slider]"))) return;
3930
+ if (["ArrowRight", "ArrowLeft", " "].includes(event.key)) event.preventDefault();
3527
3931
  if (event.key === "ArrowRight" || event.key === " ") handleCommand("next");
3528
3932
  if (event.key === "ArrowLeft") handleCommand("previous");
3529
3933
  if (event.key === "f") handleCommand("fullscreen");
@@ -3543,6 +3947,9 @@ function renderPresentationScript(nonce) {
3543
3947
  window.__honoDecksPresentationRuntime = { command: handleCommand };
3544
3948
 
3545
3949
  window.addEventListener("resize", fitDeck);
3950
+ if (typeof ResizeObserver !== "undefined" && stage instanceof HTMLElement) {
3951
+ new ResizeObserver(fitDeck).observe(stage);
3952
+ }
3546
3953
  fitDeck();
3547
3954
  const initialState = readInitialState();
3548
3955
  show(initialState.index, initialState.stepIndex);
@@ -3553,109 +3960,6 @@ function escapeHtml$7(value) {
3553
3960
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
3554
3961
  }
3555
3962
  //#endregion
3556
- //#region src/renderer/presentation-style.ts
3557
- const PRESENTATION_DESIGN_WIDTH = 1920;
3558
- const PRESENTATION_DESIGN_HEIGHT = 1080;
3559
- function presentationDesignCssVariables() {
3560
- return `--hono-decks-width:${PRESENTATION_DESIGN_WIDTH}px;--hono-decks-height:${PRESENTATION_DESIGN_HEIGHT}px`;
3561
- }
3562
- function basePresentationStyle() {
3563
- return `
3564
- :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)}
3565
- html,body{margin:0;width:100%;height:100%;overflow:hidden}
3566
- .hono-decks-stage{width:100vw;height:100vh;overflow:hidden;position:relative;display:grid;place-items:center}
3567
- .hono-decks-deck{display:grid;gap:1rem;width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;transform-origin:left top}
3568
- .slide{box-sizing:border-box;aspect-ratio:16/9;padding:clamp(1.2rem,3vw,3rem);overflow:hidden}
3569
- .hono-decks-slide-content{width:100%;height:100%}
3570
- .slide.layout-cover,.slide.layout-statement{display:flex;flex-direction:column;justify-content:center}
3571
- .slide.layout-cover>.hono-decks-slide-content,.slide.layout-statement>.hono-decks-slide-content{display:flex;flex-direction:column;justify-content:center}
3572
- .slide code{font-family:"SFMono-Regular","Cascadia Code","Liberation Mono",Menlo,Consolas,monospace;font-size:.9em;line-height:1.45}
3573
- .slide :not(pre)>code{border-radius:6px;background:var(--hono-decks-inline-code-background);padding:.12em .34em}
3574
- .slide pre{max-width:100%;overflow:auto;box-sizing:border-box;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-code-background);padding:1rem;tab-size:2;white-space:pre}
3575
- .slide pre code{display:block;min-width:max-content;background:transparent;padding:0}
3576
- .slide table{width:100%;margin:1rem 0;border-collapse:collapse}
3577
- .slide th,.slide td{border:1px solid var(--hono-decks-border-color);padding:.4em .75em;text-align:left}
3578
- .slide th{background:var(--hono-decks-card-background);font-weight:700}
3579
- .slide del{color:var(--hono-decks-muted-color)}
3580
- .slide li.task-list-item{list-style:none;margin-left:-1.4em}
3581
- .slide li.task-list-item input[type=checkbox]{margin-right:.5em;vertical-align:middle}
3582
- .hono-decks-code-block{margin:1rem 0;max-width:100%}
3583
- .hono-decks-code-caption{display:inline-flex;margin:0 0 .4rem;border:1px solid var(--hono-decks-border-color);border-radius:6px;padding:.2rem .5rem;background:var(--hono-decks-inline-code-background);color:var(--hono-decks-muted-color);font-size:.82rem}
3584
- .hono-decks-embed-frame{margin:1rem 0;max-width:100%}
3585
- .hono-decks-embed-viewport{width:min(100%,72rem);overflow:hidden}
3586
- .hono-decks-embed-viewport iframe{display:block;width:100%;height:100%;border:0}
3587
- .hono-decks-embed-print-fallback{display:none}
3588
- .hono-decks-embed-print-poster{display:block;width:100%;height:100%;object-fit:cover}
3589
- .hono-decks-embed-fallback{margin:.45rem 0 0;color:var(--hono-decks-muted-color);font-size:.84rem}
3590
- .hono-decks-embed-fallback a{color:inherit}
3591
- .hono-decks-social-embed{margin:1rem 0;max-width:min(100%,42rem)}
3592
- .hono-decks-social-card{margin:0;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem}
3593
- .hono-decks-social-card p{margin:0 0 .75rem;line-height:1.55}
3594
- .hono-decks-social-card footer{display:flex;flex-wrap:wrap;gap:.65rem;align-items:center;color:var(--hono-decks-muted-color);font-size:.9rem}
3595
- .hono-decks-social-card a{color:inherit}
3596
- .hono-decks-tweet-embed{margin:1rem 0;max-width:min(100%,42rem)}
3597
- .hono-decks-tweet-embed .twitter-tweet{margin:0;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem}
3598
- .hono-decks-tweet-embed .twitter-tweet a{color:inherit}
3599
- .hono-decks-tweet-print-fallback{display:none}
3600
- .hono-decks-link-card{margin:1rem 0;max-width:min(100%,42rem)}
3601
- .hono-decks-link-card-anchor{display:grid;grid-template-columns:minmax(9rem,32%) minmax(0,1fr);gap:.75rem;align-items:stretch;border:1px solid var(--hono-decks-border-color);border-radius:8px;background:var(--hono-decks-card-background);padding:1rem;color:inherit;text-decoration:none}
3602
- .hono-decks-link-card-body{display:grid;gap:.35rem;min-width:0}
3603
- .hono-decks-link-card-image{width:100%;height:100%;max-height:10rem;aspect-ratio:16/9;object-fit:cover;border-radius:6px;background:var(--hono-decks-card-image-background)}
3604
- .hono-decks-link-card-site{color:var(--hono-decks-accent-color);font-size:.8rem;text-transform:uppercase}
3605
- .hono-decks-link-card-title{font-weight:700}
3606
- .hono-decks-link-card-description{color:var(--hono-decks-muted-color);line-height:1.45}
3607
- .hono-decks-link-card-label{color:var(--hono-decks-accent-color);font-size:.88rem}
3608
- @media (max-width: 640px){.hono-decks-link-card-anchor{grid-template-columns:1fr}.hono-decks-link-card-image{height:auto;max-height:12rem}}
3609
- .mdx-hero{height:100%;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,42%);gap:clamp(1rem,3vw,3rem);align-items:center}
3610
- .mdx-hero:not(.has-image){grid-template-columns:1fr}
3611
- .mdx-hero-copy{min-width:0}
3612
- .mdx-hero-eyebrow{margin:0 0 .75rem;color:var(--hono-decks-accent-color);text-transform:uppercase;font-size:.85rem;letter-spacing:0}
3613
- .mdx-hero h1{margin:0;font-size:clamp(2.2rem,5vw,5rem);line-height:1.02}
3614
- .mdx-hero-subtitle{margin:1rem 0 0;font-size:clamp(1rem,1.8vw,1.5rem);line-height:1.45;color:var(--hono-decks-muted-color)}
3615
- .mdx-hero-image{width:100%;height:auto;max-height:70vh;object-fit:contain;border-radius:8px}
3616
- [data-hono-decks-fire]{--fire-duration:.18s;--fire-easing:ease;--fire-opacity:0;--fire-transform:translateY(.35rem);--fire-filter:none;visibility:visible;opacity:1;transform:none;filter:none;transition:visibility 0s linear 0s,opacity var(--fire-duration) var(--fire-easing),transform var(--fire-duration) var(--fire-easing),filter var(--fire-duration) var(--fire-easing)}
3617
- [data-hono-decks-fire][data-fire-hidden]{visibility:hidden;opacity:var(--fire-opacity);transform:var(--fire-transform);filter:var(--fire-filter);transition-delay:var(--fire-duration),0s,0s,0s}
3618
- [data-fire-effect=none]{--fire-duration:0s;--fire-transform:none}
3619
- [data-fire-effect=fade]{--fire-transform:none}
3620
- [data-fire-effect=fade-up]{--fire-transform:translateY(.85rem)}
3621
- [data-fire-effect=scale]{--fire-transform:scale(.96)}
3622
- body:not([data-overview-mode]) .hono-decks-deck{position:relative}
3623
- body:not([data-overview-mode]) .slide{position:absolute;inset:0;width:100%;height:100%}
3624
- .slide[data-active-transition]{transition:opacity var(--hono-decks-active-transition-duration,var(--hono-decks-slide-transition-duration,var(--hono-decks-transition-duration))) var(--hono-decks-active-transition-easing,var(--hono-decks-slide-transition-easing,var(--hono-decks-transition-easing))),transform var(--hono-decks-active-transition-duration,var(--hono-decks-slide-transition-duration,var(--hono-decks-transition-duration))) var(--hono-decks-active-transition-easing,var(--hono-decks-slide-transition-easing,var(--hono-decks-transition-easing)));will-change:opacity,transform}
3625
- .slide[data-slide-state="inactive"]{visibility:hidden;pointer-events:none}
3626
- .slide[data-slide-state="active"]{visibility:visible;opacity:1;transform:translate3d(0,0,0) scale(1)}
3627
- .slide[data-active-transition="fade"][data-slide-state="entering"],.slide[data-active-transition="fade"][data-slide-state="leaving"],.slide[data-active-transition="view-transition"][data-slide-state="entering"],.slide[data-active-transition="view-transition"][data-slide-state="leaving"]{opacity:0}
3628
- .slide[data-active-transition="fade-out"][data-slide-state="entering"]{opacity:1}
3629
- .slide[data-active-transition="fade-out"][data-slide-state="leaving"]{opacity:0}
3630
- .slide[data-active-transition="slide-left"][data-slide-direction="forward"][data-slide-state="entering"],.slide[data-active-transition="slide-right"][data-slide-direction="backward"][data-slide-state="leaving"]{transform:translate3d(100%,0,0)}
3631
- .slide[data-active-transition="slide-left"][data-slide-direction="forward"][data-slide-state="leaving"],.slide[data-active-transition="slide-right"][data-slide-direction="backward"][data-slide-state="entering"]{transform:translate3d(-100%,0,0)}
3632
- .slide[data-active-transition="slide-left"][data-slide-direction="backward"][data-slide-state="entering"],.slide[data-active-transition="slide-right"][data-slide-direction="forward"][data-slide-state="leaving"]{transform:translate3d(-100%,0,0)}
3633
- .slide[data-active-transition="slide-left"][data-slide-direction="backward"][data-slide-state="leaving"],.slide[data-active-transition="slide-right"][data-slide-direction="forward"][data-slide-state="entering"]{transform:translate3d(100%,0,0)}
3634
- .slide[data-active-transition="slide-up"][data-slide-direction="forward"][data-slide-state="entering"],.slide[data-active-transition="slide-down"][data-slide-direction="backward"][data-slide-state="leaving"]{transform:translate3d(0,100%,0)}
3635
- .slide[data-active-transition="slide-up"][data-slide-direction="forward"][data-slide-state="leaving"],.slide[data-active-transition="slide-down"][data-slide-direction="backward"][data-slide-state="entering"]{transform:translate3d(0,-100%,0)}
3636
- .slide[data-active-transition="slide-up"][data-slide-direction="backward"][data-slide-state="entering"],.slide[data-active-transition="slide-down"][data-slide-direction="forward"][data-slide-state="leaving"]{transform:translate3d(0,-100%,0)}
3637
- .slide[data-active-transition="slide-up"][data-slide-direction="backward"][data-slide-state="leaving"],.slide[data-active-transition="slide-down"][data-slide-direction="forward"][data-slide-state="entering"]{transform:translate3d(0,100%,0)}
3638
- body:not([data-overview-mode]) .slide[hidden]{display:none}
3639
- body[data-overview-mode] .hono-decks-deck{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
3640
- body[data-overview-mode] .slide{cursor:pointer}
3641
- body[data-presenter-mode] .speaker-notes{display:block;margin-top:1rem;padding:.75rem;border-radius:8px;background:var(--hono-decks-card-image-background)}
3642
- .hono-decks-warnings{margin:1rem;padding:.75rem;border-radius:14px;background:var(--hono-decks-warning-background);color:var(--hono-decks-warning-color)}
3643
- @media screen{html[data-hono-decks-print-preview]{width:auto;height:auto;min-height:100%;overflow:visible}
3644
- body[data-hono-decks-print-preview]{min-height:100vh;overflow:auto;color-scheme:light;color:#000;--hono-decks-print-gap:6mm;--hono-decks-print-slot-height:80mm;--hono-decks-print-scale:.28}
3645
- body[data-hono-decks-print-preview] .hono-decks-stage{display:block;width:auto;height:auto;min-height:100vh;overflow:visible;padding:12mm 0;box-sizing:border-box}
3646
- body[data-hono-decks-print-preview] .hono-decks-deck{display:grid;grid-template-columns:1fr;grid-auto-rows:var(--hono-decks-print-slot-height);gap:var(--hono-decks-print-gap);width:calc(var(--hono-decks-print-slot-height) * 16 / 9);max-width:calc(100vw - 24px);height:auto;margin:0 auto;transform:none!important}
3647
- body[data-hono-decks-print-preview] .slide{position:static;width:100%;max-width:100%;height:var(--hono-decks-print-slot-height);aspect-ratio:16/9;justify-self:center;align-self:center;padding:0;box-shadow:0 2px 10px rgba(15,23,42,.16);transition:none!important;transform:none!important}
3648
- body[data-hono-decks-print-preview] .slide.layout-cover,body[data-hono-decks-print-preview] .slide.layout-statement{display:block}
3649
- body[data-hono-decks-print-preview] .hono-decks-slide-content{width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;padding:clamp(1.2rem,3vw,3rem);transform:scale(var(--hono-decks-print-scale));transform-origin:left top;overflow:hidden}
3650
- body[data-hono-decks-print-preview]:not([data-overview-mode]) .slide[hidden]{display:block!important}
3651
- body[data-hono-decks-print-preview] .slide[data-slide-state]{visibility:visible!important;opacity:1!important;transform:none!important}
3652
- body[data-hono-decks-print-preview] [data-hono-decks-fire]{visibility:visible!important;opacity:1!important;transform:none!important;filter:none!important}}
3653
- @page{size:A4 portrait;margin:12mm}
3654
- @media print{:root{color-scheme:light;color:#000;--hono-decks-print-gap:6mm;--hono-decks-print-slot-height:80mm;--hono-decks-print-scale:.28}html,body{width:auto;height:auto;overflow:visible}.hono-decks-stage{display:block;width:auto;height:auto;overflow:visible}.hono-decks-deck{display:grid;grid-template-columns:1fr;grid-auto-rows:var(--hono-decks-print-slot-height);gap:var(--hono-decks-print-gap);width:calc(var(--hono-decks-print-slot-height) * 16 / 9);height:auto;margin:0 auto;transform:none!important}body:not([data-overview-mode]) .slide{position:relative}.slide{position:relative;width:100%;max-width:100%;height:var(--hono-decks-print-slot-height);aspect-ratio:16/9;justify-self:center;align-self:center;padding:0;page-break-after:auto;break-after:auto;break-inside:avoid;box-shadow:none;transition:none!important;transform:none!important}.slide.layout-cover,.slide.layout-statement{display:block}.hono-decks-slide-content{position:absolute;inset:0 auto auto 0;width:var(--hono-decks-width);height:var(--hono-decks-height);box-sizing:border-box;padding:clamp(1.2rem,3vw,3rem);zoom:var(--hono-decks-print-scale);overflow:hidden}.slide:nth-of-type(3n):not(:last-child){page-break-after:always;break-after:page}body:not([data-overview-mode]) .slide[hidden]{display:block!important}.slide[data-slide-state]{visibility:visible!important;opacity:1!important;transform:none!important}[data-hono-decks-fire]{visibility:visible!important;opacity:1!important;transform:none!important;filter:none!important}}
3655
- @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}}
3656
- @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}}`;
3657
- }
3658
- //#endregion
3659
3963
  //#region src/renderer/presentation-page.ts
3660
3964
  function presentationPageTitle(deck) {
3661
3965
  return deck.meta.title ?? deck.slug;
@@ -3949,12 +4253,16 @@ function slideStyleAttribute(slide, assets) {
3949
4253
  if (slide.meta.transitionEasing) declarations.push(`--hono-decks-slide-transition-easing:${slide.meta.transitionEasing}`);
3950
4254
  return declarations.length ? ` style="${escapeHtml$5(declarations.join(";"))}"` : "";
3951
4255
  }
4256
+ function requireSlideHtml(slide) {
4257
+ if (typeof slide.html !== "string") throw new RenderError("Slide has no renderable body. Import createDecks() from the generated module; compileDecks() returns build information only.", "slide-body-missing");
4258
+ return slide.html;
4259
+ }
3952
4260
  async function renderSlideBodyAsync(slide, assets, input) {
3953
4261
  try {
3954
4262
  return slide.render ? await renderJsxValue(slide.render({ components: createMdxComponents(input.components ?? builtInSlideComponents, { assets }) })) : slide.nodes?.length ? await renderSlideNodesAsync(slide.nodes, {
3955
4263
  components: input.components,
3956
4264
  assets
3957
- }) : rewriteLocalAssetUrls(slide.html, assets);
4265
+ }) : rewriteLocalAssetUrls(requireSlideHtml(slide), assets);
3958
4266
  } catch (error) {
3959
4267
  if (error instanceof RenderError) throw error;
3960
4268
  throw new RenderError(`Render failed in ${input.deck?.sourcePath ?? "unknown deck"} slide ${slide.index + 1}: ${formatErrorMessage$1(error)}`, "slide-render-error", error);
@@ -3981,7 +4289,7 @@ function formatErrorMessage$1(error) {
3981
4289
  function serveDecksClientEntry(clientEntry, options = {}) {
3982
4290
  return (c) => {
3983
4291
  const headers = { "content-type": options.contentType ?? "text/javascript; charset=utf-8" };
3984
- const cacheControl = options.cacheControl ?? "public, max-age=300";
4292
+ const cacheControl = options.cacheControl ?? "no-cache";
3985
4293
  if (cacheControl !== false) headers["cache-control"] = cacheControl;
3986
4294
  return c.body(clientEntry, 200, headers);
3987
4295
  };
@@ -4275,6 +4583,7 @@ function renderViewerScript(nonce) {
4275
4583
  }
4276
4584
 
4277
4585
  function handleKeydown(event) {
4586
+ if (event.defaultPrevented) return;
4278
4587
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "p" && printPath) {
4279
4588
  event.preventDefault();
4280
4589
  const printUrl = new URL(printPath, window.location.href);
@@ -4282,6 +4591,10 @@ function renderViewerScript(nonce) {
4282
4591
  window.location.assign(printUrl);
4283
4592
  return;
4284
4593
  }
4594
+ if (event.metaKey || event.ctrlKey || event.altKey) return;
4595
+ const target = event.target;
4596
+ if (target instanceof HTMLElement && (target.isContentEditable || target.closest("input,textarea,select,button,a[href],[role=textbox],[role=button],[role=slider]"))) return;
4597
+ if (["ArrowRight", "ArrowLeft", " "].includes(event.key)) event.preventDefault();
4285
4598
  if (event.key === "ArrowRight" || event.key === " ") sendCommand("next");
4286
4599
  if (event.key === "ArrowLeft") sendCommand("previous");
4287
4600
  if (event.key === "f") void toggleViewerFullscreen();
@@ -4345,7 +4658,7 @@ function baseViewerStyle() {
4345
4658
  html,body{margin:0;width:100%;height:100%;min-height:100vh}
4346
4659
  @supports (height:100dvh){html,body{height:100dvh;min-height:100dvh}}
4347
4660
  body{overflow:hidden}
4348
- *:focus-visible{outline:none}
4661
+ *:focus-visible{outline:2px solid currentColor;outline-offset:2px}
4349
4662
  [data-hono-decks-viewer]{width:100%;height:100vh;min-height:0;display:grid;place-items:center;box-sizing:border-box;overflow:hidden}
4350
4663
  @supports (height:100dvh){[data-hono-decks-viewer]{height:100dvh}}
4351
4664
  .hono-decks-viewer-header{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@@ -4363,7 +4676,7 @@ ${viewerViewportRule()}
4363
4676
  .hono-decks-mobile-navigation:focus-visible{outline:2px solid currentColor;outline-offset:-4px}
4364
4677
  .hono-decks-mobile-navigation:active{background:rgba(15,23,42,.28);color:#eef2ff;opacity:1}
4365
4678
  .hono-decks-mobile-navigation .hono-decks-control-icon{width:22px;height:22px}
4366
- .hono-decks-viewport>[data-hono-decks-position]{position:absolute;left:50%;bottom:max(8px,env(safe-area-inset-bottom,0));z-index:3;transform:translateX(-50%);border:0;background:transparent;color:inherit;padding:0;font:inherit;font-size:12px;line-height:1;opacity:.38;pointer-events:none;white-space:nowrap}
4679
+ .hono-decks-viewport>[data-hono-decks-position]{position:absolute;left:50%;bottom:max(8px,env(safe-area-inset-bottom,0));z-index:3;transform:translateX(-50%);border:0;border-radius:4px;background:rgba(15,23,42,.9);color:#eef2ff;padding:4px 6px;font:inherit;font-size:12px;line-height:1;opacity:1;pointer-events:none;white-space:nowrap}
4367
4680
  .hono-decks-viewer-controls{position:absolute;left:50%;bottom:max(.25rem,env(safe-area-inset-bottom,0));display:flex;gap:8px;align-items:center;justify-content:center;max-width:calc(100% - 1rem);min-width:0;z-index:4;transform:translateX(-50%)}
4368
4681
  .hono-decks-viewer-controls [data-hono-decks-navigation-control="previous"],.hono-decks-viewer-controls [data-hono-decks-navigation-control="next"],.hono-decks-viewer-controls [data-hono-decks-position]{position:absolute;visibility:hidden;pointer-events:none}
4369
4682
  .hono-decks-viewer-controls button,.hono-decks-viewer-controls a,.hono-decks-viewer-controls span{border:1px solid rgba(148,163,184,.32);border-radius:8px;background:rgba(15,23,42,.78);color:inherit;padding:8px 10px;font:inherit;font-size:14px}
@@ -4380,7 +4693,7 @@ ${viewerViewportRule()}
4380
4693
  function embeddedViewerStyle() {
4381
4694
  const root = "[data-hono-decks-viewer][data-hono-decks-embed]";
4382
4695
  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}
4383
- ${root} *:focus-visible{outline:none}
4696
+ ${root} *:focus-visible{outline:2px solid currentColor;outline-offset:2px}
4384
4697
  ${root} .hono-decks-viewer-shell{position:relative;display:grid;grid-template-rows:minmax(0,1fr);place-items:center;width:100%;min-width:0;min-height:0;box-sizing:border-box}
4385
4698
  ${root} .hono-decks-viewer-stage{display:grid;place-items:center;width:100%;min-width:0;min-height:0;container-type:inline-size}
4386
4699
  ${root} .hono-decks-viewport{width:100%;aspect-ratio:${VIEWER_ASPECT_RATIO};position:relative;overflow:hidden;touch-action:pan-y}
@@ -4393,7 +4706,7 @@ ${root} .hono-decks-mobile-navigation-next{right:.25rem}
4393
4706
  ${root} .hono-decks-mobile-navigation:focus-visible{outline:2px solid currentColor;outline-offset:-4px}
4394
4707
  ${root} .hono-decks-mobile-navigation:active{background:rgba(15,23,42,.28);color:#eef2ff;opacity:1}
4395
4708
  ${root} .hono-decks-mobile-navigation .hono-decks-control-icon{width:22px;height:22px}
4396
- ${root} .hono-decks-viewport>[data-hono-decks-position]{position:absolute;left:50%;bottom:8px;z-index:3;transform:translateX(-50%);border:0;background:transparent;color:inherit;padding:0;font:inherit;font-size:12px;line-height:1;opacity:.5;pointer-events:none;white-space:nowrap}
4709
+ ${root} .hono-decks-viewport>[data-hono-decks-position]{position:absolute;left:50%;bottom:8px;z-index:3;transform:translateX(-50%);border:0;border-radius:4px;background:rgba(15,23,42,.9);color:#eef2ff;padding:4px 6px;font:inherit;font-size:12px;line-height:1;opacity:1;pointer-events:none;white-space:nowrap}
4397
4710
  ${root} .hono-decks-viewer-controls{position:absolute;left:50%;bottom:max(.25rem,env(safe-area-inset-bottom,0));display:flex;gap:8px;align-items:center;justify-content:center;max-width:calc(100% - 1rem);min-width:0;z-index:4;transform:translateX(-50%)}
4398
4711
  ${root} .hono-decks-viewer-controls [data-hono-decks-navigation-control="previous"],${root} .hono-decks-viewer-controls [data-hono-decks-navigation-control="next"],${root} .hono-decks-viewer-controls [data-hono-decks-position]{position:absolute;visibility:hidden;pointer-events:none}
4399
4712
  ${root} .hono-decks-viewer-controls button,${root} .hono-decks-viewer-controls a,${root} .hono-decks-viewer-controls span{border:1px solid rgba(148,163,184,.32);border-radius:8px;background:rgba(15,23,42,.78);color:inherit;padding:8px 10px;font:inherit;font-size:14px}
@@ -5034,7 +5347,7 @@ function decksRouter(options) {
5034
5347
  slug,
5035
5348
  surface: "render"
5036
5349
  }, 404);
5037
- const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
5350
+ const clientEntry = options.clientEntry ?? (deck.clientEntryRequired === false ? void 0 : resolveGeneratedClientEntryUrl(options, mountPath));
5038
5351
  try {
5039
5352
  const document = await resolveRouterDocument(c, options, "render", mountPath, deck);
5040
5353
  return c.html(await renderCompiledDeckPageAsync({
@@ -5139,7 +5452,7 @@ function decksRouter(options) {
5139
5452
  slug,
5140
5453
  surface: "presentation"
5141
5454
  }, 404);
5142
- const clientEntry = options.clientEntry ?? resolveGeneratedClientEntryUrl(options, mountPath);
5455
+ const clientEntry = options.clientEntry ?? (deck.clientEntryRequired === false ? void 0 : resolveGeneratedClientEntryUrl(options, mountPath));
5143
5456
  try {
5144
5457
  const document = await resolveRouterDocument(c, options, "presentation", mountPath, deck);
5145
5458
  return c.html(await renderCompiledDeckPageAsync({
@@ -5419,7 +5732,7 @@ const DEFAULT_DECKS_CONFIG_FILE = "hono-decks.config.ts";
5419
5732
  async function loadDecksConfig(input) {
5420
5733
  const path = resolve(input.cwd, input.configFile ?? "hono-decks.config.ts");
5421
5734
  if (!existsSync(path)) throw new Error(`Config file not found: ${input.configFile ?? "hono-decks.config.ts"}. Run \`hono-decks init\` first.`);
5422
- const source = (await build({
5735
+ const result = await build({
5423
5736
  absWorkingDir: input.cwd,
5424
5737
  entryPoints: [path],
5425
5738
  bundle: true,
@@ -5427,16 +5740,20 @@ async function loadDecksConfig(input) {
5427
5740
  platform: "node",
5428
5741
  target: "node20",
5429
5742
  write: false,
5743
+ metafile: true,
5430
5744
  logLevel: "silent",
5431
5745
  alias: { "hono-decks": resolveRuntimeEntry() }
5432
- })).outputFiles[0]?.text;
5746
+ });
5747
+ const source = result.outputFiles[0]?.text;
5433
5748
  if (!source) throw new Error(`Could not load config file: ${path}`);
5434
5749
  const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
5435
5750
  if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
5436
5751
  if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
5752
+ if (config.build?.markdown !== void 0 && config.build.markdown !== "void" && (typeof config.build.markdown !== "object" || !config.build.markdown || typeof config.build.markdown.adapter !== "string" || !config.build.markdown.adapter.trim())) throw new Error("Config build.markdown must be \"void\" or an adapter reference.");
5437
5753
  return {
5438
5754
  path,
5439
5755
  config,
5756
+ dependencies: Object.keys(result.metafile.inputs).map((inputPath) => resolve(input.cwd, inputPath)),
5440
5757
  root: config.build?.root ?? "decks",
5441
5758
  outDir: config.build?.outDir ?? "src/generated",
5442
5759
  ogpCacheFile: config.build?.ogpCacheFile