@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.
- package/dist/compile-tsx.d.ts +19 -0
- package/dist/compile-tsx.d.ts.map +1 -0
- package/dist/compile-tsx.js +101 -0
- package/dist/compile-tsx.js.map +1 -0
- package/dist/dashboard-css.d.ts +2 -0
- package/dist/dashboard-css.d.ts.map +1 -0
- package/dist/dashboard-css.js +419 -0
- package/dist/dashboard-css.js.map +1 -0
- package/dist/dashboard-js.d.ts +2 -0
- package/dist/dashboard-js.d.ts.map +1 -0
- package/dist/dashboard-js.js +15 -0
- package/dist/dashboard-js.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/render-dashboard.d.ts +35 -0
- package/dist/render-dashboard.d.ts.map +1 -0
- package/dist/render-dashboard.js +96 -0
- package/dist/render-dashboard.js.map +1 -0
- package/dist/render-deck.d.ts +17 -0
- package/dist/render-deck.d.ts.map +1 -0
- package/dist/render-deck.js +117 -0
- package/dist/render-deck.js.map +1 -0
- package/dist/render-slide.d.ts +15 -0
- package/dist/render-slide.d.ts.map +1 -0
- package/dist/render-slide.js +32 -0
- package/dist/render-slide.js.map +1 -0
- package/dist/render-theme-detail.d.ts +18 -0
- package/dist/render-theme-detail.d.ts.map +1 -0
- package/dist/render-theme-detail.js +104 -0
- package/dist/render-theme-detail.js.map +1 -0
- package/dist/resolve-assets.d.ts +10 -0
- package/dist/resolve-assets.d.ts.map +1 -0
- package/dist/resolve-assets.js +48 -0
- package/dist/resolve-assets.js.map +1 -0
- package/dist/theme.d.ts +63 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +52 -0
- package/dist/theme.js.map +1 -0
- package/dist/viewer-css.d.ts +11 -0
- package/dist/viewer-css.d.ts.map +1 -0
- package/dist/viewer-css.js +281 -0
- package/dist/viewer-css.js.map +1 -0
- package/dist/viewer-js.d.ts +13 -0
- package/dist/viewer-js.d.ts.map +1 -0
- package/dist/viewer-js.js +233 -0
- package/dist/viewer-js.js.map +1 -0
- package/package.json +7 -3
- 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, "<")
|
|
7
|
+
.replace(/>/g, ">")
|
|
8
|
+
.replace(/"/g, """);
|
|
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":"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"}
|
|
@@ -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, "&")
|
|
113
|
+
.replace(/</g, "<")
|
|
114
|
+
.replace(/>/g, ">")
|
|
115
|
+
.replace(/"/g, """);
|
|
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, "&")
|
|
6
|
+
.replace(/</g, "<")
|
|
7
|
+
.replace(/>/g, ">")
|
|
8
|
+
.replace(/"/g, """);
|
|
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"}
|
package/dist/theme.d.ts
ADDED
|
@@ -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"}
|