@deckspec/renderer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/compile-tsx.d.ts +19 -0
  2. package/dist/compile-tsx.d.ts.map +1 -0
  3. package/dist/compile-tsx.js +101 -0
  4. package/dist/compile-tsx.js.map +1 -0
  5. package/dist/dashboard-css.d.ts +2 -0
  6. package/dist/dashboard-css.d.ts.map +1 -0
  7. package/dist/dashboard-css.js +419 -0
  8. package/dist/dashboard-css.js.map +1 -0
  9. package/dist/dashboard-js.d.ts +2 -0
  10. package/dist/dashboard-js.d.ts.map +1 -0
  11. package/dist/dashboard-js.js +15 -0
  12. package/dist/dashboard-js.js.map +1 -0
  13. package/dist/index.d.ts +8 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +8 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/render-dashboard.d.ts +35 -0
  18. package/dist/render-dashboard.d.ts.map +1 -0
  19. package/dist/render-dashboard.js +96 -0
  20. package/dist/render-dashboard.js.map +1 -0
  21. package/dist/render-deck.d.ts +17 -0
  22. package/dist/render-deck.d.ts.map +1 -0
  23. package/dist/render-deck.js +117 -0
  24. package/dist/render-deck.js.map +1 -0
  25. package/dist/render-slide.d.ts +15 -0
  26. package/dist/render-slide.d.ts.map +1 -0
  27. package/dist/render-slide.js +32 -0
  28. package/dist/render-slide.js.map +1 -0
  29. package/dist/render-theme-detail.d.ts +18 -0
  30. package/dist/render-theme-detail.d.ts.map +1 -0
  31. package/dist/render-theme-detail.js +104 -0
  32. package/dist/render-theme-detail.js.map +1 -0
  33. package/dist/resolve-assets.d.ts +10 -0
  34. package/dist/resolve-assets.d.ts.map +1 -0
  35. package/dist/resolve-assets.js +48 -0
  36. package/dist/resolve-assets.js.map +1 -0
  37. package/dist/theme.d.ts +63 -0
  38. package/dist/theme.d.ts.map +1 -0
  39. package/dist/theme.js +52 -0
  40. package/dist/theme.js.map +1 -0
  41. package/dist/viewer-css.d.ts +11 -0
  42. package/dist/viewer-css.d.ts.map +1 -0
  43. package/dist/viewer-css.js +281 -0
  44. package/dist/viewer-css.js.map +1 -0
  45. package/dist/viewer-js.d.ts +13 -0
  46. package/dist/viewer-js.d.ts.map +1 -0
  47. package/dist/viewer-js.js +233 -0
  48. package/dist/viewer-js.js.map +1 -0
  49. package/package.json +7 -3
  50. package/tsconfig.json +0 -14
@@ -0,0 +1,96 @@
1
+ import { dashboardCSS } from "./dashboard-css.js";
2
+ import { dashboardJS } from "./dashboard-js.js";
3
+ function escapeHtml(text) {
4
+ return text
5
+ .replace(/&/g, "&")
6
+ .replace(/</g, "&lt;")
7
+ .replace(/>/g, "&gt;")
8
+ .replace(/"/g, "&quot;");
9
+ }
10
+ function renderThemeRow(theme) {
11
+ const colorDots = theme.colors
12
+ .map((c) => `<span class="color-dot" style="background:${escapeHtml(c.hex)}" title="${escapeHtml(c.name)}: ${escapeHtml(c.hex)}"></span>`)
13
+ .join("");
14
+ return `<a class="list-row list-row-link" href="/theme/${escapeHtml(theme.name)}">
15
+ <div class="list-row-left">
16
+ <div class="color-dots">${colorDots}</div>
17
+ <div class="list-row-text">
18
+ <span class="list-row-name">${escapeHtml(theme.displayName)}</span>
19
+ <span class="list-row-meta">${theme.patternCount} patterns</span>
20
+ </div>
21
+ </div>
22
+ <span class="list-row-arrow">›</span>
23
+ </a>`;
24
+ }
25
+ function renderDeckRow(deck) {
26
+ const { summary } = deck;
27
+ const deckName = summary.relativePath
28
+ .replace(/^decks\//, "")
29
+ .replace(/\/deck\.yaml$/, "");
30
+ const themeName = escapeHtml(summary.meta.theme);
31
+ const approved = summary.approvedCount;
32
+ const total = summary.slideCount;
33
+ // First slide thumbnail
34
+ const firstSlide = deck.slidePreviews[0];
35
+ const thumbHtml = firstSlide
36
+ ? `<div class="deck-thumb"><div class="deck-thumb-inner">${firstSlide.html}</div></div>`
37
+ : `<div class="deck-thumb deck-thumb-empty"></div>`;
38
+ // Format mtime
39
+ const dateStr = deck.mtime
40
+ ? ` · ${new Date(deck.mtime).toLocaleDateString("ja-JP", { year: "numeric", month: "2-digit", day: "2-digit" })}`
41
+ : "";
42
+ return `<a class="list-row list-row-link" href="/deck/${escapeHtml(deckName)}">
43
+ <div class="list-row-left">
44
+ ${thumbHtml}
45
+ <div class="list-row-text">
46
+ <span class="list-row-name">${escapeHtml(summary.meta.title)}</span>
47
+ <span class="list-row-meta">${themeName} · ${total} slides${dateStr}</span>
48
+ </div>
49
+ </div>
50
+ <div class="list-row-right">
51
+ <span class="list-row-arrow">›</span>
52
+ </div>
53
+ </a>`;
54
+ }
55
+ /**
56
+ * Renders the dashboard as a standalone HTML page.
57
+ */
58
+ export function renderDashboard(decks, options = { mode: "static" }, themes = []) {
59
+ const themesHtml = themes.length > 0
60
+ ? themes.map((t) => renderThemeRow(t)).join("\n")
61
+ : `<div class="empty-state">No themes found.</div>`;
62
+ const decksHtml = decks.length > 0
63
+ ? decks.map((d) => renderDeckRow(d)).join("\n")
64
+ : `<div class="empty-state">No decks found. Create a deck.yaml to get started.</div>`;
65
+ return `<!DOCTYPE html>
66
+ <html lang="ja">
67
+ <head>
68
+ <meta charset="utf-8">
69
+ <meta name="viewport" content="width=device-width, initial-scale=1">
70
+ <title>DeckSpec</title>
71
+ <style>
72
+ ${options.themeCSS ?? ""}
73
+ ${dashboardCSS}
74
+ </style>
75
+ </head>
76
+ <body>
77
+ <div class="dashboard">
78
+ <h1 class="page-title">DeckSpec</h1>
79
+
80
+ <section class="section">
81
+ <h2 class="section-title">Themes</h2>
82
+ <div class="list-group">${themesHtml}</div>
83
+ </section>
84
+
85
+ <section class="section">
86
+ <h2 class="section-title">Decks</h2>
87
+ <div class="list-group">${decksHtml}</div>
88
+ </section>
89
+ </div>
90
+ <script>
91
+ ${dashboardJS}
92
+ </script>
93
+ </body>
94
+ </html>`;
95
+ }
96
+ //# sourceMappingURL=render-dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-dashboard.js","sourceRoot":"","sources":["../src/render-dashboard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA+BhD,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,cAAc,CAAC,KAAmB;IACzC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM;SAC3B,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,6CAA6C,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAChI;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,kDAAkD,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;;8BAEnD,SAAS;;oCAEH,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC;oCAC7B,KAAK,CAAC,YAAY;;;;KAIjD,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,IAAsB;IAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY;SAClC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAEjC,wBAAwB;IACxB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,yDAAyD,UAAU,CAAC,IAAI,cAAc;QACxF,CAAC,CAAC,iDAAiD,CAAC;IAEtD,eAAe;IACf,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK;QACxB,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE;QACjH,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,iDAAiD,UAAU,CAAC,QAAQ,CAAC;;MAExE,SAAS;;oCAEqB,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;oCAC9B,SAAS,MAAM,KAAK,UAAU,OAAO;;;;;;KAMpE,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAyB,EACzB,UAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAC9C,SAAyB,EAAE;IAE3B,MAAM,UAAU,GACd,MAAM,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACjD,CAAC,CAAC,iDAAiD,CAAC;IAExD,MAAM,SAAS,GACb,KAAK,CAAC,MAAM,GAAG,CAAC;QACd,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/C,CAAC,CAAC,mFAAmF,CAAC;IAE1F,OAAO;;;;;;;EAOP,OAAO,CAAC,QAAQ,IAAI,EAAE;EACtB,YAAY;;;;;;;;;8BASgB,UAAU;;;;;8BAKV,SAAS;;;;EAIrC,WAAW;;;QAGL,CAAC;AACT,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Deck } from "@deckspec/schema";
2
+ import { type RenderSlideContext } from "./render-slide.js";
3
+ /**
4
+ * Renders a full deck to a standalone HTML document string with an
5
+ * integrated slide viewer.
6
+ *
7
+ * The viewer provides:
8
+ * - Single-slide display with viewport-fit scaling (default)
9
+ * - Maximum option (M): on = fill viewport, off = cap at 1200px
10
+ * - Grid option (G): thumbnail overview, click to jump
11
+ * - Fullscreen (F): always scales slide to fill screen
12
+ * - Keyboard navigation: ←/→, Space, Home/End, Esc
13
+ * - Floating nav bar with auto-hide
14
+ * - URL params: ?page=N&maximum=off&grid=on
15
+ */
16
+ export declare function renderDeck(deck: Deck, themeCSS: string, context?: RenderSlideContext): Promise<string>;
17
+ //# sourceMappingURL=render-deck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-deck.d.ts","sourceRoot":"","sources":["../src/render-deck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAMzE;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,CA4FjB"}
@@ -0,0 +1,117 @@
1
+ import { resolveSlideFile } from "@deckspec/dsl";
2
+ import { renderSlide } from "./render-slide.js";
3
+ import { viewerCSS } from "./viewer-css.js";
4
+ import { viewerJS } from "./viewer-js.js";
5
+ import { readFile } from "node:fs/promises";
6
+ import { dirname, join } from "node:path";
7
+ /**
8
+ * Renders a full deck to a standalone HTML document string with an
9
+ * integrated slide viewer.
10
+ *
11
+ * The viewer provides:
12
+ * - Single-slide display with viewport-fit scaling (default)
13
+ * - Maximum option (M): on = fill viewport, off = cap at 1200px
14
+ * - Grid option (G): thumbnail overview, click to jump
15
+ * - Fullscreen (F): always scales slide to fill screen
16
+ * - Keyboard navigation: ←/→, Space, Home/End, Esc
17
+ * - Floating nav bar with auto-hide
18
+ * - URL params: ?page=N&maximum=off&grid=on
19
+ */
20
+ export async function renderDeck(deck, themeCSS, context) {
21
+ const total = deck.slides.length;
22
+ // Collect pattern-specific CSS
23
+ let patternCSS = "";
24
+ if (context) {
25
+ const seenPatterns = new Set();
26
+ for (const slide of deck.slides) {
27
+ const ext = slide.file.includes(".") ? slide.file.split(".").pop() : null;
28
+ if (!ext) {
29
+ // Pattern slide — try to load style.css
30
+ const patternName = slide.file;
31
+ if (!seenPatterns.has(patternName)) {
32
+ seenPatterns.add(patternName);
33
+ try {
34
+ // Resolve to find the actual pattern location (deck-local or theme)
35
+ const resolved = await resolveSlideFile(patternName, context.basePath, context.patternsDir);
36
+ let cssPath;
37
+ if (resolved.tsx || resolved.path.includes("/patterns/")) {
38
+ // Deck-local or theme source — style.css is next to the pattern source
39
+ cssPath = join(dirname(resolved.path), "style.css");
40
+ }
41
+ else {
42
+ const cssSrcDir = context.patternsSrcDir ?? context.patternsDir;
43
+ cssPath = join(cssSrcDir, patternName, "style.css");
44
+ }
45
+ const css = await readFile(cssPath, "utf-8");
46
+ patternCSS += `\n/* pattern: ${patternName} */\n${css}`;
47
+ }
48
+ catch {
49
+ // No style.css for this pattern — that's fine
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ const slideHtmlParts = [];
56
+ for (let index = 0; index < deck.slides.length; index++) {
57
+ const slide = deck.slides[index];
58
+ const state = slide.state ?? "generated";
59
+ let html;
60
+ if (context) {
61
+ html = await renderSlide(slide, context);
62
+ }
63
+ else {
64
+ // Fallback: no context, can't resolve files
65
+ html = `<div class="slide slide-center"><p>Cannot render: no context</p></div>`;
66
+ }
67
+ slideHtmlParts.push(`<div class="slide-outer" data-slide-index="${index}" data-state="${escapeHtml(state)}">${html}</div>`);
68
+ }
69
+ const slidesHtml = slideHtmlParts.join("\n");
70
+ const navHtml = `
71
+ <nav class="nav-controls" id="nav-controls">
72
+ <a href="/" class="nav-home" title="Home">←</a>
73
+ <span class="divider"></span>
74
+ <button id="btn-prev" title="Previous (\u2190)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
75
+ <span class="slide-counter"><span id="current-slide">1</span> / ${total}</span>
76
+ <button id="btn-next" title="Next (\u2192)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg></button>
77
+ <span class="divider"></span>
78
+ <button id="btn-fullscreen" title="Fullscreen (F)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></svg></button>
79
+ <span class="divider"></span>
80
+ <button id="btn-more" title="More options"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg></button>
81
+ <div class="nav-menu" id="nav-menu">
82
+ <button id="btn-maximum"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></svg><span class="menu-label">Maximum off</span><span class="menu-key">M</span></button>
83
+ <button id="btn-grid"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg><span class="menu-label">Grid on</span><span class="menu-key">G</span></button>
84
+ </div>
85
+ </nav>`;
86
+ return `<!DOCTYPE html>
87
+ <html lang="ja">
88
+ <head>
89
+ <meta charset="utf-8">
90
+ <meta name="viewport" content="width=device-width, initial-scale=1">
91
+ <title>${escapeHtml(deck.meta.title)}</title>
92
+ <style>
93
+ ${themeCSS}
94
+ ${patternCSS}
95
+ ${viewerCSS}
96
+ </style>
97
+ </head>
98
+ <body class="maximum">
99
+ ${slidesHtml}
100
+ ${navHtml}
101
+ <script>
102
+ ${viewerJS}
103
+ </script>
104
+ </body>
105
+ </html>`;
106
+ }
107
+ /**
108
+ * Escapes HTML special characters in a string.
109
+ */
110
+ function escapeHtml(text) {
111
+ return text
112
+ .replace(/&/g, "&amp;")
113
+ .replace(/</g, "&lt;")
114
+ .replace(/>/g, "&gt;")
115
+ .replace(/"/g, "&quot;");
116
+ }
117
+ //# sourceMappingURL=render-deck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-deck.js","sourceRoot":"","sources":["../src/render-deck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Slide } from "@deckspec/schema";
2
+ export interface RenderSlideContext {
3
+ basePath: string;
4
+ patternsDir: string;
5
+ patternsSrcDir?: string;
6
+ }
7
+ /**
8
+ * Renders a single slide to HTML string.
9
+ *
10
+ * - .html files: reads and returns content as-is (passthrough)
11
+ * - Pattern names: imports compiled module, validates vars with schema, SSR renders
12
+ * - .tsx patterns (deck-local): compiled on-the-fly via esbuild before import
13
+ */
14
+ export declare function renderSlide(slide: Slide, context: RenderSlideContext): Promise<string>;
15
+ //# sourceMappingURL=render-slide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-slide.d.ts","sourceRoot":"","sources":["../src/render-slide.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAK9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
@@ -0,0 +1,32 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createElement } from "react";
3
+ import { renderToStaticMarkup } from "react-dom/server";
4
+ import { resolveSlideFile } from "@deckspec/dsl";
5
+ import { resolveAssets } from "./resolve-assets.js";
6
+ import { compileTsxCached } from "./compile-tsx.js";
7
+ /**
8
+ * Renders a single slide to HTML string.
9
+ *
10
+ * - .html files: reads and returns content as-is (passthrough)
11
+ * - Pattern names: imports compiled module, validates vars with schema, SSR renders
12
+ * - .tsx patterns (deck-local): compiled on-the-fly via esbuild before import
13
+ */
14
+ export async function renderSlide(slide, context) {
15
+ const resolved = await resolveSlideFile(slide.file, context.basePath, context.patternsDir);
16
+ if (resolved.type === "html") {
17
+ return readFile(resolved.path, "utf-8");
18
+ }
19
+ // Pattern: import compiled module, validate vars, SSR
20
+ let modulePath = resolved.path;
21
+ if (resolved.tsx) {
22
+ modulePath = await compileTsxCached(resolved.path);
23
+ }
24
+ const mod = await import(modulePath);
25
+ let vars = slide.vars ?? {};
26
+ if (mod.assets && Array.isArray(mod.assets)) {
27
+ vars = await resolveAssets(vars, mod.assets, context.basePath);
28
+ }
29
+ const validated = mod.schema ? mod.schema.parse(vars) : vars;
30
+ return renderToStaticMarkup(createElement(mod.default, validated));
31
+ }
32
+ //# sourceMappingURL=render-slide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-slide.js","sourceRoot":"","sources":["../src/render-slide.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAQpD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAY,EACZ,OAA2B;IAE3B,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CACrC,KAAK,CAAC,IAAI,EACV,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,WAAW,CACpB,CAAC;IAEF,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC7B,OAAO,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,sDAAsD;IACtD,IAAI,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC/B,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;QACjB,UAAU,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IAC5B,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,OAAO,oBAAoB,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ThemeTokens } from "./theme.js";
2
+ export interface PatternInfo {
3
+ name: string;
4
+ /** SSR rendered HTML of first example (undefined if no examples.yaml) */
5
+ previewHtml?: string;
6
+ }
7
+ interface ThemeDetailOptions {
8
+ tokens: ThemeTokens;
9
+ /** Pattern info with optional previews */
10
+ patterns: PatternInfo[];
11
+ /** design.md raw content (first ~30 lines for description) */
12
+ designDescription: string;
13
+ /** Theme CSS to embed for pattern previews */
14
+ themeCSS?: string;
15
+ }
16
+ export declare function renderThemeDetail(options: ThemeDetailOptions): string;
17
+ export {};
18
+ //# sourceMappingURL=render-theme-detail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-theme-detail.d.ts","sourceRoot":"","sources":["../src/render-theme-detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAY9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,8DAA8D;IAC9D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAyBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAsFrE"}
@@ -0,0 +1,104 @@
1
+ import { dashboardCSS } from "./dashboard-css.js";
2
+ import { dashboardJS } from "./dashboard-js.js";
3
+ function escapeHtml(text) {
4
+ return text
5
+ .replace(/&/g, "&amp;")
6
+ .replace(/</g, "&lt;")
7
+ .replace(/>/g, "&gt;")
8
+ .replace(/"/g, "&quot;");
9
+ }
10
+ function renderColorSwatch(name, hex) {
11
+ return `<div class="color-swatch">
12
+ <div class="color-swatch-circle" style="background:${escapeHtml(hex)}"></div>
13
+ <div class="color-swatch-info">
14
+ <span class="color-swatch-name">${escapeHtml(name)}</span>
15
+ <span class="color-swatch-hex">${escapeHtml(hex)}</span>
16
+ </div>
17
+ </div>`;
18
+ }
19
+ function renderTypographySample(label, fontFamily, size, sample) {
20
+ return `<div class="typo-row">
21
+ <span class="typo-label">${escapeHtml(label)}</span>
22
+ <span class="typo-meta">${escapeHtml(fontFamily)} ${escapeHtml(size)}</span>
23
+ <span class="typo-sample" style="font-family:'${escapeHtml(fontFamily)}',sans-serif;font-size:${escapeHtml(size)}">${escapeHtml(sample)}</span>
24
+ </div>`;
25
+ }
26
+ export function renderThemeDetail(options) {
27
+ const { tokens, patterns, designDescription, themeCSS } = options;
28
+ // Colors section
29
+ const colorEntries = Object.entries(tokens.colors);
30
+ const colorsHtml = colorEntries
31
+ .map(([name, hex]) => renderColorSwatch(name, hex))
32
+ .join("\n");
33
+ // Typography section
34
+ const typoHtml = [
35
+ renderTypographySample("h1", tokens.fonts.heading, "32px", "The quick brown fox"),
36
+ renderTypographySample("h2", tokens.fonts.heading, "24px", "The quick brown fox"),
37
+ renderTypographySample("h3", tokens.fonts.heading, "20px", "The quick brown fox"),
38
+ renderTypographySample("h4", tokens.fonts.heading, "18px", "The quick brown fox"),
39
+ renderTypographySample("body", tokens.fonts.body, "16px", "素早い茶色のキツネが怠惰な犬を飛び越える"),
40
+ ].join("\n");
41
+ // Spacing section
42
+ const spacingValues = ["8px", "16px", "24px", "32px", "48px", "64px"];
43
+ const spacingHtml = spacingValues
44
+ .map((s) => `<div class="spacing-item"><div class="spacing-bar" style="width:${s}"></div><span class="spacing-label">${s}</span></div>`)
45
+ .join("\n");
46
+ // Patterns section — grid cards with optional SSR thumbnails
47
+ const patternsHtml = patterns
48
+ .map((p) => {
49
+ const thumb = p.previewHtml
50
+ ? `<div class="pattern-thumb"><div class="pattern-thumb-inner">${p.previewHtml}</div></div>`
51
+ : `<div class="pattern-thumb pattern-thumb-empty"></div>`;
52
+ return `<div class="pattern-card">${thumb}<span class="pattern-card-name">${escapeHtml(p.name)}</span></div>`;
53
+ })
54
+ .join("\n");
55
+ // Description from design.md
56
+ const descHtml = designDescription
57
+ ? `<div class="theme-description">${escapeHtml(designDescription)}</div>`
58
+ : "";
59
+ return `<!DOCTYPE html>
60
+ <html lang="ja">
61
+ <head>
62
+ <meta charset="utf-8">
63
+ <meta name="viewport" content="width=device-width, initial-scale=1">
64
+ <title>${escapeHtml(tokens.displayName)} — Theme Detail</title>
65
+ <link rel="preconnect" href="https://fonts.googleapis.com">
66
+ <link href="https://fonts.googleapis.com/css2?family=${encodeURIComponent(tokens.fonts.heading)}:wght@400;600;700&display=swap" rel="stylesheet">
67
+ <style>
68
+ ${themeCSS ?? ""}
69
+ ${dashboardCSS}
70
+ </style>
71
+ </head>
72
+ <body>
73
+ <div class="dashboard">
74
+ <div class="page-nav"><a href="/">← Home</a></div>
75
+ <h1 class="page-title">${escapeHtml(tokens.displayName)}</h1>
76
+ ${descHtml}
77
+
78
+ <section class="section">
79
+ <h2 class="section-title">Colors</h2>
80
+ <div class="color-grid">${colorsHtml}</div>
81
+ </section>
82
+
83
+ <section class="section">
84
+ <h2 class="section-title">Typography</h2>
85
+ <div class="typo-list">${typoHtml}</div>
86
+ </section>
87
+
88
+ <section class="section">
89
+ <h2 class="section-title">Spacing</h2>
90
+ <div class="spacing-list">${spacingHtml}</div>
91
+ </section>
92
+
93
+ <section class="section">
94
+ <h2 class="section-title">Patterns <span class="count-badge">${patterns.length}</span></h2>
95
+ <div class="pattern-grid">${patternsHtml}</div>
96
+ </section>
97
+ </div>
98
+ <script>
99
+ ${dashboardJS}
100
+ </script>
101
+ </body>
102
+ </html>`;
103
+ }
104
+ //# sourceMappingURL=render-theme-detail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-theme-detail.js","sourceRoot":"","sources":["../src/render-theme-detail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface AssetFieldSpec {
2
+ field: string;
3
+ type: "svg" | "image";
4
+ }
5
+ export declare function resolveAssets(vars: Record<string, unknown>, assets: AssetFieldSpec[], basePath: string): Promise<Record<string, unknown>>;
6
+ /**
7
+ * Remove <script> tags and event handler attributes from SVG content.
8
+ */
9
+ export declare function sanitizeSvg(raw: string): string;
10
+ //# sourceMappingURL=resolve-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-assets.d.ts","sourceRoot":"","sources":["../src/resolve-assets.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;CACvB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiClC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C"}
@@ -0,0 +1,48 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ export async function resolveAssets(vars, assets, basePath) {
4
+ const resolved = { ...vars };
5
+ for (const spec of assets) {
6
+ const rawPath = resolved[spec.field];
7
+ if (typeof rawPath !== "string")
8
+ continue;
9
+ if (spec.type === "svg") {
10
+ const fullPath = resolve(basePath, rawPath);
11
+ const content = await readFile(fullPath, "utf-8");
12
+ resolved[spec.field] = sanitizeSvg(content);
13
+ }
14
+ else if (spec.type === "image") {
15
+ if (rawPath.startsWith("https://")) {
16
+ // External URL: keep as-is
17
+ resolved[spec.field] = rawPath;
18
+ }
19
+ else {
20
+ // Local file: base64 encode
21
+ const fullPath = resolve(basePath, rawPath);
22
+ const buffer = await readFile(fullPath);
23
+ const ext = rawPath.split(".").pop()?.toLowerCase() ?? "png";
24
+ const mimeMap = {
25
+ jpg: "image/jpeg",
26
+ jpeg: "image/jpeg",
27
+ png: "image/png",
28
+ webp: "image/webp",
29
+ gif: "image/gif",
30
+ svg: "image/svg+xml",
31
+ };
32
+ const mime = mimeMap[ext] ?? "application/octet-stream";
33
+ resolved[spec.field] = `data:${mime};base64,${buffer.toString("base64")}`;
34
+ }
35
+ }
36
+ }
37
+ return resolved;
38
+ }
39
+ /**
40
+ * Remove <script> tags and event handler attributes from SVG content.
41
+ */
42
+ export function sanitizeSvg(raw) {
43
+ return raw
44
+ .replace(/<script[\s\S]*?<\/script>/gi, "")
45
+ .replace(/\bon\w+\s*=\s*"[^"]*"/gi, "")
46
+ .replace(/\bon\w+\s*=\s*'[^']*'/gi, "");
47
+ }
48
+ //# sourceMappingURL=resolve-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-assets.js","sourceRoot":"","sources":["../src/resolve-assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAA6B,EAC7B,MAAwB,EACxB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,SAAS;QAE1C,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACjC,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACnC,2BAA2B;gBAC3B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,4BAA4B;gBAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC5C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACxC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;gBAC7D,MAAM,OAAO,GAA2B;oBACtC,GAAG,EAAE,YAAY;oBACjB,IAAI,EAAE,YAAY;oBAClB,GAAG,EAAE,WAAW;oBAChB,IAAI,EAAE,YAAY;oBAClB,GAAG,EAAE,WAAW;oBAChB,GAAG,EAAE,eAAe;iBACrB,CAAC;gBACF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;gBACxD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,QAAQ,IAAI,WAAW,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG;SACP,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,yBAAyB,EAAE,EAAE,CAAC;SACtC,OAAO,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Theme token structure matching themes/{name}/tokens.json.
3
+ */
4
+ export interface ThemeTokens {
5
+ name: string;
6
+ displayName: string;
7
+ description: string;
8
+ colors: {
9
+ background: string;
10
+ foreground: string;
11
+ primary: string;
12
+ muted: string;
13
+ "muted-foreground": string;
14
+ border: string;
15
+ accent: string;
16
+ "card-background"?: string;
17
+ };
18
+ fonts: {
19
+ heading: string;
20
+ body: string;
21
+ };
22
+ spacing: {
23
+ "slide-padding": string;
24
+ "slide-padding-x": string;
25
+ "slide-gap": string;
26
+ };
27
+ borderRadius: string;
28
+ slide: {
29
+ width: number;
30
+ height: number;
31
+ aspectRatio: string;
32
+ };
33
+ }
34
+ /**
35
+ * Loads and parses the tokens.json for a given theme.
36
+ *
37
+ * @param themeName - Theme directory name (e.g. "noir-display").
38
+ * @returns Parsed theme tokens.
39
+ */
40
+ export declare function loadThemeTokens(themeName: string): Promise<ThemeTokens>;
41
+ /**
42
+ * Loads the raw CSS string from a theme's globals.css.
43
+ *
44
+ * @param themeName - Theme directory name (e.g. "noir-display").
45
+ * @returns Raw CSS string.
46
+ */
47
+ export declare function loadThemeCSS(themeName: string): Promise<string>;
48
+ /**
49
+ * Resolves theme name → compiled patterns directory (dist/patterns/).
50
+ * Used for importing compiled .js pattern modules.
51
+ */
52
+ export declare function resolveThemePatternsDir(themeName: string): string;
53
+ /**
54
+ * Resolves theme name → source patterns directory (patterns/).
55
+ * Used for reading style.css and writing new patterns (lock command).
56
+ */
57
+ export declare function resolveThemePatternsSrcDir(themeName: string): string;
58
+ /**
59
+ * Extracts the theme name from raw parsed YAML.
60
+ * Defaults to "noir-display" if not specified.
61
+ */
62
+ export declare function extractThemeName(raw: unknown): string;
63
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,kBAAkB,EAAE,MAAM,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,EAAE;QACP,eAAe,EAAE,MAAM,CAAC;QACxB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AASD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAItB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAGrD"}
package/dist/theme.js ADDED
@@ -0,0 +1,52 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /**
4
+ * Resolves the path to a theme directory relative to the project root.
5
+ */
6
+ function themeDir(themeName) {
7
+ return join(process.cwd(), "themes", themeName);
8
+ }
9
+ /**
10
+ * Loads and parses the tokens.json for a given theme.
11
+ *
12
+ * @param themeName - Theme directory name (e.g. "noir-display").
13
+ * @returns Parsed theme tokens.
14
+ */
15
+ export async function loadThemeTokens(themeName) {
16
+ const filePath = join(themeDir(themeName), "tokens.json");
17
+ const raw = await readFile(filePath, "utf-8");
18
+ return JSON.parse(raw);
19
+ }
20
+ /**
21
+ * Loads the raw CSS string from a theme's globals.css.
22
+ *
23
+ * @param themeName - Theme directory name (e.g. "noir-display").
24
+ * @returns Raw CSS string.
25
+ */
26
+ export async function loadThemeCSS(themeName) {
27
+ const filePath = join(themeDir(themeName), "globals.css");
28
+ return readFile(filePath, "utf-8");
29
+ }
30
+ /**
31
+ * Resolves theme name → compiled patterns directory (dist/patterns/).
32
+ * Used for importing compiled .js pattern modules.
33
+ */
34
+ export function resolveThemePatternsDir(themeName) {
35
+ return join(themeDir(themeName), "dist", "patterns");
36
+ }
37
+ /**
38
+ * Resolves theme name → source patterns directory (patterns/).
39
+ * Used for reading style.css and writing new patterns (lock command).
40
+ */
41
+ export function resolveThemePatternsSrcDir(themeName) {
42
+ return join(themeDir(themeName), "patterns");
43
+ }
44
+ /**
45
+ * Extracts the theme name from raw parsed YAML.
46
+ * Defaults to "noir-display" if not specified.
47
+ */
48
+ export function extractThemeName(raw) {
49
+ const obj = raw;
50
+ return obj?.meta?.theme ?? "noir-display";
51
+ }
52
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoCjC;;GAEG;AACH,SAAS,QAAQ,CAAC,SAAiB;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB;IAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAiB;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1D,OAAO,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAiB;IAC1D,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,MAAM,GAAG,GAAG,GAAoC,CAAC;IACjD,OAAO,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,cAAc,CAAC;AAC5C,CAAC"}