diff --git a/dist-cli/build.d.ts b/dist-cli/build.d.ts
new file mode 100644
index 0000000..0b172b7
--- /dev/null
+++ b/dist-cli/build.d.ts
@@ -0,0 +1 @@
+import 'dotenv/config';
diff --git a/dist-cli/build.js b/dist-cli/build.js
new file mode 100644
index 0000000..04794f8
--- /dev/null
+++ b/dist-cli/build.js
@@ -0,0 +1,129 @@
+import 'dotenv/config';
+import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { checkProject, printIssues } from "./check.js";
+import { joinBase, normalizeSiteBase } from "./lib/site-base.js";
+import { auditBuiltDecks } from "./lib/overflow-audit.js";
+import { formatOverflowAuditResult } from "./lib/overflow-report.js";
+import { runSlidev } from "./lib/run-slidev.js";
+import { resolveTheme } from "./lib/theme.js";
+const cwd = process.cwd();
+const siteBase = normalizeSiteBase(process.env.SITE_BASE);
+let config;
+let dist;
+async function main() {
+ const result = await checkProject(cwd);
+ config = result.config;
+ dist = path.join(cwd, config.outDir);
+ const { decks, issues } = result;
+ printIssues(issues, cwd);
+ if (issues.some(issue => issue.level === 'error'))
+ throw new Error('内容检查失败,已停止构建');
+ const published = decks.filter(deck => !deck.draft);
+ await rm(dist, { recursive: true, force: true });
+ await mkdir(dist, { recursive: true });
+ for (const deck of published)
+ await buildDeck(deck);
+ await reportSlideOverflow(published);
+ await writeFile(path.join(dist, 'index.html'), renderIndex(published), 'utf8');
+ await writeFile(path.join(dist, '_headers'), renderHeaders(), 'utf8');
+ await writeFile(path.join(dist, '_redirects'), renderRedirects(published), 'utf8');
+ console.log(`\n构建完成:${published.length} 套演示 → ${path.relative(cwd, dist)}`);
+}
+async function buildDeck(deck) {
+ const output = path.join(dist, deck.slug);
+ const base = joinBase(siteBase, deck.slug);
+ console.log(`\nBuilding ${deck.slug} (${base})`);
+ await runSlidev([
+ 'build',
+ deck.entry,
+ '--theme',
+ resolveTheme(config.theme),
+ '--out',
+ output,
+ '--base',
+ base,
+ '--router-mode',
+ 'hash',
+ ]);
+ const assets = path.join(deck.directory, 'assets');
+ await cp(assets, path.join(output, 'assets'), { recursive: true, force: true }).catch(() => undefined);
+ await copySpaEntry(output, 'presenter');
+ await copySpaEntry(output, 'overview');
+ await copySpaEntry(output, 'notes-edit');
+}
+async function copySpaEntry(output, route) {
+ const html = await readFile(path.join(output, 'index.html'), 'utf8');
+ const routeBootstrap = ``;
+ const routedHtml = html.replace('', `${routeBootstrap}`);
+ const directory = path.join(output, route);
+ await mkdir(directory, { recursive: true });
+ await writeFile(path.join(directory, 'index.html'), routedHtml, 'utf8');
+}
+async function reportSlideOverflow(decks) {
+ if (!decks.length)
+ return;
+ console.log('\n检查编译后的幻灯片内容边界...');
+ const result = await auditBuiltDecks({ root: dist, decks, siteBase });
+ formatOverflowAuditResult(result, cwd).forEach(line => console.log(line));
+}
+function renderIndex(decks) {
+ const cards = decks.map((deck) => {
+ const href = joinBase(siteBase, deck.slug);
+ const cover = deck.cover
+ ? `
`
+ : `
${escapeHtml(deck.title.slice(0, 1))}
`;
+ const tags = deck.tags.map(tag => `${escapeHtml(tag)}`).join('');
+ return `
+ ${cover}
+
+
${escapeHtml(deck.date || 'Undated')}${deck.author ? ` · ${escapeHtml(deck.author)}` : ''}
+
+ ${deck.description ? `
${escapeHtml(deck.description)}
` : ''}
+ ${tags ? `
` : ''}
+
+ `;
+ }).join('\n');
+ return `
+
+
+
+
+
+ ${escapeHtml(config.title)}
+
+
+
+
+
+
+ ${cards || '暂无公开演示。
'}
+
+
+
+`;
+}
+function landingStyles() {
+ return `:root{font-family:Inter,"Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;color:#222;background:#f6f5f2}*{box-sizing:border-box}body{margin:0}main{width:min(1120px,calc(100% - 40px));margin:auto;padding:72px 0 96px}header{border-bottom:2px solid #1d4ed8;padding-bottom:28px;margin-bottom:36px}.eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;color:#1d4ed8}h1{font-size:clamp(48px,8vw,82px);line-height:1;margin:14px 0}.lede{font-size:clamp(19px,2.2vw,26px);color:#555;margin:0}.deck-list{display:grid;gap:28px}.deck-card{display:grid;grid-template-columns:minmax(220px,36%) 1fr;background:#fff;border:1px solid #d9d7d2;border-radius:10px;overflow:hidden;box-shadow:0 12px 36px rgba(30,64,175,.08)}.cover{display:block;min-height:230px;background:#eae7e2}.cover img{display:block;width:100%;height:100%;min-height:230px;object-fit:cover}.cover-fallback{height:100%;min-height:230px;display:grid;place-items:center;background:linear-gradient(135deg,#eff6ff,#dbeafe);color:#1d4ed8;font-size:92px;font-weight:700}.deck-copy{padding:32px}.meta{font-size:13px;color:#777;margin:0 0 10px}h2{font-size:clamp(26px,3vw,38px);line-height:1.15;margin:0}h2 a{color:inherit;text-decoration:none}h2 a:hover{color:#1d4ed8}.description{font-size:17px;line-height:1.7;color:#555}.tags{display:flex;gap:8px;flex-wrap:wrap;list-style:none;padding:0;margin:24px 0 0}.tags li{font-size:12px;background:#eff6ff;color:#1d4ed8;padding:5px 9px;border-radius:999px}@media(max-width:680px){main{padding-top:44px}.deck-card{grid-template-columns:1fr}.cover,.cover img,.cover-fallback{min-height:190px}.deck-copy{padding:24px}}`;
+}
+function renderHeaders() {
+ return `/*.html\n Cache-Control: public, max-age=0, must-revalidate\n\n/*/assets/*\n Cache-Control: public, max-age=31536000, immutable\n`;
+}
+function renderRedirects(decks) {
+ return decks.map(deck => `${joinBase(siteBase, deck.slug)}* ${joinBase(siteBase, deck.slug)}index.html 200`).join('\n');
+}
+function escapeHtml(value) {
+ return value.replace(/[&<>"']/g, character => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character]);
+}
+function escapeAttribute(value) {
+ return escapeHtml(value);
+}
+await main().catch((error) => {
+ console.error(error instanceof Error ? error.message : error);
+ process.exitCode = 1;
+});
+//# sourceMappingURL=build.js.map
\ No newline at end of file
diff --git a/dist-cli/build.js.map b/dist-cli/build.js.map
new file mode 100644
index 0000000..0d66892
--- /dev/null
+++ b/dist-cli/build.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"build.js","sourceRoot":"","sources":["../scripts/build.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/check.d.ts b/dist-cli/check.d.ts
new file mode 100644
index 0000000..40ba78d
--- /dev/null
+++ b/dist-cli/check.d.ts
@@ -0,0 +1,11 @@
+import 'dotenv/config';
+import { type ValidationIssue } from './lib/content.ts';
+import { type EasySlidesConfig } from './lib/config.ts';
+export declare function checkProject(cwd?: string, providedConfig?: EasySlidesConfig): Promise<{
+ config: EasySlidesConfig;
+ decks: import("./lib/content.ts").DeckMetadata[];
+ issues: ValidationIssue[];
+ staticOverflow: import("./lib/static-overflow.ts").StaticOverflowResult;
+}>;
+export declare function printIssues(issues: ValidationIssue[], cwd?: string): void;
+export declare function runCheck(): Promise;
diff --git a/dist-cli/check.js b/dist-cli/check.js
new file mode 100644
index 0000000..6fe3784
--- /dev/null
+++ b/dist-cli/check.js
@@ -0,0 +1,49 @@
+import 'dotenv/config';
+import path from 'node:path';
+import { discoverDecks, validateDeck } from "./lib/content.js";
+import { loadProjectConfig } from "./lib/config.js";
+import { formatStaticOverflowResult } from "./lib/overflow-report.js";
+import { auditStaticDecks } from "./lib/static-overflow.js";
+import { hasPresenterToken } from "./lib/token.js";
+export async function checkProject(cwd = process.cwd(), providedConfig) {
+ const config = providedConfig ?? await loadProjectConfig(cwd);
+ const decks = await discoverDecks(cwd, config.slidesDir);
+ const issues = [];
+ if (decks.length === 0)
+ issues.push({ level: 'error', file: path.join(cwd, config.slidesDir), message: `没有发现 ${config.slidesDir}//slides.md` });
+ for (const deck of decks)
+ issues.push(...await validateDeck(deck));
+ const staticOverflow = await auditStaticDecks(decks);
+ if (!hasPresenterToken()) {
+ issues.push({
+ level: 'warning',
+ file: path.join(cwd, '.env'),
+ message: '未设置 PRESENTER_TOKEN;生产构建将隐藏演示者入口',
+ });
+ }
+ return { config, decks, issues, staticOverflow };
+}
+export function printIssues(issues, cwd = process.cwd()) {
+ for (const issue of issues) {
+ const marker = issue.level === 'error' ? 'ERROR' : 'WARN ';
+ console.log(`${marker} ${path.relative(cwd, issue.file)}: ${issue.message}`);
+ }
+}
+export async function runCheck() {
+ const result = await checkProject();
+ printIssues(result.issues);
+ formatStaticOverflowResult(result.staticOverflow).forEach(line => console.log(line));
+ const errors = result.issues.filter(issue => issue.level === 'error');
+ if (errors.length > 0) {
+ console.error(`\n检查失败:${errors.length} 个错误`);
+ process.exitCode = 1;
+ return;
+ }
+ const warnings = result.issues.filter(issue => issue.level === 'warning').length
+ + result.staticOverflow.issues.length
+ + result.staticOverflow.errors.length;
+ console.log(`\n检查通过:${result.decks.length} 套演示,${warnings} 个警告`);
+}
+if (import.meta.url === new URL(process.argv[1], 'file:').href)
+ await runCheck();
+//# sourceMappingURL=check.js.map
\ No newline at end of file
diff --git a/dist-cli/check.js.map b/dist-cli/check.js.map
new file mode 100644
index 0000000..80e87d1
--- /dev/null
+++ b/dist-cli/check.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"check.js","sourceRoot":"","sources":["../scripts/check.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/cli.d.ts b/dist-cli/cli.d.ts
new file mode 100644
index 0000000..b798801
--- /dev/null
+++ b/dist-cli/cli.d.ts
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+export {};
diff --git a/dist-cli/cli.js b/dist-cli/cli.js
new file mode 100755
index 0000000..a8a9f5e
--- /dev/null
+++ b/dist-cli/cli.js
@@ -0,0 +1,58 @@
+#!/usr/bin/env node
+import path from 'node:path';
+const rawArgs = process.argv.slice(2);
+const command = rawArgs.shift();
+const rootIndex = rawArgs.indexOf('--root');
+if (rootIndex >= 0) {
+ const root = rawArgs[rootIndex + 1];
+ if (!root)
+ throw new Error('--root 需要一个目录');
+ rawArgs.splice(rootIndex, 2);
+ process.chdir(path.resolve(root));
+}
+process.argv = [process.argv[0], process.argv[1], ...rawArgs];
+switch (command) {
+ case 'dev':
+ await import("./dev.js");
+ break;
+ case 'present':
+ await import("./present.js");
+ break;
+ case 'build':
+ await import("./build.js");
+ break;
+ case 'check':
+ await (await import("./check.js")).runCheck();
+ break;
+ case 'export':
+ await import("./export.js");
+ break;
+ case 'init':
+ await import("./init.js");
+ break;
+ case undefined:
+ case 'help':
+ case '--help':
+ case '-h':
+ printHelp();
+ break;
+ default:
+ console.error(`未知命令:${command}`);
+ printHelp();
+ process.exitCode = 1;
+}
+function printHelp() {
+ console.log(`easy-slides
+
+用法:
+ easy-slides init [directory] [--engine ]
+ easy-slides dev [slug|slides.md]
+ easy-slides present
+ easy-slides build
+ easy-slides check
+ easy-slides export --format pdf|png|pptx
+
+全局选项:
+ --root 在指定内容项目中运行`);
+}
+//# sourceMappingURL=cli.js.map
\ No newline at end of file
diff --git a/dist-cli/cli.js.map b/dist-cli/cli.js.map
new file mode 100644
index 0000000..9577c20
--- /dev/null
+++ b/dist-cli/cli.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"cli.js","sourceRoot":"","sources":["../scripts/cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACrC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAA;AAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;AAC3C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;IACnC,IAAI,CAAC,IAAI;QACP,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAA;IAClC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;IAC5B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;AACnC,CAAC;AACD,OAAO,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAA;AAE7D,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,KAAK;QACR,MAAM,MAAM,CAAC,UAAU,CAAC,CAAA;QACxB,MAAK;IACP,KAAK,SAAS;QACZ,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;QAC5B,MAAK;IACP,KAAK,OAAO;QACV,MAAM,MAAM,CAAC,YAAY,CAAC,CAAA;QAC1B,MAAK;IACP,KAAK,OAAO;QACV,MAAM,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC7C,MAAK;IACP,KAAK,QAAQ;QACX,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;QAC3B,MAAK;IACP,KAAK,MAAM;QACT,MAAM,MAAM,CAAC,WAAW,CAAC,CAAA;QACzB,MAAK;IACP,KAAK,SAAS,CAAC;IACf,KAAK,MAAM,CAAC;IACZ,KAAK,QAAQ,CAAC;IACd,KAAK,IAAI;QACP,SAAS,EAAE,CAAA;QACX,MAAK;IACP;QACE,OAAO,CAAC,KAAK,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAA;QAChC,SAAS,EAAE,CAAA;QACX,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;iCAWmB,CAAC,CAAA;AAClC,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/dev.d.ts b/dist-cli/dev.d.ts
new file mode 100644
index 0000000..0b172b7
--- /dev/null
+++ b/dist-cli/dev.d.ts
@@ -0,0 +1 @@
+import 'dotenv/config';
diff --git a/dist-cli/dev.js b/dist-cli/dev.js
new file mode 100644
index 0000000..7810885
--- /dev/null
+++ b/dist-cli/dev.js
@@ -0,0 +1,29 @@
+import 'dotenv/config';
+import { loadProjectConfig } from "./lib/config.js";
+import { resolveDeck } from "./lib/content.js";
+import { startDevOverflowAudit } from "./lib/dev-overflow-audit.js";
+import { startSlidev, waitForSlidev } from "./lib/run-slidev.js";
+import { resolveTheme } from "./lib/theme.js";
+const input = process.argv.slice(2).find(argument => !argument.startsWith('-'));
+const config = await loadProjectConfig();
+const deck = await resolveDeck(input, process.cwd(), config.slidesDir);
+const port = process.env.PORT || '3030';
+const slidev = startSlidev([
+ deck.entry,
+ '--theme',
+ resolveTheme(config.theme),
+ '--open',
+ '--port',
+ port,
+]);
+const overflowAudit = startDevOverflowAudit({
+ deck,
+});
+slidev.once('exit', () => void overflowAudit.close());
+try {
+ await waitForSlidev(slidev);
+}
+finally {
+ await overflowAudit.close();
+}
+//# sourceMappingURL=dev.js.map
\ No newline at end of file
diff --git a/dist-cli/dev.js.map b/dist-cli/dev.js.map
new file mode 100644
index 0000000..74ccb86
--- /dev/null
+++ b/dist-cli/dev.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"dev.js","sourceRoot":"","sources":["../scripts/dev.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AAC/E,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAA;AACxC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AACtE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAA;AACvC,MAAM,MAAM,GAAG,WAAW,CAAC;IACzB,IAAI,CAAC,KAAK;IACV,SAAS;IACT,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,QAAQ;IACR,QAAQ;IACR,IAAI;CACL,CAAC,CAAA;AACF,MAAM,aAAa,GAAG,qBAAqB,CAAC;IAC1C,IAAI;CACL,CAAC,CAAA;AACF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,aAAa,CAAC,KAAK,EAAE,CAAC,CAAA;AAErD,IAAI,CAAC;IACH,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;AAC7B,CAAC;QACO,CAAC;IACP,MAAM,aAAa,CAAC,KAAK,EAAE,CAAA;AAC7B,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/export.d.ts b/dist-cli/export.d.ts
new file mode 100644
index 0000000..0b172b7
--- /dev/null
+++ b/dist-cli/export.d.ts
@@ -0,0 +1 @@
+import 'dotenv/config';
diff --git a/dist-cli/export.js b/dist-cli/export.js
new file mode 100644
index 0000000..baf9e79
--- /dev/null
+++ b/dist-cli/export.js
@@ -0,0 +1,37 @@
+import 'dotenv/config';
+import { mkdir } from 'node:fs/promises';
+import path from 'node:path';
+import { loadProjectConfig } from "./lib/config.js";
+import { resolveDeck } from "./lib/content.js";
+import { runSlidev } from "./lib/run-slidev.js";
+import { resolveTheme } from "./lib/theme.js";
+const args = process.argv.slice(2);
+const slug = args.find(argument => !argument.startsWith('-'));
+const formatIndex = args.indexOf('--format');
+const format = formatIndex >= 0 ? args[formatIndex + 1] : 'pdf';
+const allowed = new Set(['pdf', 'png', 'pptx', 'md']);
+if (!allowed.has(format))
+ throw new Error(`不支持导出格式:${format}`);
+const config = await loadProjectConfig();
+const deck = await resolveDeck(slug, process.cwd(), config.slidesDir);
+const exportDir = path.join(process.cwd(), config.exportDir);
+await mkdir(exportDir, { recursive: true });
+const output = path.join(exportDir, `${deck.slug}.${format}`);
+const slidevArgs = [
+ 'export',
+ deck.entry,
+ '--theme',
+ resolveTheme(config.theme),
+ '--format',
+ format,
+ '--output',
+ output,
+];
+// Slidev's one-piece exporter may capture off-screen lazy images before they
+// are painted. Per-slide export renders each canvas as the active slide and is
+// reliable for Markdown images across PNG, PDF, and screenshot-based PPTX.
+if (format !== 'md')
+ slidevArgs.push('--per-slide');
+await runSlidev(slidevArgs);
+console.log(`导出完成:${path.relative(process.cwd(), output)}`);
+//# sourceMappingURL=export.js.map
\ No newline at end of file
diff --git a/dist-cli/export.js.map b/dist-cli/export.js.map
new file mode 100644
index 0000000..77dc6ca
--- /dev/null
+++ b/dist-cli/export.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"export.js","sourceRoot":"","sources":["../scripts/export.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AACtB,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAC5C,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AAErD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IACtB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,EAAE,CAAC,CAAA;AAEtC,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAA;AACxC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AACrE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AAC5D,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;AAE7D,MAAM,UAAU,GAAG;IACjB,QAAQ;IACR,IAAI,CAAC,KAAK;IACV,SAAS;IACT,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,UAAU;IACV,MAAM;IACN,UAAU;IACV,MAAM;CACP,CAAA;AAED,6EAA6E;AAC7E,+EAA+E;AAC/E,2EAA2E;AAC3E,IAAI,MAAM,KAAK,IAAI;IACjB,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;AAEhC,MAAM,SAAS,CAAC,UAAU,CAAC,CAAA;AAE3B,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA"}
\ No newline at end of file
diff --git a/dist-cli/index.d.ts b/dist-cli/index.d.ts
new file mode 100644
index 0000000..f41af26
--- /dev/null
+++ b/dist-cli/index.d.ts
@@ -0,0 +1,2 @@
+export { loadProjectConfig, normalizeConfig } from './lib/config.ts';
+export type { EasySlidesConfig } from './lib/config.ts';
diff --git a/dist-cli/index.js b/dist-cli/index.js
new file mode 100644
index 0000000..9b24c40
--- /dev/null
+++ b/dist-cli/index.js
@@ -0,0 +1,2 @@
+export { loadProjectConfig, normalizeConfig } from "./lib/config.js";
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/dist-cli/index.js.map b/dist-cli/index.js.map
new file mode 100644
index 0000000..b85c7ba
--- /dev/null
+++ b/dist-cli/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
\ No newline at end of file
diff --git a/dist-cli/init.d.ts b/dist-cli/init.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/dist-cli/init.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/dist-cli/init.js b/dist-cli/init.js
new file mode 100644
index 0000000..62bf180
--- /dev/null
+++ b/dist-cli/init.js
@@ -0,0 +1,124 @@
+import { mkdir, writeFile } from 'node:fs/promises';
+import { createRequire } from 'node:module';
+import path from 'node:path';
+const require = createRequire(import.meta.url);
+const engineVersion = String(require('../package.json').version);
+const args = process.argv.slice(2);
+const engineIndex = args.indexOf('--engine');
+const engineSpec = engineIndex >= 0 ? args[engineIndex + 1] : `^${engineVersion}`;
+if (!engineSpec)
+ throw new Error('--engine 需要 npm 版本、Git URL 或本地路径');
+const targetInput = args.find((argument, index) => !argument.startsWith('-') && index !== engineIndex + 1) ?? '.';
+const target = path.resolve(targetInput);
+await mkdir(path.join(target, 'slides', 'welcome', 'assets'), { recursive: true });
+await mkdir(path.join(target, '.github', 'workflows'), { recursive: true });
+await writeNew('package.json', `${JSON.stringify({
+ name: path.basename(target),
+ private: true,
+ scripts: {
+ dev: 'easy-slides dev',
+ present: 'easy-slides present',
+ build: 'easy-slides build',
+ check: 'easy-slides check',
+ export: 'easy-slides export',
+ },
+ devDependencies: {
+ '@easy-slides/cli': engineSpec,
+ },
+}, null, 2)}\n`);
+await writeNew('easy-slides.config.mjs', `export default {
+ title: '${escapeJavaScript(path.basename(target))}',
+ description: 'Markdown presentations',
+ slidesDir: 'slides',
+ outDir: 'dist',
+}
+`);
+await writeNew(path.join('slides', 'welcome', 'slides.md'), `---
+title: Welcome
+description: Your first easy-slides deck
+date: ${new Date().toISOString().slice(0, 10)}
+tags: [Markdown]
+---
+
+# Welcome
+
+> Edit slides/welcome/slides.md and push to deploy.
+
+
+
+---
+
+## Content lives here
+
+- The engine is an npm dependency
+- This repository owns slides and assets
+- The static build can deploy anywhere
+`);
+await writeNew('.gitignore', `node_modules/
+dist/
+exports/
+.env
+.DS_Store
+`);
+await writeNew('pnpm-workspace.yaml', `allowBuilds:
+ playwright-chromium: false
+`);
+await writeNew(path.join('.github', 'workflows', 'deploy-pages.yml'), `name: Deploy slides
+
+on:
+ push:
+ branches: [main]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: \${{ steps.deployment.outputs.page_url }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 11.19.0
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: pnpm
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm check
+ env:
+ PRESENTER_TOKEN: \${{ secrets.PRESENTER_TOKEN }}
+ - run: pnpm build
+ env:
+ SITE_BASE: /\${{ github.event.repository.name }}/
+ PRESENTER_TOKEN: \${{ secrets.PRESENTER_TOKEN }}
+ - uses: actions/configure-pages@v5
+ - uses: actions/upload-pages-artifact@v4
+ with:
+ path: dist
+ - id: deployment
+ uses: actions/deploy-pages@v4
+`);
+console.log(`内容项目已创建:${target}`);
+console.log('下一步:pnpm install && pnpm dev -- welcome');
+async function writeNew(relative, content) {
+ const filename = path.join(target, relative);
+ await mkdir(path.dirname(filename), { recursive: true });
+ await writeFile(filename, content, { encoding: 'utf8', flag: 'wx' }).catch((error) => {
+ if (error.code === 'EEXIST')
+ throw new Error(`拒绝覆盖已有文件:${filename}`);
+ throw error;
+ });
+}
+function escapeJavaScript(value) {
+ return value.replaceAll('\\', '\\\\').replaceAll("'", "\\'");
+}
+//# sourceMappingURL=init.js.map
\ No newline at end of file
diff --git a/dist-cli/init.js.map b/dist-cli/init.js.map
new file mode 100644
index 0000000..03bafb2
--- /dev/null
+++ b/dist-cli/init.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"init.js","sourceRoot":"","sources":["../scripts/init.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/config.d.ts b/dist-cli/lib/config.d.ts
new file mode 100644
index 0000000..73608c4
--- /dev/null
+++ b/dist-cli/lib/config.d.ts
@@ -0,0 +1,10 @@
+export interface EasySlidesConfig {
+ title: string;
+ description: string;
+ slidesDir: string;
+ outDir: string;
+ exportDir: string;
+ theme?: string;
+}
+export declare function loadProjectConfig(cwd?: string): Promise;
+export declare function normalizeConfig(value: Partial | undefined): EasySlidesConfig;
diff --git a/dist-cli/lib/config.js b/dist-cli/lib/config.js
new file mode 100644
index 0000000..e5df1ec
--- /dev/null
+++ b/dist-cli/lib/config.js
@@ -0,0 +1,61 @@
+var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
+ });
+ }
+ return path;
+};
+import { access, readFile } from 'node:fs/promises';
+import path from 'node:path';
+import { pathToFileURL } from 'node:url';
+const defaults = {
+ title: 'easy-slides',
+ description: 'Markdown presentations powered by easy-slides',
+ slidesDir: 'slides',
+ outDir: 'dist',
+ exportDir: 'exports',
+};
+export async function loadProjectConfig(cwd = process.cwd()) {
+ const candidates = [
+ 'easy-slides.config.mjs',
+ 'easy-slides.config.js',
+ 'easy-slides.config.json',
+ ];
+ for (const candidate of candidates) {
+ const filename = path.join(cwd, candidate);
+ if (!(await exists(filename)))
+ continue;
+ const loaded = candidate.endsWith('.json')
+ ? JSON.parse(await readFile(filename, 'utf8'))
+ : (await import(__rewriteRelativeImportExtension(`${pathToFileURL(filename).href}?t=${Date.now()}`))).default;
+ return normalizeConfig(loaded);
+ }
+ return { ...defaults };
+}
+export function normalizeConfig(value) {
+ return {
+ ...defaults,
+ ...value,
+ slidesDir: normalizeRelativePath(value?.slidesDir ?? defaults.slidesDir, 'slidesDir'),
+ outDir: normalizeRelativePath(value?.outDir ?? defaults.outDir, 'outDir'),
+ exportDir: normalizeRelativePath(value?.exportDir ?? defaults.exportDir, 'exportDir'),
+ };
+}
+function normalizeRelativePath(value, field) {
+ const normalized = value.trim().replace(/[\\/]+$/, '');
+ const portable = normalized.replaceAll('\\', '/');
+ if (!normalized || path.isAbsolute(normalized) || portable === '..' || portable.startsWith('../'))
+ throw new Error(`${field} 必须是项目内的相对路径`);
+ return normalized;
+}
+async function exists(filename) {
+ try {
+ await access(filename);
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+//# sourceMappingURL=config.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/config.js.map b/dist-cli/lib/config.js.map
new file mode 100644
index 0000000..cd6d12c
--- /dev/null
+++ b/dist-cli/lib/config.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"config.js","sourceRoot":"","sources":["../../scripts/lib/config.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAWxC,MAAM,QAAQ,GAAqB;IACjC,KAAK,EAAE,aAAa;IACpB,WAAW,EAAE,+CAA+C;IAC5D,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,MAAM;IACd,SAAS,EAAE,SAAS;CACrB,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACzD,MAAM,UAAU,GAAG;QACjB,wBAAwB;QACxB,uBAAuB;QACvB,yBAAyB;KAC1B,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;QAC1C,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC3B,SAAQ;QAEV,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;YACxC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC,MAAM,MAAM,kCAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAC,CAAC,CAAC,OAAO,CAAA;QAE7E,OAAO,eAAe,CAAC,MAAM,CAAC,CAAA;IAChC,CAAC;IAED,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAA4C;IAC1E,OAAO;QACL,GAAG,QAAQ;QACX,GAAG,KAAK;QACR,SAAS,EAAE,qBAAqB,CAAC,KAAK,EAAE,SAAS,IAAI,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;QACrF,MAAM,EAAE,qBAAqB,CAAC,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;QACzE,SAAS,EAAE,qBAAqB,CAAC,KAAK,EAAE,SAAS,IAAI,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;KACtF,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa,EAAE,KAAa;IACzD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACjD,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC;QAC/F,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,CAAC,CAAA;IACzC,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,QAAgB;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,CAAC;QACL,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/lib/content.d.ts b/dist-cli/lib/content.d.ts
new file mode 100644
index 0000000..da16f14
--- /dev/null
+++ b/dist-cli/lib/content.d.ts
@@ -0,0 +1,26 @@
+export interface DeckMetadata {
+ slug: string;
+ entry: string;
+ directory: string;
+ title: string;
+ description: string;
+ author: string;
+ date: string;
+ tags: string[];
+ cover?: string;
+ draft: boolean;
+}
+export interface ValidationIssue {
+ level: 'error' | 'warning';
+ file: string;
+ message: string;
+}
+export declare function discoverDecks(cwd?: string, slidesDir?: string): Promise;
+export declare function readDeck(entry: string): Promise;
+export declare function resolveDeck(input: string | undefined, cwd?: string, slidesDir?: string): Promise;
+export declare function validateDeck(deck: DeckMetadata): Promise;
+export declare function extractMarkdownImages(markdown: string): {
+ source: string;
+ attributes: Record;
+}[];
+export declare function parseAttributes(input: string): Record;
diff --git a/dist-cli/lib/content.js b/dist-cli/lib/content.js
new file mode 100644
index 0000000..ae74b32
--- /dev/null
+++ b/dist-cli/lib/content.js
@@ -0,0 +1,124 @@
+import { access, readFile } from 'node:fs/promises';
+import path from 'node:path';
+import fg from 'fast-glob';
+import matter from 'gray-matter';
+const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
+const allowedFits = new Set(['contain', 'cover', 'fill', 'scale-down', 'none']);
+export async function discoverDecks(cwd = process.cwd(), slidesDir = 'slides') {
+ const entries = await fg(`${slidesDir.replaceAll('\\', '/')}/*/slides.md`, { cwd, absolute: true, onlyFiles: true });
+ const decks = await Promise.all(entries.map(entry => readDeck(entry)));
+ return decks.sort((a, b) => compareDecks(a, b));
+}
+export async function readDeck(entry) {
+ const source = await readFile(entry, 'utf8');
+ const parsed = matter(source);
+ const directory = path.dirname(entry);
+ const slug = path.basename(directory);
+ const tags = Array.isArray(parsed.data.tags)
+ ? parsed.data.tags.map((tag) => String(tag).trim()).filter(Boolean)
+ : [];
+ return {
+ slug,
+ entry,
+ directory,
+ title: String(parsed.data.title ?? '').trim(),
+ description: String(parsed.data.description ?? '').trim(),
+ author: String(parsed.data.author ?? '').trim(),
+ date: normalizeDate(parsed.data.date),
+ tags,
+ cover: parsed.data.cover ? String(parsed.data.cover).trim() : undefined,
+ draft: parsed.data.draft === true,
+ };
+}
+export async function resolveDeck(input, cwd = process.cwd(), slidesDir = 'slides') {
+ const decks = await discoverDecks(cwd, slidesDir);
+ if (!input)
+ return decks.find(deck => !deck.draft) ?? decks[0];
+ const direct = path.resolve(cwd, input);
+ const bySlug = decks.find(deck => deck.slug === input);
+ const byPath = decks.find(deck => path.resolve(deck.entry) === direct);
+ const deck = bySlug ?? byPath;
+ if (!deck)
+ throw new Error(`找不到演示“${input}”。可用 slug:${decks.map(item => item.slug).join(', ') || '无'}`);
+ return deck;
+}
+export async function validateDeck(deck) {
+ const issues = [];
+ const source = await readFile(deck.entry, 'utf8');
+ const parsed = matter(source);
+ if (!slugPattern.test(deck.slug))
+ issues.push(error(deck.entry, '目录名必须是小写 kebab-case slug'));
+ if (!deck.title)
+ issues.push(error(deck.entry, 'frontmatter 缺少必填 title'));
+ if (deck.date && Number.isNaN(Date.parse(deck.date)))
+ issues.push(error(deck.entry, `date 不是有效日期:${deck.date}`));
+ if (parsed.data.tags !== undefined && !Array.isArray(parsed.data.tags))
+ issues.push(error(deck.entry, 'tags 必须是数组'));
+ if (parsed.data.theme && parsed.data.theme !== 'easy-jyy')
+ issues.push(warning(deck.entry, `当前主题为 ${parsed.data.theme},easy-jyy 主题能力可能不会生效`));
+ if (deck.cover && !isRemote(deck.cover)) {
+ const coverPath = path.resolve(deck.directory, deck.cover);
+ if (!(await exists(coverPath)))
+ issues.push(error(deck.entry, `封面不存在:${deck.cover}`));
+ }
+ for (const image of extractMarkdownImages(parsed.content)) {
+ if (!isRemote(image.source) && !image.source.startsWith('/') && !(await exists(path.resolve(deck.directory, image.source))))
+ issues.push(error(deck.entry, `图片不存在:${image.source}`));
+ if (image.attributes.fit && !allowedFits.has(image.attributes.fit))
+ issues.push(error(deck.entry, `图片 fit 不受支持:${image.attributes.fit}`));
+ if (image.attributes['max-height'] && !isSafeCssSize(image.attributes['max-height']))
+ issues.push(error(deck.entry, `图片 max-height 不合法:${image.attributes['max-height']}`));
+ }
+ return issues;
+}
+export function extractMarkdownImages(markdown) {
+ const images = [];
+ const pattern = /!\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)\s*(?:\{([^}]*)\})?/g;
+ for (const match of markdown.matchAll(pattern)) {
+ images.push({
+ source: match[1],
+ attributes: parseAttributes(match[2] ?? ''),
+ });
+ }
+ return images;
+}
+export function parseAttributes(input) {
+ const attributes = {};
+ const pattern = /([\w-]+)=(?:"([^"]*)"|'([^']*)'|([^\s]+))/g;
+ for (const match of input.matchAll(pattern))
+ attributes[match[1]] = match[2] ?? match[3] ?? match[4] ?? '';
+ return attributes;
+}
+function normalizeDate(value) {
+ if (!value)
+ return '';
+ if (value instanceof Date)
+ return value.toISOString().slice(0, 10);
+ return String(value).trim();
+}
+function compareDecks(a, b) {
+ const byDate = (Date.parse(b.date || '1970-01-01') || 0) - (Date.parse(a.date || '1970-01-01') || 0);
+ return byDate || a.title.localeCompare(b.title, 'zh-CN');
+}
+function isRemote(value) {
+ return /^(?:https?:)?\/\//.test(value) || value.startsWith('data:');
+}
+function isSafeCssSize(value) {
+ return /^(?:\d+(?:\.\d+)?)(?:px|rem|em|vh|vw|%|vmin|vmax)$/.test(value);
+}
+async function exists(file) {
+ try {
+ await access(file);
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+function error(file, message) {
+ return { level: 'error', file, message };
+}
+function warning(file, message) {
+ return { level: 'warning', file, message };
+}
+//# sourceMappingURL=content.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/content.js.map b/dist-cli/lib/content.js.map
new file mode 100644
index 0000000..f1e71ca
--- /dev/null
+++ b/dist-cli/lib/content.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"content.js","sourceRoot":"","sources":["../../scripts/lib/content.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/dev-overflow-audit.d.ts b/dist-cli/lib/dev-overflow-audit.d.ts
new file mode 100644
index 0000000..3322821
--- /dev/null
+++ b/dist-cli/lib/dev-overflow-audit.d.ts
@@ -0,0 +1,13 @@
+import type { DeckMetadata } from './content.ts';
+interface DevOverflowAuditOptions {
+ cwd?: string;
+ debounceMs?: number;
+ deck: DeckMetadata;
+ output?: (line: string) => void;
+}
+export interface DevOverflowAuditController {
+ close: () => Promise;
+ ready: Promise;
+}
+export declare function startDevOverflowAudit(options: DevOverflowAuditOptions): DevOverflowAuditController;
+export {};
diff --git a/dist-cli/lib/dev-overflow-audit.js b/dist-cli/lib/dev-overflow-audit.js
new file mode 100644
index 0000000..2759354
--- /dev/null
+++ b/dist-cli/lib/dev-overflow-audit.js
@@ -0,0 +1,90 @@
+import { watch } from 'node:fs';
+import path from 'node:path';
+import { formatStaticOverflowResult, staticOverflowFingerprint } from "./overflow-report.js";
+import { StaticOverflowAuditor } from "./static-overflow.js";
+export function startDevOverflowAudit(options) {
+ const auditor = new StaticOverflowAuditor();
+ const cwd = options.cwd ?? process.cwd();
+ const debounceMs = options.debounceMs ?? 250;
+ const output = options.output ?? console.log;
+ let activeAudit;
+ let closed = false;
+ let fingerprint = '';
+ let queued = false;
+ let timer;
+ let watcher;
+ function scheduleAudit(delay = debounceMs) {
+ if (closed)
+ return;
+ if (timer)
+ clearTimeout(timer);
+ timer = setTimeout(() => {
+ timer = undefined;
+ void runAudit();
+ }, delay);
+ }
+ async function runAudit() {
+ if (closed)
+ return;
+ if (activeAudit) {
+ queued = true;
+ return activeAudit;
+ }
+ activeAudit = auditor.audit([options.deck])
+ .then((result) => {
+ if (closed)
+ return;
+ const nextFingerprint = staticOverflowFingerprint(result);
+ if (nextFingerprint !== fingerprint) {
+ fingerprint = nextFingerprint;
+ formatStaticOverflowResult(result, cwd).forEach(line => output(line));
+ }
+ })
+ .catch((error) => {
+ if (closed)
+ return;
+ const message = error instanceof Error ? error.message : String(error);
+ const nextFingerprint = `error:${message}`;
+ if (nextFingerprint !== fingerprint) {
+ fingerprint = nextFingerprint;
+ output(`WARN ${path.relative(cwd, options.deck.entry)}: 无法完成静态内容检查:${message}`);
+ }
+ })
+ .finally(() => {
+ activeAudit = undefined;
+ if (queued) {
+ queued = false;
+ scheduleAudit(0);
+ }
+ });
+ return activeAudit;
+ }
+ function startWatcher() {
+ const handleChange = () => scheduleAudit();
+ try {
+ watcher = watch(options.deck.directory, { recursive: true }, handleChange);
+ }
+ catch {
+ watcher = watch(options.deck.entry, handleChange);
+ }
+ }
+ const ready = (async () => {
+ output('\n检查开发内容中的静态溢出风险...');
+ startWatcher();
+ await runAudit();
+ })();
+ return {
+ ready,
+ async close() {
+ if (closed)
+ return;
+ closed = true;
+ watcher?.close();
+ if (timer)
+ clearTimeout(timer);
+ await ready.catch(() => undefined);
+ await activeAudit?.catch(() => undefined);
+ },
+ };
+}
+//# sourceMappingURL=dev-overflow-audit.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/dev-overflow-audit.js.map b/dist-cli/lib/dev-overflow-audit.js.map
new file mode 100644
index 0000000..db7cb25
--- /dev/null
+++ b/dist-cli/lib/dev-overflow-audit.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"dev-overflow-audit.js","sourceRoot":"","sources":["../../scripts/lib/dev-overflow-audit.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/overflow-audit.d.ts b/dist-cli/lib/overflow-audit.d.ts
new file mode 100644
index 0000000..6f93320
--- /dev/null
+++ b/dist-cli/lib/overflow-audit.d.ts
@@ -0,0 +1,37 @@
+export interface OverflowAuditDeck {
+ entry: string;
+ slug: string;
+}
+export interface SlideOverflowIssue {
+ deck: OverflowAuditDeck;
+ heading: string;
+ horizontal: number;
+ slide: number;
+ vertical: number;
+}
+export interface OverflowAuditResult {
+ errors: Array<{
+ deck: OverflowAuditDeck;
+ message: string;
+ }>;
+ issues: SlideOverflowIssue[];
+}
+interface OverflowAuditOptions {
+ decks: OverflowAuditDeck[];
+ root: string;
+ siteBase: string;
+}
+export interface OverflowAuditTarget {
+ deck: OverflowAuditDeck;
+ routerMode?: 'hash' | 'history';
+ url: string;
+}
+export declare class OverflowAuditor {
+ private browser?;
+ private page?;
+ audit(targets: OverflowAuditTarget[]): Promise;
+ close(): Promise;
+ private ensurePage;
+}
+export declare function auditBuiltDecks(options: OverflowAuditOptions): Promise;
+export {};
diff --git a/dist-cli/lib/overflow-audit.js b/dist-cli/lib/overflow-audit.js
new file mode 100644
index 0000000..d341a9f
--- /dev/null
+++ b/dist-cli/lib/overflow-audit.js
@@ -0,0 +1,228 @@
+import { createReadStream } from 'node:fs';
+import { stat } from 'node:fs/promises';
+import { createServer } from 'node:http';
+import path from 'node:path';
+import { chromium } from 'playwright-chromium';
+const overflowTolerance = 2;
+export class OverflowAuditor {
+ browser;
+ page;
+ async audit(targets) {
+ const page = await this.ensurePage();
+ const result = { errors: [], issues: [] };
+ for (const target of targets) {
+ try {
+ result.issues.push(...await auditDeck(page, target));
+ }
+ catch (error) {
+ result.errors.push({
+ deck: target.deck,
+ message: error instanceof Error ? error.message : String(error),
+ });
+ }
+ }
+ return result;
+ }
+ async close() {
+ await this.browser?.close();
+ this.browser = undefined;
+ this.page = undefined;
+ }
+ async ensurePage() {
+ if (this.page && !this.page.isClosed())
+ return this.page;
+ await this.browser?.close().catch(() => undefined);
+ this.browser = await chromium.launch({ headless: true });
+ const context = await this.browser.newContext({ viewport: { height: 900, width: 1200 } });
+ await context.addInitScript(() => {
+ Object.defineProperty(navigator, 'wakeLock', {
+ configurable: true,
+ value: {
+ async request(type) {
+ const sentinel = new EventTarget();
+ sentinel.released = false;
+ sentinel.type = type;
+ sentinel.release = async () => {
+ if (sentinel.released)
+ return;
+ sentinel.released = true;
+ sentinel.dispatchEvent(new Event('release'));
+ };
+ return sentinel;
+ },
+ },
+ });
+ });
+ this.page = await context.newPage();
+ return this.page;
+ }
+}
+export async function auditBuiltDecks(options) {
+ const server = await startStaticServer(options.root, options.siteBase);
+ const address = server.address();
+ const origin = `http://127.0.0.1:${address.port}`;
+ const auditor = new OverflowAuditor();
+ try {
+ return await auditor.audit(options.decks.map(deck => ({
+ deck,
+ url: new URL(`${options.siteBase}${deck.slug}/`.replace(/\/{2,}/g, '/'), origin).href,
+ })));
+ }
+ finally {
+ await auditor.close();
+ await new Promise(resolve => server.close(() => resolve()));
+ }
+}
+async function auditDeck(page, target) {
+ const issues = [];
+ const overviewUrl = auditRouteUrl(target, 'overview');
+ await page.goto(overviewUrl.href, { timeout: 30_000, waitUntil: 'networkidle' });
+ await page.locator('.slidev-layout').first().waitFor({ state: 'attached', timeout: 15_000 });
+ await waitForPageAssets(page);
+ const slideNumbers = await page.locator('[class*="slidev-page-"]').evaluateAll((pages) => {
+ const numbers = pages.flatMap((page) => {
+ const pageClass = Array.from(page.classList).find(className => /^slidev-page-\d+$/.test(className));
+ const number = Number.parseInt(pageClass?.replace('slidev-page-', '') ?? '', 10);
+ return Number.isFinite(number) ? [number] : [];
+ });
+ return Array.from(new Set(numbers)).sort((a, b) => a - b);
+ });
+ if (slideNumbers.length && target.routerMode !== 'history') {
+ await page.evaluate((number) => {
+ window.location.hash = `#/${number}`;
+ }, slideNumbers[0]);
+ await page.reload({ timeout: 30_000, waitUntil: 'networkidle' });
+ await page.evaluate(async () => {
+ await document.fonts?.ready;
+ });
+ }
+ for (const slide of slideNumbers) {
+ if (target.routerMode === 'history')
+ await page.goto(auditRouteUrl(target, String(slide)).href, { timeout: 30_000, waitUntil: 'networkidle' });
+ else
+ await page.evaluate((number) => {
+ window.location.hash = `#/${number}`;
+ }, slide);
+ await page.waitForFunction((number) => {
+ const layouts = Array.from(document.querySelectorAll(`.slidev-page-${number} .slidev-layout`));
+ return layouts.some(layout => layout.getBoundingClientRect().width > 0);
+ }, slide, { timeout: 10_000 });
+ await waitForSlideAssets(page, slide);
+ await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));
+ const measurement = await page.evaluate((number) => {
+ const layouts = Array.from(document.querySelectorAll(`.slidev-page-${number} .slidev-layout`));
+ const layout = layouts.find(item => item.getBoundingClientRect().width > 0) ?? layouts[0];
+ return {
+ heading: layout?.querySelector('h1, h2, h3')?.textContent?.trim() ?? '',
+ horizontal: layout ? Math.max(0, Math.ceil(layout.scrollWidth - layout.clientWidth)) : 0,
+ slide: number,
+ vertical: layout ? Math.max(0, Math.ceil(layout.scrollHeight - layout.clientHeight)) : 0,
+ };
+ }, slide);
+ if (measurement.vertical <= overflowTolerance && measurement.horizontal <= overflowTolerance)
+ continue;
+ issues.push({ deck: target.deck, ...measurement });
+ }
+ return issues;
+}
+function auditRouteUrl(target, route) {
+ const url = target.routerMode === 'history'
+ ? new URL(`${route.replace(/^\/+/, '')}${route === 'overview' ? '/' : ''}`, ensureTrailingSlash(target.url))
+ : new URL(target.url);
+ url.searchParams.set('easy-slides-audit', '1');
+ if (target.routerMode !== 'history')
+ url.hash = `#/${route}`;
+ return url;
+}
+function ensureTrailingSlash(value) {
+ return value.endsWith('/') ? value : `${value}/`;
+}
+async function waitForPageAssets(page) {
+ await page.evaluate(async () => {
+ await document.fonts?.ready;
+ const images = Array.from(document.images);
+ await Promise.race([
+ Promise.all(images.map(image => image.complete
+ ? Promise.resolve()
+ : new Promise((resolve) => {
+ image.addEventListener('load', () => resolve(), { once: true });
+ image.addEventListener('error', () => resolve(), { once: true });
+ }))),
+ new Promise(resolve => setTimeout(resolve, 5_000)),
+ ]);
+ });
+}
+async function waitForSlideAssets(page, slide) {
+ await page.evaluate(async (number) => {
+ const layout = Array.from(document.querySelectorAll(`.slidev-page-${number} .slidev-layout`))
+ .find(item => item.getBoundingClientRect().width > 0);
+ const images = Array.from(layout?.querySelectorAll('img') ?? []);
+ await Promise.race([
+ Promise.all(images.map(image => image.complete
+ ? Promise.resolve()
+ : new Promise((resolve) => {
+ image.addEventListener('load', () => resolve(), { once: true });
+ image.addEventListener('error', () => resolve(), { once: true });
+ }))),
+ new Promise(resolve => setTimeout(resolve, 5_000)),
+ ]);
+ }, slide);
+}
+async function startStaticServer(root, siteBase) {
+ const server = createServer(async (request, response) => {
+ if (request.method !== 'GET' && request.method !== 'HEAD') {
+ response.writeHead(405).end();
+ return;
+ }
+ try {
+ const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
+ const file = await resolveStaticFile(root, requestUrl.pathname, siteBase);
+ response.statusCode = 200;
+ response.setHeader('Content-Type', contentType(file));
+ if (request.method === 'HEAD') {
+ response.end();
+ return;
+ }
+ createReadStream(file).pipe(response);
+ }
+ catch {
+ response.writeHead(404).end('Not found');
+ }
+ });
+ await new Promise((resolve, reject) => {
+ server.once('error', reject);
+ server.listen(0, '127.0.0.1', () => resolve());
+ });
+ return server;
+}
+async function resolveStaticFile(root, requestPath, siteBase) {
+ let pathname = decodeURIComponent(requestPath);
+ const basePrefix = siteBase === '/' ? '' : siteBase.replace(/\/$/, '');
+ if (basePrefix && pathname.startsWith(`${basePrefix}/`))
+ pathname = pathname.slice(basePrefix.length);
+ const candidate = path.resolve(root, pathname.replace(/^\/+/, ''));
+ const relative = path.relative(root, candidate);
+ if (relative.startsWith('..') || path.isAbsolute(relative))
+ throw new Error('Path outside static root');
+ const details = await stat(candidate);
+ return details.isDirectory() ? path.join(candidate, 'index.html') : candidate;
+}
+function contentType(file) {
+ const extension = path.extname(file).toLowerCase();
+ return {
+ '.css': 'text/css; charset=utf-8',
+ '.gif': 'image/gif',
+ '.html': 'text/html; charset=utf-8',
+ '.jpeg': 'image/jpeg',
+ '.jpg': 'image/jpeg',
+ '.js': 'text/javascript; charset=utf-8',
+ '.json': 'application/json; charset=utf-8',
+ '.png': 'image/png',
+ '.svg': 'image/svg+xml',
+ '.ttf': 'font/ttf',
+ '.webp': 'image/webp',
+ '.woff': 'font/woff',
+ '.woff2': 'font/woff2',
+ }[extension] ?? 'application/octet-stream';
+}
+//# sourceMappingURL=overflow-audit.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/overflow-audit.js.map b/dist-cli/lib/overflow-audit.js.map
new file mode 100644
index 0000000..5358f4f
--- /dev/null
+++ b/dist-cli/lib/overflow-audit.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"overflow-audit.js","sourceRoot":"","sources":["../../scripts/lib/overflow-audit.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/overflow-report.d.ts b/dist-cli/lib/overflow-report.d.ts
new file mode 100644
index 0000000..219a495
--- /dev/null
+++ b/dist-cli/lib/overflow-report.d.ts
@@ -0,0 +1,6 @@
+import type { OverflowAuditResult } from './overflow-audit.ts';
+import type { StaticOverflowResult } from './static-overflow.ts';
+export declare function formatOverflowAuditResult(result: OverflowAuditResult, cwd?: string): string[];
+export declare function overflowAuditFingerprint(result: OverflowAuditResult): string;
+export declare function formatStaticOverflowResult(result: StaticOverflowResult, cwd?: string): string[];
+export declare function staticOverflowFingerprint(result: StaticOverflowResult): string;
diff --git a/dist-cli/lib/overflow-report.js b/dist-cli/lib/overflow-report.js
new file mode 100644
index 0000000..c3e1016
--- /dev/null
+++ b/dist-cli/lib/overflow-report.js
@@ -0,0 +1,58 @@
+import path from 'node:path';
+const overflowTolerance = 2;
+export function formatOverflowAuditResult(result, cwd = process.cwd()) {
+ const lines = [];
+ for (const issue of result.issues) {
+ const dimensions = [
+ issue.vertical > overflowTolerance ? `纵向 ${issue.vertical}px` : '',
+ issue.horizontal > overflowTolerance ? `横向 ${issue.horizontal}px` : '',
+ ].filter(Boolean).join('、');
+ const heading = issue.heading ? `(${issue.heading})` : '';
+ lines.push(`WARN ${path.relative(cwd, issue.deck.entry)}: 第 ${issue.slide} 页${heading}内容溢出:${dimensions};放映时可上下滚动,请优先拆页或精简内容`);
+ }
+ for (const error of result.errors)
+ lines.push(`WARN ${path.relative(cwd, error.deck.entry)}: 无法完成内容溢出检查:${error.message}`);
+ if (!lines.length)
+ lines.push('内容边界检查通过:未发现溢出');
+ return lines;
+}
+export function overflowAuditFingerprint(result) {
+ return JSON.stringify({
+ errors: result.errors.map(error => [error.deck.entry, error.message]),
+ issues: result.issues.map(issue => [
+ issue.deck.entry,
+ issue.slide,
+ issue.heading,
+ issue.horizontal,
+ issue.vertical,
+ ]),
+ });
+}
+export function formatStaticOverflowResult(result, cwd = process.cwd()) {
+ const lines = [];
+ for (const issue of result.issues) {
+ const directions = issue.directions
+ .map(direction => direction === 'vertical' ? '纵向' : '横向')
+ .join('、');
+ const heading = issue.heading ? `(${issue.heading})` : '';
+ lines.push(`WARN ${path.relative(cwd, issue.deck.entry)}: 第 ${issue.slide} 页${heading}存在静态${directions}溢出风险:${issue.reason};请优先拆页或精简内容(最终以 build 渲染检查为准)`);
+ }
+ for (const error of result.errors)
+ lines.push(`WARN ${path.relative(cwd, error.deck.entry)}: 无法完成静态内容检查:${error.message}`);
+ if (!lines.length)
+ lines.push('静态内容检查通过:未发现明显溢出风险(最终以 build 渲染检查为准)');
+ return lines;
+}
+export function staticOverflowFingerprint(result) {
+ return JSON.stringify({
+ errors: result.errors.map(error => [error.deck.entry, error.message]),
+ issues: result.issues.map(issue => [
+ issue.deck.entry,
+ issue.slide,
+ issue.heading,
+ issue.directions,
+ issue.reason,
+ ]),
+ });
+}
+//# sourceMappingURL=overflow-report.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/overflow-report.js.map b/dist-cli/lib/overflow-report.js.map
new file mode 100644
index 0000000..189c3f6
--- /dev/null
+++ b/dist-cli/lib/overflow-report.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"overflow-report.js","sourceRoot":"","sources":["../../scripts/lib/overflow-report.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/run-slidev.d.ts b/dist-cli/lib/run-slidev.d.ts
new file mode 100644
index 0000000..012b6d3
--- /dev/null
+++ b/dist-cli/lib/run-slidev.d.ts
@@ -0,0 +1,8 @@
+import type { ChildProcess } from 'node:child_process';
+interface RunSlidevOptions {
+ env?: NodeJS.ProcessEnv;
+}
+export declare function startSlidev(args: string[], options?: RunSlidevOptions): ChildProcess;
+export declare function waitForSlidev(child: ChildProcess): Promise;
+export declare function runSlidev(args: string[], options?: RunSlidevOptions): Promise;
+export {};
diff --git a/dist-cli/lib/run-slidev.js b/dist-cli/lib/run-slidev.js
new file mode 100644
index 0000000..73df963
--- /dev/null
+++ b/dist-cli/lib/run-slidev.js
@@ -0,0 +1,33 @@
+import { spawn } from 'node:child_process';
+import { createRequire } from 'node:module';
+import { hashPresenterToken } from "./token.js";
+const require = createRequire(import.meta.url);
+const slidevCli = require.resolve('@slidev/cli/bin/slidev.mjs');
+export function startSlidev(args, options = {}) {
+ const token = options.env?.PRESENTER_TOKEN ?? process.env.PRESENTER_TOKEN;
+ const presenterHash = token?.trim() ? hashPresenterToken(token) : '';
+ return spawn(process.execPath, [slidevCli, ...args], {
+ cwd: process.cwd(),
+ env: {
+ ...process.env,
+ ...options.env,
+ VITE_EASY_SLIDES_PRESENTER_HASH: presenterHash,
+ },
+ stdio: 'inherit',
+ });
+}
+export async function waitForSlidev(child) {
+ await new Promise((resolve, reject) => {
+ child.once('error', reject);
+ child.once('exit', (code, signal) => {
+ if (code === 0)
+ resolve();
+ else
+ reject(new Error(`Slidev 退出:${signal ?? `code ${code}`}`));
+ });
+ });
+}
+export async function runSlidev(args, options = {}) {
+ await waitForSlidev(startSlidev(args, options));
+}
+//# sourceMappingURL=run-slidev.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/run-slidev.js.map b/dist-cli/lib/run-slidev.js.map
new file mode 100644
index 0000000..794ab9a
--- /dev/null
+++ b/dist-cli/lib/run-slidev.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"run-slidev.js","sourceRoot":"","sources":["../../scripts/lib/run-slidev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;AAM/D,MAAM,UAAU,WAAW,CAAC,IAAc,EAAE,UAA4B,EAAE;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IACzE,MAAM,aAAa,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACpE,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE;QACnD,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,GAAG,OAAO,CAAC,GAAG;YACd,+BAA+B,EAAE,aAAa;SAC/C;QACD,KAAK,EAAE,SAAS;KACjB,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAmB;IACrD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC3B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAClC,IAAI,IAAI,KAAK,CAAC;gBACZ,OAAO,EAAE,CAAA;;gBAET,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,MAAM,IAAI,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;QAC9D,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,UAA4B,EAAE;IAC5E,MAAM,aAAa,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;AACjD,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/lib/site-base.d.ts b/dist-cli/lib/site-base.d.ts
new file mode 100644
index 0000000..b0ea1e8
--- /dev/null
+++ b/dist-cli/lib/site-base.d.ts
@@ -0,0 +1,2 @@
+export declare function normalizeSiteBase(value?: string): string;
+export declare function joinBase(base: string, ...parts: string[]): string;
diff --git a/dist-cli/lib/site-base.js b/dist-cli/lib/site-base.js
new file mode 100644
index 0000000..6804381
--- /dev/null
+++ b/dist-cli/lib/site-base.js
@@ -0,0 +1,15 @@
+export function normalizeSiteBase(value = '/') {
+ const trimmed = value.trim();
+ if (!trimmed || trimmed === '/')
+ return '/';
+ return `/${trimmed.replace(/^\/+|\/+$/g, '')}/`;
+}
+export function joinBase(base, ...parts) {
+ const normalized = normalizeSiteBase(base);
+ const suffix = parts
+ .map(part => part.replace(/^\/+|\/+$/g, ''))
+ .filter(Boolean)
+ .join('/');
+ return suffix ? `${normalized}${suffix}/` : normalized;
+}
+//# sourceMappingURL=site-base.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/site-base.js.map b/dist-cli/lib/site-base.js.map
new file mode 100644
index 0000000..0159098
--- /dev/null
+++ b/dist-cli/lib/site-base.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"site-base.js","sourceRoot":"","sources":["../../scripts/lib/site-base.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,KAAK,GAAG,GAAG;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG;QAC7B,OAAO,GAAG,CAAA;IAEZ,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,GAAG,KAAe;IACvD,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,KAAK;SACjB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;SAC3C,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAA;IAEZ,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,CAAA;AACxD,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/lib/static-overflow.d.ts b/dist-cli/lib/static-overflow.d.ts
new file mode 100644
index 0000000..861f013
--- /dev/null
+++ b/dist-cli/lib/static-overflow.d.ts
@@ -0,0 +1,28 @@
+import type { DeckMetadata } from './content.ts';
+export type StaticOverflowDirection = 'horizontal' | 'vertical';
+export interface StaticOverflowIssue {
+ deck: DeckMetadata;
+ directions: StaticOverflowDirection[];
+ heading: string;
+ reason: string;
+ slide: number;
+}
+export interface StaticOverflowResult {
+ errors: Array<{
+ deck: DeckMetadata;
+ message: string;
+ }>;
+ issues: StaticOverflowIssue[];
+}
+interface StaticOverflowAuditorOptions {
+ onAnalyzeSlide?: (deck: DeckMetadata, slide: number) => void;
+}
+export declare class StaticOverflowAuditor {
+ private cache;
+ private onAnalyzeSlide?;
+ constructor(options?: StaticOverflowAuditorOptions);
+ audit(decks: DeckMetadata[]): Promise;
+ private auditDeck;
+}
+export declare function auditStaticDecks(decks: DeckMetadata[]): Promise;
+export {};
diff --git a/dist-cli/lib/static-overflow.js b/dist-cli/lib/static-overflow.js
new file mode 100644
index 0000000..c67e9d4
--- /dev/null
+++ b/dist-cli/lib/static-overflow.js
@@ -0,0 +1,313 @@
+import { readFile } from 'node:fs/promises';
+import { parseSync } from '@slidev/parser';
+const canvasHeight = 768;
+const canvasWidth = 1024;
+const slidePaddingX = 52;
+const slidePaddingY = 42;
+const contentHeight = canvasHeight - slidePaddingY * 2;
+const contentWidth = canvasWidth - slidePaddingX * 2;
+const twoColumnGap = 46;
+const baseFontSize = 32;
+const baseLineHeight = baseFontSize * 1.42;
+const verticalRiskTolerance = 1.08;
+const codeHeightPresets = new Map([
+ ['easy-code-height-sm', 180],
+ ['easy-code-height-md', 300],
+ ['easy-code-height-lg', 420],
+]);
+export class StaticOverflowAuditor {
+ cache = new Map();
+ onAnalyzeSlide;
+ constructor(options = {}) {
+ this.onAnalyzeSlide = options.onAnalyzeSlide;
+ }
+ async audit(decks) {
+ const result = { errors: [], issues: [] };
+ for (const deck of decks) {
+ try {
+ result.issues.push(...await this.auditDeck(deck));
+ }
+ catch (error) {
+ result.errors.push({
+ deck,
+ message: error instanceof Error ? error.message : String(error),
+ });
+ }
+ }
+ return result;
+ }
+ async auditDeck(deck) {
+ const source = await readFile(deck.entry, 'utf8');
+ const parsed = parseSync(source, deck.entry);
+ const previous = this.cache.get(deck.entry) ?? new Map();
+ const next = new Map();
+ const issues = [];
+ parsed.slides.forEach((slide, index) => {
+ const number = index + 1;
+ const fingerprint = slide.revision ?? slide.raw;
+ const cached = previous.get(number);
+ if (cached?.fingerprint === fingerprint) {
+ next.set(number, cached);
+ issues.push(...cached.issues);
+ return;
+ }
+ this.onAnalyzeSlide?.(deck, number);
+ const slideIssues = analyzeSlide(deck, number, slide);
+ next.set(number, { fingerprint, issues: slideIssues });
+ issues.push(...slideIssues);
+ });
+ this.cache.set(deck.entry, next);
+ return issues;
+ }
+}
+export async function auditStaticDecks(decks) {
+ return await new StaticOverflowAuditor().audit(decks);
+}
+function analyzeSlide(deck, number, slide) {
+ const content = stripComments(slide.content);
+ const layout = String(slide.frontmatter.layout ?? (number === 1 ? 'cover' : 'default'));
+ const classNames = normalizeClasses(slide.frontmatter.class);
+ const heading = slide.title?.trim() || extractHeading(content);
+ const estimates = estimateSlide(content, layout, classNames, String(slide.frontmatter.imagePlacement ?? 'auto'));
+ const directions = [];
+ if (estimates.some(estimate => estimate.height > contentHeight * verticalRiskTolerance))
+ directions.push('vertical');
+ if (estimates.some(estimate => estimate.horizontalRisk))
+ directions.push('horizontal');
+ if (!directions.length)
+ return [];
+ const reason = directions.length === 2
+ ? '估算内容高度超过画布容量,且存在无法可靠换行的宽内容'
+ : directions[0] === 'vertical'
+ ? '估算内容高度超过 easy-jyy 画布容量'
+ : '存在无法可靠换行的宽内容';
+ return [{ deck, directions, heading, reason, slide: number }];
+}
+function estimateSlide(content, layout, classNames, imagePlacement) {
+ if (layout === 'two-cols') {
+ const slots = splitTwoColumnContent(content);
+ const headingEstimate = estimateRegion(slots.before, contentWidth, layout, classNames);
+ const columnWidth = (contentWidth - twoColumnGap) / 2;
+ const left = estimateRegion(slots.left, columnWidth, layout, classNames);
+ const right = estimateRegion(slots.right, columnWidth, layout, classNames);
+ return [
+ {
+ height: headingEstimate.height + Math.max(left.height, right.height),
+ horizontalRisk: headingEstimate.horizontalRisk || left.horizontalRisk || right.horizontalRisk,
+ },
+ ];
+ }
+ const splitImageLayout = ['image-left', 'image-right'].includes(layout)
+ || (layout === 'image-auto' && ['left', 'right'].includes(imagePlacement));
+ const width = splitImageLayout ? (contentWidth - 42) / 2 : contentWidth;
+ return [estimateRegion(content, width, layout, classNames)];
+}
+function splitTwoColumnContent(content) {
+ const leftMarker = /^::left::\s*$/m;
+ const rightMarker = /^::right::\s*$/m;
+ const leftMatch = leftMarker.exec(content);
+ const rightMatch = rightMarker.exec(content);
+ if (!leftMatch && !rightMatch)
+ return { before: content, left: '', right: '' };
+ const firstIndex = Math.min(leftMatch?.index ?? Number.POSITIVE_INFINITY, rightMatch?.index ?? Number.POSITIVE_INFINITY);
+ const before = content.slice(0, firstIndex);
+ const left = leftMatch
+ ? content.slice(leftMatch.index + leftMatch[0].length, rightMatch && rightMatch.index > leftMatch.index ? rightMatch.index : undefined)
+ : '';
+ const right = rightMatch
+ ? content.slice(rightMatch.index + rightMatch[0].length, leftMatch && leftMatch.index > rightMatch.index ? leftMatch.index : undefined)
+ : '';
+ return { before, left, right };
+}
+function estimateRegion(markdown, width, layout, classNames) {
+ const lines = markdown.replace(/\r/g, '').split('\n');
+ let height = 0;
+ let horizontalRisk = false;
+ for (let index = 0; index < lines.length;) {
+ const raw = lines[index];
+ const line = raw.trim();
+ if (!line || isStructuralLine(line)) {
+ index++;
+ continue;
+ }
+ const fence = /^(?:`{3,}|~{3,})([^\s{]*)/.exec(line);
+ if (fence) {
+ const marker = line.startsWith('`') ? '`' : '~';
+ const markerLength = line.match(new RegExp(`^\\${marker}+`))?.[0].length ?? 3;
+ const language = fence[1].toLowerCase();
+ let end = index + 1;
+ while (end < lines.length && !new RegExp(`^\\s*\\${marker}{${markerLength},}`).test(lines[end]))
+ end++;
+ if (language === 'mermaid')
+ height += 180;
+ else
+ height += explicitCodeHeight(lines, index, classNames);
+ index = Math.min(lines.length, end + 1);
+ continue;
+ }
+ const heading = /^(#{1,4})\s+(.+)$/.exec(line);
+ if (heading) {
+ const level = heading[1].length;
+ const fontSize = level === 1 ? (layout === 'cover' ? 68 : 60) : level === 2 ? 48 : level === 3 ? 38 : 32;
+ const wraps = wrappedLines(heading[2], width, fontSize);
+ const margins = level === 1 ? 26 : level === 2 ? 39 : level === 3 ? 34 : 24;
+ height += wraps * fontSize * 1.15 + margins;
+ horizontalRisk ||= hasHorizontalRisk(heading[2], width, fontSize);
+ index++;
+ continue;
+ }
+ if (line === '$$' || line.startsWith('$$')) {
+ let end = index + 1;
+ while (end < lines.length && !lines[end].includes('$$'))
+ end++;
+ height += 82 + Math.max(0, end - index - 2) * 38;
+ index = Math.min(lines.length, end + 1);
+ continue;
+ }
+ if (isStandaloneImage(line)) {
+ height += canvasHeight * 0.46 + 16;
+ index++;
+ continue;
+ }
+ if (isTableRow(line) && isTableSeparator(lines[index + 1] ?? '')) {
+ const tableLines = [line];
+ index += 2;
+ while (index < lines.length && isTableRow(lines[index].trim())) {
+ tableLines.push(lines[index].trim());
+ index++;
+ }
+ const columnCount = Math.max(1, splitTableRow(tableLines[0]).length);
+ const cellWidth = width / columnCount - 28;
+ for (const row of tableLines) {
+ const rowWraps = Math.max(...splitTableRow(row).map(cell => wrappedLines(cell, cellWidth, 30)), 1);
+ height += rowWraps * 30 * 1.42 + 20;
+ }
+ continue;
+ }
+ if (isListLine(line)) {
+ const items = [];
+ while (index < lines.length && (isListLine(lines[index].trim()) || /^\s{2,}\S/.test(lines[index]))) {
+ if (isListLine(lines[index].trim()))
+ items.push(lines[index].trim().replace(/^(?:[-+*]|\d+[.)])\s+/, ''));
+ index++;
+ }
+ const itemHeight = items.reduce((sum, item) => sum + wrappedLines(item, width - baseFontSize * 1.2, baseFontSize) * baseLineHeight, 0);
+ height += itemHeight + Math.max(0, items.length - 1) * 10 + 28;
+ horizontalRisk ||= items.some(item => hasHorizontalRisk(item, width - baseFontSize * 1.2, baseFontSize));
+ continue;
+ }
+ if (line.startsWith('>')) {
+ const quote = [];
+ while (index < lines.length && lines[index].trim().startsWith('>')) {
+ quote.push(lines[index].trim().replace(/^>\s?/, ''));
+ index++;
+ }
+ const fontSize = layout === 'quote' ? 46 : baseFontSize;
+ height += wrappedLines(quote.join(' '), width - 29, fontSize) * fontSize * (layout === 'quote' ? 1.35 : 1.42) + 48;
+ horizontalRisk ||= hasHorizontalRisk(quote.join(' '), width - 29, fontSize);
+ continue;
+ }
+ const paragraph = [];
+ while (index < lines.length && !isBlockStart(lines, index)) {
+ const text = stripMarkup(lines[index]);
+ if (text)
+ paragraph.push(text);
+ index++;
+ }
+ if (paragraph.length) {
+ const text = paragraph.join(' ');
+ height += wrappedLines(text, width, baseFontSize) * baseLineHeight + 28;
+ horizontalRisk ||= hasHorizontalRisk(text, width, baseFontSize);
+ }
+ else {
+ index++;
+ }
+ }
+ horizontalRisk ||= explicitWidthRisk(markdown, width);
+ return { height, horizontalRisk };
+}
+function isBlockStart(lines, index) {
+ const line = lines[index].trim();
+ if (!line || isStructuralLine(line) || /^(?:`{3,}|~{3,}|#{1,4}\s|\$\$|>)/.test(line))
+ return true;
+ if (isStandaloneImage(line) || isListLine(line))
+ return true;
+ return isTableRow(line) && isTableSeparator(lines[index + 1] ?? '');
+}
+function explicitCodeHeight(lines, fenceIndex, classNames) {
+ const nearby = `${classNames.join(' ')} ${lines.slice(Math.max(0, fenceIndex - 8), fenceIndex + 1).join(' ')}`;
+ for (const [className, height] of codeHeightPresets) {
+ if (new RegExp(`(?:^|\\s|["'])${className}(?:$|\\s|["'])`).test(nearby))
+ return height;
+ }
+ const inlineHeight = /(?:height|max-height)\s*:\s*(\d+(?:\.\d+)?)px/i.exec(nearby);
+ return inlineHeight ? Number.parseFloat(inlineHeight[1]) : 0;
+}
+function wrappedLines(value, width, fontSize) {
+ const available = Math.max(1, width / fontSize);
+ return Math.max(1, Math.ceil(displayWidth(stripMarkup(value)) / available));
+}
+function displayWidth(value) {
+ let width = 0;
+ for (const character of value) {
+ if (/\s/.test(character))
+ width += 0.33;
+ else if (/[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe10-\ufe6f\uff01-\uff60\uffe0-\uffe6]/.test(character))
+ width += 1;
+ else
+ width += 0.56;
+ }
+ return width;
+}
+function hasHorizontalRisk(value, width, fontSize) {
+ const available = width / fontSize;
+ return stripMarkup(value)
+ .split(/\s+/)
+ .some(token => !/[\u2e80-\u9fff]/.test(token) && displayWidth(token) > available * 1.05);
+}
+function explicitWidthRisk(markdown, width) {
+ for (const match of markdown.matchAll(/(?:width\s*:\s*|width\s*=\s*["']?)(\d+(?:\.\d+)?)px/gi)) {
+ if (Number.parseFloat(match[1]) > width)
+ return true;
+ }
+ return false;
+}
+function normalizeClasses(value) {
+ if (Array.isArray(value))
+ return value.flatMap(item => String(item).split(/\s+/)).filter(Boolean);
+ return String(value ?? '').split(/\s+/).filter(Boolean);
+}
+function extractHeading(markdown) {
+ return /^(?:#{1,4})\s+(.+)$/m.exec(markdown)?.[1].trim() ?? '';
+}
+function stripComments(markdown) {
+ return markdown.replace(//g, '');
+}
+function stripMarkup(value) {
+ return value
+ .replace(/!\[[^\]]*\]\([^)]*\)(?:\{[^}]*\})?/g, '')
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
+ .replace(/<[^>]+>/g, ' ')
+ .replace(/[`*_~]/g, '')
+ .trim();
+}
+function isStructuralLine(line) {
+ return /^::(?:left|right|image)::$/i.test(line)
+ || /^<\/?(?:v-clicks|div|span|template|slot)(?:\s[^>]*)?>$/i.test(line);
+}
+function isStandaloneImage(line) {
+ return /^!\[[^\]]*\]\([^)]*\)(?:\{[^}]*\})?$/.test(line);
+}
+function isListLine(line) {
+ return /^(?:[-+*]|\d+[.)])\s+/.test(line);
+}
+function isTableRow(line) {
+ return line.includes('|');
+}
+function isTableSeparator(line) {
+ return /^\s*\|?\s*:?-{3,}:?(?:\s*\|\s*:?-{3,}:?)+\s*\|?\s*$/.test(line);
+}
+function splitTableRow(line) {
+ return line.replace(/^\s*\||\|\s*$/g, '').split('|').map(cell => cell.trim());
+}
+//# sourceMappingURL=static-overflow.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/static-overflow.js.map b/dist-cli/lib/static-overflow.js.map
new file mode 100644
index 0000000..0403bcd
--- /dev/null
+++ b/dist-cli/lib/static-overflow.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"static-overflow.js","sourceRoot":"","sources":["../../scripts/lib/static-overflow.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist-cli/lib/theme.d.ts b/dist-cli/lib/theme.d.ts
new file mode 100644
index 0000000..69d8815
--- /dev/null
+++ b/dist-cli/lib/theme.d.ts
@@ -0,0 +1 @@
+export declare function resolveTheme(theme?: string): string;
diff --git a/dist-cli/lib/theme.js b/dist-cli/lib/theme.js
new file mode 100644
index 0000000..dcbeb5d
--- /dev/null
+++ b/dist-cli/lib/theme.js
@@ -0,0 +1,16 @@
+import { existsSync } from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+const directory = path.dirname(fileURLToPath(import.meta.url));
+const bundledTheme = path.resolve(directory, '..', 'theme');
+const workspaceTheme = path.resolve(directory, '..', '..', 'packages', 'slidev-theme-easy-jyy');
+export function resolveTheme(theme) {
+ if (theme)
+ return theme;
+ if (existsSync(path.join(bundledTheme, 'package.json')))
+ return bundledTheme;
+ if (existsSync(path.join(workspaceTheme, 'package.json')))
+ return workspaceTheme;
+ throw new Error('easy-slides 主题不存在;请重新安装或构建 @easy-slides/cli');
+}
+//# sourceMappingURL=theme.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/theme.js.map b/dist-cli/lib/theme.js.map
new file mode 100644
index 0000000..cf9819f
--- /dev/null
+++ b/dist-cli/lib/theme.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../scripts/lib/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AAC3D,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAA;AAE/F,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK;QACP,OAAO,KAAK,CAAA;IACd,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QACrD,OAAO,YAAY,CAAA;IACrB,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QACvD,OAAO,cAAc,CAAA;IACvB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;AAChE,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/lib/token.d.ts b/dist-cli/lib/token.d.ts
new file mode 100644
index 0000000..8b1a1fb
--- /dev/null
+++ b/dist-cli/lib/token.d.ts
@@ -0,0 +1,2 @@
+export declare function hashPresenterToken(token: string): string;
+export declare function hasPresenterToken(env?: NodeJS.ProcessEnv): boolean;
diff --git a/dist-cli/lib/token.js b/dist-cli/lib/token.js
new file mode 100644
index 0000000..c7f03a0
--- /dev/null
+++ b/dist-cli/lib/token.js
@@ -0,0 +1,8 @@
+import { createHash } from 'node:crypto';
+export function hashPresenterToken(token) {
+ return createHash('sha256').update(token.trim()).digest('hex');
+}
+export function hasPresenterToken(env = process.env) {
+ return Boolean(env.PRESENTER_TOKEN?.trim());
+}
+//# sourceMappingURL=token.js.map
\ No newline at end of file
diff --git a/dist-cli/lib/token.js.map b/dist-cli/lib/token.js.map
new file mode 100644
index 0000000..87e8272
--- /dev/null
+++ b/dist-cli/lib/token.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"token.js","sourceRoot":"","sources":["../../scripts/lib/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAChE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACpE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAA;AAC7C,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/present.d.ts b/dist-cli/present.d.ts
new file mode 100644
index 0000000..0b172b7
--- /dev/null
+++ b/dist-cli/present.d.ts
@@ -0,0 +1 @@
+import 'dotenv/config';
diff --git a/dist-cli/present.js b/dist-cli/present.js
new file mode 100644
index 0000000..4c3b79d
--- /dev/null
+++ b/dist-cli/present.js
@@ -0,0 +1,41 @@
+import 'dotenv/config';
+import { networkInterfaces } from 'node:os';
+import qrcode from 'qrcode-terminal';
+import { loadProjectConfig } from "./lib/config.js";
+import { resolveDeck } from "./lib/content.js";
+import { runSlidev } from "./lib/run-slidev.js";
+import { resolveTheme } from "./lib/theme.js";
+const input = process.argv.slice(2).find(argument => !argument.startsWith('-'));
+const config = await loadProjectConfig();
+const deck = await resolveDeck(input, process.cwd(), config.slidesDir);
+const port = process.env.PORT || '3030';
+const address = firstLanAddress();
+const remoteToken = process.env.SLIDEV_REMOTE_TOKEN || 'easy-slides-local';
+const remotePassword = encodeURIComponent(remoteToken);
+const presenterUrl = `http://${address}:${port}/presenter/?password=${remotePassword}`;
+const remoteUrl = `http://${address}:${port}/entry?password=${remotePassword}`;
+console.log(`\n观众视图:http://${address}:${port}/`);
+console.log(`演示者视图:${presenterUrl}`);
+console.log(`手机遥控器:${remoteUrl}`);
+qrcode.generate(presenterUrl, { small: true });
+await runSlidev([
+ deck.entry,
+ '--theme',
+ resolveTheme(config.theme),
+ '--port',
+ port,
+ '--remote',
+ remoteToken,
+ '--bind',
+ '0.0.0.0',
+]);
+function firstLanAddress() {
+ for (const entries of Object.values(networkInterfaces())) {
+ for (const entry of entries ?? []) {
+ if (entry.family === 'IPv4' && !entry.internal)
+ return entry.address;
+ }
+ }
+ return 'localhost';
+}
+//# sourceMappingURL=present.js.map
\ No newline at end of file
diff --git a/dist-cli/present.js.map b/dist-cli/present.js.map
new file mode 100644
index 0000000..a5a9078
--- /dev/null
+++ b/dist-cli/present.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"present.js","sourceRoot":"","sources":["../scripts/present.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AAC/E,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAA;AACxC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AACtE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAA;AACvC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;AACjC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,mBAAmB,CAAA;AAC1E,MAAM,cAAc,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAA;AACtD,MAAM,YAAY,GAAG,UAAU,OAAO,IAAI,IAAI,wBAAwB,cAAc,EAAE,CAAA;AACtF,MAAM,SAAS,GAAG,UAAU,OAAO,IAAI,IAAI,mBAAmB,cAAc,EAAE,CAAA;AAE9E,OAAO,CAAC,GAAG,CAAC,iBAAiB,OAAO,IAAI,IAAI,GAAG,CAAC,CAAA;AAChD,OAAO,CAAC,GAAG,CAAC,SAAS,YAAY,EAAE,CAAC,CAAA;AACpC,OAAO,CAAC,GAAG,CAAC,SAAS,SAAS,EAAE,CAAC,CAAA;AACjC,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAE9C,MAAM,SAAS,CAAC;IACd,IAAI,CAAC,KAAK;IACV,SAAS;IACT,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,QAAQ;IACR,IAAI;IACJ,UAAU;IACV,WAAW;IACX,QAAQ;IACR,SAAS;CACV,CAAC,CAAA;AAEF,SAAS,eAAe;IACtB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACzD,KAAK,MAAM,KAAK,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ;gBAC5C,OAAO,KAAK,CAAC,OAAO,CAAA;QACxB,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAA;AACpB,CAAC"}
\ No newline at end of file
diff --git a/dist-cli/theme/components/ContentScaleControls.vue b/dist-cli/theme/components/ContentScaleControls.vue
new file mode 100644
index 0000000..f2074ce
--- /dev/null
+++ b/dist-cli/theme/components/ContentScaleControls.vue
@@ -0,0 +1,699 @@
+
+
+
+
+
diff --git a/dist-cli/theme/components/PresenterAccess.vue b/dist-cli/theme/components/PresenterAccess.vue
new file mode 100644
index 0000000..c5fc84f
--- /dev/null
+++ b/dist-cli/theme/components/PresenterAccess.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/components/SlideScrollFallback.vue b/dist-cli/theme/components/SlideScrollFallback.vue
new file mode 100644
index 0000000..9ed8596
--- /dev/null
+++ b/dist-cli/theme/components/SlideScrollFallback.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/dist-cli/theme/components/SmartImages.vue b/dist-cli/theme/components/SmartImages.vue
new file mode 100644
index 0000000..17222bf
--- /dev/null
+++ b/dist-cli/theme/components/SmartImages.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/dist-cli/theme/global-bottom.vue b/dist-cli/theme/global-bottom.vue
new file mode 100644
index 0000000..34f2681
--- /dev/null
+++ b/dist-cli/theme/global-bottom.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/center.vue b/dist-cli/theme/layouts/center.vue
new file mode 100644
index 0000000..ff4b658
--- /dev/null
+++ b/dist-cli/theme/layouts/center.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/cover.vue b/dist-cli/theme/layouts/cover.vue
new file mode 100644
index 0000000..f333814
--- /dev/null
+++ b/dist-cli/theme/layouts/cover.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/default.vue b/dist-cli/theme/layouts/default.vue
new file mode 100644
index 0000000..13c4c9a
--- /dev/null
+++ b/dist-cli/theme/layouts/default.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/image-auto.vue b/dist-cli/theme/layouts/image-auto.vue
new file mode 100644
index 0000000..f6d3732
--- /dev/null
+++ b/dist-cli/theme/layouts/image-auto.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/image-full.vue b/dist-cli/theme/layouts/image-full.vue
new file mode 100644
index 0000000..8653b53
--- /dev/null
+++ b/dist-cli/theme/layouts/image-full.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/image-left.vue b/dist-cli/theme/layouts/image-left.vue
new file mode 100644
index 0000000..2a650b2
--- /dev/null
+++ b/dist-cli/theme/layouts/image-left.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/image-right.vue b/dist-cli/theme/layouts/image-right.vue
new file mode 100644
index 0000000..e6d880b
--- /dev/null
+++ b/dist-cli/theme/layouts/image-right.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/quote.vue b/dist-cli/theme/layouts/quote.vue
new file mode 100644
index 0000000..51a5def
--- /dev/null
+++ b/dist-cli/theme/layouts/quote.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/section.vue b/dist-cli/theme/layouts/section.vue
new file mode 100644
index 0000000..3d1f164
--- /dev/null
+++ b/dist-cli/theme/layouts/section.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/dist-cli/theme/layouts/two-cols.vue b/dist-cli/theme/layouts/two-cols.vue
new file mode 100644
index 0000000..9b89a75
--- /dev/null
+++ b/dist-cli/theme/layouts/two-cols.vue
@@ -0,0 +1,9 @@
+
+
+
diff --git a/dist-cli/theme/package.json b/dist-cli/theme/package.json
new file mode 100644
index 0000000..c15e394
--- /dev/null
+++ b/dist-cli/theme/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "slidev-theme-easy-jyy",
+ "version": "0.2.0",
+ "description": "JYY-inspired presentation theme for easy-slides and Slidev",
+ "license": "MIT",
+ "private": false,
+ "type": "module",
+ "keywords": ["slidev-theme", "slidev"],
+ "publishConfig": {
+ "access": "public"
+ },
+ "engines": {
+ "node": ">=20.12.0",
+ "slidev": ">=52.0.0"
+ },
+ "slidev": {
+ "defaults": {
+ "aspectRatio": "4/3",
+ "canvasWidth": 1024,
+ "colorSchema": "light",
+ "fonts": {
+ "sans": "Lato,Noto Sans SC,PingFang SC,Microsoft YaHei,sans-serif",
+ "mono": "Inconsolata,JetBrains Mono,Fira Code,monospace",
+ "serif": "Noto Serif SC,STKaiti,KaiTi,serif"
+ }
+ }
+ },
+ "dependencies": {
+ "@slidev/client": "52.19.1",
+ "@slidev/types": "52.19.1",
+ "markdown-it-attrs": "4.3.1"
+ }
+}
diff --git a/dist-cli/theme/setup/markdown.ts b/dist-cli/theme/setup/markdown.ts
new file mode 100644
index 0000000..4869f80
--- /dev/null
+++ b/dist-cli/theme/setup/markdown.ts
@@ -0,0 +1,8 @@
+import { defineMarkdownSetup } from '@slidev/types'
+import markdownItAttrs from 'markdown-it-attrs'
+
+export default defineMarkdownSetup(() => ({
+ markdownItSetup(md) {
+ md.use(markdownItAttrs)
+ },
+}))
diff --git a/dist-cli/theme/styles/base.css b/dist-cli/theme/styles/base.css
new file mode 100644
index 0000000..6975ab3
--- /dev/null
+++ b/dist-cli/theme/styles/base.css
@@ -0,0 +1,233 @@
+:root {
+ --easy-accent: #1d4ed8;
+ --easy-accent-dark: #1e40af;
+ --easy-accent-soft: #eff6ff;
+ --easy-ink: #222;
+ --easy-muted: #666;
+ --easy-border: #ddd;
+ --easy-code-bg: #eee;
+ --easy-code-font-size: 28px;
+ --easy-table-font-size: 30px;
+ --easy-table-padding-x: 14px;
+ --easy-table-padding-y: 10px;
+ --easy-slide-padding-x: 52px;
+ --easy-slide-padding-y: 42px;
+ --easy-presenter-note-size: 20px;
+}
+
+.easy-table-sm {
+ --easy-table-font-size: 24px;
+ --easy-table-padding-x: 10px;
+ --easy-table-padding-y: 7px;
+}
+
+.easy-table-md {
+ --easy-table-font-size: 30px;
+ --easy-table-padding-x: 14px;
+ --easy-table-padding-y: 10px;
+}
+
+.easy-table-lg {
+ --easy-table-font-size: 36px;
+ --easy-table-padding-x: 18px;
+ --easy-table-padding-y: 13px;
+}
+
+.easy-code-sm { --easy-code-font-size: 22px; }
+.easy-code-md { --easy-code-font-size: 28px; }
+.easy-code-lg { --easy-code-font-size: 34px; }
+
+.easy-code-height-sm { --easy-code-height: 180px; }
+.easy-code-height-md { --easy-code-height: 300px; }
+.easy-code-height-lg { --easy-code-height: 420px; }
+
+.slidev-layout {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ padding: var(--easy-slide-padding-y) var(--easy-slide-padding-x);
+ overflow: hidden;
+ border: 1.5px solid var(--easy-border);
+ border-radius: 7px;
+ background: #fff;
+ color: var(--easy-ink);
+ text-align: left;
+ font-family: Lato, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
+ font-size: 32px;
+ font-weight: 300;
+ line-height: 1.42;
+}
+
+.slidev-layout h1,
+.slidev-layout h2,
+.slidev-layout h3,
+.slidev-layout h4 {
+ color: var(--easy-ink);
+ font-weight: 400;
+ letter-spacing: 0;
+ line-height: 1.15;
+}
+
+.slidev-layout h1 {
+ margin: 0 0 26px;
+ font-size: 60px;
+}
+
+.slidev-layout h2 {
+ margin: 0 0 28px;
+ padding-bottom: 8px;
+ border-bottom: 3px solid var(--easy-accent);
+ font-size: 48px;
+}
+
+.slidev-layout h3 {
+ margin: 20px 0 14px;
+ font-size: 38px;
+}
+
+.slidev-layout p,
+.slidev-layout ul,
+.slidev-layout ol {
+ margin-top: 14px;
+ margin-bottom: 14px;
+}
+
+.slidev-layout ul,
+.slidev-layout ol {
+ display: block;
+ padding-left: 1.2em;
+}
+
+.slidev-layout li + li {
+ margin-top: 10px;
+}
+
+.slidev-layout a {
+ color: var(--easy-accent);
+ text-decoration-thickness: 1px;
+ text-underline-offset: .13em;
+}
+
+.slidev-layout strong {
+ font-weight: 700;
+}
+
+.slidev-layout blockquote {
+ margin: 24px 0;
+ padding: 4px 0 4px 24px;
+ border-left: 5px solid var(--easy-accent);
+ color: #1e3a5f;
+ font-family: "Noto Serif SC", STKaiti, KaiTi, serif;
+ font-style: normal;
+}
+
+.slidev-layout code {
+ font-family: Inconsolata, "JetBrains Mono", "Fira Code", monospace;
+}
+
+.slidev-layout :not(pre) > code {
+ border-radius: 5px;
+ background: var(--easy-accent-soft);
+ color: var(--easy-accent-dark);
+ padding: .08em .28em;
+ font-size: .86em;
+}
+
+.slidev-layout pre {
+ box-sizing: border-box;
+ height: var(--easy-code-height, var(--easy-code-auto-height, auto));
+ max-height: var(--easy-code-height, var(--easy-code-auto-max-height, var(--easy-code-auto-height, none)));
+ overflow-x: hidden;
+ overflow-y: auto;
+ border-radius: 10px;
+ background: var(--easy-code-bg);
+ padding: 18px 20px;
+ font-size: var(--easy-code-scaled-font-size, var(--easy-code-font-size)) !important;
+ line-height: 1.38 !important;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+.slidev-layout pre code,
+.slidev-layout pre code .line {
+ min-width: 0 !important;
+ white-space: pre-wrap !important;
+ overflow-wrap: anywhere;
+ word-break: normal;
+}
+
+.slidev-layout table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--easy-table-font-size);
+}
+
+.slidev-layout th,
+.slidev-layout td {
+ padding: var(--easy-table-padding-y) var(--easy-table-padding-x);
+ border-bottom: 1px solid #d5d5d5;
+}
+
+.slidev-layout th {
+ background: #eee;
+ font-weight: 600;
+}
+
+.slidev-layout tr:nth-child(even) {
+ background: #f7faff;
+}
+
+.slidev-layout img {
+ display: block;
+ max-width: 100%;
+ max-height: 60vh;
+ margin: 16px auto;
+ object-fit: contain;
+}
+
+.slidev-layout p:has(> img:only-child) {
+ display: grid;
+ place-items: center;
+ min-height: 46vh;
+ margin: 8px 0;
+}
+
+.slidev-layout p:has(> img:only-child) > img {
+ width: 100%;
+ height: 46vh;
+ max-height: 60vh;
+ margin: 0;
+ object-fit: contain;
+}
+
+.slidev-layout img[fit="cover"] {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.slidev-layout img[fit="fill"] { object-fit: fill; }
+.slidev-layout img[fit="scale-down"] { object-fit: scale-down; }
+.slidev-layout img[fit="none"] { object-fit: none; }
+
+.slidev-layout .katex-display,
+.slidev-layout .mermaid {
+ max-width: 100%;
+ max-height: 60vh;
+ overflow: auto;
+}
+
+.slidev-page-number {
+ border-radius: 5px;
+ background: rgba(0, 0, 0, .26);
+ padding: 3px 9px;
+ color: #fff;
+ font-size: 18px;
+}
+
+@media (max-width: 760px) {
+ :root {
+ --easy-slide-padding-x: 34px;
+ --easy-slide-padding-y: 30px;
+ }
+}
diff --git a/dist-cli/theme/styles/content.css b/dist-cli/theme/styles/content.css
new file mode 100644
index 0000000..0d60a59
--- /dev/null
+++ b/dist-cli/theme/styles/content.css
@@ -0,0 +1,69 @@
+/* Shared content utilities for course and technical decks. */
+.slidev-layout .lead {
+ color: var(--easy-accent);
+ font-size: 1.16em;
+ line-height: 1.35;
+}
+
+.slidev-layout .muted {
+ color: var(--easy-muted);
+ font-size: .72em;
+}
+
+.slidev-layout .compact,
+.slidev-layout.compact {
+ font-size: 27px;
+}
+
+.slidev-layout .compact li + li,
+.slidev-layout.compact li + li {
+ margin-top: 6px;
+}
+
+.slidev-layout .outline {
+ list-style: none;
+ margin: 22px 0 0;
+ padding: 0;
+}
+
+.slidev-layout .outline > li {
+ margin: 12px 0;
+ padding-left: 22px;
+ border-left: 5px solid #ddd;
+}
+
+.slidev-layout .outline .current {
+ border-left-color: var(--easy-accent);
+ color: var(--easy-accent);
+ font-weight: 600;
+}
+
+.slidev-layout .source-figure {
+ width: 100%;
+ max-height: 56vh;
+ object-fit: contain;
+}
+
+.slidev-layout .course-table {
+ font-size: 21px;
+ line-height: 1.2;
+}
+
+.slidev-layout .course-table th,
+.slidev-layout .course-table td {
+ padding: 6px 9px;
+}
+
+.slidev-layout .v-word {
+ color: var(--easy-accent);
+ font-size: 1.22em;
+ font-weight: 600;
+}
+
+.easy-layout-cover .cover-identity {
+ width: 68%;
+ height: 20vh;
+ margin: 22px 0 0;
+ object-fit: contain;
+ object-position: left center;
+}
diff --git a/dist-cli/theme/styles/index.ts b/dist-cli/theme/styles/index.ts
new file mode 100644
index 0000000..e4c99bc
--- /dev/null
+++ b/dist-cli/theme/styles/index.ts
@@ -0,0 +1,4 @@
+import './base.css'
+import './content.css'
+import './layouts.css'
+import './presenter.css'
diff --git a/dist-cli/theme/styles/layouts.css b/dist-cli/theme/styles/layouts.css
new file mode 100644
index 0000000..b1ed0cb
--- /dev/null
+++ b/dist-cli/theme/styles/layouts.css
@@ -0,0 +1,283 @@
+.easy-layout-center,
+.easy-layout-section,
+.easy-layout-cover {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.easy-layout-center {
+ align-items: center;
+ text-align: center;
+}
+
+.easy-layout-center h1,
+.easy-layout-center h2 {
+ text-align: center;
+}
+
+.easy-layout-cover h1 {
+ margin-bottom: 28px;
+ font-size: 68px;
+}
+
+.easy-layout-cover blockquote {
+ border: 0;
+ padding: 0;
+ color: var(--easy-accent);
+}
+
+.easy-layout-section h1,
+.easy-layout-section h2 {
+ max-width: 980px;
+}
+
+.easy-layout-quote {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.easy-layout-quote blockquote {
+ border: 0;
+ padding: 0;
+ color: var(--easy-accent);
+ font-size: 46px;
+ line-height: 1.35;
+ text-align: center;
+}
+
+.easy-layout-two-cols {
+ display: grid;
+ grid-template-rows: auto 1fr;
+}
+
+.easy-layout-two-cols .easy-two-cols-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ gap: 46px;
+ min-height: 0;
+}
+
+.easy-layout-image-split {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ gap: 42px;
+}
+
+.easy-layout-image-split.easy-image-left .easy-image-pane { order: -1; }
+
+.easy-layout-image-auto.easy-image-top,
+.easy-layout-image-auto.easy-image-bottom {
+ grid-template-columns: minmax(0, 1fr);
+ grid-template-rows: auto minmax(0, 1fr);
+ gap: 22px;
+}
+
+.easy-layout-image-auto.easy-image-top .easy-image-pane { order: -1; }
+
+.easy-image-pane {
+ min-width: 0;
+ min-height: 0;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ grid-template-rows: minmax(0, 1fr);
+ place-items: center;
+ overflow: hidden;
+ border-radius: 8px;
+}
+
+.easy-image-pane img {
+ min-width: 0;
+ min-height: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ margin: 0;
+}
+
+.easy-content-scale-host {
+ position: relative;
+ min-width: 0;
+ min-height: 0;
+}
+
+.easy-content-table-host {
+ width: 100%;
+ overflow: auto;
+}
+
+.easy-content-code-host {
+ width: 100%;
+ margin-bottom: 0 !important;
+}
+
+.easy-image-preview-trigger {
+ cursor: zoom-in;
+}
+
+.easy-image-preview-trigger:focus-visible {
+ outline: 3px solid var(--easy-accent);
+ outline-offset: 4px;
+}
+
+.easy-image-lightbox {
+ position: fixed;
+ inset: 0;
+ z-index: 2147483647;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ grid-template-rows: minmax(0, 1fr);
+ place-items: center;
+ box-sizing: border-box;
+ min-width: 0;
+ min-height: 0;
+ padding: clamp(16px, 4vmin, 42px);
+ overflow: hidden;
+ overscroll-behavior: contain;
+ background: rgba(0, 0, 0, .92);
+}
+
+.easy-image-lightbox-preview {
+ display: block;
+ place-self: center;
+ min-width: 0;
+ min-height: 0;
+ width: auto;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
+ max-inline-size: 100%;
+ max-block-size: 100%;
+ margin: 0;
+ object-fit: contain;
+ object-position: center;
+}
+
+.easy-image-lightbox-close {
+ position: absolute;
+ top: 18px;
+ right: 22px;
+ display: grid;
+ width: 46px;
+ height: 46px;
+ place-items: center;
+ border: 1px solid rgba(255, 255, 255, .5);
+ border-radius: 50%;
+ background: rgba(0, 0, 0, .55);
+ color: #fff;
+ padding: 0 0 4px;
+ font: 300 38px/1 Arial, sans-serif;
+ cursor: pointer;
+}
+
+.easy-image-lightbox-close:hover,
+.easy-image-lightbox-close:focus-visible {
+ border-color: #fff;
+ background: var(--easy-accent);
+ outline: none;
+}
+
+.easy-content-scale-toolbar {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ z-index: 30;
+ display: flex;
+ gap: 3px;
+ padding: 4px;
+ border: 1px solid rgba(180, 180, 180, .9);
+ border-radius: 7px;
+ background: rgba(255, 255, 255, .94);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, .12);
+ opacity: 0;
+ transition: opacity .14s ease;
+}
+
+.easy-content-scale-host:hover > .easy-content-scale-toolbar,
+.easy-content-scale-host:focus-within > .easy-content-scale-toolbar {
+ opacity: 1;
+}
+
+.easy-content-scale-button {
+ min-width: 32px;
+ border: 0;
+ border-radius: 5px;
+ background: #eee;
+ color: #222;
+ padding: 6px 8px;
+ font: 600 14px/1 Lato, "Noto Sans SC", sans-serif;
+ cursor: pointer;
+}
+
+.easy-content-scale-button:hover,
+.easy-content-scale-button:focus-visible {
+ background: var(--easy-accent);
+ color: #fff;
+ outline: none;
+}
+
+.easy-content-scale-reset {
+ min-width: 52px;
+}
+
+.easy-content-code-host > .easy-content-scale-toolbar {
+ right: 48px;
+}
+
+body.easy-slide-scroll-fallback .slidev-layout {
+ overflow-x: hidden;
+ overflow-y: auto;
+ overscroll-behavior-y: contain;
+ scrollbar-width: thin;
+ scrollbar-color: color-mix(in srgb, var(--easy-accent) 65%, transparent) transparent;
+}
+
+.easy-image-content {
+ min-width: 0;
+ align-self: center;
+}
+
+.easy-layout-image-full {
+ padding: 0;
+}
+
+.easy-layout-image-full > img {
+ width: 100%;
+ height: 100%;
+ max-height: none;
+ margin: 0;
+}
+
+.easy-layout-image-full .easy-image-overlay {
+ position: absolute;
+ inset: auto 0 0;
+ padding: 46px 54px;
+ background: linear-gradient(transparent, rgba(0, 0, 0, .78));
+ color: #fff;
+}
+
+.easy-layout-image-full .easy-image-overlay :is(h1, h2, h3, p) {
+ color: inherit;
+}
+
+@media (max-aspect-ratio: 4 / 3) {
+ .easy-layout-two-cols .easy-two-cols-grid,
+ .easy-layout-image-split {
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr 1fr;
+ gap: 22px;
+ }
+}
+
+@media (hover: none) {
+ .easy-content-scale-toolbar { opacity: .78; }
+}
+
+@media print {
+ .easy-content-scale-toolbar,
+ .easy-image-lightbox { display: none !important; }
+
+ body.easy-slide-scroll-fallback .slidev-layout { overflow: hidden !important; }
+}
diff --git a/dist-cli/theme/styles/presenter.css b/dist-cli/theme/styles/presenter.css
new file mode 100644
index 0000000..b6d50a4
--- /dev/null
+++ b/dist-cli/theme/styles/presenter.css
@@ -0,0 +1,143 @@
+.easy-presenter-gate {
+ position: fixed;
+ inset: 0;
+ z-index: 2147483646;
+ display: none;
+ width: 100vw;
+ height: 100dvh;
+ max-width: none;
+ max-height: none;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ place-items: center;
+ overflow: auto;
+ background: #f6f5f2;
+ color: #222;
+ font-family: Lato, "Noto Sans SC", "PingFang SC", sans-serif;
+}
+
+.easy-presenter-gate[open] {
+ display: grid;
+}
+
+.easy-presenter-gate::backdrop {
+ background: #f6f5f2;
+}
+
+body.easy-presenter-locked {
+ overflow: hidden;
+}
+
+.easy-presenter-dialog {
+ width: min(440px, calc(100vw - 40px));
+ border: 1px solid #d8d4d7;
+ border-top: 4px solid var(--easy-accent);
+ border-radius: 9px;
+ background: #fff;
+ padding: 34px;
+ box-shadow: 0 20px 70px rgba(45, 26, 42, .14);
+}
+
+.easy-presenter-dialog h1 {
+ margin: 0 0 10px;
+ font-size: 30px;
+}
+
+.easy-presenter-dialog p {
+ color: #666;
+ line-height: 1.55;
+}
+
+.easy-presenter-dialog form {
+ display: flex;
+ gap: 10px;
+ margin-top: 24px;
+}
+
+.easy-presenter-dialog input {
+ min-width: 0;
+ flex: 1;
+ border: 1px solid #aaa;
+ border-radius: 6px;
+ padding: 11px 12px;
+ font: inherit;
+}
+
+.easy-presenter-dialog button,
+.easy-presenter-tools button,
+.easy-presenter-launcher {
+ border: 0;
+ border-radius: 6px;
+ background: var(--easy-accent);
+ color: #fff;
+ padding: 10px 14px;
+ font: 600 14px/1.1 Lato, "Noto Sans SC", sans-serif;
+ cursor: pointer;
+}
+
+.easy-presenter-error {
+ color: #a21d32 !important;
+ font-weight: 600;
+}
+
+.easy-presenter-launcher {
+ position: fixed;
+ right: 18px;
+ bottom: 18px;
+ z-index: 60;
+ opacity: .22;
+ transition: opacity .16s ease;
+}
+
+.easy-presenter-launcher:hover,
+.easy-presenter-launcher:focus-visible {
+ opacity: 1;
+}
+
+.easy-presenter-tools {
+ position: fixed;
+ top: 12px;
+ right: 12px;
+ z-index: 1000;
+ display: flex;
+ gap: 6px;
+ padding: 6px;
+ border: 1px solid #d4d0d3;
+ border-radius: 8px;
+ background: rgba(255, 255, 255, .94);
+ box-shadow: 0 5px 22px rgba(0, 0, 0, .12);
+}
+
+.easy-presenter-tools button {
+ min-width: 34px;
+ background: #eee9ed;
+ color: #351231;
+ padding: 8px 10px;
+}
+
+.easy-presenter-tools button:last-child {
+ background: #5d1357;
+ color: #fff;
+}
+
+@media (max-width: 680px) {
+ .easy-presenter-tools {
+ right: auto;
+ left: 12px;
+ }
+}
+
+body.easy-presenter-active :is(.presenter-notes, [class*="presenter"] [class*="notes"], [class*="presenter"] [class*="note"] p) {
+ font-size: var(--easy-presenter-note-size) !important;
+ line-height: 1.65 !important;
+}
+
+body.easy-notes-contrast :is(.presenter-notes, [class*="presenter"] [class*="notes"], [class*="presenter"] [class*="note"]) {
+ background: #050505 !important;
+ color: #fff !important;
+}
+
+body.easy-notes-contrast :is(.presenter-notes, [class*="presenter"] [class*="notes"], [class*="presenter"] [class*="note"]) * {
+ color: inherit !important;
+}
diff --git a/dist-cli/theme/utils/resolve-image.ts b/dist-cli/theme/utils/resolve-image.ts
new file mode 100644
index 0000000..437049c
--- /dev/null
+++ b/dist-cli/theme/utils/resolve-image.ts
@@ -0,0 +1,15 @@
+const deckBase = (import.meta as ImportMeta & { env: { BASE_URL: string } }).env.BASE_URL
+
+export function resolveImageSource(source: string | undefined, base = deckBase) {
+ if (!source || isAbsoluteSource(source))
+ return source
+
+ const normalizedBase = base.endsWith('/') ? base : `${base}/`
+ return `${normalizedBase}${source.replace(/^\.\//, '')}`
+}
+
+function isAbsoluteSource(source: string) {
+ return source.startsWith('/')
+ || source.startsWith('#')
+ || /^[a-z][a-z\d+.-]*:/i.test(source)
+}