mirror of
https://github.com/delibae/claude-prism.git
synced 2026-08-28 05:14:59 +00:00
- Fix unused imports and variables - Apply biome formatting (template literals, semicolons, line width) - Fix a11y issues (label associations, anchor content) - Fix Node.js import protocol (node:fs, node:path) - Use self.onmessage in web worker Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
/**
|
|
* Generate example projects for all LaTeX templates.
|
|
*
|
|
* Usage: pnpm --filter @claude-prism/desktop generate-previews
|
|
*
|
|
* Requires pdflatex to be installed (part of TeX Live / MacTeX).
|
|
* Output: public/examples/{template-id}/main.tex, main.pdf, references.bib (if applicable)
|
|
*/
|
|
|
|
import { execSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import os from "node:os";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
// Import template registry (pure data, no DOM dependencies)
|
|
const registryPath = path.resolve(__dirname, "../src/lib/template-registry.ts");
|
|
|
|
// Since this is a .ts file with exports, we rely on tsx to handle it
|
|
async function loadTemplates() {
|
|
const mod = await import(registryPath);
|
|
return mod.getAllTemplates() as Array<{
|
|
id: string;
|
|
name: string;
|
|
mainFileName: string;
|
|
content: string;
|
|
hasBibliography: boolean;
|
|
}>;
|
|
}
|
|
|
|
const EXAMPLES_DIR = path.resolve(__dirname, "../public/examples");
|
|
const COMPILE_TIMEOUT = 30_000;
|
|
|
|
async function main() {
|
|
console.log("Generating example projects...\n");
|
|
|
|
const templates = await loadTemplates();
|
|
let successCount = 0;
|
|
let failCount = 0;
|
|
|
|
for (const template of templates) {
|
|
process.stdout.write(` ${template.id}... `);
|
|
|
|
const exampleDir = path.join(EXAMPLES_DIR, template.id);
|
|
fs.mkdirSync(exampleDir, { recursive: true });
|
|
|
|
const tmpDir = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), `prism-preview-${template.id}-`),
|
|
);
|
|
|
|
try {
|
|
// Write .tex file (inject \null for blank templates to force a page)
|
|
let content = template.content;
|
|
if (template.id === "blank") {
|
|
content = content.replace(
|
|
"\\begin{document}",
|
|
"\\begin{document}\n\\null",
|
|
);
|
|
}
|
|
const texPath = path.join(tmpDir, template.mainFileName);
|
|
fs.writeFileSync(texPath, content, "utf-8");
|
|
|
|
// Write stub .bib if template uses bibliography
|
|
if (template.hasBibliography) {
|
|
fs.writeFileSync(
|
|
path.join(tmpDir, "references.bib"),
|
|
"% empty bibliography\n",
|
|
"utf-8",
|
|
);
|
|
}
|
|
|
|
const cmd = [
|
|
"pdflatex",
|
|
"-interaction=nonstopmode",
|
|
`-output-directory=${tmpDir}`,
|
|
texPath,
|
|
].join(" ");
|
|
|
|
// First pass
|
|
execSync(cmd, { cwd: tmpDir, timeout: COMPILE_TIMEOUT, stdio: "pipe" });
|
|
|
|
// Second pass (for TOC, references, etc.)
|
|
try {
|
|
execSync(cmd, { cwd: tmpDir, timeout: COMPILE_TIMEOUT, stdio: "pipe" });
|
|
} catch {
|
|
// Second pass failure is non-fatal
|
|
}
|
|
|
|
// Copy source files to example folder
|
|
fs.copyFileSync(texPath, path.join(exampleDir, template.mainFileName));
|
|
if (template.hasBibliography) {
|
|
fs.copyFileSync(
|
|
path.join(tmpDir, "references.bib"),
|
|
path.join(exampleDir, "references.bib"),
|
|
);
|
|
}
|
|
|
|
// Copy compiled PDF to example folder
|
|
const pdfName = template.mainFileName.replace(/\.tex$/, ".pdf");
|
|
const pdfPath = path.join(tmpDir, pdfName);
|
|
|
|
if (fs.existsSync(pdfPath)) {
|
|
fs.copyFileSync(pdfPath, path.join(exampleDir, pdfName));
|
|
const sizeKb = Math.round(
|
|
fs.statSync(path.join(exampleDir, pdfName)).size / 1024,
|
|
);
|
|
console.log(`OK (${sizeKb} KB)`);
|
|
successCount++;
|
|
} else {
|
|
console.log("WARN: no PDF output");
|
|
failCount++;
|
|
}
|
|
} catch (err) {
|
|
const msg =
|
|
err instanceof Error ? err.message.slice(0, 120) : String(err);
|
|
console.log(`FAIL: ${msg}`);
|
|
failCount++;
|
|
} finally {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
console.log(`\nDone: ${successCount} succeeded, ${failCount} failed`);
|
|
if (failCount > 0) {
|
|
console.log(
|
|
"Note: Failed templates may require document classes not installed in your TeX distribution.",
|
|
);
|
|
console.log(
|
|
"The gallery will show CSS fallback thumbnails for those templates.",
|
|
);
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("Fatal error:", err);
|
|
process.exit(1);
|
|
});
|