pdfn 0.8.6 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +15 -10
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ const client = pdfn();
|
|
|
45
45
|
// Or pdfn Cloud
|
|
46
46
|
// const client = pdfn(process.env.PDFN_API_KEY);
|
|
47
47
|
|
|
48
|
-
const { data, error } = await client.generate(<Invoice />);
|
|
48
|
+
const { data, error } = await client.generate({ react: <Invoice /> });
|
|
49
49
|
|
|
50
50
|
if (error) {
|
|
51
51
|
console.error(error.message);
|
package/dist/cli.js
CHANGED
|
@@ -439,13 +439,12 @@ import { resolve } from "path";
|
|
|
439
439
|
import { config } from "dotenv";
|
|
440
440
|
function loadEnv(mode) {
|
|
441
441
|
const cwd = process.cwd();
|
|
442
|
-
const envFiles = [
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
for (const file of envFiles) {
|
|
442
|
+
const envFiles = [".env", ".env.local"];
|
|
443
|
+
if (mode) {
|
|
444
|
+
envFiles.push(`.env.${mode}`, `.env.${mode}.local`);
|
|
445
|
+
}
|
|
446
|
+
const unique = [...new Set(envFiles)];
|
|
447
|
+
for (const file of unique) {
|
|
449
448
|
const filePath = resolve(cwd, file);
|
|
450
449
|
if (existsSync(filePath)) {
|
|
451
450
|
config({ path: filePath, override: true, quiet: true });
|
|
@@ -2222,10 +2221,16 @@ async function startDevServer(options) {
|
|
|
2222
2221
|
async function renderTemplate(template, debugOptions = false) {
|
|
2223
2222
|
const mod = await vite.ssrLoadModule(template.path);
|
|
2224
2223
|
const Component = mod.default;
|
|
2225
|
-
const {
|
|
2226
|
-
|
|
2224
|
+
const { pdfn: pdfn2 } = await vite.ssrLoadModule("@pdfn/react");
|
|
2225
|
+
const client = pdfn2();
|
|
2226
|
+
const { data, error } = await client.render({
|
|
2227
|
+
react: React.createElement(Component, {}),
|
|
2227
2228
|
debug: debugOptions || void 0
|
|
2228
2229
|
});
|
|
2230
|
+
if (error) {
|
|
2231
|
+
throw new Error(error.message);
|
|
2232
|
+
}
|
|
2233
|
+
return data.html;
|
|
2229
2234
|
}
|
|
2230
2235
|
function parseDebugOptions(query) {
|
|
2231
2236
|
const options2 = {
|
|
@@ -2561,7 +2566,7 @@ async function startDevServer(options) {
|
|
|
2561
2566
|
});
|
|
2562
2567
|
}
|
|
2563
2568
|
}
|
|
2564
|
-
var devCommand = new Command("dev").description("Start development server with live preview").option("--port <number>", "Server port", "3456").option("--open", "Open browser automatically").option("--mode <mode>", "
|
|
2569
|
+
var devCommand = new Command("dev").description("Start development server with live preview").option("--port <number>", "Server port", "3456").option("--open", "Open browser automatically").option("--mode <mode>", "Load additional .env.[mode] files").action(async (options) => {
|
|
2565
2570
|
loadEnv(options.mode);
|
|
2566
2571
|
const port = parseInt(options.port, 10);
|
|
2567
2572
|
await startDevServer({
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/dev.ts","../src/server/base.ts","../src/server/browser.ts","../src/utils/debug.ts","../src/server/pdf.ts","../src/server/routes.ts","../src/utils/env.ts","../src/commands/add.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { addCommand } from \"./commands/add.js\";\n\nconst program = new Command()\n .name(\"pdfn\")\n .description(\"PDFN CLI - PDF generation from React components\")\n .version(\"0.0.1-alpha.1\");\n\nprogram.addCommand(devCommand);\nprogram.addCommand(addCommand);\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport { existsSync, readdirSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport type { Request, Response } from \"express\";\nimport { createServer as createViteServer } from \"vite\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport chokidar from \"chokidar\";\nimport { createServer as createHttpServer } from \"http\";\nimport { spawn } from \"child_process\";\nimport puppeteer from \"puppeteer\";\nimport { createBaseServer } from \"../server/base\";\nimport { generatePdf } from \"../server/pdf\";\nimport type { DebugOptions } from \"@pdfn/react\";\nimport { pdfn } from \"@pdfn/vite\";\nimport chalk from \"chalk\";\nimport { loadEnv } from \"../utils/env\";\nimport React from \"react\";\nimport axeCore from \"axe-core\";\n\ninterface TemplateInfo {\n id: string;\n name: string;\n file: string;\n path: string;\n sampleData?: Record<string, unknown>;\n}\n\n/**\n * Standardized templates directory (convention over configuration)\n */\nconst TEMPLATES_DIR = \"./pdfn-templates\";\n\ninterface DevServerOptions {\n port: number;\n open: boolean;\n mode: string;\n}\n\nasync function scanTemplates(templatesDir: string): Promise<TemplateInfo[]> {\n if (!existsSync(templatesDir)) {\n return [];\n }\n\n // Try to load config file with sample data\n let configData: Record<string, Record<string, unknown>> = {};\n const configPaths = [\n join(process.cwd(), \"src\", \"config\", \"templates.json\"),\n join(process.cwd(), \"templates.json\"),\n join(templatesDir, \"templates.json\"),\n ];\n\n for (const configPath of configPaths) {\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (config.templates && Array.isArray(config.templates)) {\n for (const t of config.templates) {\n if (t.id && t.sampleData) {\n configData[t.id] = t.sampleData;\n }\n }\n }\n break;\n } catch {\n // Ignore config parse errors\n }\n }\n }\n\n const files = readdirSync(templatesDir).filter((f) => f.endsWith(\".tsx\"));\n\n // Filter to only include files that look like templates\n // (have \"export default function\" or similar patterns)\n const templates: TemplateInfo[] = [];\n\n for (const file of files) {\n const id = file.replace(\".tsx\", \"\");\n const filePath = join(templatesDir, file);\n\n // Quick check: read file and look for default export pattern\n try {\n const content = readFileSync(filePath, \"utf-8\");\n // Must have a default export that looks like a component\n if (!content.includes(\"export default\") || !content.includes(\"function\")) {\n continue;\n }\n // Skip files that are clearly not templates (no Document/Page imports)\n if (!content.includes(\"Document\") && !content.includes(\"Page\")) {\n continue;\n }\n } catch {\n continue;\n }\n\n // Convert kebab/snake case to Title Case\n const name = id\n .split(/[-_]/)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n\n templates.push({\n id,\n name,\n file,\n path: filePath,\n sampleData: configData[id],\n });\n }\n\n return templates;\n}\n\nfunction createPreviewHTML(templates: TemplateInfo[], activeTemplate: string | null, hasCloudAccess: boolean): string {\n const templateList = templates\n .map(\n (t) => `\n <button\n class=\"template-btn ${t.id === activeTemplate ? \"active\" : \"\"}\"\n data-template=\"${t.id}\"\n data-file=\"${t.file}\"\n >\n <span class=\"template-name\">${t.name}</span>\n </button>\n `\n )\n .join(\"\");\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>PDFN Dev Server</title>\n <style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --primary: #22d3ee;\n --primary-hover: #06b6d4;\n --bg: #0a0a0a;\n --surface-1: #111;\n --surface-2: #1a1a1a;\n --border: #222;\n --text: #fafafa;\n --text-muted: #9a9a9a;\n --text-secondary: #b0b0b0;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: var(--bg);\n color: var(--text);\n min-height: 100vh;\n }\n\n .header {\n background: var(--surface-1);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .logo {\n font-size: 20px;\n font-weight: 800;\n letter-spacing: -0.5px;\n }\n\n .logo span { color: var(--primary); }\n\n .status {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n }\n\n .status-dot.disconnected { background: #ef4444; }\n\n .container {\n display: flex;\n height: calc(100vh - 57px);\n }\n\n .main-wrapper {\n flex: 1;\n display: flex;\n overflow: hidden;\n }\n\n .sidebar {\n width: 200px;\n background: var(--surface-1);\n border-right: 1px solid var(--border);\n padding: 16px;\n overflow-y: auto;\n }\n\n .sidebar-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-muted);\n margin-bottom: 12px;\n }\n\n .template-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 12px;\n background: transparent;\n border: 1px solid #333;\n border-radius: 6px;\n color: #ccc;\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n margin-bottom: 8px;\n transition: all 0.15s;\n }\n\n .template-btn:hover {\n background: var(--surface-2);\n border-color: #444;\n }\n\n .template-btn.active {\n background: rgba(34, 211, 238, 0.1);\n border-color: var(--primary);\n color: var(--primary);\n }\n\n .template-name { font-weight: 500; }\n\n .main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* Context bar - top */\n .context-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--surface-1);\n border-bottom: 1px solid var(--border);\n }\n\n .context-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .file-name {\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 13px;\n color: var(--text);\n font-weight: 500;\n }\n\n .file-name:empty::after {\n content: \"Select a template\";\n color: var(--text-muted);\n font-style: italic;\n font-family: inherit;\n }\n\n .page-info {\n font-size: 12px;\n color: var(--text-muted);\n }\n\n .context-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Preview area */\n .preview-area {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #27272a;\n overflow: hidden;\n padding: 24px;\n }\n\n .preview-frame {\n background: #fff;\n border-radius: 4px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.4);\n overflow: hidden;\n transition: opacity 0.2s;\n }\n\n .preview-frame.loading { opacity: 0.4; }\n\n .preview-frame iframe {\n display: block;\n border: none;\n }\n\n .empty-state {\n text-align: center;\n color: var(--text-muted);\n }\n\n .empty-state h2 {\n font-size: 18px;\n margin-bottom: 8px;\n color: var(--text-secondary);\n }\n\n .empty-state p {\n font-size: 14px;\n margin-bottom: 16px;\n }\n\n .empty-state code {\n background: var(--surface-2);\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 13px;\n }\n\n .loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--text-muted);\n }\n\n .spinner {\n width: 32px;\n height: 32px;\n border: 2px solid #333;\n border-top-color: var(--primary);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .spin {\n animation: spin 1s linear infinite;\n }\n\n /* Inspector panel - right side */\n .inspector-panel {\n width: 260px;\n background: var(--surface-1);\n border-left: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n }\n\n .inspector-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text);\n padding: 12px 16px;\n border-bottom: 1px solid var(--border);\n }\n\n .inspector-content {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n .inspector-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .inspector-section-title {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-muted);\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .metrics-note {\n font-size: 10px;\n color: var(--text-muted);\n margin-top: 8px;\n font-style: italic;\n }\n\n /* Metrics display */\n .metrics-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .metric-item {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n }\n\n .metric-label {\n font-size: 12px;\n color: var(--text-muted);\n }\n\n .metric-value {\n font-size: 14px;\n font-weight: 600;\n color: var(--primary);\n font-variant-numeric: tabular-nums;\n }\n\n /* Overlay checkboxes */\n .overlay-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .overlay-checkbox {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n .overlay-checkbox:hover {\n color: var(--text);\n }\n\n .overlay-checkbox input {\n appearance: none;\n width: 16px;\n height: 16px;\n border: 1px solid #444;\n border-radius: 3px;\n background: var(--surface-2);\n cursor: pointer;\n position: relative;\n flex-shrink: 0;\n }\n\n .overlay-checkbox input:hover {\n border-color: #555;\n }\n\n .overlay-checkbox input:checked {\n background: var(--primary);\n border-color: var(--primary);\n }\n\n .overlay-checkbox input:checked::after {\n content: \"\";\n position: absolute;\n left: 5px;\n top: 2px;\n width: 4px;\n height: 8px;\n border: solid #000;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .debug-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 8px;\n font-size: 12px;\n color: var(--text-muted);\n text-decoration: none;\n transition: color 0.15s;\n }\n\n .debug-link:hover {\n color: var(--primary);\n }\n\n .debug-link svg {\n opacity: 0.7;\n }\n\n /* Accessibility section */\n .a11y-check-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 8px 12px;\n background: var(--surface-2);\n border: 1px solid #444;\n border-radius: 6px;\n color: var(--text-secondary);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .a11y-check-btn:hover {\n background: var(--surface-3);\n border-color: #555;\n color: var(--text);\n }\n\n .a11y-check-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .a11y-check-btn.loading {\n color: var(--text-muted);\n }\n\n .a11y-check-btn .status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n margin-left: 4px;\n }\n\n .a11y-check-btn .status-dot.pass { background: #22c55e; }\n .a11y-check-btn .status-dot.fail { background: #ef4444; }\n\n .a11y-results {\n margin-top: 10px;\n max-height: 350px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: #555 transparent;\n }\n\n .a11y-results::-webkit-scrollbar {\n width: 6px;\n }\n\n .a11y-results::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .a11y-results::-webkit-scrollbar-thumb {\n background: #555;\n border-radius: 3px;\n }\n\n .a11y-results::-webkit-scrollbar-thumb:hover {\n background: #666;\n }\n\n .a11y-timestamp {\n font-size: 10px;\n color: var(--text-muted);\n text-align: right;\n margin-bottom: 8px;\n }\n\n .a11y-stale {\n font-size: 11px;\n color: #f59e0b;\n background: rgba(245, 158, 11, 0.1);\n border: 1px solid rgba(245, 158, 11, 0.2);\n border-radius: 4px;\n padding: 6px 10px;\n margin-bottom: 8px;\n text-align: center;\n }\n\n .a11y-empty {\n font-size: 12px;\n color: var(--text-muted);\n text-align: center;\n padding: 12px 8px;\n }\n\n .a11y-pass {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #22c55e;\n padding: 10px;\n background: rgba(34, 197, 94, 0.1);\n border-radius: 4px;\n border: 1px solid rgba(34, 197, 94, 0.2);\n }\n\n .a11y-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n padding: 8px 10px;\n border-radius: 4px;\n margin-bottom: 10px;\n }\n\n .a11y-summary.has-issues {\n background: rgba(239, 68, 68, 0.1);\n border: 1px solid rgba(239, 68, 68, 0.2);\n color: #fca5a5;\n }\n\n .a11y-summary .issue-count {\n font-weight: 600;\n }\n\n .a11y-violation {\n padding: 10px;\n margin-bottom: 8px;\n background: var(--surface-2);\n border-radius: 4px;\n border-left: 3px solid;\n animation: slideIn 0.2s ease-out;\n }\n\n @keyframes slideIn {\n from { opacity: 0; transform: translateX(-4px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n .a11y-violation.critical { border-color: #ef4444; }\n .a11y-violation.serious { border-color: #f97316; }\n .a11y-violation.moderate { border-color: #eab308; }\n .a11y-violation.minor { border-color: #3b82f6; }\n\n .a11y-violation-header {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--text);\n }\n\n .a11y-violation-impact {\n font-size: 9px;\n padding: 2px 6px;\n border-radius: 3px;\n text-transform: uppercase;\n font-weight: 600;\n letter-spacing: 0.3px;\n }\n\n .a11y-violation-impact.critical { background: #ef4444; color: white; }\n .a11y-violation-impact.serious { background: #f97316; color: white; }\n .a11y-violation-impact.moderate { background: #eab308; color: #000; }\n .a11y-violation-impact.minor { background: #3b82f6; color: white; }\n\n .a11y-violation-id {\n font-family: monospace;\n font-size: 11px;\n }\n\n .a11y-violation-desc {\n font-size: 11px;\n color: var(--text-muted);\n margin-top: 6px;\n line-height: 1.4;\n }\n\n .a11y-violation-count {\n font-size: 10px;\n color: var(--text-muted);\n margin-top: 4px;\n opacity: 0.8;\n }\n\n /* Accessibility accordion */\n .a11y-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n user-select: none;\n }\n\n .a11y-header:hover {\n color: var(--text-secondary);\n }\n\n .a11y-toggle {\n transition: transform 0.2s;\n }\n\n .a11y-toggle.collapsed {\n transform: rotate(-90deg);\n }\n\n .a11y-content {\n margin-top: 8px;\n }\n\n /* Console section */\n .console-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n user-select: none;\n }\n\n .console-header:hover {\n color: var(--text-secondary);\n }\n\n .console-toggle {\n transition: transform 0.2s;\n }\n\n .console-toggle.collapsed {\n transform: rotate(-90deg);\n }\n\n .console-content {\n font-size: 12px;\n }\n\n .console-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--text-muted);\n }\n\n .console-empty svg {\n color: #22c55e;\n }\n\n .console-message {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 4px 0;\n }\n\n .console-message.error {\n color: #ef4444;\n }\n\n .console-message.warning {\n color: #eab308;\n }\n\n .console-message-icon {\n flex-shrink: 0;\n }\n\n .console-message-text {\n word-break: break-all;\n }\n\n /* Buttons */\n .btn {\n padding: 6px 10px;\n background: var(--surface-2);\n border: 1px solid #333;\n border-radius: 5px;\n color: #ccc;\n font-size: 12px;\n cursor: pointer;\n transition: all 0.15s;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n }\n\n .btn:hover { background: #333; border-color: #444; }\n\n .btn-primary {\n background: var(--primary);\n border-color: var(--primary);\n color: #000;\n font-weight: 600;\n }\n\n .btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }\n\n .standard-select {\n padding: 6px 10px;\n background: var(--surface-2);\n border: 1px solid #333;\n border-radius: 6px;\n color: var(--text-secondary);\n font-size: 12px;\n cursor: pointer;\n }\n\n .standard-select:hover { border-color: #444; }\n .standard-select:focus { outline: none; border-color: var(--primary); }\n\n .cloud-key-message {\n display: none;\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 8px;\n width: 280px;\n padding: 12px;\n background: var(--surface-2);\n border: 1px solid #444;\n border-radius: 8px;\n font-size: 12px;\n color: var(--text-secondary);\n z-index: 100;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n }\n\n .cloud-key-message.visible { display: block; }\n\n .cloud-key-message-title {\n display: flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n color: var(--text);\n margin-bottom: 8px;\n }\n\n .cloud-key-message code {\n display: block;\n background: var(--bg);\n padding: 8px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 11px;\n margin: 8px 0;\n word-break: break-all;\n }\n\n .cloud-key-message a {\n color: var(--primary);\n text-decoration: none;\n }\n\n .cloud-key-message a:hover { text-decoration: underline; }\n\n .context-actions {\n position: relative;\n }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div class=\"logo\">pdf<span>n</span> <span style=\"font-weight: 400; color: var(--text-muted); font-size: 14px; margin-left: 8px;\">dev</span></div>\n <div class=\"status\">\n <div class=\"status-dot\" id=\"status-dot\"></div>\n <span id=\"status-text\">Connected</span>\n </div>\n </header>\n\n <div class=\"container\">\n <aside class=\"sidebar\">\n <div class=\"sidebar-title\">Templates</div>\n ${\n templates.length > 0\n ? templateList\n : '<div class=\"empty-state\"><p>No templates found</p><code>pdfn add invoice</code></div>'\n }\n </aside>\n\n <div class=\"main-wrapper\">\n <main class=\"main\">\n <!-- Context bar: filename + page info + actions -->\n <div class=\"context-bar\">\n <div class=\"context-left\">\n <div class=\"file-name\" id=\"file-name\"></div>\n <div class=\"page-info\" id=\"page-info\"></div>\n </div>\n <div class=\"context-actions\">\n <select id=\"standard-select\" class=\"standard-select\" title=\"PDF standard\">\n <option value=\"\">Standard PDF (Local)</option>\n <option value=\"PDF/A-1b\">PDF/A-1b (Cloud)</option>\n <option value=\"PDF/A-2b\">PDF/A-2b (Cloud)</option>\n <option value=\"PDF/A-3b\">PDF/A-3b (Cloud)</option>\n </select>\n <button class=\"btn\" id=\"preview-pdf\" title=\"Preview PDF\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"/>\n </svg>\n Preview\n </button>\n <button class=\"btn btn-primary\" id=\"download-pdf\" title=\"Download PDF\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/>\n </svg>\n Download\n </button>\n <div class=\"cloud-key-message\" id=\"cloud-key-message\">\n <div class=\"cloud-key-message-title\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10\"/>\n </svg>\n API key required\n </div>\n <div>Set <strong>PDFN_API_KEY</strong> in your environment</div>\n <code>PDFN_API_KEY=pdfn_...</code>\n <div>\n <a href=\"https://console.pdfn.dev\" target=\"_blank\">Get your API key →</a>\n </div>\n <div style=\"margin-top: 8px; font-size: 11px; color: var(--text-muted);\">\n Restart dev server after setting\n </div>\n </div>\n </div>\n </div>\n\n <!-- Preview area -->\n <div class=\"preview-area\" id=\"preview-area\">\n ${\n activeTemplate\n ? '<div class=\"loading-spinner\"><div class=\"spinner\"></div><span>Loading preview...</span></div>'\n : '<div class=\"empty-state\"><h2>Select a template</h2><p>Choose a template from the sidebar to preview</p></div>'\n }\n </div>\n </main>\n\n <!-- Inspector panel: right side -->\n <aside class=\"inspector-panel\" id=\"inspector-panel\">\n <div class=\"inspector-title\">Inspector</div>\n <div class=\"inspector-content\">\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title\">Performance</div>\n <div class=\"metrics-grid\">\n <div class=\"metric-item\">\n <div class=\"metric-label\">Render</div>\n <div class=\"metric-value\" id=\"metric-render\">--</div>\n </div>\n <div class=\"metric-item\">\n <div class=\"metric-label\">Pagination</div>\n <div class=\"metric-value\" id=\"metric-pagination\">--</div>\n </div>\n <div class=\"metric-item\">\n <div class=\"metric-label\">Pages</div>\n <div class=\"metric-value\" id=\"metric-pages\">--</div>\n </div>\n </div>\n <div class=\"metrics-note\">Measured in browser. Times vary on server.</div>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title\">Debug</div>\n <div class=\"overlay-list\">\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-grid\">\n Grid (1cm)\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-margins\">\n Margins\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-headers\">\n Headers/Footers\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-breaks\">\n Page numbers\n </label>\n </div>\n <a class=\"debug-link\" id=\"view-html\" href=\"#\" target=\"_blank\">\n View HTML\n <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"/>\n </svg>\n </a>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title a11y-header\" id=\"a11y-header\">\n <span>Accessibility</span>\n <svg class=\"a11y-toggle\" id=\"a11y-toggle\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"/>\n </svg>\n </div>\n <div class=\"a11y-content\" id=\"a11y-content\">\n <button class=\"a11y-check-btn\" id=\"a11y-check-btn\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/>\n </svg>\n Check\n </button>\n <div id=\"a11y-results\" class=\"a11y-results\">\n <div class=\"a11y-empty\">Analyze template for WCAG compliance</div>\n </div>\n </div>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title console-header\" id=\"console-header\">\n <span>Console</span>\n <svg class=\"console-toggle\" id=\"console-toggle\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"/>\n </svg>\n </div>\n <div class=\"console-content\" id=\"console-content\">\n <div class=\"console-empty\" id=\"console-empty\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"/>\n </svg>\n No issues\n </div>\n <div id=\"console-messages\" style=\"display: none;\"></div>\n </div>\n </div>\n </div>\n </aside>\n </div>\n </div>\n\n <script>\n // Cloud access (API key available)\n const hasCloudAccess = ${hasCloudAccess};\n\n // Page sizes in points (72 dpi)\n const PAGE_SIZES = {\n A3: { width: 842, height: 1191 },\n A4: { width: 595, height: 842 },\n A5: { width: 420, height: 595 },\n B4: { width: 709, height: 1001 },\n B5: { width: 499, height: 709 },\n Letter: { width: 612, height: 792 },\n Legal: { width: 612, height: 1008 },\n Tabloid: { width: 792, height: 1224 },\n };\n\n const PT_TO_PX = 96 / 72;\n const STORAGE_KEY = 'pdfn-inspector';\n\n let ws;\n let currentTemplate = ${activeTemplate ? `\"${activeTemplate}\"` : \"null\"};\n let templateInfo = {};\n\n // Inspector state with defaults (all overlays off by default)\n let inspectorState = {\n overlays: {\n grid: false,\n margins: false,\n headers: false,\n breaks: false,\n },\n a11yExpanded: true,\n consoleExpanded: true\n };\n\n // Console messages\n let consoleMessages = [];\n\n // Load state from localStorage\n function loadState() {\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n const parsed = JSON.parse(saved);\n inspectorState = { ...inspectorState, ...parsed };\n }\n } catch (e) {}\n }\n\n // Save state to localStorage\n function saveState() {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(inspectorState));\n } catch (e) {}\n }\n\n // Apply state to UI\n function applyState() {\n // Overlay checkboxes\n document.getElementById('overlay-grid').checked = inspectorState.overlays.grid;\n document.getElementById('overlay-margins').checked = inspectorState.overlays.margins;\n document.getElementById('overlay-headers').checked = inspectorState.overlays.headers;\n document.getElementById('overlay-breaks').checked = inspectorState.overlays.breaks;\n\n // Accessibility accordion state\n const a11yContent = document.getElementById('a11y-content');\n const a11yToggle = document.getElementById('a11y-toggle');\n if (inspectorState.a11yExpanded) {\n a11yContent.style.display = 'block';\n a11yToggle.classList.remove('collapsed');\n } else {\n a11yContent.style.display = 'none';\n a11yToggle.classList.add('collapsed');\n }\n\n // Console state\n const consoleContent = document.getElementById('console-content');\n const consoleToggle = document.getElementById('console-toggle');\n if (inspectorState.consoleExpanded) {\n consoleContent.style.display = 'block';\n consoleToggle.classList.remove('collapsed');\n } else {\n consoleContent.style.display = 'none';\n consoleToggle.classList.add('collapsed');\n }\n }\n\n // Toggle accessibility expanded state\n function toggleA11y() {\n inspectorState.a11yExpanded = !inspectorState.a11yExpanded;\n saveState();\n applyState();\n }\n\n // Toggle console expanded state\n function toggleConsole() {\n inspectorState.consoleExpanded = !inspectorState.consoleExpanded;\n saveState();\n applyState();\n }\n\n // Add console message\n function addConsoleMessage(type, message) {\n consoleMessages.push({ type, message });\n renderConsoleMessages();\n }\n\n // Clear console messages\n function clearConsoleMessages() {\n consoleMessages = [];\n renderConsoleMessages();\n }\n\n // Render console messages\n function renderConsoleMessages() {\n const emptyEl = document.getElementById('console-empty');\n const messagesEl = document.getElementById('console-messages');\n\n if (consoleMessages.length === 0) {\n emptyEl.style.display = 'flex';\n messagesEl.style.display = 'none';\n messagesEl.innerHTML = '';\n } else {\n emptyEl.style.display = 'none';\n messagesEl.style.display = 'block';\n messagesEl.innerHTML = consoleMessages.map(msg =>\n \\`<div class=\"console-message \\${msg.type}\">\n <span class=\"console-message-icon\">\\${msg.type === 'error' ? '✗' : '⚠'}</span>\n <span class=\"console-message-text\">\\${escapeHtml(msg.message)}</span>\n </div>\\`\n ).join('');\n }\n }\n\n // Escape HTML\n function escapeHtml(text) {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n // Get debug query string from overlay state\n function getDebugQuery() {\n const { overlays } = inspectorState;\n const hasAny = Object.values(overlays).some(v => v);\n if (!hasAny) return '';\n\n const params = new URLSearchParams();\n if (overlays.grid) params.set('grid', '1');\n if (overlays.margins) params.set('margins', '1');\n if (overlays.headers) params.set('headers', '1');\n if (overlays.breaks) params.set('breaks', '1');\n\n return '?' + params.toString();\n }\n\n // Check if any overlay is enabled\n function hasAnyOverlay() {\n return Object.values(inspectorState.overlays).some(v => v);\n }\n\n loadState();\n\n function connect() {\n ws = new WebSocket('ws://' + location.host);\n\n ws.onopen = () => {\n document.getElementById('status-dot').classList.remove('disconnected');\n document.getElementById('status-text').textContent = 'Connected';\n };\n\n ws.onclose = () => {\n document.getElementById('status-dot').classList.add('disconnected');\n document.getElementById('status-text').textContent = 'Disconnected';\n setTimeout(connect, 2000);\n };\n\n ws.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.type === 'reload') {\n if (currentTemplate) loadPreview(currentTemplate, { hotReload: true });\n } else if (data.type === 'templates') {\n updateTemplateList(data.templates);\n }\n };\n }\n\n connect();\n\n // Update template list in sidebar (called when templates are added/removed)\n function updateTemplateList(templates) {\n const sidebar = document.querySelector('.sidebar');\n const titleEl = sidebar.querySelector('.sidebar-title');\n\n // Clear existing buttons\n sidebar.innerHTML = '';\n sidebar.appendChild(titleEl);\n\n if (templates.length === 0) {\n sidebar.innerHTML += '<div class=\"empty-state\"><p>No templates found</p><code>pdfn add invoice</code></div>';\n // Clear preview if no templates\n currentTemplate = null;\n document.getElementById('file-name').textContent = '';\n document.getElementById('page-info').textContent = '';\n document.getElementById('preview-area').innerHTML = '<div class=\"empty-state\"><h2>No templates</h2><p>Add a template to get started</p><code>pdfn add invoice</code></div>';\n return;\n }\n\n // Add template buttons\n templates.forEach(t => {\n const btn = document.createElement('button');\n btn.className = 'template-btn' + (t.id === currentTemplate ? ' active' : '');\n btn.dataset.template = t.id;\n btn.dataset.file = t.file;\n btn.innerHTML = '<span class=\"template-name\">' + t.name + '</span>';\n btn.addEventListener('click', () => loadPreview(t.id));\n sidebar.appendChild(btn);\n });\n\n // If current template was deleted, switch to first template\n const templateIds = templates.map(t => t.id);\n if (currentTemplate && !templateIds.includes(currentTemplate)) {\n loadPreview(templates[0].id);\n }\n }\n\n // Listen for metrics from iframe (postMessage from PDFN script)\n window.addEventListener('message', function(event) {\n if (event.data && event.data.type === 'pdfn:metrics') {\n const metrics = event.data.metrics;\n if (metrics.paginationTime !== undefined) {\n document.getElementById('metric-pagination').textContent = metrics.paginationTime + 'ms';\n }\n if (metrics.pages !== undefined) {\n document.getElementById('metric-pages').textContent = metrics.pages;\n }\n }\n });\n\n function detectPageInfo(html) {\n let pageSize = 'A4';\n let orientation = 'portrait';\n\n // Check for data-pdfn-size attribute (e.g., \"A4\", \"Letter Landscape\", \"Tabloid\")\n const sizeMatch = html.match(/data-pdfn-size=\"([^\"]+)\"/i);\n if (sizeMatch) {\n const sizeStr = sizeMatch[1];\n // Check if it includes \"Landscape\"\n if (sizeStr.includes('Landscape')) {\n orientation = 'landscape';\n }\n // Extract base size name (remove \" Landscape\" suffix if present)\n const baseName = sizeStr.replace(' Landscape', '').replace(' Portrait', '');\n if (PAGE_SIZES[baseName]) {\n pageSize = baseName;\n }\n }\n\n return { pageSize, orientation };\n }\n\n function calculateScale(pageSize, orientation) {\n const container = document.getElementById('preview-area');\n const rect = container.getBoundingClientRect();\n const maxWidth = rect.width - 48;\n const maxHeight = rect.height - 48;\n\n const size = PAGE_SIZES[pageSize] || PAGE_SIZES.A4;\n const pageW = (orientation === 'landscape' ? size.height : size.width) * PT_TO_PX;\n const pageH = (orientation === 'landscape' ? size.width : size.height) * PT_TO_PX;\n\n const scale = Math.min(maxWidth / pageW, maxHeight / pageH, 1);\n return { pageW, pageH, scale };\n }\n\n function resetA11yState() {\n a11yHasRun = false;\n a11yLastStatus = null;\n const a11yBtn = document.getElementById('a11y-check-btn');\n const a11yResults = document.getElementById('a11y-results');\n a11yBtn.innerHTML = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg> Check';\n a11yResults.innerHTML = '<div class=\"a11y-empty\">Analyze template for WCAG compliance</div>';\n }\n\n function markA11yStale() {\n if (!a11yHasRun) return; // Nothing to mark stale\n const a11yResults = document.getElementById('a11y-results');\n // Add stale indicator at top of results\n const staleMsg = '<div class=\"a11y-stale\">Template changed — results may be outdated</div>';\n if (!a11yResults.innerHTML.includes('a11y-stale')) {\n a11yResults.insertAdjacentHTML('afterbegin', staleMsg);\n }\n }\n\n async function loadPreview(templateId, options) {\n options = options || {};\n const isTemplateSwitch = templateId !== currentTemplate;\n const isHotReload = options.hotReload === true;\n\n currentTemplate = templateId;\n\n // Handle accessibility state based on context\n if (isTemplateSwitch) {\n // Switching to different template - full reset\n resetA11yState();\n } else if (isHotReload) {\n // Same template but content changed - mark as stale\n if (a11yHasRun) markA11yStale();\n }\n // For resize/overlay changes - preserve state (do nothing)\n\n // Get template file name from button\n const activeBtn = document.querySelector('.template-btn[data-template=\"' + templateId + '\"]');\n const fileName = activeBtn ? activeBtn.dataset.file : templateId + '.tsx';\n\n document.querySelectorAll('.template-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.template === templateId);\n });\n\n // Update context bar\n document.getElementById('file-name').textContent = fileName;\n\n const previewArea = document.getElementById('preview-area');\n previewArea.innerHTML = '<div class=\"loading-spinner\"><div class=\"spinner\"></div><span>Rendering...</span></div>';\n\n try {\n const debugQuery = getDebugQuery();\n const htmlRes = await fetch('/api/template/' + templateId + '/html' + debugQuery);\n const html = await htmlRes.text();\n\n templateInfo = detectPageInfo(html);\n document.getElementById('page-info').textContent =\n templateInfo.pageSize + ' · ' + templateInfo.orientation;\n\n // Update metrics (render time from server, pagination from iframe postMessage)\n const renderTime = htmlRes.headers.get('X-Render-Time');\n\n if (renderTime) {\n document.getElementById('metric-render').textContent = renderTime + 'ms';\n }\n\n // Reset pagination metrics (will be updated via postMessage from iframe)\n document.getElementById('metric-pagination').textContent = '--';\n document.getElementById('metric-pages').textContent = '--';\n\n const { pageW, pageH, scale } = calculateScale(templateInfo.pageSize, templateInfo.orientation);\n const displayW = pageW * scale;\n const displayH = pageH * scale;\n\n previewArea.innerHTML = '';\n const frame = document.createElement('div');\n frame.className = 'preview-frame';\n frame.style.width = displayW + 'px';\n frame.style.height = displayH + 'px';\n\n const iframe = document.createElement('iframe');\n iframe.style.width = pageW + 'px';\n iframe.style.height = pageH + 'px';\n iframe.style.transform = 'scale(' + scale + ')';\n iframe.style.transformOrigin = 'top left';\n iframe.srcdoc = html;\n\n frame.classList.add('loading');\n iframe.onload = () => frame.classList.remove('loading');\n\n frame.appendChild(iframe);\n previewArea.appendChild(frame);\n\n // Set up action buttons\n const pdfUrl = '/api/template/' + templateId + '/pdf' + debugQuery;\n const htmlUrl = '/api/template/' + templateId + '/html' + debugQuery;\n const spinnerSvg = '<svg class=\"spin\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"2\" stroke-dasharray=\"32\" stroke-dashoffset=\"12\"/></svg>';\n\n document.getElementById('view-html').href = htmlUrl;\n\n // Preview button handler (local only - hidden when standard selected)\n document.getElementById('preview-pdf').onclick = () => {\n window.open(pdfUrl, '_blank');\n };\n\n // Download button handler\n document.getElementById('download-pdf').onclick = async () => {\n const standard = document.getElementById('standard-select').value;\n const btn = document.getElementById('download-pdf');\n\n if (standard) {\n // Use pdfn cloud for PDF/A standard\n btn.innerHTML = spinnerSvg + ' Generating...';\n btn.disabled = true;\n\n try {\n const response = await fetch('/api/template/' + templateId + '/pdf-standard?standard=' + encodeURIComponent(standard));\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error || 'Failed to generate PDF');\n }\n const blob = await response.blob();\n const link = document.createElement('a');\n link.href = URL.createObjectURL(blob);\n link.download = templateId + '.pdf';\n link.click();\n URL.revokeObjectURL(link.href);\n } catch (err) {\n alert('Error: ' + err.message);\n } finally {\n updateActionButtons();\n btn.disabled = false;\n }\n } else {\n // Standard local PDF\n const link = document.createElement('a');\n link.href = pdfUrl;\n link.download = templateId + '.pdf';\n link.click();\n }\n };\n\n } catch (err) {\n previewArea.innerHTML = '<div class=\"empty-state\"><h2>Error</h2><p>' + err.message + '</p></div>';\n }\n }\n\n document.querySelectorAll('.template-btn').forEach(btn => {\n btn.addEventListener('click', () => loadPreview(btn.dataset.template));\n });\n\n // Overlay checkbox handlers\n const overlayCheckboxes = ['grid', 'margins', 'headers', 'breaks'];\n overlayCheckboxes.forEach(name => {\n document.getElementById('overlay-' + name).onchange = (e) => {\n inspectorState.overlays[name] = e.target.checked;\n saveState();\n if (currentTemplate) loadPreview(currentTemplate);\n };\n });\n\n // Button icons\n const previewIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"/></svg>';\n const downloadIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg>';\n const cloudDownloadIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10\"/></svg>';\n\n // Update Preview and Download buttons based on standard selection\n function updateActionButtons() {\n const standard = document.getElementById('standard-select').value;\n const previewBtn = document.getElementById('preview-pdf');\n const downloadBtn = document.getElementById('download-pdf');\n const cloudMessage = document.getElementById('cloud-key-message');\n const needsCloud = !!standard;\n const cloudAvailable = hasCloudAccess;\n\n if (needsCloud) {\n // Cloud mode - hide Preview, show cloud Download\n previewBtn.style.display = 'none';\n downloadBtn.innerHTML = cloudDownloadIcon + ' Download';\n downloadBtn.title = 'Download ' + standard + ' PDF via pdfn Cloud';\n\n if (!cloudAvailable) {\n // Show message and disable Download\n cloudMessage.classList.add('visible');\n downloadBtn.disabled = true;\n downloadBtn.style.opacity = '0.5';\n downloadBtn.style.cursor = 'not-allowed';\n } else {\n // Hide message and enable Download\n cloudMessage.classList.remove('visible');\n downloadBtn.disabled = false;\n downloadBtn.style.opacity = '';\n downloadBtn.style.cursor = '';\n }\n } else {\n // Local mode - show Preview, show local Download\n previewBtn.style.display = '';\n previewBtn.innerHTML = previewIcon + ' Preview';\n previewBtn.title = 'Preview PDF';\n downloadBtn.innerHTML = downloadIcon + ' Download';\n downloadBtn.title = 'Download PDF';\n // Hide message and enable buttons\n cloudMessage.classList.remove('visible');\n downloadBtn.disabled = false;\n downloadBtn.style.opacity = '';\n downloadBtn.style.cursor = '';\n }\n }\n\n // Conformance select change handler\n document.getElementById('standard-select').onchange = updateActionButtons;\n\n // Accessibility check handler\n let a11yHasRun = false;\n let a11yLastStatus = null; // 'pass' | 'fail' | null\n\n async function runAccessibilityCheck() {\n if (!currentTemplate) return;\n\n const btn = document.getElementById('a11y-check-btn');\n const results = document.getElementById('a11y-results');\n\n btn.disabled = true;\n btn.classList.add('loading');\n btn.innerHTML = '<svg class=\"spin\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"2\" stroke-dasharray=\"31.4\" stroke-dashoffset=\"10\"/></svg> Checking...';\n results.innerHTML = '<div class=\"a11y-empty\">Running accessibility checks...</div>';\n\n try {\n const res = await fetch('/api/template/' + currentTemplate + '/accessibility');\n const data = await res.json();\n const timestamp = new Date().toLocaleTimeString();\n\n if (data.violations.length === 0) {\n a11yLastStatus = 'pass';\n results.innerHTML = '<div class=\"a11y-timestamp\">Checked at ' + timestamp + '</div>' +\n '<div class=\"a11y-pass\"><svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"/></svg> No accessibility issues found</div>';\n } else {\n a11yLastStatus = 'fail';\n // Count by severity\n const counts = { critical: 0, serious: 0, moderate: 0, minor: 0 };\n data.violations.forEach(function(v) { counts[v.impact || 'minor']++; });\n\n let html = '<div class=\"a11y-timestamp\">Checked at ' + timestamp + '</div>';\n html += '<div class=\"a11y-summary has-issues\">';\n html += '<span class=\"issue-count\">' + data.violations.length + ' issue' + (data.violations.length > 1 ? 's' : '') + '</span>';\n // Show severity breakdown\n const parts = [];\n if (counts.critical > 0) parts.push(counts.critical + ' critical');\n if (counts.serious > 0) parts.push(counts.serious + ' serious');\n if (counts.moderate > 0) parts.push(counts.moderate + ' moderate');\n if (counts.minor > 0) parts.push(counts.minor + ' minor');\n html += '<span style=\"font-size: 10px; opacity: 0.8;\">' + parts.join(', ') + '</span>';\n html += '</div>';\n\n // Sort violations by severity (critical first)\n const severityOrder = { critical: 0, serious: 1, moderate: 2, minor: 3 };\n const sorted = data.violations.slice().sort(function(a, b) {\n return (severityOrder[a.impact] || 3) - (severityOrder[b.impact] || 3);\n });\n\n for (let i = 0; i < sorted.length; i++) {\n const v = sorted[i];\n const impact = v.impact || 'minor';\n // Escape HTML in description and id\n const desc = String(v.description || '').replace(/</g, '<').replace(/>/g, '>');\n const ruleId = String(v.id || 'unknown').replace(/</g, '<').replace(/>/g, '>');\n html += '<div class=\"a11y-violation ' + impact + '\" style=\"animation-delay: ' + (i * 0.05) + 's\">';\n html += '<div class=\"a11y-violation-header\">';\n html += '<span class=\"a11y-violation-impact ' + impact + '\">' + impact + '</span>';\n html += '<span class=\"a11y-violation-id\">' + ruleId + '</span>';\n html += '</div>';\n html += '<div class=\"a11y-violation-desc\">' + desc + '</div>';\n if (v.nodes && v.nodes.length > 0) {\n html += '<div class=\"a11y-violation-count\">' + v.nodes.length + ' element' + (v.nodes.length > 1 ? 's' : '') + ' affected</div>';\n }\n html += '</div>';\n }\n\n results.innerHTML = html;\n }\n } catch (err) {\n a11yLastStatus = null;\n results.innerHTML = '<div class=\"a11y-empty\" style=\"color: #ef4444;\">Error: ' + err.message + '</div>';\n }\n\n a11yHasRun = true;\n btn.disabled = false;\n btn.classList.remove('loading');\n\n // Update button with status indicator\n const statusDot = a11yLastStatus ? '<span class=\"status-dot ' + a11yLastStatus + '\"></span>' : '';\n btn.innerHTML = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg> Recheck' + statusDot;\n }\n\n document.getElementById('a11y-check-btn').onclick = runAccessibilityCheck;\n document.getElementById('a11y-header').onclick = toggleA11y;\n\n // Console header click handler\n document.getElementById('console-header').onclick = toggleConsole;\n\n let resizeTimeout;\n window.addEventListener('resize', () => {\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(() => {\n if (currentTemplate) loadPreview(currentTemplate);\n }, 150);\n });\n\n // Apply saved state and load first template\n applyState();\n ${activeTemplate ? `loadPreview(\"${activeTemplate}\");` : \"\"}\n </script>\n</body>\n</html>`;\n}\n\nasync function startDevServer(options: DevServerOptions) {\n const { port, open, mode } = options;\n const absoluteTemplatesDir = resolve(process.cwd(), TEMPLATES_DIR);\n\n // Show header and initializing message\n console.log(chalk.bold(\"\\n pdfn dev\\n\"));\n console.log(chalk.dim(\" Initializing...\"));\n\n // Scan templates\n let templates = await scanTemplates(absoluteTemplatesDir);\n\n // Show relative path for cleaner output\n const displayPath = TEMPLATES_DIR;\n const templateCount = templates.length === 1 ? \"1 template\" : `${templates.length} templates`;\n\n // Create base server with shared /v1/generate and /health endpoints\n const { app, browserManager } = createBaseServer({\n enableLogging: false, // Dev uses custom logging per route\n onSuccess: (result) => {\n const { metrics } = result;\n console.log(\n chalk.green(\" ✓\"),\n chalk.dim(\"/v1/generate\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`,\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n },\n onError: (message) => {\n console.log(chalk.red(\" ✗\"), chalk.dim(\"/v1/generate\"), chalk.red(message));\n },\n });\n const server = createHttpServer(app);\n\n // Create WebSocket server for hot reload\n const wss = new WebSocketServer({ server });\n const clients = new Set<WebSocket>();\n\n wss.on(\"connection\", (ws) => {\n clients.add(ws);\n ws.on(\"close\", () => clients.delete(ws));\n });\n\n wss.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n // Handled by server error handler\n return;\n }\n console.error(chalk.red(\" ✗ WebSocket error:\"), err.message);\n });\n\n function broadcast(message: object) {\n const data = JSON.stringify(message);\n clients.forEach((client) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(data);\n }\n });\n }\n\n // Create Vite server for template compilation\n // Custom logger to silence Vite's default output (we handle our own logging)\n const viteLogger = {\n info: () => {},\n warn: () => {},\n error: (msg: string) => console.error(chalk.red(\" ✗ Vite:\"), msg),\n warnOnce: () => {},\n hasWarned: false,\n clearScreen: () => {},\n hasErrorLogged: () => false,\n };\n\n const vite = await createViteServer({\n root: process.cwd(),\n mode,\n server: {\n middlewareMode: true,\n hmr: { server } // Use our HTTP server for Vite's WebSocket\n },\n appType: \"custom\",\n customLogger: viteLogger,\n optimizeDeps: {\n include: [\"react\", \"react-dom\"],\n },\n esbuild: {\n jsx: \"automatic\",\n },\n ssr: {\n // Don't externalize these - we need to transform them\n noExternal: [\"@pdfn/react\", \"@pdfn/tailwind\", \"@pdfn/client\", \"server-only\"],\n },\n plugins: [\n // Unified pdfn plugin: Tailwind pre-compilation + client/template markers\n ...pdfn(),\n {\n name: \"mock-server-only\",\n enforce: \"pre\",\n resolveId(id) {\n if (id === \"server-only\") {\n return { id: \"\\0server-only-mock\", moduleSideEffects: false };\n }\n },\n load(id) {\n if (id === \"\\0server-only-mock\") {\n // Return empty module - we're in SSR context so server-only is fine\n return \"export default {};\";\n }\n },\n },\n ],\n });\n\n\n // Watch for template and CSS changes\n // Watch templates dir and styles.css + styles/ directory for CSS HMR\n const watchPaths = [\n absoluteTemplatesDir,\n join(absoluteTemplatesDir, \"styles.css\"),\n join(absoluteTemplatesDir, \"styles\"),\n ];\n const watcher = chokidar.watch(watchPaths, {\n ignored: /(^|[\\/\\\\])\\../,\n persistent: true,\n });\n\n // Helper to check if a file is a template (matches what we show in sidebar)\n function isTemplateFile(filePath: string): boolean {\n const fileName = filePath.split(\"/\").pop() || \"\";\n // Must be a .tsx file in the root of templates dir (not in subdirectories)\n if (!fileName.endsWith(\".tsx\")) return false;\n // Check if it's a direct child of templates dir\n const relativePath = filePath.replace(absoluteTemplatesDir + \"/\", \"\");\n if (relativePath.includes(\"/\")) return false;\n return true;\n }\n\n // Helper to check if file is a code file (tsx/ts/js/jsx)\n function isCodeFile(filePath: string): boolean {\n return /\\.(tsx?|jsx?)$/.test(filePath);\n }\n\n // Helper to check if file is a CSS file in templates\n function isCssFile(filePath: string): boolean {\n return filePath.endsWith(\".css\");\n }\n\n // Suppress logging during initial scan\n let watcherReady = false;\n\n watcher.on(\"ready\", () => {\n watcherReady = true;\n });\n\n watcher.on(\"change\", async (filePath) => {\n if (!watcherReady) return;\n const fileName = filePath.split(\"/\").pop() || filePath;\n\n // Handle CSS file changes (styles.css or styles/*.css)\n if (isCssFile(filePath)) {\n console.log(chalk.blue(\" ↻\"), fileName, chalk.dim(\"changed\"));\n\n // Invalidate the virtual Tailwind CSS module to force recompilation\n const virtualMod = vite.moduleGraph.getModuleById(\"\\0virtual:pdfn-tailwind-css\");\n if (virtualMod) {\n vite.moduleGraph.invalidateModule(virtualMod);\n // Invalidate all modules that import the virtual module\n for (const importer of virtualMod.importers) {\n vite.moduleGraph.invalidateModule(importer);\n }\n }\n\n templates = await scanTemplates(absoluteTemplatesDir);\n broadcast({ type: \"reload\" });\n return;\n }\n\n // Log all code file changes (templates and components)\n if (isCodeFile(filePath)) {\n console.log(chalk.blue(\" ↻\"), fileName, chalk.dim(\"changed\"));\n\n // Invalidate the changed module and its dependencies in Vite's SSR cache\n const mod = vite.moduleGraph.getModuleById(filePath);\n if (mod) {\n vite.moduleGraph.invalidateModule(mod);\n }\n\n // Also invalidate the virtual Tailwind CSS module to force recompilation\n const virtualMod = vite.moduleGraph.getModuleById(\"\\0virtual:pdfn-tailwind-css\");\n if (virtualMod) {\n vite.moduleGraph.invalidateModule(virtualMod);\n // Invalidate all modules that import the virtual module\n for (const importer of virtualMod.importers) {\n vite.moduleGraph.invalidateModule(importer);\n }\n }\n }\n templates = await scanTemplates(absoluteTemplatesDir);\n broadcast({ type: \"reload\" });\n });\n\n watcher.on(\"add\", async (filePath) => {\n if (!watcherReady) return;\n const oldCount = templates.length;\n templates = await scanTemplates(absoluteTemplatesDir);\n // Only log if a new template was actually added (not components)\n if (templates.length > oldCount && isTemplateFile(filePath)) {\n const fileName = filePath.split(\"/\").pop() || filePath;\n console.log(chalk.green(\" +\"), fileName, chalk.dim(\"added\"));\n }\n // Send updated template list so sidebar can be refreshed\n broadcast({ type: \"templates\", templates: templates.map(t => ({ id: t.id, name: t.name, file: t.file })) });\n });\n\n watcher.on(\"unlink\", async (filePath) => {\n if (!watcherReady) return;\n const oldCount = templates.length;\n const fileName = filePath.split(\"/\").pop() || filePath;\n templates = await scanTemplates(absoluteTemplatesDir);\n // Only log if a template was actually removed (not components)\n if (templates.length < oldCount && isTemplateFile(filePath)) {\n console.log(chalk.red(\" -\"), fileName, chalk.dim(\"removed\"));\n }\n // Send updated template list so sidebar can be refreshed\n broadcast({ type: \"templates\", templates: templates.map(t => ({ id: t.id, name: t.name, file: t.file })) });\n });\n\n // Serve preview UI\n app.get(\"/\", (_req: Request, res: Response) => {\n const activeTemplate = templates[0]?.id ?? null;\n const hasCloudAccess = !!process.env.PDFN_API_KEY;\n res.send(createPreviewHTML(templates, activeTemplate, hasCloudAccess));\n });\n\n // API: Get template code\n app.get(\"/api/template/:id/code\", (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const code = readFileSync(template.path, \"utf-8\");\n res.type(\"text/plain\").send(code);\n } catch {\n res.status(500).send(\"Error reading template\");\n }\n });\n\n // Helper: Render a template to HTML using Vite\n // Templates use default parameter values for sample data (React Email pattern)\n async function renderTemplate(\n template: TemplateInfo,\n debugOptions: DebugOptions | false = false\n ): Promise<string> {\n const mod = await vite.ssrLoadModule(template.path);\n const Component = mod.default;\n const { render } = await vite.ssrLoadModule(\"@pdfn/react\");\n // Use React.createElement so element.type === Component (preserves markers)\n // Empty props - component's default parameter values provide sample data\n return render(React.createElement(Component, {}), {\n debug: debugOptions || undefined,\n });\n }\n\n // Parse debug options from query params\n function parseDebugOptions(query: Record<string, unknown>): DebugOptions | false {\n const options: DebugOptions = {\n grid: query.grid === \"1\",\n margins: query.margins === \"1\",\n headers: query.headers === \"1\",\n breaks: query.breaks === \"1\",\n };\n\n // Return false if no options enabled\n const hasAny = Object.values(options).some((v) => v);\n return hasAny ? options : false;\n }\n\n // API: Get rendered HTML\n app.get(\"/api/template/:id/html\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const start = performance.now();\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const duration = Math.round(performance.now() - start);\n\n // Count pages from rendered HTML (look for pagedjs page markers)\n const pageMatches = html.match(/class=\"pagedjs_page\"/g);\n const pageCount = pageMatches ? pageMatches.length : 1;\n\n // Log render\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ HTML\"),\n chalk.cyan(`${duration}ms`)\n );\n\n res.setHeader(\"X-Render-Time\", duration.toString());\n res.setHeader(\"X-Page-Count\", pageCount.toString());\n // Note: Pagination time is measured client-side after Paged.js runs\n res.type(\"text/html\").send(html);\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"render failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).send(`Error rendering template: ${error}`);\n }\n });\n\n // Helper: Format bytes for display\n function formatBytes(bytes: number): string {\n if (bytes < 1024) return bytes + \"B\";\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + \"KB\";\n return (bytes / (1024 * 1024)).toFixed(2) + \"MB\";\n }\n\n // API: Generate PDF\n app.get(\"/api/template/:id/pdf\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, { timeout: 30000 });\n });\n\n // Log PDF generation - first line: request summary\n const { metrics, warnings, assets } = result;\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ PDF •\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n\n // Second line: details (pages, assets, timing) - all separated by •\n const pageStr = `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`;\n const images = assets.filter((a) => a.type === \"image\");\n const fonts = assets.filter((a) => a.type === \"font\");\n const assetParts: string[] = [];\n if (images.length > 0) assetParts.push(`${images.length} image${images.length > 1 ? \"s\" : \"\"}`);\n if (fonts.length > 0) assetParts.push(`${fonts.length} font${fonts.length > 1 ? \"s\" : \"\"}`);\n const assetStr = assetParts.length > 0 ? assetParts.join(\" • \") + \" • \" : \"\";\n const timingStr = `load ${metrics.contentLoad}ms • paginate ${metrics.pagedJs}ms • capture ${metrics.pdfCapture}ms`;\n console.log(chalk.dim(` ${pageStr} • ${assetStr}${timingStr}`));\n\n // Log warnings\n if (warnings.length > 0) {\n warnings.forEach((w) => {\n console.log(chalk.yellow(\" ⚠\"), chalk.yellow(w));\n });\n }\n\n // Add metrics headers\n res.setHeader(\"X-PDF-Total-Time\", metrics.total.toString());\n res.setHeader(\"X-PDF-Pages\", metrics.pageCount.toString());\n res.setHeader(\"X-PDF-Size\", metrics.pdfSize.toString());\n res.setHeader(\"X-PDF-Capture-Time\", metrics.pdfCapture.toString());\n res.setHeader(\"X-PDF-Assets\", result.assets.length.toString());\n res.setHeader(\"X-PDF-Warnings\", warnings.length.toString());\n\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"Content-Disposition\", `inline; filename=\"${template.id}.pdf\"`);\n res.send(result.buffer);\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"PDF generation failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).send(`Error generating PDF: ${error}`);\n }\n });\n\n // API: Generate PDF with standard (via pdfn Cloud)\n app.get(\"/api/template/:id/pdf-standard\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n const standard = req.query.standard as string;\n const validStandards = [\"PDF/A-1b\", \"PDF/A-2b\", \"PDF/A-3b\"];\n if (!standard || !validStandards.includes(standard)) {\n res.status(400).json({ error: `Invalid standard. Must be one of: ${validStandards.join(\", \")}` });\n return;\n }\n\n const apiKey = process.env.PDFN_API_KEY;\n if (!apiKey) {\n res.status(400).json({\n error: \"PDFN_API_KEY required for PDF/A standard. Get one at https://console.pdfn.dev\"\n });\n return;\n }\n\n try {\n const start = performance.now();\n const html = await renderTemplate(template, false);\n\n // Call pdfn Cloud API with standard\n const response = await fetch(\"https://api.pdfn.dev/v1/generate\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ html, standard }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: response.statusText }));\n throw new Error(error.message || error.error || `API error: ${response.status}`);\n }\n\n const pdfBuffer = Buffer.from(await response.arrayBuffer());\n const duration = Math.round(performance.now() - start);\n const pdfSize = parseInt(response.headers.get(\"X-PDF-Size\") || String(pdfBuffer.length));\n\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(`→ ${standard}`),\n chalk.magenta(\"(Cloud)\"),\n chalk.dim(\"•\"),\n chalk.cyan(`${duration}ms`),\n chalk.dim(\"•\"),\n formatBytes(pdfSize)\n );\n\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"Content-Disposition\", `attachment; filename=\"${template.id}.pdf\"`);\n res.send(pdfBuffer);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(`${standard} generation failed:`), message);\n res.status(500).json({ error: message });\n }\n });\n\n // API: Run accessibility check\n app.get(\"/api/template/:id/accessibility\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n try {\n const start = performance.now();\n const html = await renderTemplate(template, false);\n\n // Run axe-core in Puppeteer\n const results = await browserManager.withPage(async (page) => {\n await page.setContent(html, { waitUntil: \"domcontentloaded\" });\n\n // Wait for PDFN.ready (Paged.js to finish transforming DOM)\n await page.waitForFunction(\"window.PDFN?.ready === true\", { timeout: 30000 });\n\n // Inject axe-core\n await page.addScriptTag({ content: axeCore.source });\n\n // Run axe with PDF-relevant configuration\n const axeResults = await page.evaluate(() => {\n return new Promise<{\n violations: Array<{\n id: string;\n impact?: string;\n description: string;\n nodes: Array<{ target: string[] }>;\n }>;\n }>((resolve, reject) => {\n // @ts-expect-error axe is injected\n window.axe.run(document, {\n rules: {\n // Disable rules not relevant for PDFs\n \"scrollable-region-focusable\": { enabled: false },\n \"nested-interactive\": { enabled: false },\n tabindex: { enabled: false },\n \"focus-order-semantics\": { enabled: false },\n \"aria-hidden-focus\": { enabled: false },\n \"landmark-one-main\": { enabled: false },\n \"landmark-no-duplicate-banner\": { enabled: false },\n \"landmark-no-duplicate-contentinfo\": { enabled: false },\n \"landmark-no-duplicate-main\": { enabled: false },\n \"landmark-unique\": { enabled: false },\n region: { enabled: false },\n bypass: { enabled: false },\n \"skip-link\": { enabled: false },\n },\n }, (err: Error | null, results: { violations: Array<{ id: string; impact?: string; description: string; nodes: Array<{ target: string[] }> }> }) => {\n if (err) reject(err);\n else resolve(results);\n });\n });\n });\n\n return axeResults;\n });\n\n const duration = Math.round(performance.now() - start);\n\n // Log result\n const violationCount = results.violations.length;\n if (violationCount === 0) {\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ a11y\"),\n chalk.green(\"pass\"),\n chalk.dim(`(${duration}ms)`)\n );\n } else {\n console.log(\n chalk.yellow(\" ⚠\"),\n template.file,\n chalk.dim(\"→ a11y\"),\n chalk.yellow(`${violationCount} issue${violationCount > 1 ? \"s\" : \"\"}`),\n chalk.dim(`(${duration}ms)`)\n );\n }\n\n res.json({\n violations: results.violations.map((v) => ({\n id: v.id,\n impact: v.impact,\n description: v.description,\n nodes: v.nodes.map((n) => ({ target: n.target })),\n })),\n duration,\n });\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"a11y check failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).json({ error: `Error running accessibility check: ${error}` });\n }\n });\n\n // API: Generate PDF and return metrics (JSON)\n app.get(\"/api/template/:id/pdf-metrics\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n try {\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, { timeout: 30000 });\n });\n\n // Log PDF metrics - first line: request summary\n const { metrics, warnings, assets } = result;\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ PDF metrics •\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n\n // Second line: details (pages, assets, timing) - all separated by •\n {\n const pageStr = `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`;\n const images = assets.filter((a) => a.type === \"image\");\n const fonts = assets.filter((a) => a.type === \"font\");\n const assetParts: string[] = [];\n if (images.length > 0) assetParts.push(`${images.length} image${images.length > 1 ? \"s\" : \"\"}`);\n if (fonts.length > 0) assetParts.push(`${fonts.length} font${fonts.length > 1 ? \"s\" : \"\"}`);\n const assetStr = assetParts.length > 0 ? assetParts.join(\" • \") + \" • \" : \"\";\n const timingStr = `load ${metrics.contentLoad}ms • paginate ${metrics.pagedJs}ms • capture ${metrics.pdfCapture}ms`;\n console.log(chalk.dim(` ${pageStr} • ${assetStr}${timingStr}`));\n }\n\n if (warnings.length > 0) {\n warnings.forEach((w) => {\n console.log(chalk.yellow(\" ⚠\"), chalk.yellow(w));\n });\n }\n\n res.json({\n metrics: result.metrics,\n assets: result.assets,\n warnings: result.warnings,\n });\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"PDF metrics failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).json({ error: `Error generating PDF: ${error}` });\n }\n });\n\n // Start server\n await new Promise<void>((resolve, reject) => {\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n console.error(chalk.red(`\\n ✗ Port ${port} is already in use.\\n`));\n console.error(chalk.dim(` Either stop the existing server or use a different port:`));\n console.error(chalk.dim(` npx pdfn dev --port ${port + 1}\\n`));\n process.exit(1);\n }\n reject(err);\n });\n server.listen(port, () => resolve());\n });\n\n // Pre-launch Chromium for PDF generation\n await browserManager.getBrowser();\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(chalk.dim(\"\\n Shutting down...\"));\n watcher.close();\n await vite.close();\n await browserManager.close();\n server.close();\n process.exit(0);\n };\n\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n\n // Open Chromium (same browser used for PDF generation = true WYSIWYG)\n function openChromium() {\n const chromiumPath = puppeteer.executablePath();\n console.log(chalk.dim(\" Opening in Chromium...\\n\"));\n spawn(chromiumPath, [`http://localhost:${port}`], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n }\n\n console.log(chalk.dim(` Templates: ${displayPath} (${templateCount})`));\n console.log(chalk.green(`\\n ✓ Ready at ${chalk.cyan(`http://localhost:${port}`)}\\n`));\n\n if (!open) {\n console.log(chalk.dim(` Tip: PDFs are generated with Chromium. For accurate preview,`));\n console.log(chalk.dim(` open in Chrome or Chromium-based browsers.\\n`));\n }\n\n console.log(chalk.dim(` Shortcuts`));\n console.log(chalk.dim(` › ${chalk.white(\"o\")} open in Chromium`));\n console.log(chalk.dim(` › ${chalk.white(\"q\")} quit\\n`));\n\n if (open) {\n openChromium();\n }\n\n // Keyboard shortcuts\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (key: string) => {\n if (key === \"o\" || key === \"O\") {\n openChromium();\n } else if (key === \"q\" || key === \"Q\" || key === \"\\u0003\") {\n // q or Ctrl+C\n shutdown();\n }\n });\n }\n}\n\nexport const devCommand = new Command(\"dev\")\n .description(\"Start development server with live preview\")\n .option(\"--port <number>\", \"Server port\", \"3456\")\n .option(\"--open\", \"Open browser automatically\")\n .option(\"--mode <mode>\", \"Environment mode (loads .env.[mode])\", \"development\")\n .action(async (options) => {\n // Load environment variables based on mode (Vite pattern)\n loadEnv(options.mode);\n\n const port = parseInt(options.port, 10);\n\n await startDevServer({\n port,\n open: options.open ?? false,\n mode: options.mode,\n });\n });\n","import express from \"express\";\nimport type { Express, Request, Response, NextFunction } from \"express\";\nimport { BrowserManager } from \"./browser\";\nimport { createGenerateHandler } from \"./routes\";\nimport { type PdfResult } from \"./pdf\";\n\nexport interface BaseServerOptions {\n /** Max concurrent pages (default: env.PDFN_MAX_CONCURRENT || 5) */\n maxConcurrent?: number;\n /** Request timeout in ms (default: env.PDFN_TIMEOUT || 30000) */\n timeout?: number;\n /** Enable request logging (default: true) */\n enableLogging?: boolean;\n /** Custom logger for requests */\n onRequest?: (method: string, path: string, status: number, duration: number, extra?: string) => void;\n /** Custom logger for PDF details */\n onPdfResult?: (result: PdfResult) => void;\n /** Callback on successful PDF generation */\n onSuccess?: (result: PdfResult) => void;\n /** Custom logger for errors */\n onError?: (message: string) => void;\n}\n\nexport interface BaseServer {\n app: Express;\n browserManager: BrowserManager;\n maxConcurrent: number;\n timeout: number;\n}\n\n/**\n * Create a base PDFN server with common middleware and routes.\n *\n * This is shared between `dev` and `serve` commands.\n * Returns the Express app and BrowserManager for further customization.\n *\n * Includes:\n * - JSON body parsing (50mb limit)\n * - Request logging middleware (optional)\n * - POST /v1/generate endpoint\n * - GET /health endpoint\n */\nexport function createBaseServer(options: BaseServerOptions = {}): BaseServer {\n const maxConcurrent =\n options.maxConcurrent ??\n parseInt(process.env.PDFN_MAX_CONCURRENT ?? \"5\", 10);\n const timeout =\n options.timeout ?? parseInt(process.env.PDFN_TIMEOUT ?? \"30000\", 10);\n const enableLogging = options.enableLogging ?? true;\n\n const app = express();\n const browserManager = new BrowserManager({ maxConcurrent, timeout });\n\n // JSON body parsing\n app.use(express.json({ limit: \"50mb\" }));\n\n // Request logging middleware\n if (enableLogging && options.onRequest) {\n const onRequest = options.onRequest;\n const onPdfResult = options.onPdfResult;\n\n app.use((req: Request, res: Response, next: NextFunction) => {\n // Skip noisy requests\n const skipPaths = [\"/favicon.ico\", \"/robots.txt\"];\n if (skipPaths.includes(req.path)) {\n next();\n return;\n }\n\n const start = performance.now();\n\n res.on(\"finish\", () => {\n const duration = performance.now() - start;\n\n // For /v1/generate, include page count and size\n let extra: string | undefined;\n if (req.path === \"/v1/generate\" && res.statusCode === 200) {\n const pages = res.getHeader(\"X-PDFN-Page-Count\");\n const size = Number(res.getHeader(\"X-PDFN-PDF-Size\")) || 0;\n const sizeKB = (size / 1024).toFixed(1);\n extra = `${pages} pages • ${sizeKB}KB`;\n }\n\n onRequest(req.method, req.path, res.statusCode, duration, extra);\n\n // Log PDF details if available\n const pdfResult = (res as any)._pdfResult as PdfResult | undefined;\n if (pdfResult && onPdfResult) {\n onPdfResult(pdfResult);\n }\n });\n\n next();\n });\n }\n\n // Health check endpoint\n app.get(\"/health\", (_req: Request, res: Response) => {\n res.json({\n status: \"ok\",\n browser: browserManager.isConnected() ? \"connected\" : \"disconnected\",\n activePages: browserManager.getActivePages(),\n maxConcurrent: browserManager.getMaxConcurrent(),\n });\n });\n\n // PDF generation endpoint (matches pdfn Cloud API)\n app.post(\n \"/v1/generate\",\n createGenerateHandler(browserManager, {\n timeout,\n onSuccess: options.onSuccess,\n onError: options.onError,\n })\n );\n\n return {\n app,\n browserManager,\n maxConcurrent,\n timeout,\n };\n}\n","import puppeteer, { Browser, Page } from \"puppeteer\";\n\nexport interface BrowserManagerOptions {\n /** Max concurrent pages (default: env.PDFN_MAX_CONCURRENT || 5) */\n maxConcurrent?: number;\n /** Request timeout in ms (default: env.PDFN_TIMEOUT || 30000) */\n timeout?: number;\n}\n\n/**\n * Manages a single Puppeteer browser instance with multiple concurrent pages\n *\n * Architecture: Single browser + multiple pages (industry standard pattern)\n * - Lower memory footprint (~150MB browser + ~30MB/page)\n * - Fast page creation (~50ms vs ~1-2s for new browser)\n * - Auto-restart on browser crash/disconnect\n */\nexport class BrowserManager {\n private browser: Browser | null = null;\n private launching: Promise<Browser> | null = null;\n private activePages = 0;\n private maxConcurrent: number;\n private timeout: number;\n\n constructor(options: BrowserManagerOptions = {}) {\n this.maxConcurrent =\n options.maxConcurrent ??\n parseInt(process.env.PDFN_MAX_CONCURRENT ?? \"5\", 10);\n this.timeout =\n options.timeout ?? parseInt(process.env.PDFN_TIMEOUT ?? \"30000\", 10);\n }\n\n /**\n * Get or create the browser instance\n */\n async getBrowser(): Promise<Browser> {\n if (this.browser?.connected) {\n return this.browser;\n }\n\n // If already launching, wait for that\n if (this.launching) {\n return this.launching;\n }\n\n // Launch new browser\n this.launching = puppeteer.launch({\n headless: true,\n args: [\n \"--no-sandbox\",\n \"--disable-setuid-sandbox\",\n \"--disable-dev-shm-usage\",\n \"--disable-gpu\",\n \"--disable-extensions\",\n \"--mute-audio\",\n \"--disable-notifications\",\n ],\n });\n\n try {\n this.browser = await this.launching;\n this.setupBrowserListeners(this.browser);\n return this.browser;\n } finally {\n this.launching = null;\n }\n }\n\n /**\n * Setup browser event listeners for auto-restart on crash\n */\n private setupBrowserListeners(browser: Browser): void {\n browser.on(\"disconnected\", () => {\n this.browser = null;\n // activePages will naturally drain as withPage catches errors\n });\n }\n\n /**\n * Create a new page for PDF generation\n */\n async createPage(): Promise<Page> {\n const browser = await this.getBrowser();\n const page = await browser.newPage();\n page.setDefaultTimeout(this.timeout);\n return page;\n }\n\n /**\n * Execute a function with a managed page\n *\n * This pattern ensures:\n * - Concurrency limits are respected\n * - Pages are always closed after use\n * - Active page count is accurate\n *\n * @throws Error if max concurrent pages reached\n */\n async withPage<T>(fn: (page: Page) => Promise<T>): Promise<T> {\n if (this.activePages >= this.maxConcurrent) {\n throw new Error(\"Server busy - max concurrent requests reached\");\n }\n\n this.activePages++;\n let page: Page | null = null;\n\n try {\n page = await this.createPage();\n return await fn(page);\n } finally {\n this.activePages--;\n if (page) {\n await page.close().catch(() => {\n // Ignore close errors (browser may have disconnected)\n });\n }\n }\n }\n\n /**\n * Check if browser is connected\n */\n isConnected(): boolean {\n return this.browser?.connected ?? false;\n }\n\n /**\n * Get current number of active pages\n */\n getActivePages(): number {\n return this.activePages;\n }\n\n /**\n * Get max concurrent pages limit\n */\n getMaxConcurrent(): number {\n return this.maxConcurrent;\n }\n\n /**\n * Get configured timeout\n */\n getTimeout(): number {\n return this.timeout;\n }\n\n /**\n * Close the browser and clean up\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n}\n","/**\n * Simple debug logging utility\n *\n * Enable with DEBUG=pdfn:cli or DEBUG=pdfn:* or DEBUG=pdfn\n *\n * This is a lightweight alternative to the `debug` npm package.\n * Supports namespace filtering similar to the debug package convention.\n */\n\n// Debug logging - enable with DEBUG=pdfn:cli or DEBUG=pdfn:* or DEBUG=pdfn\nconst debugEnv = process.env.DEBUG ?? \"\";\nconst isEnabled =\n debugEnv.includes(\"pdfn:cli\") ||\n debugEnv.includes(\"pdfn:*\") ||\n debugEnv === \"pdfn\" ||\n debugEnv.includes(\"pdfn,\") ||\n debugEnv.endsWith(\",pdfn\");\n\n/**\n * Debug logger - only logs when DEBUG=pdfn:cli (or pdfn:* or pdfn) is set\n */\nexport const debug = isEnabled\n ? (message: string, ...args: unknown[]) => {\n console.log(`[pdfn:cli] ${message}`, ...args);\n }\n : () => {};\n\n/**\n * Check if debug logging is enabled\n */\nexport const isDebugEnabled = () => isEnabled;\n","import type { Page, PDFOptions, HTTPRequest, HTTPResponse } from \"puppeteer\";\nimport { debug } from \"../utils/debug\";\n\nexport interface PdfGenerationOptions {\n /** PDF format (default: A4) */\n format?: \"A4\" | \"Letter\" | \"Legal\" | \"Tabloid\" | \"A3\" | \"A5\";\n /** Print background graphics */\n printBackground?: boolean;\n /** Timeout for waiting for ready state (ms) */\n timeout?: number;\n}\n\n/** Asset information tracked during PDF generation */\nexport interface AssetInfo {\n /** Asset URL */\n url: string;\n /** Asset type (image, font, stylesheet, script, other) */\n type: \"image\" | \"font\" | \"stylesheet\" | \"script\" | \"other\";\n /** Size in bytes (0 if failed) */\n size: number;\n /** Load time in ms */\n duration: number;\n /** Whether the asset loaded successfully */\n success: boolean;\n /** Error message if failed */\n error?: string;\n}\n\nexport interface PdfResult {\n /** PDF buffer */\n buffer: Buffer;\n /** Generation metrics */\n metrics: {\n /** Total generation time in ms */\n total: number;\n /** Time waiting for content to load */\n contentLoad: number;\n /** Time waiting for Paged.js */\n pagedJs: number;\n /** Time for PDF capture */\n pdfCapture: number;\n /** Number of pages */\n pageCount: number;\n /** PDF file size in bytes */\n pdfSize: number;\n };\n /** Assets loaded during generation */\n assets: AssetInfo[];\n /** Warnings detected during generation */\n warnings: string[];\n}\n\n/** Size threshold for large asset warning (200KB - web best practice) */\nconst LARGE_ASSET_THRESHOLD = 200 * 1024;\n\n/** Duration threshold for slow asset warning (1000ms - lenient for fonts) */\nconst SLOW_ASSET_THRESHOLD = 1000;\n\n/** Determine asset type from resource type */\nfunction getAssetType(resourceType: string): AssetInfo[\"type\"] {\n switch (resourceType) {\n case \"image\":\n return \"image\";\n case \"font\":\n return \"font\";\n case \"stylesheet\":\n return \"stylesheet\";\n case \"script\":\n return \"script\";\n default:\n return \"other\";\n }\n}\n\n/** Get short URL for display (remove data: prefix, truncate long URLs) */\nfunction getShortUrl(url: string): string {\n if (url.startsWith(\"data:\")) {\n const match = url.match(/^data:([^;,]+)/);\n return match ? `data:${match[1]}...` : \"data:...\";\n }\n if (url.length > 80) {\n return url.substring(0, 77) + \"...\";\n }\n return url;\n}\n\n/**\n * Generate PDF from HTML content using a Puppeteer page\n */\nexport async function generatePdf(\n page: Page,\n html: string,\n options: PdfGenerationOptions = {}\n): Promise<PdfResult> {\n const {\n format = \"A4\",\n printBackground = true,\n timeout = 30000,\n } = options;\n\n const startTime = performance.now();\n let contentLoadTime = 0;\n let pagedJsTime = 0;\n let pdfCaptureTime = 0;\n let pageCount = 0;\n\n // Asset tracking\n const assets: AssetInfo[] = [];\n const requestStartTimes = new Map<string, number>();\n const warnings: string[] = [];\n\n // Request handler to track start times\n const onRequest = (request: HTTPRequest) => {\n const url = request.url();\n const resourceType = request.resourceType();\n\n // Only track relevant resource types\n if ([\"image\", \"font\", \"stylesheet\", \"script\"].includes(resourceType)) {\n requestStartTimes.set(url, performance.now());\n }\n };\n\n // Response handler to track completed requests\n const onResponse = (response: HTTPResponse) => {\n const url = response.url();\n const request = response.request();\n const resourceType = request.resourceType();\n const startTime = requestStartTimes.get(url);\n\n if (startTime === undefined) return;\n\n const duration = Math.round(performance.now() - startTime);\n const status = response.status();\n const success = status >= 200 && status < 400;\n\n // Get content length from headers\n const contentLength = response.headers()[\"content-length\"];\n const size = contentLength ? parseInt(contentLength, 10) : 0;\n\n const asset: AssetInfo = {\n url: getShortUrl(url),\n type: getAssetType(resourceType),\n size,\n duration,\n success,\n };\n\n if (!success) {\n asset.error = `HTTP ${status}`;\n warnings.push(`Failed: ${asset.url} (${status})`);\n } else {\n if (size > LARGE_ASSET_THRESHOLD) {\n warnings.push(`Large: ${asset.url} (${(size / 1024).toFixed(0)}KB)`);\n }\n if (duration > SLOW_ASSET_THRESHOLD) {\n warnings.push(`Slow: ${asset.url} (${duration}ms)`);\n }\n }\n\n assets.push(asset);\n requestStartTimes.delete(url);\n };\n\n // Request failed handler\n const onRequestFailed = (request: HTTPRequest) => {\n const url = request.url();\n const resourceType = request.resourceType();\n const startTime = requestStartTimes.get(url);\n\n if (startTime === undefined) return;\n\n const duration = Math.round(performance.now() - startTime);\n const failure = request.failure();\n const errorText = failure?.errorText || \"Unknown error\";\n\n const asset: AssetInfo = {\n url: getShortUrl(url),\n type: getAssetType(resourceType),\n size: 0,\n duration,\n success: false,\n error: errorText,\n };\n\n warnings.push(`Failed: ${asset.url} (${errorText})`);\n assets.push(asset);\n requestStartTimes.delete(url);\n };\n\n // Set up request interception\n page.on(\"request\", onRequest);\n page.on(\"response\", onResponse);\n page.on(\"requestfailed\", onRequestFailed);\n\n try {\n // Set content and wait for network idle\n const contentStart = performance.now();\n await page.setContent(html, {\n waitUntil: \"networkidle0\",\n timeout,\n });\n contentLoadTime = performance.now() - contentStart;\n\n // Wait for PDFN.ready (pdfn HTML) or proceed immediately (plain HTML)\n const pagedStart = performance.now();\n await page.waitForFunction(\n () => typeof (window as any).PDFN === \"undefined\" || (window as any).PDFN?.ready === true,\n { timeout }\n );\n pagedJsTime = performance.now() - pagedStart;\n\n // Get page count from PDFN metrics\n pageCount = await page.evaluate(() => {\n return (window as any).PDFN?.metrics?.pages ?? 1;\n });\n\n // Generate PDF\n const pdfStart = performance.now();\n const pdfOptions: PDFOptions = {\n format,\n printBackground,\n preferCSSPageSize: true,\n tagged: true, // Accessibility: generates tagged PDF for screen readers\n outline: true, // Accessibility: auto-generates bookmarks from headings\n };\n\n const buffer = await page.pdf(pdfOptions);\n pdfCaptureTime = performance.now() - pdfStart;\n\n const totalTime = performance.now() - startTime;\n const pdfBuffer = Buffer.from(buffer);\n\n debug(\n `pdf: ${Math.round(totalTime)}ms (load: ${Math.round(contentLoadTime)}ms, paged: ${Math.round(pagedJsTime)}ms, capture: ${Math.round(pdfCaptureTime)}ms) - ${pageCount} pages, ${(pdfBuffer.length / 1024).toFixed(1)}KB`\n );\n\n if (assets.length > 0) {\n debug(`assets: ${assets.length} (${assets.filter(a => !a.success).length} failed)`);\n }\n\n if (warnings.length > 0) {\n warnings.forEach(w => debug(`warning: ${w}`));\n }\n\n return {\n buffer: pdfBuffer,\n metrics: {\n total: Math.round(totalTime),\n contentLoad: Math.round(contentLoadTime),\n pagedJs: Math.round(pagedJsTime),\n pdfCapture: Math.round(pdfCaptureTime),\n pageCount,\n pdfSize: pdfBuffer.length,\n },\n assets,\n warnings,\n };\n } finally {\n // Clean up event listeners\n page.off(\"request\", onRequest);\n page.off(\"response\", onResponse);\n page.off(\"requestfailed\", onRequestFailed);\n\n // Close the page after use\n await page.close().catch(() => {});\n }\n}\n","import type { Request, Response } from \"express\";\nimport type { BrowserManager } from \"./browser\";\nimport { generatePdf, type PdfResult } from \"./pdf\";\n\ninterface GenerateRequestBody {\n html?: string;\n standard?: string;\n options?: {\n timeout?: number;\n printBackground?: boolean;\n preferCSSPageSize?: boolean;\n };\n}\n\ninterface GenerateHandlerOptions {\n /** Default timeout in ms (default: 30000) */\n timeout?: number;\n /** Optional logger for PDF generation results */\n onSuccess?: (result: PdfResult) => void;\n /** Optional logger for errors */\n onError?: (error: string) => void;\n}\n\n/**\n * Create a /v1/generate POST handler for PDF generation\n *\n * @example\n * ```ts\n * const browserManager = new BrowserManager({ maxConcurrent: 5 });\n * app.post(\"/v1/generate\", createGenerateHandler(browserManager));\n * ```\n */\nexport function createGenerateHandler(\n browserManager: BrowserManager,\n options: GenerateHandlerOptions = {}\n) {\n const { timeout = 30000, onSuccess, onError } = options;\n\n return async (req: Request, res: Response) => {\n const { html, standard, options: pdfOptions } = req.body as GenerateRequestBody;\n const format = req.query.format as string | undefined;\n\n if (!html) {\n res.status(400).json({ error: \"HTML content is required\" });\n return;\n }\n\n // PDF/A standard requires pdfn Cloud archival compliance pipeline\n if (standard) {\n res.status(400).json({\n error: `PDF/A archival compliance requires the pdfn Cloud compliance pipeline.\n\nLayout is identical in local dev. Compliance does not change layout or rendering — it only adds validation and archival metadata.\n\nTo generate ${standard}-compliant PDFs:\n Set: PDFN_API_KEY=pdfn_live_...\n Get key at: https://console.pdfn.dev\n\nTo preview layout without compliance:\n Remove the 'standard' option`,\n });\n return;\n }\n\n // Return HTML directly if format=html (for debugging)\n if (format === \"html\") {\n res.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n res.send(html);\n return;\n }\n\n try {\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, {\n ...pdfOptions,\n timeout: pdfOptions?.timeout ?? timeout,\n });\n });\n\n // Set response headers with metrics\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"X-PDFN-Total-Time\", result.metrics.total.toString());\n res.setHeader(\"X-PDFN-Content-Load\", result.metrics.contentLoad.toString());\n res.setHeader(\"X-PDFN-PagedJs-Time\", result.metrics.pagedJs.toString());\n res.setHeader(\"X-PDFN-PDF-Capture\", result.metrics.pdfCapture.toString());\n res.setHeader(\"X-PDFN-Page-Count\", result.metrics.pageCount.toString());\n res.setHeader(\"X-PDFN-PDF-Size\", result.metrics.pdfSize.toString());\n\n // Attach result for logging middleware (used by serve command)\n (res as any)._pdfResult = result;\n\n // Call success callback if provided\n onSuccess?.(result);\n\n res.send(result.buffer);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n\n // Call error callback if provided\n onError?.(message);\n\n if (message.includes(\"Server busy\")) {\n res.status(503).json({ error: message });\n return;\n }\n\n res.status(500).json({ error: message });\n }\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { config } from \"dotenv\";\n\n/**\n * Load environment variables following Vite's pattern.\n * Priority (later overrides earlier):\n * .env → .env.local → .env.[mode] → .env.[mode].local\n *\n * @param mode - Environment mode (e.g., \"development\", \"production\")\n */\nexport function loadEnv(mode: string): void {\n const cwd = process.cwd();\n\n // Files in order of priority (first loaded = lowest priority)\n const envFiles = [\n \".env\",\n \".env.local\",\n `.env.${mode}`,\n `.env.${mode}.local`,\n ];\n\n for (const file of envFiles) {\n const filePath = resolve(cwd, file);\n if (existsSync(filePath)) {\n // quiet: true suppresses dotenv's verbose output in v17+\n config({ path: filePath, override: true, quiet: true });\n }\n }\n}\n","import { Command } from \"commander\";\nimport { existsSync, mkdirSync, copyFileSync, readFileSync } from \"fs\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport chalk from \"chalk\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Available templates with descriptions\nconst TEMPLATES: Record<string, { name: string; description: string; pageSize: string }> = {\n invoice: {\n name: \"Invoice\",\n description: \"Professional invoice with itemized billing\",\n pageSize: \"A4\",\n },\n letter: {\n name: \"Business Letter\",\n description: \"US business correspondence\",\n pageSize: \"Letter\",\n },\n contract: {\n name: \"Contract\",\n description: \"Legal service agreement with terms\",\n pageSize: \"Legal\",\n },\n ticket: {\n name: \"Event Ticket\",\n description: \"Admission ticket with QR placeholder\",\n pageSize: \"A5\",\n },\n poster: {\n name: \"Poster\",\n description: \"Event poster (landscape)\",\n pageSize: \"Tabloid\",\n },\n report: {\n name: \"Sales Report\",\n description: \"Report with Recharts (npm install recharts)\",\n pageSize: \"A4\",\n },\n};\n\ntype TemplateStyle = \"inline\" | \"tailwind\";\n\nfunction getTemplatesDir(style: TemplateStyle): string {\n // After build, cli.js is in dist/, templates are in templates/\n // So from dist/ we go up one level to package root, then into templates/<style>/\n return join(__dirname, \"..\", \"templates\", style);\n}\n\n/**\n * Check if @pdfn/tailwind is installed in user's project\n * Checks both package.json and node_modules for compatibility with npm, pnpm, yarn\n */\nfunction isTailwindInstalled(cwd: string): boolean {\n // First check package.json for explicit dependency\n try {\n const pkgPath = join(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.dependencies?.[\"@pdfn/tailwind\"] || pkg.devDependencies?.[\"@pdfn/tailwind\"]) {\n return true;\n }\n }\n } catch {\n // Ignore parse errors\n }\n\n // Fall back to checking node_modules (for linked packages, global installs, etc.)\n const tailwindPath = join(cwd, \"node_modules\", \"@pdfn\", \"tailwind\");\n return existsSync(tailwindPath);\n}\n\nexport const addCommand = new Command(\"add\")\n .description(\"Add a starter template to your project\")\n .argument(\"[template]\", \"Template name (e.g., invoice, letter, contract)\")\n .option(\"--list\", \"List available templates\")\n .option(\"--tailwind\", \"Use Tailwind CSS styling (requires @pdfn/tailwind)\")\n .option(\"--inline\", \"Use inline styles (default)\")\n .option(\"--force\", \"Overwrite existing files\")\n .action(async (template, options) => {\n const cwd = process.cwd();\n const outputDir = \"./pdfn-templates\";\n\n // List templates\n if (options.list || !template) {\n console.log(chalk.bold(\"\\nAvailable templates:\\n\"));\n\n for (const [id, info] of Object.entries(TEMPLATES)) {\n console.log(` ${chalk.cyan(id.padEnd(12))} ${info.description} ${chalk.dim(`(${info.pageSize})`)}`);\n }\n\n console.log(chalk.dim(\"\\nUsage: pdfn add <template> [--tailwind]\"));\n console.log(chalk.dim(\"Example: pdfn add invoice\"));\n console.log(chalk.dim(\"Example: pdfn add invoice --tailwind\\n\"));\n console.log(chalk.bold(\"Options:\"));\n console.log(chalk.dim(\" --inline Use inline styles (default)\"));\n console.log(chalk.dim(\" --tailwind Use Tailwind CSS (requires @pdfn/tailwind)\\n\"));\n return;\n }\n\n // Validate template\n if (!TEMPLATES[template]) {\n console.error(chalk.red(`\\nError: Unknown template \"${template}\"`));\n console.log(chalk.dim(\"Run 'pdfn add --list' to see available templates\\n\"));\n process.exit(1);\n }\n\n // Determine style (default to inline)\n const style: TemplateStyle = options.tailwind ? \"tailwind\" : \"inline\";\n\n // Check for @pdfn/tailwind if tailwind style requested\n if (style === \"tailwind\" && !isTailwindInstalled(cwd)) {\n console.error(chalk.yellow(`\\n⚠ @pdfn/tailwind is not installed.`));\n console.log(chalk.dim(\"Install it first to use Tailwind templates:\\n\"));\n console.log(chalk.cyan(\" npm install @pdfn/tailwind\\n\"));\n console.log(chalk.dim(\"Or use inline styles (default):\\n\"));\n console.log(chalk.cyan(` pdfn add ${template}\\n`));\n process.exit(1);\n }\n\n const templatesDir = getTemplatesDir(style);\n const sourceFile = join(templatesDir, `${template}.tsx`);\n const outputFile = join(outputDir, `${template}.tsx`);\n\n // Check if source template exists\n if (!existsSync(sourceFile)) {\n console.error(chalk.red(`\\nError: Template file not found: ${sourceFile}`));\n console.log(chalk.dim(\"This may be a package installation issue.\\n\"));\n process.exit(1);\n }\n\n // Create output directory\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n console.log(chalk.dim(`Created ${outputDir}/`));\n }\n\n // Check if file already exists\n if (existsSync(outputFile) && !options.force) {\n console.error(chalk.yellow(`\\nFile already exists: ${outputFile}`));\n console.log(chalk.dim(\"Use --force to overwrite\\n\"));\n process.exit(1);\n }\n\n // Copy template\n try {\n copyFileSync(sourceFile, outputFile);\n\n const info = TEMPLATES[template];\n const styleLabel = style === \"tailwind\" ? chalk.cyan(\" (Tailwind)\") : chalk.dim(\" (inline styles)\");\n console.log(chalk.green(`\\n✓ Added ${info.name} template`) + styleLabel);\n console.log(chalk.dim(` ${outputFile}\\n`));\n\n // Show next steps\n console.log(chalk.bold(\"Next steps:\"));\n console.log(chalk.dim(` 1. Edit ${outputFile} to customize`));\n console.log(chalk.dim(` 2. Run 'npx pdfn dev' to preview\\n`));\n\n // Additional info for tailwind\n if (style === \"tailwind\") {\n console.log(chalk.dim(\"Note: Tailwind templates require @pdfn/tailwind to be installed.\\n\"));\n }\n } catch (error) {\n console.error(chalk.red(`\\nError copying template: ${error}`));\n process.exit(1);\n }\n });\n\n// Also export the template info for use by dev server\nexport { TEMPLATES };\n"],"mappings":";;;AAAA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,eAAe;AACxB,SAAS,cAAAC,aAAY,aAAa,oBAAoB;AACtD,SAAS,MAAM,WAAAC,gBAAe;AAE9B,SAAS,gBAAgB,wBAAwB;AACjD,SAAS,iBAAiB,iBAAiB;AAC3C,OAAO,cAAc;AACrB,SAAS,gBAAgB,wBAAwB;AACjD,SAAS,aAAa;AACtB,OAAOC,gBAAe;;;ACTtB,OAAO,aAAa;;;ACApB,OAAO,eAAkC;AAiBlC,IAAM,iBAAN,MAAqB;AAAA,EAClB,UAA0B;AAAA,EAC1B,YAAqC;AAAA,EACrC,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EAER,YAAY,UAAiC,CAAC,GAAG;AAC/C,SAAK,gBACH,QAAQ,iBACR,SAAS,QAAQ,IAAI,uBAAuB,KAAK,EAAE;AACrD,SAAK,UACH,QAAQ,WAAW,SAAS,QAAQ,IAAI,gBAAgB,SAAS,EAAE;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA+B;AACnC,QAAI,KAAK,SAAS,WAAW;AAC3B,aAAO,KAAK;AAAA,IACd;AAGA,QAAI,KAAK,WAAW;AAClB,aAAO,KAAK;AAAA,IACd;AAGA,SAAK,YAAY,UAAU,OAAO;AAAA,MAChC,UAAU;AAAA,MACV,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI;AACF,WAAK,UAAU,MAAM,KAAK;AAC1B,WAAK,sBAAsB,KAAK,OAAO;AACvC,aAAO,KAAK;AAAA,IACd,UAAE;AACA,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,SAAwB;AACpD,YAAQ,GAAG,gBAAgB,MAAM;AAC/B,WAAK,UAAU;AAAA,IAEjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,UAAM,UAAU,MAAM,KAAK,WAAW;AACtC,UAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,SAAK,kBAAkB,KAAK,OAAO;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SAAY,IAA4C;AAC5D,QAAI,KAAK,eAAe,KAAK,eAAe;AAC1C,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAEA,SAAK;AACL,QAAI,OAAoB;AAExB,QAAI;AACF,aAAO,MAAM,KAAK,WAAW;AAC7B,aAAO,MAAM,GAAG,IAAI;AAAA,IACtB,UAAE;AACA,WAAK;AACL,UAAI,MAAM;AACR,cAAM,KAAK,MAAM,EAAE,MAAM,MAAM;AAAA,QAE/B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,SAAS,aAAa;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,SAAS;AAChB,YAAM,KAAK,QAAQ,MAAM;AACzB,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AACF;;;AClJA,IAAM,WAAW,QAAQ,IAAI,SAAS;AACtC,IAAM,YACJ,SAAS,SAAS,UAAU,KAC5B,SAAS,SAAS,QAAQ,KAC1B,aAAa,UACb,SAAS,SAAS,OAAO,KACzB,SAAS,SAAS,OAAO;AAKpB,IAAM,QAAQ,YACjB,CAAC,YAAoB,SAAoB;AACvC,UAAQ,IAAI,cAAc,OAAO,IAAI,GAAG,IAAI;AAC9C,IACA,MAAM;AAAC;;;AC4BX,IAAM,wBAAwB,MAAM;AAGpC,IAAM,uBAAuB;AAG7B,SAAS,aAAa,cAAyC;AAC7D,UAAQ,cAAc;AAAA,IACpB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,YAAY,KAAqB;AACxC,MAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,UAAM,QAAQ,IAAI,MAAM,gBAAgB;AACxC,WAAO,QAAQ,QAAQ,MAAM,CAAC,CAAC,QAAQ;AAAA,EACzC;AACA,MAAI,IAAI,SAAS,IAAI;AACnB,WAAO,IAAI,UAAU,GAAG,EAAE,IAAI;AAAA,EAChC;AACA,SAAO;AACT;AAKA,eAAsB,YACpB,MACA,MACA,UAAgC,CAAC,GACb;AACpB,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB,UAAU;AAAA,EACZ,IAAI;AAEJ,QAAM,YAAY,YAAY,IAAI;AAClC,MAAI,kBAAkB;AACtB,MAAI,cAAc;AAClB,MAAI,iBAAiB;AACrB,MAAI,YAAY;AAGhB,QAAM,SAAsB,CAAC;AAC7B,QAAM,oBAAoB,oBAAI,IAAoB;AAClD,QAAM,WAAqB,CAAC;AAG5B,QAAM,YAAY,CAAC,YAAyB;AAC1C,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,eAAe,QAAQ,aAAa;AAG1C,QAAI,CAAC,SAAS,QAAQ,cAAc,QAAQ,EAAE,SAAS,YAAY,GAAG;AACpE,wBAAkB,IAAI,KAAK,YAAY,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,aAA2B;AAC7C,UAAM,MAAM,SAAS,IAAI;AACzB,UAAM,UAAU,SAAS,QAAQ;AACjC,UAAM,eAAe,QAAQ,aAAa;AAC1C,UAAMC,aAAY,kBAAkB,IAAI,GAAG;AAE3C,QAAIA,eAAc,OAAW;AAE7B,UAAM,WAAW,KAAK,MAAM,YAAY,IAAI,IAAIA,UAAS;AACzD,UAAM,SAAS,SAAS,OAAO;AAC/B,UAAM,UAAU,UAAU,OAAO,SAAS;AAG1C,UAAM,gBAAgB,SAAS,QAAQ,EAAE,gBAAgB;AACzD,UAAM,OAAO,gBAAgB,SAAS,eAAe,EAAE,IAAI;AAE3D,UAAM,QAAmB;AAAA,MACvB,KAAK,YAAY,GAAG;AAAA,MACpB,MAAM,aAAa,YAAY;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,CAAC,SAAS;AACZ,YAAM,QAAQ,QAAQ,MAAM;AAC5B,eAAS,KAAK,WAAW,MAAM,GAAG,KAAK,MAAM,GAAG;AAAA,IAClD,OAAO;AACL,UAAI,OAAO,uBAAuB;AAChC,iBAAS,KAAK,UAAU,MAAM,GAAG,MAAM,OAAO,MAAM,QAAQ,CAAC,CAAC,KAAK;AAAA,MACrE;AACA,UAAI,WAAW,sBAAsB;AACnC,iBAAS,KAAK,SAAS,MAAM,GAAG,KAAK,QAAQ,KAAK;AAAA,MACpD;AAAA,IACF;AAEA,WAAO,KAAK,KAAK;AACjB,sBAAkB,OAAO,GAAG;AAAA,EAC9B;AAGA,QAAM,kBAAkB,CAAC,YAAyB;AAChD,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,eAAe,QAAQ,aAAa;AAC1C,UAAMA,aAAY,kBAAkB,IAAI,GAAG;AAE3C,QAAIA,eAAc,OAAW;AAE7B,UAAM,WAAW,KAAK,MAAM,YAAY,IAAI,IAAIA,UAAS;AACzD,UAAM,UAAU,QAAQ,QAAQ;AAChC,UAAM,YAAY,SAAS,aAAa;AAExC,UAAM,QAAmB;AAAA,MACvB,KAAK,YAAY,GAAG;AAAA,MACpB,MAAM,aAAa,YAAY;AAAA,MAC/B,MAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAEA,aAAS,KAAK,WAAW,MAAM,GAAG,KAAK,SAAS,GAAG;AACnD,WAAO,KAAK,KAAK;AACjB,sBAAkB,OAAO,GAAG;AAAA,EAC9B;AAGA,OAAK,GAAG,WAAW,SAAS;AAC5B,OAAK,GAAG,YAAY,UAAU;AAC9B,OAAK,GAAG,iBAAiB,eAAe;AAExC,MAAI;AAEF,UAAM,eAAe,YAAY,IAAI;AACrC,UAAM,KAAK,WAAW,MAAM;AAAA,MAC1B,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AACD,sBAAkB,YAAY,IAAI,IAAI;AAGtC,UAAM,aAAa,YAAY,IAAI;AACnC,UAAM,KAAK;AAAA,MACT,MAAM,OAAQ,OAAe,SAAS,eAAgB,OAAe,MAAM,UAAU;AAAA,MACrF,EAAE,QAAQ;AAAA,IACZ;AACA,kBAAc,YAAY,IAAI,IAAI;AAGlC,gBAAY,MAAM,KAAK,SAAS,MAAM;AACpC,aAAQ,OAAe,MAAM,SAAS,SAAS;AAAA,IACjD,CAAC;AAGD,UAAM,WAAW,YAAY,IAAI;AACjC,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,QAAQ;AAAA;AAAA,MACR,SAAS;AAAA;AAAA,IACX;AAEA,UAAM,SAAS,MAAM,KAAK,IAAI,UAAU;AACxC,qBAAiB,YAAY,IAAI,IAAI;AAErC,UAAM,YAAY,YAAY,IAAI,IAAI;AACtC,UAAM,YAAY,OAAO,KAAK,MAAM;AAEpC;AAAA,MACE,QAAQ,KAAK,MAAM,SAAS,CAAC,aAAa,KAAK,MAAM,eAAe,CAAC,cAAc,KAAK,MAAM,WAAW,CAAC,gBAAgB,KAAK,MAAM,cAAc,CAAC,SAAS,SAAS,YAAY,UAAU,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,IACvN;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,WAAW,OAAO,MAAM,KAAK,OAAO,OAAO,OAAK,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU;AAAA,IACpF;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,eAAS,QAAQ,OAAK,MAAM,YAAY,CAAC,EAAE,CAAC;AAAA,IAC9C;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,OAAO,KAAK,MAAM,SAAS;AAAA,QAC3B,aAAa,KAAK,MAAM,eAAe;AAAA,QACvC,SAAS,KAAK,MAAM,WAAW;AAAA,QAC/B,YAAY,KAAK,MAAM,cAAc;AAAA,QACrC;AAAA,QACA,SAAS,UAAU;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AAEA,SAAK,IAAI,WAAW,SAAS;AAC7B,SAAK,IAAI,YAAY,UAAU;AAC/B,SAAK,IAAI,iBAAiB,eAAe;AAGzC,UAAM,KAAK,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnC;AACF;;;AC1OO,SAAS,sBACd,gBACA,UAAkC,CAAC,GACnC;AACA,QAAM,EAAE,UAAU,KAAO,WAAW,QAAQ,IAAI;AAEhD,SAAO,OAAO,KAAc,QAAkB;AAC5C,UAAM,EAAE,MAAM,UAAU,SAAS,WAAW,IAAI,IAAI;AACpD,UAAM,SAAS,IAAI,MAAM;AAEzB,QAAI,CAAC,MAAM;AACT,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAC1D;AAAA,IACF;AAGA,QAAI,UAAU;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QACnB,OAAO;AAAA;AAAA;AAAA;AAAA,cAID,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMhB,CAAC;AACD;AAAA,IACF;AAGA,QAAI,WAAW,QAAQ;AACrB,UAAI,UAAU,gBAAgB,0BAA0B;AACxD,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,eAAe,SAAS,OAAO,SAAS;AAC3D,eAAO,YAAY,MAAM,MAAM;AAAA,UAC7B,GAAG;AAAA,UACH,SAAS,YAAY,WAAW;AAAA,QAClC,CAAC;AAAA,MACH,CAAC;AAGD,UAAI,UAAU,gBAAgB,iBAAiB;AAC/C,UAAI,UAAU,qBAAqB,OAAO,QAAQ,MAAM,SAAS,CAAC;AAClE,UAAI,UAAU,uBAAuB,OAAO,QAAQ,YAAY,SAAS,CAAC;AAC1E,UAAI,UAAU,uBAAuB,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACtE,UAAI,UAAU,sBAAsB,OAAO,QAAQ,WAAW,SAAS,CAAC;AACxE,UAAI,UAAU,qBAAqB,OAAO,QAAQ,UAAU,SAAS,CAAC;AACtE,UAAI,UAAU,mBAAmB,OAAO,QAAQ,QAAQ,SAAS,CAAC;AAGlE,MAAC,IAAY,aAAa;AAG1B,kBAAY,MAAM;AAElB,UAAI,KAAK,OAAO,MAAM;AAAA,IACxB,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AAGzD,gBAAU,OAAO;AAEjB,UAAI,QAAQ,SAAS,aAAa,GAAG;AACnC,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AACvC;AAAA,MACF;AAEA,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,IACzC;AAAA,EACF;AACF;;;AJnEO,SAAS,iBAAiB,UAA6B,CAAC,GAAe;AAC5E,QAAM,gBACJ,QAAQ,iBACR,SAAS,QAAQ,IAAI,uBAAuB,KAAK,EAAE;AACrD,QAAM,UACJ,QAAQ,WAAW,SAAS,QAAQ,IAAI,gBAAgB,SAAS,EAAE;AACrE,QAAM,gBAAgB,QAAQ,iBAAiB;AAE/C,QAAM,MAAM,QAAQ;AACpB,QAAM,iBAAiB,IAAI,eAAe,EAAE,eAAe,QAAQ,CAAC;AAGpE,MAAI,IAAI,QAAQ,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC;AAGvC,MAAI,iBAAiB,QAAQ,WAAW;AACtC,UAAM,YAAY,QAAQ;AAC1B,UAAM,cAAc,QAAQ;AAE5B,QAAI,IAAI,CAAC,KAAc,KAAe,SAAuB;AAE3D,YAAM,YAAY,CAAC,gBAAgB,aAAa;AAChD,UAAI,UAAU,SAAS,IAAI,IAAI,GAAG;AAChC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,QAAQ,YAAY,IAAI;AAE9B,UAAI,GAAG,UAAU,MAAM;AACrB,cAAM,WAAW,YAAY,IAAI,IAAI;AAGrC,YAAI;AACJ,YAAI,IAAI,SAAS,kBAAkB,IAAI,eAAe,KAAK;AACzD,gBAAM,QAAQ,IAAI,UAAU,mBAAmB;AAC/C,gBAAM,OAAO,OAAO,IAAI,UAAU,iBAAiB,CAAC,KAAK;AACzD,gBAAM,UAAU,OAAO,MAAM,QAAQ,CAAC;AACtC,kBAAQ,GAAG,KAAK,iBAAY,MAAM;AAAA,QACpC;AAEA,kBAAU,IAAI,QAAQ,IAAI,MAAM,IAAI,YAAY,UAAU,KAAK;AAG/D,cAAM,YAAa,IAAY;AAC/B,YAAI,aAAa,aAAa;AAC5B,sBAAY,SAAS;AAAA,QACvB;AAAA,MACF,CAAC;AAED,WAAK;AAAA,IACP,CAAC;AAAA,EACH;AAGA,MAAI,IAAI,WAAW,CAAC,MAAe,QAAkB;AACnD,QAAI,KAAK;AAAA,MACP,QAAQ;AAAA,MACR,SAAS,eAAe,YAAY,IAAI,cAAc;AAAA,MACtD,aAAa,eAAe,eAAe;AAAA,MAC3C,eAAe,eAAe,iBAAiB;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AAGD,MAAI;AAAA,IACF;AAAA,IACA,sBAAsB,gBAAgB;AAAA,MACpC;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AD7GA,SAAS,YAAY;AACrB,OAAO,WAAW;;;AMdlB,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,cAAc;AAShB,SAAS,QAAQ,MAAoB;AAC1C,QAAM,MAAM,QAAQ,IAAI;AAGxB,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI;AAAA,EACd;AAEA,aAAW,QAAQ,UAAU;AAC3B,UAAM,WAAW,QAAQ,KAAK,IAAI;AAClC,QAAI,WAAW,QAAQ,GAAG;AAExB,aAAO,EAAE,MAAM,UAAU,UAAU,MAAM,OAAO,KAAK,CAAC;AAAA,IACxD;AAAA,EACF;AACF;;;ANbA,OAAO,WAAW;AAClB,OAAO,aAAa;AAapB,IAAM,gBAAgB;AAQtB,eAAe,cAAc,cAA+C;AAC1E,MAAI,CAACC,YAAW,YAAY,GAAG;AAC7B,WAAO,CAAC;AAAA,EACV;AAGA,MAAI,aAAsD,CAAC;AAC3D,QAAM,cAAc;AAAA,IAClB,KAAK,QAAQ,IAAI,GAAG,OAAO,UAAU,gBAAgB;AAAA,IACrD,KAAK,QAAQ,IAAI,GAAG,gBAAgB;AAAA,IACpC,KAAK,cAAc,gBAAgB;AAAA,EACrC;AAEA,aAAW,cAAc,aAAa;AACpC,QAAIA,YAAW,UAAU,GAAG;AAC1B,UAAI;AACF,cAAMC,UAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,YAAIA,QAAO,aAAa,MAAM,QAAQA,QAAO,SAAS,GAAG;AACvD,qBAAW,KAAKA,QAAO,WAAW;AAChC,gBAAI,EAAE,MAAM,EAAE,YAAY;AACxB,yBAAW,EAAE,EAAE,IAAI,EAAE;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,YAAY,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,CAAC;AAIxE,QAAM,YAA4B,CAAC;AAEnC,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,KAAK,QAAQ,QAAQ,EAAE;AAClC,UAAM,WAAW,KAAK,cAAc,IAAI;AAGxC,QAAI;AACF,YAAM,UAAU,aAAa,UAAU,OAAO;AAE9C,UAAI,CAAC,QAAQ,SAAS,gBAAgB,KAAK,CAAC,QAAQ,SAAS,UAAU,GAAG;AACxE;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,SAAS,UAAU,KAAK,CAAC,QAAQ,SAAS,MAAM,GAAG;AAC9D;AAAA,MACF;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAGA,UAAM,OAAO,GACV,MAAM,MAAM,EACZ,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,CAAC,EACjD,KAAK,GAAG;AAEX,cAAU,KAAK;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,YAAY,WAAW,EAAE;AAAA,IAC3B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,WAA2B,gBAA+B,gBAAiC;AACpH,QAAM,eAAe,UAClB;AAAA,IACC,CAAC,MAAM;AAAA;AAAA,8BAEiB,EAAE,OAAO,iBAAiB,WAAW,EAAE;AAAA,yBAC5C,EAAE,EAAE;AAAA,qBACR,EAAE,IAAI;AAAA;AAAA,sCAEW,EAAE,IAAI;AAAA;AAAA;AAAA,EAGxC,EACC,KAAK,EAAE;AAEV,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA0vBD,UAAU,SAAS,IACf,eACA,uFACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAqDM,iBACI,kGACA,+GACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAmGmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAkBf,iBAAiB,IAAI,cAAc,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["Command","existsSync","resolve","puppeteer","startTime","existsSync","config","resolve","options","results","puppeteer","Command","existsSync","readFileSync","join","chalk","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/dev.ts","../src/server/base.ts","../src/server/browser.ts","../src/utils/debug.ts","../src/server/pdf.ts","../src/server/routes.ts","../src/utils/env.ts","../src/commands/add.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { addCommand } from \"./commands/add.js\";\n\nconst program = new Command()\n .name(\"pdfn\")\n .description(\"PDFN CLI - PDF generation from React components\")\n .version(\"0.0.1-alpha.1\");\n\nprogram.addCommand(devCommand);\nprogram.addCommand(addCommand);\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport { existsSync, readdirSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport type { Request, Response } from \"express\";\nimport { createServer as createViteServer } from \"vite\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport chokidar from \"chokidar\";\nimport { createServer as createHttpServer } from \"http\";\nimport { spawn } from \"child_process\";\nimport puppeteer from \"puppeteer\";\nimport { createBaseServer } from \"../server/base\";\nimport { generatePdf } from \"../server/pdf\";\nimport type { DebugOptions } from \"@pdfn/react\";\nimport { pdfn } from \"@pdfn/vite\";\nimport chalk from \"chalk\";\nimport { loadEnv } from \"../utils/env\";\nimport React from \"react\";\nimport axeCore from \"axe-core\";\n\ninterface TemplateInfo {\n id: string;\n name: string;\n file: string;\n path: string;\n sampleData?: Record<string, unknown>;\n}\n\n/**\n * Standardized templates directory (convention over configuration)\n */\nconst TEMPLATES_DIR = \"./pdfn-templates\";\n\ninterface DevServerOptions {\n port: number;\n open: boolean;\n mode?: string;\n}\n\nasync function scanTemplates(templatesDir: string): Promise<TemplateInfo[]> {\n if (!existsSync(templatesDir)) {\n return [];\n }\n\n // Try to load config file with sample data\n let configData: Record<string, Record<string, unknown>> = {};\n const configPaths = [\n join(process.cwd(), \"src\", \"config\", \"templates.json\"),\n join(process.cwd(), \"templates.json\"),\n join(templatesDir, \"templates.json\"),\n ];\n\n for (const configPath of configPaths) {\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (config.templates && Array.isArray(config.templates)) {\n for (const t of config.templates) {\n if (t.id && t.sampleData) {\n configData[t.id] = t.sampleData;\n }\n }\n }\n break;\n } catch {\n // Ignore config parse errors\n }\n }\n }\n\n const files = readdirSync(templatesDir).filter((f) => f.endsWith(\".tsx\"));\n\n // Filter to only include files that look like templates\n // (have \"export default function\" or similar patterns)\n const templates: TemplateInfo[] = [];\n\n for (const file of files) {\n const id = file.replace(\".tsx\", \"\");\n const filePath = join(templatesDir, file);\n\n // Quick check: read file and look for default export pattern\n try {\n const content = readFileSync(filePath, \"utf-8\");\n // Must have a default export that looks like a component\n if (!content.includes(\"export default\") || !content.includes(\"function\")) {\n continue;\n }\n // Skip files that are clearly not templates (no Document/Page imports)\n if (!content.includes(\"Document\") && !content.includes(\"Page\")) {\n continue;\n }\n } catch {\n continue;\n }\n\n // Convert kebab/snake case to Title Case\n const name = id\n .split(/[-_]/)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n\n templates.push({\n id,\n name,\n file,\n path: filePath,\n sampleData: configData[id],\n });\n }\n\n return templates;\n}\n\nfunction createPreviewHTML(templates: TemplateInfo[], activeTemplate: string | null, hasCloudAccess: boolean): string {\n const templateList = templates\n .map(\n (t) => `\n <button\n class=\"template-btn ${t.id === activeTemplate ? \"active\" : \"\"}\"\n data-template=\"${t.id}\"\n data-file=\"${t.file}\"\n >\n <span class=\"template-name\">${t.name}</span>\n </button>\n `\n )\n .join(\"\");\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>PDFN Dev Server</title>\n <style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --primary: #22d3ee;\n --primary-hover: #06b6d4;\n --bg: #0a0a0a;\n --surface-1: #111;\n --surface-2: #1a1a1a;\n --border: #222;\n --text: #fafafa;\n --text-muted: #9a9a9a;\n --text-secondary: #b0b0b0;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: var(--bg);\n color: var(--text);\n min-height: 100vh;\n }\n\n .header {\n background: var(--surface-1);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .logo {\n font-size: 20px;\n font-weight: 800;\n letter-spacing: -0.5px;\n }\n\n .logo span { color: var(--primary); }\n\n .status {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n }\n\n .status-dot.disconnected { background: #ef4444; }\n\n .container {\n display: flex;\n height: calc(100vh - 57px);\n }\n\n .main-wrapper {\n flex: 1;\n display: flex;\n overflow: hidden;\n }\n\n .sidebar {\n width: 200px;\n background: var(--surface-1);\n border-right: 1px solid var(--border);\n padding: 16px;\n overflow-y: auto;\n }\n\n .sidebar-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-muted);\n margin-bottom: 12px;\n }\n\n .template-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 12px;\n background: transparent;\n border: 1px solid #333;\n border-radius: 6px;\n color: #ccc;\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n margin-bottom: 8px;\n transition: all 0.15s;\n }\n\n .template-btn:hover {\n background: var(--surface-2);\n border-color: #444;\n }\n\n .template-btn.active {\n background: rgba(34, 211, 238, 0.1);\n border-color: var(--primary);\n color: var(--primary);\n }\n\n .template-name { font-weight: 500; }\n\n .main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* Context bar - top */\n .context-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--surface-1);\n border-bottom: 1px solid var(--border);\n }\n\n .context-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .file-name {\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 13px;\n color: var(--text);\n font-weight: 500;\n }\n\n .file-name:empty::after {\n content: \"Select a template\";\n color: var(--text-muted);\n font-style: italic;\n font-family: inherit;\n }\n\n .page-info {\n font-size: 12px;\n color: var(--text-muted);\n }\n\n .context-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Preview area */\n .preview-area {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #27272a;\n overflow: hidden;\n padding: 24px;\n }\n\n .preview-frame {\n background: #fff;\n border-radius: 4px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.4);\n overflow: hidden;\n transition: opacity 0.2s;\n }\n\n .preview-frame.loading { opacity: 0.4; }\n\n .preview-frame iframe {\n display: block;\n border: none;\n }\n\n .empty-state {\n text-align: center;\n color: var(--text-muted);\n }\n\n .empty-state h2 {\n font-size: 18px;\n margin-bottom: 8px;\n color: var(--text-secondary);\n }\n\n .empty-state p {\n font-size: 14px;\n margin-bottom: 16px;\n }\n\n .empty-state code {\n background: var(--surface-2);\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 13px;\n }\n\n .loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--text-muted);\n }\n\n .spinner {\n width: 32px;\n height: 32px;\n border: 2px solid #333;\n border-top-color: var(--primary);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .spin {\n animation: spin 1s linear infinite;\n }\n\n /* Inspector panel - right side */\n .inspector-panel {\n width: 260px;\n background: var(--surface-1);\n border-left: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n }\n\n .inspector-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text);\n padding: 12px 16px;\n border-bottom: 1px solid var(--border);\n }\n\n .inspector-content {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n .inspector-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .inspector-section-title {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-muted);\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .metrics-note {\n font-size: 10px;\n color: var(--text-muted);\n margin-top: 8px;\n font-style: italic;\n }\n\n /* Metrics display */\n .metrics-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .metric-item {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n }\n\n .metric-label {\n font-size: 12px;\n color: var(--text-muted);\n }\n\n .metric-value {\n font-size: 14px;\n font-weight: 600;\n color: var(--primary);\n font-variant-numeric: tabular-nums;\n }\n\n /* Overlay checkboxes */\n .overlay-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .overlay-checkbox {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n .overlay-checkbox:hover {\n color: var(--text);\n }\n\n .overlay-checkbox input {\n appearance: none;\n width: 16px;\n height: 16px;\n border: 1px solid #444;\n border-radius: 3px;\n background: var(--surface-2);\n cursor: pointer;\n position: relative;\n flex-shrink: 0;\n }\n\n .overlay-checkbox input:hover {\n border-color: #555;\n }\n\n .overlay-checkbox input:checked {\n background: var(--primary);\n border-color: var(--primary);\n }\n\n .overlay-checkbox input:checked::after {\n content: \"\";\n position: absolute;\n left: 5px;\n top: 2px;\n width: 4px;\n height: 8px;\n border: solid #000;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .debug-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 8px;\n font-size: 12px;\n color: var(--text-muted);\n text-decoration: none;\n transition: color 0.15s;\n }\n\n .debug-link:hover {\n color: var(--primary);\n }\n\n .debug-link svg {\n opacity: 0.7;\n }\n\n /* Accessibility section */\n .a11y-check-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 8px 12px;\n background: var(--surface-2);\n border: 1px solid #444;\n border-radius: 6px;\n color: var(--text-secondary);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .a11y-check-btn:hover {\n background: var(--surface-3);\n border-color: #555;\n color: var(--text);\n }\n\n .a11y-check-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .a11y-check-btn.loading {\n color: var(--text-muted);\n }\n\n .a11y-check-btn .status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n margin-left: 4px;\n }\n\n .a11y-check-btn .status-dot.pass { background: #22c55e; }\n .a11y-check-btn .status-dot.fail { background: #ef4444; }\n\n .a11y-results {\n margin-top: 10px;\n max-height: 350px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: #555 transparent;\n }\n\n .a11y-results::-webkit-scrollbar {\n width: 6px;\n }\n\n .a11y-results::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .a11y-results::-webkit-scrollbar-thumb {\n background: #555;\n border-radius: 3px;\n }\n\n .a11y-results::-webkit-scrollbar-thumb:hover {\n background: #666;\n }\n\n .a11y-timestamp {\n font-size: 10px;\n color: var(--text-muted);\n text-align: right;\n margin-bottom: 8px;\n }\n\n .a11y-stale {\n font-size: 11px;\n color: #f59e0b;\n background: rgba(245, 158, 11, 0.1);\n border: 1px solid rgba(245, 158, 11, 0.2);\n border-radius: 4px;\n padding: 6px 10px;\n margin-bottom: 8px;\n text-align: center;\n }\n\n .a11y-empty {\n font-size: 12px;\n color: var(--text-muted);\n text-align: center;\n padding: 12px 8px;\n }\n\n .a11y-pass {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #22c55e;\n padding: 10px;\n background: rgba(34, 197, 94, 0.1);\n border-radius: 4px;\n border: 1px solid rgba(34, 197, 94, 0.2);\n }\n\n .a11y-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n padding: 8px 10px;\n border-radius: 4px;\n margin-bottom: 10px;\n }\n\n .a11y-summary.has-issues {\n background: rgba(239, 68, 68, 0.1);\n border: 1px solid rgba(239, 68, 68, 0.2);\n color: #fca5a5;\n }\n\n .a11y-summary .issue-count {\n font-weight: 600;\n }\n\n .a11y-violation {\n padding: 10px;\n margin-bottom: 8px;\n background: var(--surface-2);\n border-radius: 4px;\n border-left: 3px solid;\n animation: slideIn 0.2s ease-out;\n }\n\n @keyframes slideIn {\n from { opacity: 0; transform: translateX(-4px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n .a11y-violation.critical { border-color: #ef4444; }\n .a11y-violation.serious { border-color: #f97316; }\n .a11y-violation.moderate { border-color: #eab308; }\n .a11y-violation.minor { border-color: #3b82f6; }\n\n .a11y-violation-header {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--text);\n }\n\n .a11y-violation-impact {\n font-size: 9px;\n padding: 2px 6px;\n border-radius: 3px;\n text-transform: uppercase;\n font-weight: 600;\n letter-spacing: 0.3px;\n }\n\n .a11y-violation-impact.critical { background: #ef4444; color: white; }\n .a11y-violation-impact.serious { background: #f97316; color: white; }\n .a11y-violation-impact.moderate { background: #eab308; color: #000; }\n .a11y-violation-impact.minor { background: #3b82f6; color: white; }\n\n .a11y-violation-id {\n font-family: monospace;\n font-size: 11px;\n }\n\n .a11y-violation-desc {\n font-size: 11px;\n color: var(--text-muted);\n margin-top: 6px;\n line-height: 1.4;\n }\n\n .a11y-violation-count {\n font-size: 10px;\n color: var(--text-muted);\n margin-top: 4px;\n opacity: 0.8;\n }\n\n /* Accessibility accordion */\n .a11y-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n user-select: none;\n }\n\n .a11y-header:hover {\n color: var(--text-secondary);\n }\n\n .a11y-toggle {\n transition: transform 0.2s;\n }\n\n .a11y-toggle.collapsed {\n transform: rotate(-90deg);\n }\n\n .a11y-content {\n margin-top: 8px;\n }\n\n /* Console section */\n .console-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n user-select: none;\n }\n\n .console-header:hover {\n color: var(--text-secondary);\n }\n\n .console-toggle {\n transition: transform 0.2s;\n }\n\n .console-toggle.collapsed {\n transform: rotate(-90deg);\n }\n\n .console-content {\n font-size: 12px;\n }\n\n .console-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--text-muted);\n }\n\n .console-empty svg {\n color: #22c55e;\n }\n\n .console-message {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 4px 0;\n }\n\n .console-message.error {\n color: #ef4444;\n }\n\n .console-message.warning {\n color: #eab308;\n }\n\n .console-message-icon {\n flex-shrink: 0;\n }\n\n .console-message-text {\n word-break: break-all;\n }\n\n /* Buttons */\n .btn {\n padding: 6px 10px;\n background: var(--surface-2);\n border: 1px solid #333;\n border-radius: 5px;\n color: #ccc;\n font-size: 12px;\n cursor: pointer;\n transition: all 0.15s;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n }\n\n .btn:hover { background: #333; border-color: #444; }\n\n .btn-primary {\n background: var(--primary);\n border-color: var(--primary);\n color: #000;\n font-weight: 600;\n }\n\n .btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }\n\n .standard-select {\n padding: 6px 10px;\n background: var(--surface-2);\n border: 1px solid #333;\n border-radius: 6px;\n color: var(--text-secondary);\n font-size: 12px;\n cursor: pointer;\n }\n\n .standard-select:hover { border-color: #444; }\n .standard-select:focus { outline: none; border-color: var(--primary); }\n\n .cloud-key-message {\n display: none;\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 8px;\n width: 280px;\n padding: 12px;\n background: var(--surface-2);\n border: 1px solid #444;\n border-radius: 8px;\n font-size: 12px;\n color: var(--text-secondary);\n z-index: 100;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n }\n\n .cloud-key-message.visible { display: block; }\n\n .cloud-key-message-title {\n display: flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n color: var(--text);\n margin-bottom: 8px;\n }\n\n .cloud-key-message code {\n display: block;\n background: var(--bg);\n padding: 8px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 11px;\n margin: 8px 0;\n word-break: break-all;\n }\n\n .cloud-key-message a {\n color: var(--primary);\n text-decoration: none;\n }\n\n .cloud-key-message a:hover { text-decoration: underline; }\n\n .context-actions {\n position: relative;\n }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div class=\"logo\">pdf<span>n</span> <span style=\"font-weight: 400; color: var(--text-muted); font-size: 14px; margin-left: 8px;\">dev</span></div>\n <div class=\"status\">\n <div class=\"status-dot\" id=\"status-dot\"></div>\n <span id=\"status-text\">Connected</span>\n </div>\n </header>\n\n <div class=\"container\">\n <aside class=\"sidebar\">\n <div class=\"sidebar-title\">Templates</div>\n ${\n templates.length > 0\n ? templateList\n : '<div class=\"empty-state\"><p>No templates found</p><code>pdfn add invoice</code></div>'\n }\n </aside>\n\n <div class=\"main-wrapper\">\n <main class=\"main\">\n <!-- Context bar: filename + page info + actions -->\n <div class=\"context-bar\">\n <div class=\"context-left\">\n <div class=\"file-name\" id=\"file-name\"></div>\n <div class=\"page-info\" id=\"page-info\"></div>\n </div>\n <div class=\"context-actions\">\n <select id=\"standard-select\" class=\"standard-select\" title=\"PDF standard\">\n <option value=\"\">Standard PDF (Local)</option>\n <option value=\"PDF/A-1b\">PDF/A-1b (Cloud)</option>\n <option value=\"PDF/A-2b\">PDF/A-2b (Cloud)</option>\n <option value=\"PDF/A-3b\">PDF/A-3b (Cloud)</option>\n </select>\n <button class=\"btn\" id=\"preview-pdf\" title=\"Preview PDF\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"/>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"/>\n </svg>\n Preview\n </button>\n <button class=\"btn btn-primary\" id=\"download-pdf\" title=\"Download PDF\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/>\n </svg>\n Download\n </button>\n <div class=\"cloud-key-message\" id=\"cloud-key-message\">\n <div class=\"cloud-key-message-title\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10\"/>\n </svg>\n API key required\n </div>\n <div>Set <strong>PDFN_API_KEY</strong> in your environment</div>\n <code>PDFN_API_KEY=pdfn_...</code>\n <div>\n <a href=\"https://console.pdfn.dev\" target=\"_blank\">Get your API key →</a>\n </div>\n <div style=\"margin-top: 8px; font-size: 11px; color: var(--text-muted);\">\n Restart dev server after setting\n </div>\n </div>\n </div>\n </div>\n\n <!-- Preview area -->\n <div class=\"preview-area\" id=\"preview-area\">\n ${\n activeTemplate\n ? '<div class=\"loading-spinner\"><div class=\"spinner\"></div><span>Loading preview...</span></div>'\n : '<div class=\"empty-state\"><h2>Select a template</h2><p>Choose a template from the sidebar to preview</p></div>'\n }\n </div>\n </main>\n\n <!-- Inspector panel: right side -->\n <aside class=\"inspector-panel\" id=\"inspector-panel\">\n <div class=\"inspector-title\">Inspector</div>\n <div class=\"inspector-content\">\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title\">Performance</div>\n <div class=\"metrics-grid\">\n <div class=\"metric-item\">\n <div class=\"metric-label\">Render</div>\n <div class=\"metric-value\" id=\"metric-render\">--</div>\n </div>\n <div class=\"metric-item\">\n <div class=\"metric-label\">Pagination</div>\n <div class=\"metric-value\" id=\"metric-pagination\">--</div>\n </div>\n <div class=\"metric-item\">\n <div class=\"metric-label\">Pages</div>\n <div class=\"metric-value\" id=\"metric-pages\">--</div>\n </div>\n </div>\n <div class=\"metrics-note\">Measured in browser. Times vary on server.</div>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title\">Debug</div>\n <div class=\"overlay-list\">\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-grid\">\n Grid (1cm)\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-margins\">\n Margins\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-headers\">\n Headers/Footers\n </label>\n <label class=\"overlay-checkbox\">\n <input type=\"checkbox\" id=\"overlay-breaks\">\n Page numbers\n </label>\n </div>\n <a class=\"debug-link\" id=\"view-html\" href=\"#\" target=\"_blank\">\n View HTML\n <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"/>\n </svg>\n </a>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title a11y-header\" id=\"a11y-header\">\n <span>Accessibility</span>\n <svg class=\"a11y-toggle\" id=\"a11y-toggle\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"/>\n </svg>\n </div>\n <div class=\"a11y-content\" id=\"a11y-content\">\n <button class=\"a11y-check-btn\" id=\"a11y-check-btn\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/>\n </svg>\n Check\n </button>\n <div id=\"a11y-results\" class=\"a11y-results\">\n <div class=\"a11y-empty\">Analyze template for WCAG compliance</div>\n </div>\n </div>\n </div>\n\n <div class=\"inspector-section\">\n <div class=\"inspector-section-title console-header\" id=\"console-header\">\n <span>Console</span>\n <svg class=\"console-toggle\" id=\"console-toggle\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"/>\n </svg>\n </div>\n <div class=\"console-content\" id=\"console-content\">\n <div class=\"console-empty\" id=\"console-empty\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"/>\n </svg>\n No issues\n </div>\n <div id=\"console-messages\" style=\"display: none;\"></div>\n </div>\n </div>\n </div>\n </aside>\n </div>\n </div>\n\n <script>\n // Cloud access (API key available)\n const hasCloudAccess = ${hasCloudAccess};\n\n // Page sizes in points (72 dpi)\n const PAGE_SIZES = {\n A3: { width: 842, height: 1191 },\n A4: { width: 595, height: 842 },\n A5: { width: 420, height: 595 },\n B4: { width: 709, height: 1001 },\n B5: { width: 499, height: 709 },\n Letter: { width: 612, height: 792 },\n Legal: { width: 612, height: 1008 },\n Tabloid: { width: 792, height: 1224 },\n };\n\n const PT_TO_PX = 96 / 72;\n const STORAGE_KEY = 'pdfn-inspector';\n\n let ws;\n let currentTemplate = ${activeTemplate ? `\"${activeTemplate}\"` : \"null\"};\n let templateInfo = {};\n\n // Inspector state with defaults (all overlays off by default)\n let inspectorState = {\n overlays: {\n grid: false,\n margins: false,\n headers: false,\n breaks: false,\n },\n a11yExpanded: true,\n consoleExpanded: true\n };\n\n // Console messages\n let consoleMessages = [];\n\n // Load state from localStorage\n function loadState() {\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n const parsed = JSON.parse(saved);\n inspectorState = { ...inspectorState, ...parsed };\n }\n } catch (e) {}\n }\n\n // Save state to localStorage\n function saveState() {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(inspectorState));\n } catch (e) {}\n }\n\n // Apply state to UI\n function applyState() {\n // Overlay checkboxes\n document.getElementById('overlay-grid').checked = inspectorState.overlays.grid;\n document.getElementById('overlay-margins').checked = inspectorState.overlays.margins;\n document.getElementById('overlay-headers').checked = inspectorState.overlays.headers;\n document.getElementById('overlay-breaks').checked = inspectorState.overlays.breaks;\n\n // Accessibility accordion state\n const a11yContent = document.getElementById('a11y-content');\n const a11yToggle = document.getElementById('a11y-toggle');\n if (inspectorState.a11yExpanded) {\n a11yContent.style.display = 'block';\n a11yToggle.classList.remove('collapsed');\n } else {\n a11yContent.style.display = 'none';\n a11yToggle.classList.add('collapsed');\n }\n\n // Console state\n const consoleContent = document.getElementById('console-content');\n const consoleToggle = document.getElementById('console-toggle');\n if (inspectorState.consoleExpanded) {\n consoleContent.style.display = 'block';\n consoleToggle.classList.remove('collapsed');\n } else {\n consoleContent.style.display = 'none';\n consoleToggle.classList.add('collapsed');\n }\n }\n\n // Toggle accessibility expanded state\n function toggleA11y() {\n inspectorState.a11yExpanded = !inspectorState.a11yExpanded;\n saveState();\n applyState();\n }\n\n // Toggle console expanded state\n function toggleConsole() {\n inspectorState.consoleExpanded = !inspectorState.consoleExpanded;\n saveState();\n applyState();\n }\n\n // Add console message\n function addConsoleMessage(type, message) {\n consoleMessages.push({ type, message });\n renderConsoleMessages();\n }\n\n // Clear console messages\n function clearConsoleMessages() {\n consoleMessages = [];\n renderConsoleMessages();\n }\n\n // Render console messages\n function renderConsoleMessages() {\n const emptyEl = document.getElementById('console-empty');\n const messagesEl = document.getElementById('console-messages');\n\n if (consoleMessages.length === 0) {\n emptyEl.style.display = 'flex';\n messagesEl.style.display = 'none';\n messagesEl.innerHTML = '';\n } else {\n emptyEl.style.display = 'none';\n messagesEl.style.display = 'block';\n messagesEl.innerHTML = consoleMessages.map(msg =>\n \\`<div class=\"console-message \\${msg.type}\">\n <span class=\"console-message-icon\">\\${msg.type === 'error' ? '✗' : '⚠'}</span>\n <span class=\"console-message-text\">\\${escapeHtml(msg.message)}</span>\n </div>\\`\n ).join('');\n }\n }\n\n // Escape HTML\n function escapeHtml(text) {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n // Get debug query string from overlay state\n function getDebugQuery() {\n const { overlays } = inspectorState;\n const hasAny = Object.values(overlays).some(v => v);\n if (!hasAny) return '';\n\n const params = new URLSearchParams();\n if (overlays.grid) params.set('grid', '1');\n if (overlays.margins) params.set('margins', '1');\n if (overlays.headers) params.set('headers', '1');\n if (overlays.breaks) params.set('breaks', '1');\n\n return '?' + params.toString();\n }\n\n // Check if any overlay is enabled\n function hasAnyOverlay() {\n return Object.values(inspectorState.overlays).some(v => v);\n }\n\n loadState();\n\n function connect() {\n ws = new WebSocket('ws://' + location.host);\n\n ws.onopen = () => {\n document.getElementById('status-dot').classList.remove('disconnected');\n document.getElementById('status-text').textContent = 'Connected';\n };\n\n ws.onclose = () => {\n document.getElementById('status-dot').classList.add('disconnected');\n document.getElementById('status-text').textContent = 'Disconnected';\n setTimeout(connect, 2000);\n };\n\n ws.onmessage = (event) => {\n const data = JSON.parse(event.data);\n if (data.type === 'reload') {\n if (currentTemplate) loadPreview(currentTemplate, { hotReload: true });\n } else if (data.type === 'templates') {\n updateTemplateList(data.templates);\n }\n };\n }\n\n connect();\n\n // Update template list in sidebar (called when templates are added/removed)\n function updateTemplateList(templates) {\n const sidebar = document.querySelector('.sidebar');\n const titleEl = sidebar.querySelector('.sidebar-title');\n\n // Clear existing buttons\n sidebar.innerHTML = '';\n sidebar.appendChild(titleEl);\n\n if (templates.length === 0) {\n sidebar.innerHTML += '<div class=\"empty-state\"><p>No templates found</p><code>pdfn add invoice</code></div>';\n // Clear preview if no templates\n currentTemplate = null;\n document.getElementById('file-name').textContent = '';\n document.getElementById('page-info').textContent = '';\n document.getElementById('preview-area').innerHTML = '<div class=\"empty-state\"><h2>No templates</h2><p>Add a template to get started</p><code>pdfn add invoice</code></div>';\n return;\n }\n\n // Add template buttons\n templates.forEach(t => {\n const btn = document.createElement('button');\n btn.className = 'template-btn' + (t.id === currentTemplate ? ' active' : '');\n btn.dataset.template = t.id;\n btn.dataset.file = t.file;\n btn.innerHTML = '<span class=\"template-name\">' + t.name + '</span>';\n btn.addEventListener('click', () => loadPreview(t.id));\n sidebar.appendChild(btn);\n });\n\n // If current template was deleted, switch to first template\n const templateIds = templates.map(t => t.id);\n if (currentTemplate && !templateIds.includes(currentTemplate)) {\n loadPreview(templates[0].id);\n }\n }\n\n // Listen for metrics from iframe (postMessage from PDFN script)\n window.addEventListener('message', function(event) {\n if (event.data && event.data.type === 'pdfn:metrics') {\n const metrics = event.data.metrics;\n if (metrics.paginationTime !== undefined) {\n document.getElementById('metric-pagination').textContent = metrics.paginationTime + 'ms';\n }\n if (metrics.pages !== undefined) {\n document.getElementById('metric-pages').textContent = metrics.pages;\n }\n }\n });\n\n function detectPageInfo(html) {\n let pageSize = 'A4';\n let orientation = 'portrait';\n\n // Check for data-pdfn-size attribute (e.g., \"A4\", \"Letter Landscape\", \"Tabloid\")\n const sizeMatch = html.match(/data-pdfn-size=\"([^\"]+)\"/i);\n if (sizeMatch) {\n const sizeStr = sizeMatch[1];\n // Check if it includes \"Landscape\"\n if (sizeStr.includes('Landscape')) {\n orientation = 'landscape';\n }\n // Extract base size name (remove \" Landscape\" suffix if present)\n const baseName = sizeStr.replace(' Landscape', '').replace(' Portrait', '');\n if (PAGE_SIZES[baseName]) {\n pageSize = baseName;\n }\n }\n\n return { pageSize, orientation };\n }\n\n function calculateScale(pageSize, orientation) {\n const container = document.getElementById('preview-area');\n const rect = container.getBoundingClientRect();\n const maxWidth = rect.width - 48;\n const maxHeight = rect.height - 48;\n\n const size = PAGE_SIZES[pageSize] || PAGE_SIZES.A4;\n const pageW = (orientation === 'landscape' ? size.height : size.width) * PT_TO_PX;\n const pageH = (orientation === 'landscape' ? size.width : size.height) * PT_TO_PX;\n\n const scale = Math.min(maxWidth / pageW, maxHeight / pageH, 1);\n return { pageW, pageH, scale };\n }\n\n function resetA11yState() {\n a11yHasRun = false;\n a11yLastStatus = null;\n const a11yBtn = document.getElementById('a11y-check-btn');\n const a11yResults = document.getElementById('a11y-results');\n a11yBtn.innerHTML = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg> Check';\n a11yResults.innerHTML = '<div class=\"a11y-empty\">Analyze template for WCAG compliance</div>';\n }\n\n function markA11yStale() {\n if (!a11yHasRun) return; // Nothing to mark stale\n const a11yResults = document.getElementById('a11y-results');\n // Add stale indicator at top of results\n const staleMsg = '<div class=\"a11y-stale\">Template changed — results may be outdated</div>';\n if (!a11yResults.innerHTML.includes('a11y-stale')) {\n a11yResults.insertAdjacentHTML('afterbegin', staleMsg);\n }\n }\n\n async function loadPreview(templateId, options) {\n options = options || {};\n const isTemplateSwitch = templateId !== currentTemplate;\n const isHotReload = options.hotReload === true;\n\n currentTemplate = templateId;\n\n // Handle accessibility state based on context\n if (isTemplateSwitch) {\n // Switching to different template - full reset\n resetA11yState();\n } else if (isHotReload) {\n // Same template but content changed - mark as stale\n if (a11yHasRun) markA11yStale();\n }\n // For resize/overlay changes - preserve state (do nothing)\n\n // Get template file name from button\n const activeBtn = document.querySelector('.template-btn[data-template=\"' + templateId + '\"]');\n const fileName = activeBtn ? activeBtn.dataset.file : templateId + '.tsx';\n\n document.querySelectorAll('.template-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.template === templateId);\n });\n\n // Update context bar\n document.getElementById('file-name').textContent = fileName;\n\n const previewArea = document.getElementById('preview-area');\n previewArea.innerHTML = '<div class=\"loading-spinner\"><div class=\"spinner\"></div><span>Rendering...</span></div>';\n\n try {\n const debugQuery = getDebugQuery();\n const htmlRes = await fetch('/api/template/' + templateId + '/html' + debugQuery);\n const html = await htmlRes.text();\n\n templateInfo = detectPageInfo(html);\n document.getElementById('page-info').textContent =\n templateInfo.pageSize + ' · ' + templateInfo.orientation;\n\n // Update metrics (render time from server, pagination from iframe postMessage)\n const renderTime = htmlRes.headers.get('X-Render-Time');\n\n if (renderTime) {\n document.getElementById('metric-render').textContent = renderTime + 'ms';\n }\n\n // Reset pagination metrics (will be updated via postMessage from iframe)\n document.getElementById('metric-pagination').textContent = '--';\n document.getElementById('metric-pages').textContent = '--';\n\n const { pageW, pageH, scale } = calculateScale(templateInfo.pageSize, templateInfo.orientation);\n const displayW = pageW * scale;\n const displayH = pageH * scale;\n\n previewArea.innerHTML = '';\n const frame = document.createElement('div');\n frame.className = 'preview-frame';\n frame.style.width = displayW + 'px';\n frame.style.height = displayH + 'px';\n\n const iframe = document.createElement('iframe');\n iframe.style.width = pageW + 'px';\n iframe.style.height = pageH + 'px';\n iframe.style.transform = 'scale(' + scale + ')';\n iframe.style.transformOrigin = 'top left';\n iframe.srcdoc = html;\n\n frame.classList.add('loading');\n iframe.onload = () => frame.classList.remove('loading');\n\n frame.appendChild(iframe);\n previewArea.appendChild(frame);\n\n // Set up action buttons\n const pdfUrl = '/api/template/' + templateId + '/pdf' + debugQuery;\n const htmlUrl = '/api/template/' + templateId + '/html' + debugQuery;\n const spinnerSvg = '<svg class=\"spin\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"2\" stroke-dasharray=\"32\" stroke-dashoffset=\"12\"/></svg>';\n\n document.getElementById('view-html').href = htmlUrl;\n\n // Preview button handler (local only - hidden when standard selected)\n document.getElementById('preview-pdf').onclick = () => {\n window.open(pdfUrl, '_blank');\n };\n\n // Download button handler\n document.getElementById('download-pdf').onclick = async () => {\n const standard = document.getElementById('standard-select').value;\n const btn = document.getElementById('download-pdf');\n\n if (standard) {\n // Use pdfn cloud for PDF/A standard\n btn.innerHTML = spinnerSvg + ' Generating...';\n btn.disabled = true;\n\n try {\n const response = await fetch('/api/template/' + templateId + '/pdf-standard?standard=' + encodeURIComponent(standard));\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error || 'Failed to generate PDF');\n }\n const blob = await response.blob();\n const link = document.createElement('a');\n link.href = URL.createObjectURL(blob);\n link.download = templateId + '.pdf';\n link.click();\n URL.revokeObjectURL(link.href);\n } catch (err) {\n alert('Error: ' + err.message);\n } finally {\n updateActionButtons();\n btn.disabled = false;\n }\n } else {\n // Standard local PDF\n const link = document.createElement('a');\n link.href = pdfUrl;\n link.download = templateId + '.pdf';\n link.click();\n }\n };\n\n } catch (err) {\n previewArea.innerHTML = '<div class=\"empty-state\"><h2>Error</h2><p>' + err.message + '</p></div>';\n }\n }\n\n document.querySelectorAll('.template-btn').forEach(btn => {\n btn.addEventListener('click', () => loadPreview(btn.dataset.template));\n });\n\n // Overlay checkbox handlers\n const overlayCheckboxes = ['grid', 'margins', 'headers', 'breaks'];\n overlayCheckboxes.forEach(name => {\n document.getElementById('overlay-' + name).onchange = (e) => {\n inspectorState.overlays[name] = e.target.checked;\n saveState();\n if (currentTemplate) loadPreview(currentTemplate);\n };\n });\n\n // Button icons\n const previewIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"/></svg>';\n const downloadIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg>';\n const cloudDownloadIcon = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10\"/></svg>';\n\n // Update Preview and Download buttons based on standard selection\n function updateActionButtons() {\n const standard = document.getElementById('standard-select').value;\n const previewBtn = document.getElementById('preview-pdf');\n const downloadBtn = document.getElementById('download-pdf');\n const cloudMessage = document.getElementById('cloud-key-message');\n const needsCloud = !!standard;\n const cloudAvailable = hasCloudAccess;\n\n if (needsCloud) {\n // Cloud mode - hide Preview, show cloud Download\n previewBtn.style.display = 'none';\n downloadBtn.innerHTML = cloudDownloadIcon + ' Download';\n downloadBtn.title = 'Download ' + standard + ' PDF via pdfn Cloud';\n\n if (!cloudAvailable) {\n // Show message and disable Download\n cloudMessage.classList.add('visible');\n downloadBtn.disabled = true;\n downloadBtn.style.opacity = '0.5';\n downloadBtn.style.cursor = 'not-allowed';\n } else {\n // Hide message and enable Download\n cloudMessage.classList.remove('visible');\n downloadBtn.disabled = false;\n downloadBtn.style.opacity = '';\n downloadBtn.style.cursor = '';\n }\n } else {\n // Local mode - show Preview, show local Download\n previewBtn.style.display = '';\n previewBtn.innerHTML = previewIcon + ' Preview';\n previewBtn.title = 'Preview PDF';\n downloadBtn.innerHTML = downloadIcon + ' Download';\n downloadBtn.title = 'Download PDF';\n // Hide message and enable buttons\n cloudMessage.classList.remove('visible');\n downloadBtn.disabled = false;\n downloadBtn.style.opacity = '';\n downloadBtn.style.cursor = '';\n }\n }\n\n // Conformance select change handler\n document.getElementById('standard-select').onchange = updateActionButtons;\n\n // Accessibility check handler\n let a11yHasRun = false;\n let a11yLastStatus = null; // 'pass' | 'fail' | null\n\n async function runAccessibilityCheck() {\n if (!currentTemplate) return;\n\n const btn = document.getElementById('a11y-check-btn');\n const results = document.getElementById('a11y-results');\n\n btn.disabled = true;\n btn.classList.add('loading');\n btn.innerHTML = '<svg class=\"spin\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"2\" stroke-dasharray=\"31.4\" stroke-dashoffset=\"10\"/></svg> Checking...';\n results.innerHTML = '<div class=\"a11y-empty\">Running accessibility checks...</div>';\n\n try {\n const res = await fetch('/api/template/' + currentTemplate + '/accessibility');\n const data = await res.json();\n const timestamp = new Date().toLocaleTimeString();\n\n if (data.violations.length === 0) {\n a11yLastStatus = 'pass';\n results.innerHTML = '<div class=\"a11y-timestamp\">Checked at ' + timestamp + '</div>' +\n '<div class=\"a11y-pass\"><svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"/></svg> No accessibility issues found</div>';\n } else {\n a11yLastStatus = 'fail';\n // Count by severity\n const counts = { critical: 0, serious: 0, moderate: 0, minor: 0 };\n data.violations.forEach(function(v) { counts[v.impact || 'minor']++; });\n\n let html = '<div class=\"a11y-timestamp\">Checked at ' + timestamp + '</div>';\n html += '<div class=\"a11y-summary has-issues\">';\n html += '<span class=\"issue-count\">' + data.violations.length + ' issue' + (data.violations.length > 1 ? 's' : '') + '</span>';\n // Show severity breakdown\n const parts = [];\n if (counts.critical > 0) parts.push(counts.critical + ' critical');\n if (counts.serious > 0) parts.push(counts.serious + ' serious');\n if (counts.moderate > 0) parts.push(counts.moderate + ' moderate');\n if (counts.minor > 0) parts.push(counts.minor + ' minor');\n html += '<span style=\"font-size: 10px; opacity: 0.8;\">' + parts.join(', ') + '</span>';\n html += '</div>';\n\n // Sort violations by severity (critical first)\n const severityOrder = { critical: 0, serious: 1, moderate: 2, minor: 3 };\n const sorted = data.violations.slice().sort(function(a, b) {\n return (severityOrder[a.impact] || 3) - (severityOrder[b.impact] || 3);\n });\n\n for (let i = 0; i < sorted.length; i++) {\n const v = sorted[i];\n const impact = v.impact || 'minor';\n // Escape HTML in description and id\n const desc = String(v.description || '').replace(/</g, '<').replace(/>/g, '>');\n const ruleId = String(v.id || 'unknown').replace(/</g, '<').replace(/>/g, '>');\n html += '<div class=\"a11y-violation ' + impact + '\" style=\"animation-delay: ' + (i * 0.05) + 's\">';\n html += '<div class=\"a11y-violation-header\">';\n html += '<span class=\"a11y-violation-impact ' + impact + '\">' + impact + '</span>';\n html += '<span class=\"a11y-violation-id\">' + ruleId + '</span>';\n html += '</div>';\n html += '<div class=\"a11y-violation-desc\">' + desc + '</div>';\n if (v.nodes && v.nodes.length > 0) {\n html += '<div class=\"a11y-violation-count\">' + v.nodes.length + ' element' + (v.nodes.length > 1 ? 's' : '') + ' affected</div>';\n }\n html += '</div>';\n }\n\n results.innerHTML = html;\n }\n } catch (err) {\n a11yLastStatus = null;\n results.innerHTML = '<div class=\"a11y-empty\" style=\"color: #ef4444;\">Error: ' + err.message + '</div>';\n }\n\n a11yHasRun = true;\n btn.disabled = false;\n btn.classList.remove('loading');\n\n // Update button with status indicator\n const statusDot = a11yLastStatus ? '<span class=\"status-dot ' + a11yLastStatus + '\"></span>' : '';\n btn.innerHTML = '<svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg> Recheck' + statusDot;\n }\n\n document.getElementById('a11y-check-btn').onclick = runAccessibilityCheck;\n document.getElementById('a11y-header').onclick = toggleA11y;\n\n // Console header click handler\n document.getElementById('console-header').onclick = toggleConsole;\n\n let resizeTimeout;\n window.addEventListener('resize', () => {\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(() => {\n if (currentTemplate) loadPreview(currentTemplate);\n }, 150);\n });\n\n // Apply saved state and load first template\n applyState();\n ${activeTemplate ? `loadPreview(\"${activeTemplate}\");` : \"\"}\n </script>\n</body>\n</html>`;\n}\n\nasync function startDevServer(options: DevServerOptions) {\n const { port, open, mode } = options;\n const absoluteTemplatesDir = resolve(process.cwd(), TEMPLATES_DIR);\n\n // Show header and initializing message\n console.log(chalk.bold(\"\\n pdfn dev\\n\"));\n console.log(chalk.dim(\" Initializing...\"));\n\n // Scan templates\n let templates = await scanTemplates(absoluteTemplatesDir);\n\n // Show relative path for cleaner output\n const displayPath = TEMPLATES_DIR;\n const templateCount = templates.length === 1 ? \"1 template\" : `${templates.length} templates`;\n\n // Create base server with shared /v1/generate and /health endpoints\n const { app, browserManager } = createBaseServer({\n enableLogging: false, // Dev uses custom logging per route\n onSuccess: (result) => {\n const { metrics } = result;\n console.log(\n chalk.green(\" ✓\"),\n chalk.dim(\"/v1/generate\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`,\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n },\n onError: (message) => {\n console.log(chalk.red(\" ✗\"), chalk.dim(\"/v1/generate\"), chalk.red(message));\n },\n });\n const server = createHttpServer(app);\n\n // Create WebSocket server for hot reload\n const wss = new WebSocketServer({ server });\n const clients = new Set<WebSocket>();\n\n wss.on(\"connection\", (ws) => {\n clients.add(ws);\n ws.on(\"close\", () => clients.delete(ws));\n });\n\n wss.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n // Handled by server error handler\n return;\n }\n console.error(chalk.red(\" ✗ WebSocket error:\"), err.message);\n });\n\n function broadcast(message: object) {\n const data = JSON.stringify(message);\n clients.forEach((client) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(data);\n }\n });\n }\n\n // Create Vite server for template compilation\n // Custom logger to silence Vite's default output (we handle our own logging)\n const viteLogger = {\n info: () => {},\n warn: () => {},\n error: (msg: string) => console.error(chalk.red(\" ✗ Vite:\"), msg),\n warnOnce: () => {},\n hasWarned: false,\n clearScreen: () => {},\n hasErrorLogged: () => false,\n };\n\n const vite = await createViteServer({\n root: process.cwd(),\n mode,\n server: {\n middlewareMode: true,\n hmr: { server } // Use our HTTP server for Vite's WebSocket\n },\n appType: \"custom\",\n customLogger: viteLogger,\n optimizeDeps: {\n include: [\"react\", \"react-dom\"],\n },\n esbuild: {\n jsx: \"automatic\",\n },\n ssr: {\n // Don't externalize these - we need to transform them\n noExternal: [\"@pdfn/react\", \"@pdfn/tailwind\", \"@pdfn/client\", \"server-only\"],\n },\n plugins: [\n // Unified pdfn plugin: Tailwind pre-compilation + client/template markers\n ...pdfn(),\n {\n name: \"mock-server-only\",\n enforce: \"pre\",\n resolveId(id) {\n if (id === \"server-only\") {\n return { id: \"\\0server-only-mock\", moduleSideEffects: false };\n }\n },\n load(id) {\n if (id === \"\\0server-only-mock\") {\n // Return empty module - we're in SSR context so server-only is fine\n return \"export default {};\";\n }\n },\n },\n ],\n });\n\n\n // Watch for template and CSS changes\n // Watch templates dir and styles.css + styles/ directory for CSS HMR\n const watchPaths = [\n absoluteTemplatesDir,\n join(absoluteTemplatesDir, \"styles.css\"),\n join(absoluteTemplatesDir, \"styles\"),\n ];\n const watcher = chokidar.watch(watchPaths, {\n ignored: /(^|[\\/\\\\])\\../,\n persistent: true,\n });\n\n // Helper to check if a file is a template (matches what we show in sidebar)\n function isTemplateFile(filePath: string): boolean {\n const fileName = filePath.split(\"/\").pop() || \"\";\n // Must be a .tsx file in the root of templates dir (not in subdirectories)\n if (!fileName.endsWith(\".tsx\")) return false;\n // Check if it's a direct child of templates dir\n const relativePath = filePath.replace(absoluteTemplatesDir + \"/\", \"\");\n if (relativePath.includes(\"/\")) return false;\n return true;\n }\n\n // Helper to check if file is a code file (tsx/ts/js/jsx)\n function isCodeFile(filePath: string): boolean {\n return /\\.(tsx?|jsx?)$/.test(filePath);\n }\n\n // Helper to check if file is a CSS file in templates\n function isCssFile(filePath: string): boolean {\n return filePath.endsWith(\".css\");\n }\n\n // Suppress logging during initial scan\n let watcherReady = false;\n\n watcher.on(\"ready\", () => {\n watcherReady = true;\n });\n\n watcher.on(\"change\", async (filePath) => {\n if (!watcherReady) return;\n const fileName = filePath.split(\"/\").pop() || filePath;\n\n // Handle CSS file changes (styles.css or styles/*.css)\n if (isCssFile(filePath)) {\n console.log(chalk.blue(\" ↻\"), fileName, chalk.dim(\"changed\"));\n\n // Invalidate the virtual Tailwind CSS module to force recompilation\n const virtualMod = vite.moduleGraph.getModuleById(\"\\0virtual:pdfn-tailwind-css\");\n if (virtualMod) {\n vite.moduleGraph.invalidateModule(virtualMod);\n // Invalidate all modules that import the virtual module\n for (const importer of virtualMod.importers) {\n vite.moduleGraph.invalidateModule(importer);\n }\n }\n\n templates = await scanTemplates(absoluteTemplatesDir);\n broadcast({ type: \"reload\" });\n return;\n }\n\n // Log all code file changes (templates and components)\n if (isCodeFile(filePath)) {\n console.log(chalk.blue(\" ↻\"), fileName, chalk.dim(\"changed\"));\n\n // Invalidate the changed module and its dependencies in Vite's SSR cache\n const mod = vite.moduleGraph.getModuleById(filePath);\n if (mod) {\n vite.moduleGraph.invalidateModule(mod);\n }\n\n // Also invalidate the virtual Tailwind CSS module to force recompilation\n const virtualMod = vite.moduleGraph.getModuleById(\"\\0virtual:pdfn-tailwind-css\");\n if (virtualMod) {\n vite.moduleGraph.invalidateModule(virtualMod);\n // Invalidate all modules that import the virtual module\n for (const importer of virtualMod.importers) {\n vite.moduleGraph.invalidateModule(importer);\n }\n }\n }\n templates = await scanTemplates(absoluteTemplatesDir);\n broadcast({ type: \"reload\" });\n });\n\n watcher.on(\"add\", async (filePath) => {\n if (!watcherReady) return;\n const oldCount = templates.length;\n templates = await scanTemplates(absoluteTemplatesDir);\n // Only log if a new template was actually added (not components)\n if (templates.length > oldCount && isTemplateFile(filePath)) {\n const fileName = filePath.split(\"/\").pop() || filePath;\n console.log(chalk.green(\" +\"), fileName, chalk.dim(\"added\"));\n }\n // Send updated template list so sidebar can be refreshed\n broadcast({ type: \"templates\", templates: templates.map(t => ({ id: t.id, name: t.name, file: t.file })) });\n });\n\n watcher.on(\"unlink\", async (filePath) => {\n if (!watcherReady) return;\n const oldCount = templates.length;\n const fileName = filePath.split(\"/\").pop() || filePath;\n templates = await scanTemplates(absoluteTemplatesDir);\n // Only log if a template was actually removed (not components)\n if (templates.length < oldCount && isTemplateFile(filePath)) {\n console.log(chalk.red(\" -\"), fileName, chalk.dim(\"removed\"));\n }\n // Send updated template list so sidebar can be refreshed\n broadcast({ type: \"templates\", templates: templates.map(t => ({ id: t.id, name: t.name, file: t.file })) });\n });\n\n // Serve preview UI\n app.get(\"/\", (_req: Request, res: Response) => {\n const activeTemplate = templates[0]?.id ?? null;\n const hasCloudAccess = !!process.env.PDFN_API_KEY;\n res.send(createPreviewHTML(templates, activeTemplate, hasCloudAccess));\n });\n\n // API: Get template code\n app.get(\"/api/template/:id/code\", (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const code = readFileSync(template.path, \"utf-8\");\n res.type(\"text/plain\").send(code);\n } catch {\n res.status(500).send(\"Error reading template\");\n }\n });\n\n // Helper: Render a template to HTML using Vite\n // Templates use default parameter values for sample data (React Email pattern)\n async function renderTemplate(\n template: TemplateInfo,\n debugOptions: DebugOptions | false = false\n ): Promise<string> {\n const mod = await vite.ssrLoadModule(template.path);\n const Component = mod.default;\n const { pdfn } = await vite.ssrLoadModule(\"@pdfn/react\");\n const client = pdfn();\n // Use React.createElement so element.type === Component (preserves markers)\n // Empty props - component's default parameter values provide sample data\n const { data, error } = await client.render({\n react: React.createElement(Component, {}),\n debug: debugOptions || undefined,\n });\n if (error) {\n throw new Error(error.message);\n }\n return data.html;\n }\n\n // Parse debug options from query params\n function parseDebugOptions(query: Record<string, unknown>): DebugOptions | false {\n const options: DebugOptions = {\n grid: query.grid === \"1\",\n margins: query.margins === \"1\",\n headers: query.headers === \"1\",\n breaks: query.breaks === \"1\",\n };\n\n // Return false if no options enabled\n const hasAny = Object.values(options).some((v) => v);\n return hasAny ? options : false;\n }\n\n // API: Get rendered HTML\n app.get(\"/api/template/:id/html\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const start = performance.now();\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const duration = Math.round(performance.now() - start);\n\n // Count pages from rendered HTML (look for pagedjs page markers)\n const pageMatches = html.match(/class=\"pagedjs_page\"/g);\n const pageCount = pageMatches ? pageMatches.length : 1;\n\n // Log render\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ HTML\"),\n chalk.cyan(`${duration}ms`)\n );\n\n res.setHeader(\"X-Render-Time\", duration.toString());\n res.setHeader(\"X-Page-Count\", pageCount.toString());\n // Note: Pagination time is measured client-side after Paged.js runs\n res.type(\"text/html\").send(html);\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"render failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).send(`Error rendering template: ${error}`);\n }\n });\n\n // Helper: Format bytes for display\n function formatBytes(bytes: number): string {\n if (bytes < 1024) return bytes + \"B\";\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + \"KB\";\n return (bytes / (1024 * 1024)).toFixed(2) + \"MB\";\n }\n\n // API: Generate PDF\n app.get(\"/api/template/:id/pdf\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).send(\"Template not found\");\n return;\n }\n\n try {\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, { timeout: 30000 });\n });\n\n // Log PDF generation - first line: request summary\n const { metrics, warnings, assets } = result;\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ PDF •\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n\n // Second line: details (pages, assets, timing) - all separated by •\n const pageStr = `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`;\n const images = assets.filter((a) => a.type === \"image\");\n const fonts = assets.filter((a) => a.type === \"font\");\n const assetParts: string[] = [];\n if (images.length > 0) assetParts.push(`${images.length} image${images.length > 1 ? \"s\" : \"\"}`);\n if (fonts.length > 0) assetParts.push(`${fonts.length} font${fonts.length > 1 ? \"s\" : \"\"}`);\n const assetStr = assetParts.length > 0 ? assetParts.join(\" • \") + \" • \" : \"\";\n const timingStr = `load ${metrics.contentLoad}ms • paginate ${metrics.pagedJs}ms • capture ${metrics.pdfCapture}ms`;\n console.log(chalk.dim(` ${pageStr} • ${assetStr}${timingStr}`));\n\n // Log warnings\n if (warnings.length > 0) {\n warnings.forEach((w) => {\n console.log(chalk.yellow(\" ⚠\"), chalk.yellow(w));\n });\n }\n\n // Add metrics headers\n res.setHeader(\"X-PDF-Total-Time\", metrics.total.toString());\n res.setHeader(\"X-PDF-Pages\", metrics.pageCount.toString());\n res.setHeader(\"X-PDF-Size\", metrics.pdfSize.toString());\n res.setHeader(\"X-PDF-Capture-Time\", metrics.pdfCapture.toString());\n res.setHeader(\"X-PDF-Assets\", result.assets.length.toString());\n res.setHeader(\"X-PDF-Warnings\", warnings.length.toString());\n\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"Content-Disposition\", `inline; filename=\"${template.id}.pdf\"`);\n res.send(result.buffer);\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"PDF generation failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).send(`Error generating PDF: ${error}`);\n }\n });\n\n // API: Generate PDF with standard (via pdfn Cloud)\n app.get(\"/api/template/:id/pdf-standard\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n const standard = req.query.standard as string;\n const validStandards = [\"PDF/A-1b\", \"PDF/A-2b\", \"PDF/A-3b\"];\n if (!standard || !validStandards.includes(standard)) {\n res.status(400).json({ error: `Invalid standard. Must be one of: ${validStandards.join(\", \")}` });\n return;\n }\n\n const apiKey = process.env.PDFN_API_KEY;\n if (!apiKey) {\n res.status(400).json({\n error: \"PDFN_API_KEY required for PDF/A standard. Get one at https://console.pdfn.dev\"\n });\n return;\n }\n\n try {\n const start = performance.now();\n const html = await renderTemplate(template, false);\n\n // Call pdfn Cloud API with standard\n const response = await fetch(\"https://api.pdfn.dev/v1/generate\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ html, standard }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: response.statusText }));\n throw new Error(error.message || error.error || `API error: ${response.status}`);\n }\n\n const pdfBuffer = Buffer.from(await response.arrayBuffer());\n const duration = Math.round(performance.now() - start);\n const pdfSize = parseInt(response.headers.get(\"X-PDF-Size\") || String(pdfBuffer.length));\n\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(`→ ${standard}`),\n chalk.magenta(\"(Cloud)\"),\n chalk.dim(\"•\"),\n chalk.cyan(`${duration}ms`),\n chalk.dim(\"•\"),\n formatBytes(pdfSize)\n );\n\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"Content-Disposition\", `attachment; filename=\"${template.id}.pdf\"`);\n res.send(pdfBuffer);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(`${standard} generation failed:`), message);\n res.status(500).json({ error: message });\n }\n });\n\n // API: Run accessibility check\n app.get(\"/api/template/:id/accessibility\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n try {\n const start = performance.now();\n const html = await renderTemplate(template, false);\n\n // Run axe-core in Puppeteer\n const results = await browserManager.withPage(async (page) => {\n await page.setContent(html, { waitUntil: \"domcontentloaded\" });\n\n // Wait for PDFN.ready (Paged.js to finish transforming DOM)\n await page.waitForFunction(\"window.PDFN?.ready === true\", { timeout: 30000 });\n\n // Inject axe-core\n await page.addScriptTag({ content: axeCore.source });\n\n // Run axe with PDF-relevant configuration\n const axeResults = await page.evaluate(() => {\n return new Promise<{\n violations: Array<{\n id: string;\n impact?: string;\n description: string;\n nodes: Array<{ target: string[] }>;\n }>;\n }>((resolve, reject) => {\n // @ts-expect-error axe is injected\n window.axe.run(document, {\n rules: {\n // Disable rules not relevant for PDFs\n \"scrollable-region-focusable\": { enabled: false },\n \"nested-interactive\": { enabled: false },\n tabindex: { enabled: false },\n \"focus-order-semantics\": { enabled: false },\n \"aria-hidden-focus\": { enabled: false },\n \"landmark-one-main\": { enabled: false },\n \"landmark-no-duplicate-banner\": { enabled: false },\n \"landmark-no-duplicate-contentinfo\": { enabled: false },\n \"landmark-no-duplicate-main\": { enabled: false },\n \"landmark-unique\": { enabled: false },\n region: { enabled: false },\n bypass: { enabled: false },\n \"skip-link\": { enabled: false },\n },\n }, (err: Error | null, results: { violations: Array<{ id: string; impact?: string; description: string; nodes: Array<{ target: string[] }> }> }) => {\n if (err) reject(err);\n else resolve(results);\n });\n });\n });\n\n return axeResults;\n });\n\n const duration = Math.round(performance.now() - start);\n\n // Log result\n const violationCount = results.violations.length;\n if (violationCount === 0) {\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ a11y\"),\n chalk.green(\"pass\"),\n chalk.dim(`(${duration}ms)`)\n );\n } else {\n console.log(\n chalk.yellow(\" ⚠\"),\n template.file,\n chalk.dim(\"→ a11y\"),\n chalk.yellow(`${violationCount} issue${violationCount > 1 ? \"s\" : \"\"}`),\n chalk.dim(`(${duration}ms)`)\n );\n }\n\n res.json({\n violations: results.violations.map((v) => ({\n id: v.id,\n impact: v.impact,\n description: v.description,\n nodes: v.nodes.map((n) => ({ target: n.target })),\n })),\n duration,\n });\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"a11y check failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).json({ error: `Error running accessibility check: ${error}` });\n }\n });\n\n // API: Generate PDF and return metrics (JSON)\n app.get(\"/api/template/:id/pdf-metrics\", async (req: Request, res: Response) => {\n const template = templates.find((t) => t.id === req.params.id);\n if (!template) {\n res.status(404).json({ error: \"Template not found\" });\n return;\n }\n\n try {\n const debugOptions = parseDebugOptions(req.query as Record<string, unknown>);\n const html = await renderTemplate(template, debugOptions);\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, { timeout: 30000 });\n });\n\n // Log PDF metrics - first line: request summary\n const { metrics, warnings, assets } = result;\n console.log(\n chalk.green(\" ✓\"),\n template.file,\n chalk.dim(\"→ PDF metrics •\"),\n chalk.cyan(`${metrics.total}ms`),\n chalk.dim(\"•\"),\n formatBytes(metrics.pdfSize)\n );\n\n // Second line: details (pages, assets, timing) - all separated by •\n {\n const pageStr = `${metrics.pageCount} page${metrics.pageCount > 1 ? \"s\" : \"\"}`;\n const images = assets.filter((a) => a.type === \"image\");\n const fonts = assets.filter((a) => a.type === \"font\");\n const assetParts: string[] = [];\n if (images.length > 0) assetParts.push(`${images.length} image${images.length > 1 ? \"s\" : \"\"}`);\n if (fonts.length > 0) assetParts.push(`${fonts.length} font${fonts.length > 1 ? \"s\" : \"\"}`);\n const assetStr = assetParts.length > 0 ? assetParts.join(\" • \") + \" • \" : \"\";\n const timingStr = `load ${metrics.contentLoad}ms • paginate ${metrics.pagedJs}ms • capture ${metrics.pdfCapture}ms`;\n console.log(chalk.dim(` ${pageStr} • ${assetStr}${timingStr}`));\n }\n\n if (warnings.length > 0) {\n warnings.forEach((w) => {\n console.log(chalk.yellow(\" ⚠\"), chalk.yellow(w));\n });\n }\n\n res.json({\n metrics: result.metrics,\n assets: result.assets,\n warnings: result.warnings,\n });\n } catch (error) {\n console.log(chalk.red(\" ✗\"), template.file, chalk.red(\"PDF metrics failed\"));\n console.error(chalk.dim(\" \"), error);\n res.status(500).json({ error: `Error generating PDF: ${error}` });\n }\n });\n\n // Start server\n await new Promise<void>((resolve, reject) => {\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n console.error(chalk.red(`\\n ✗ Port ${port} is already in use.\\n`));\n console.error(chalk.dim(` Either stop the existing server or use a different port:`));\n console.error(chalk.dim(` npx pdfn dev --port ${port + 1}\\n`));\n process.exit(1);\n }\n reject(err);\n });\n server.listen(port, () => resolve());\n });\n\n // Pre-launch Chromium for PDF generation\n await browserManager.getBrowser();\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(chalk.dim(\"\\n Shutting down...\"));\n watcher.close();\n await vite.close();\n await browserManager.close();\n server.close();\n process.exit(0);\n };\n\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n\n // Open Chromium (same browser used for PDF generation = true WYSIWYG)\n function openChromium() {\n const chromiumPath = puppeteer.executablePath();\n console.log(chalk.dim(\" Opening in Chromium...\\n\"));\n spawn(chromiumPath, [`http://localhost:${port}`], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n }\n\n console.log(chalk.dim(` Templates: ${displayPath} (${templateCount})`));\n console.log(chalk.green(`\\n ✓ Ready at ${chalk.cyan(`http://localhost:${port}`)}\\n`));\n\n if (!open) {\n console.log(chalk.dim(` Tip: PDFs are generated with Chromium. For accurate preview,`));\n console.log(chalk.dim(` open in Chrome or Chromium-based browsers.\\n`));\n }\n\n console.log(chalk.dim(` Shortcuts`));\n console.log(chalk.dim(` › ${chalk.white(\"o\")} open in Chromium`));\n console.log(chalk.dim(` › ${chalk.white(\"q\")} quit\\n`));\n\n if (open) {\n openChromium();\n }\n\n // Keyboard shortcuts\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (key: string) => {\n if (key === \"o\" || key === \"O\") {\n openChromium();\n } else if (key === \"q\" || key === \"Q\" || key === \"\\u0003\") {\n // q or Ctrl+C\n shutdown();\n }\n });\n }\n}\n\nexport const devCommand = new Command(\"dev\")\n .description(\"Start development server with live preview\")\n .option(\"--port <number>\", \"Server port\", \"3456\")\n .option(\"--open\", \"Open browser automatically\")\n .option(\"--mode <mode>\", \"Load additional .env.[mode] files\")\n .action(async (options) => {\n // Load .env and .env.local (+ .env.[mode] if --mode is passed)\n loadEnv(options.mode);\n\n const port = parseInt(options.port, 10);\n\n await startDevServer({\n port,\n open: options.open ?? false,\n mode: options.mode,\n });\n });\n","import express from \"express\";\nimport type { Express, Request, Response, NextFunction } from \"express\";\nimport { BrowserManager } from \"./browser\";\nimport { createGenerateHandler } from \"./routes\";\nimport { type PdfResult } from \"./pdf\";\n\nexport interface BaseServerOptions {\n /** Max concurrent pages (default: env.PDFN_MAX_CONCURRENT || 5) */\n maxConcurrent?: number;\n /** Request timeout in ms (default: env.PDFN_TIMEOUT || 30000) */\n timeout?: number;\n /** Enable request logging (default: true) */\n enableLogging?: boolean;\n /** Custom logger for requests */\n onRequest?: (method: string, path: string, status: number, duration: number, extra?: string) => void;\n /** Custom logger for PDF details */\n onPdfResult?: (result: PdfResult) => void;\n /** Callback on successful PDF generation */\n onSuccess?: (result: PdfResult) => void;\n /** Custom logger for errors */\n onError?: (message: string) => void;\n}\n\nexport interface BaseServer {\n app: Express;\n browserManager: BrowserManager;\n maxConcurrent: number;\n timeout: number;\n}\n\n/**\n * Create a base PDFN server with common middleware and routes.\n *\n * This is shared between `dev` and `serve` commands.\n * Returns the Express app and BrowserManager for further customization.\n *\n * Includes:\n * - JSON body parsing (50mb limit)\n * - Request logging middleware (optional)\n * - POST /v1/generate endpoint\n * - GET /health endpoint\n */\nexport function createBaseServer(options: BaseServerOptions = {}): BaseServer {\n const maxConcurrent =\n options.maxConcurrent ??\n parseInt(process.env.PDFN_MAX_CONCURRENT ?? \"5\", 10);\n const timeout =\n options.timeout ?? parseInt(process.env.PDFN_TIMEOUT ?? \"30000\", 10);\n const enableLogging = options.enableLogging ?? true;\n\n const app = express();\n const browserManager = new BrowserManager({ maxConcurrent, timeout });\n\n // JSON body parsing\n app.use(express.json({ limit: \"50mb\" }));\n\n // Request logging middleware\n if (enableLogging && options.onRequest) {\n const onRequest = options.onRequest;\n const onPdfResult = options.onPdfResult;\n\n app.use((req: Request, res: Response, next: NextFunction) => {\n // Skip noisy requests\n const skipPaths = [\"/favicon.ico\", \"/robots.txt\"];\n if (skipPaths.includes(req.path)) {\n next();\n return;\n }\n\n const start = performance.now();\n\n res.on(\"finish\", () => {\n const duration = performance.now() - start;\n\n // For /v1/generate, include page count and size\n let extra: string | undefined;\n if (req.path === \"/v1/generate\" && res.statusCode === 200) {\n const pages = res.getHeader(\"X-PDFN-Page-Count\");\n const size = Number(res.getHeader(\"X-PDFN-PDF-Size\")) || 0;\n const sizeKB = (size / 1024).toFixed(1);\n extra = `${pages} pages • ${sizeKB}KB`;\n }\n\n onRequest(req.method, req.path, res.statusCode, duration, extra);\n\n // Log PDF details if available\n const pdfResult = (res as any)._pdfResult as PdfResult | undefined;\n if (pdfResult && onPdfResult) {\n onPdfResult(pdfResult);\n }\n });\n\n next();\n });\n }\n\n // Health check endpoint\n app.get(\"/health\", (_req: Request, res: Response) => {\n res.json({\n status: \"ok\",\n browser: browserManager.isConnected() ? \"connected\" : \"disconnected\",\n activePages: browserManager.getActivePages(),\n maxConcurrent: browserManager.getMaxConcurrent(),\n });\n });\n\n // PDF generation endpoint (matches pdfn Cloud API)\n app.post(\n \"/v1/generate\",\n createGenerateHandler(browserManager, {\n timeout,\n onSuccess: options.onSuccess,\n onError: options.onError,\n })\n );\n\n return {\n app,\n browserManager,\n maxConcurrent,\n timeout,\n };\n}\n","import puppeteer, { Browser, Page } from \"puppeteer\";\n\nexport interface BrowserManagerOptions {\n /** Max concurrent pages (default: env.PDFN_MAX_CONCURRENT || 5) */\n maxConcurrent?: number;\n /** Request timeout in ms (default: env.PDFN_TIMEOUT || 30000) */\n timeout?: number;\n}\n\n/**\n * Manages a single Puppeteer browser instance with multiple concurrent pages\n *\n * Architecture: Single browser + multiple pages (industry standard pattern)\n * - Lower memory footprint (~150MB browser + ~30MB/page)\n * - Fast page creation (~50ms vs ~1-2s for new browser)\n * - Auto-restart on browser crash/disconnect\n */\nexport class BrowserManager {\n private browser: Browser | null = null;\n private launching: Promise<Browser> | null = null;\n private activePages = 0;\n private maxConcurrent: number;\n private timeout: number;\n\n constructor(options: BrowserManagerOptions = {}) {\n this.maxConcurrent =\n options.maxConcurrent ??\n parseInt(process.env.PDFN_MAX_CONCURRENT ?? \"5\", 10);\n this.timeout =\n options.timeout ?? parseInt(process.env.PDFN_TIMEOUT ?? \"30000\", 10);\n }\n\n /**\n * Get or create the browser instance\n */\n async getBrowser(): Promise<Browser> {\n if (this.browser?.connected) {\n return this.browser;\n }\n\n // If already launching, wait for that\n if (this.launching) {\n return this.launching;\n }\n\n // Launch new browser\n this.launching = puppeteer.launch({\n headless: true,\n args: [\n \"--no-sandbox\",\n \"--disable-setuid-sandbox\",\n \"--disable-dev-shm-usage\",\n \"--disable-gpu\",\n \"--disable-extensions\",\n \"--mute-audio\",\n \"--disable-notifications\",\n ],\n });\n\n try {\n this.browser = await this.launching;\n this.setupBrowserListeners(this.browser);\n return this.browser;\n } finally {\n this.launching = null;\n }\n }\n\n /**\n * Setup browser event listeners for auto-restart on crash\n */\n private setupBrowserListeners(browser: Browser): void {\n browser.on(\"disconnected\", () => {\n this.browser = null;\n // activePages will naturally drain as withPage catches errors\n });\n }\n\n /**\n * Create a new page for PDF generation\n */\n async createPage(): Promise<Page> {\n const browser = await this.getBrowser();\n const page = await browser.newPage();\n page.setDefaultTimeout(this.timeout);\n return page;\n }\n\n /**\n * Execute a function with a managed page\n *\n * This pattern ensures:\n * - Concurrency limits are respected\n * - Pages are always closed after use\n * - Active page count is accurate\n *\n * @throws Error if max concurrent pages reached\n */\n async withPage<T>(fn: (page: Page) => Promise<T>): Promise<T> {\n if (this.activePages >= this.maxConcurrent) {\n throw new Error(\"Server busy - max concurrent requests reached\");\n }\n\n this.activePages++;\n let page: Page | null = null;\n\n try {\n page = await this.createPage();\n return await fn(page);\n } finally {\n this.activePages--;\n if (page) {\n await page.close().catch(() => {\n // Ignore close errors (browser may have disconnected)\n });\n }\n }\n }\n\n /**\n * Check if browser is connected\n */\n isConnected(): boolean {\n return this.browser?.connected ?? false;\n }\n\n /**\n * Get current number of active pages\n */\n getActivePages(): number {\n return this.activePages;\n }\n\n /**\n * Get max concurrent pages limit\n */\n getMaxConcurrent(): number {\n return this.maxConcurrent;\n }\n\n /**\n * Get configured timeout\n */\n getTimeout(): number {\n return this.timeout;\n }\n\n /**\n * Close the browser and clean up\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n}\n","/**\n * Simple debug logging utility\n *\n * Enable with DEBUG=pdfn:cli or DEBUG=pdfn:* or DEBUG=pdfn\n *\n * This is a lightweight alternative to the `debug` npm package.\n * Supports namespace filtering similar to the debug package convention.\n */\n\n// Debug logging - enable with DEBUG=pdfn:cli or DEBUG=pdfn:* or DEBUG=pdfn\nconst debugEnv = process.env.DEBUG ?? \"\";\nconst isEnabled =\n debugEnv.includes(\"pdfn:cli\") ||\n debugEnv.includes(\"pdfn:*\") ||\n debugEnv === \"pdfn\" ||\n debugEnv.includes(\"pdfn,\") ||\n debugEnv.endsWith(\",pdfn\");\n\n/**\n * Debug logger - only logs when DEBUG=pdfn:cli (or pdfn:* or pdfn) is set\n */\nexport const debug = isEnabled\n ? (message: string, ...args: unknown[]) => {\n console.log(`[pdfn:cli] ${message}`, ...args);\n }\n : () => {};\n\n/**\n * Check if debug logging is enabled\n */\nexport const isDebugEnabled = () => isEnabled;\n","import type { Page, PDFOptions, HTTPRequest, HTTPResponse } from \"puppeteer\";\nimport { debug } from \"../utils/debug\";\n\nexport interface PdfGenerationOptions {\n /** PDF format (default: A4) */\n format?: \"A4\" | \"Letter\" | \"Legal\" | \"Tabloid\" | \"A3\" | \"A5\";\n /** Print background graphics */\n printBackground?: boolean;\n /** Timeout for waiting for ready state (ms) */\n timeout?: number;\n}\n\n/** Asset information tracked during PDF generation */\nexport interface AssetInfo {\n /** Asset URL */\n url: string;\n /** Asset type (image, font, stylesheet, script, other) */\n type: \"image\" | \"font\" | \"stylesheet\" | \"script\" | \"other\";\n /** Size in bytes (0 if failed) */\n size: number;\n /** Load time in ms */\n duration: number;\n /** Whether the asset loaded successfully */\n success: boolean;\n /** Error message if failed */\n error?: string;\n}\n\nexport interface PdfResult {\n /** PDF buffer */\n buffer: Buffer;\n /** Generation metrics */\n metrics: {\n /** Total generation time in ms */\n total: number;\n /** Time waiting for content to load */\n contentLoad: number;\n /** Time waiting for Paged.js */\n pagedJs: number;\n /** Time for PDF capture */\n pdfCapture: number;\n /** Number of pages */\n pageCount: number;\n /** PDF file size in bytes */\n pdfSize: number;\n };\n /** Assets loaded during generation */\n assets: AssetInfo[];\n /** Warnings detected during generation */\n warnings: string[];\n}\n\n/** Size threshold for large asset warning (200KB - web best practice) */\nconst LARGE_ASSET_THRESHOLD = 200 * 1024;\n\n/** Duration threshold for slow asset warning (1000ms - lenient for fonts) */\nconst SLOW_ASSET_THRESHOLD = 1000;\n\n/** Determine asset type from resource type */\nfunction getAssetType(resourceType: string): AssetInfo[\"type\"] {\n switch (resourceType) {\n case \"image\":\n return \"image\";\n case \"font\":\n return \"font\";\n case \"stylesheet\":\n return \"stylesheet\";\n case \"script\":\n return \"script\";\n default:\n return \"other\";\n }\n}\n\n/** Get short URL for display (remove data: prefix, truncate long URLs) */\nfunction getShortUrl(url: string): string {\n if (url.startsWith(\"data:\")) {\n const match = url.match(/^data:([^;,]+)/);\n return match ? `data:${match[1]}...` : \"data:...\";\n }\n if (url.length > 80) {\n return url.substring(0, 77) + \"...\";\n }\n return url;\n}\n\n/**\n * Generate PDF from HTML content using a Puppeteer page\n */\nexport async function generatePdf(\n page: Page,\n html: string,\n options: PdfGenerationOptions = {}\n): Promise<PdfResult> {\n const {\n format = \"A4\",\n printBackground = true,\n timeout = 30000,\n } = options;\n\n const startTime = performance.now();\n let contentLoadTime = 0;\n let pagedJsTime = 0;\n let pdfCaptureTime = 0;\n let pageCount = 0;\n\n // Asset tracking\n const assets: AssetInfo[] = [];\n const requestStartTimes = new Map<string, number>();\n const warnings: string[] = [];\n\n // Request handler to track start times\n const onRequest = (request: HTTPRequest) => {\n const url = request.url();\n const resourceType = request.resourceType();\n\n // Only track relevant resource types\n if ([\"image\", \"font\", \"stylesheet\", \"script\"].includes(resourceType)) {\n requestStartTimes.set(url, performance.now());\n }\n };\n\n // Response handler to track completed requests\n const onResponse = (response: HTTPResponse) => {\n const url = response.url();\n const request = response.request();\n const resourceType = request.resourceType();\n const startTime = requestStartTimes.get(url);\n\n if (startTime === undefined) return;\n\n const duration = Math.round(performance.now() - startTime);\n const status = response.status();\n const success = status >= 200 && status < 400;\n\n // Get content length from headers\n const contentLength = response.headers()[\"content-length\"];\n const size = contentLength ? parseInt(contentLength, 10) : 0;\n\n const asset: AssetInfo = {\n url: getShortUrl(url),\n type: getAssetType(resourceType),\n size,\n duration,\n success,\n };\n\n if (!success) {\n asset.error = `HTTP ${status}`;\n warnings.push(`Failed: ${asset.url} (${status})`);\n } else {\n if (size > LARGE_ASSET_THRESHOLD) {\n warnings.push(`Large: ${asset.url} (${(size / 1024).toFixed(0)}KB)`);\n }\n if (duration > SLOW_ASSET_THRESHOLD) {\n warnings.push(`Slow: ${asset.url} (${duration}ms)`);\n }\n }\n\n assets.push(asset);\n requestStartTimes.delete(url);\n };\n\n // Request failed handler\n const onRequestFailed = (request: HTTPRequest) => {\n const url = request.url();\n const resourceType = request.resourceType();\n const startTime = requestStartTimes.get(url);\n\n if (startTime === undefined) return;\n\n const duration = Math.round(performance.now() - startTime);\n const failure = request.failure();\n const errorText = failure?.errorText || \"Unknown error\";\n\n const asset: AssetInfo = {\n url: getShortUrl(url),\n type: getAssetType(resourceType),\n size: 0,\n duration,\n success: false,\n error: errorText,\n };\n\n warnings.push(`Failed: ${asset.url} (${errorText})`);\n assets.push(asset);\n requestStartTimes.delete(url);\n };\n\n // Set up request interception\n page.on(\"request\", onRequest);\n page.on(\"response\", onResponse);\n page.on(\"requestfailed\", onRequestFailed);\n\n try {\n // Set content and wait for network idle\n const contentStart = performance.now();\n await page.setContent(html, {\n waitUntil: \"networkidle0\",\n timeout,\n });\n contentLoadTime = performance.now() - contentStart;\n\n // Wait for PDFN.ready (pdfn HTML) or proceed immediately (plain HTML)\n const pagedStart = performance.now();\n await page.waitForFunction(\n () => typeof (window as any).PDFN === \"undefined\" || (window as any).PDFN?.ready === true,\n { timeout }\n );\n pagedJsTime = performance.now() - pagedStart;\n\n // Get page count from PDFN metrics\n pageCount = await page.evaluate(() => {\n return (window as any).PDFN?.metrics?.pages ?? 1;\n });\n\n // Generate PDF\n const pdfStart = performance.now();\n const pdfOptions: PDFOptions = {\n format,\n printBackground,\n preferCSSPageSize: true,\n tagged: true, // Accessibility: generates tagged PDF for screen readers\n outline: true, // Accessibility: auto-generates bookmarks from headings\n };\n\n const buffer = await page.pdf(pdfOptions);\n pdfCaptureTime = performance.now() - pdfStart;\n\n const totalTime = performance.now() - startTime;\n const pdfBuffer = Buffer.from(buffer);\n\n debug(\n `pdf: ${Math.round(totalTime)}ms (load: ${Math.round(contentLoadTime)}ms, paged: ${Math.round(pagedJsTime)}ms, capture: ${Math.round(pdfCaptureTime)}ms) - ${pageCount} pages, ${(pdfBuffer.length / 1024).toFixed(1)}KB`\n );\n\n if (assets.length > 0) {\n debug(`assets: ${assets.length} (${assets.filter(a => !a.success).length} failed)`);\n }\n\n if (warnings.length > 0) {\n warnings.forEach(w => debug(`warning: ${w}`));\n }\n\n return {\n buffer: pdfBuffer,\n metrics: {\n total: Math.round(totalTime),\n contentLoad: Math.round(contentLoadTime),\n pagedJs: Math.round(pagedJsTime),\n pdfCapture: Math.round(pdfCaptureTime),\n pageCount,\n pdfSize: pdfBuffer.length,\n },\n assets,\n warnings,\n };\n } finally {\n // Clean up event listeners\n page.off(\"request\", onRequest);\n page.off(\"response\", onResponse);\n page.off(\"requestfailed\", onRequestFailed);\n\n // Close the page after use\n await page.close().catch(() => {});\n }\n}\n","import type { Request, Response } from \"express\";\nimport type { BrowserManager } from \"./browser\";\nimport { generatePdf, type PdfResult } from \"./pdf\";\n\ninterface GenerateRequestBody {\n html?: string;\n standard?: string;\n options?: {\n timeout?: number;\n printBackground?: boolean;\n preferCSSPageSize?: boolean;\n };\n}\n\ninterface GenerateHandlerOptions {\n /** Default timeout in ms (default: 30000) */\n timeout?: number;\n /** Optional logger for PDF generation results */\n onSuccess?: (result: PdfResult) => void;\n /** Optional logger for errors */\n onError?: (error: string) => void;\n}\n\n/**\n * Create a /v1/generate POST handler for PDF generation\n *\n * @example\n * ```ts\n * const browserManager = new BrowserManager({ maxConcurrent: 5 });\n * app.post(\"/v1/generate\", createGenerateHandler(browserManager));\n * ```\n */\nexport function createGenerateHandler(\n browserManager: BrowserManager,\n options: GenerateHandlerOptions = {}\n) {\n const { timeout = 30000, onSuccess, onError } = options;\n\n return async (req: Request, res: Response) => {\n const { html, standard, options: pdfOptions } = req.body as GenerateRequestBody;\n const format = req.query.format as string | undefined;\n\n if (!html) {\n res.status(400).json({ error: \"HTML content is required\" });\n return;\n }\n\n // PDF/A standard requires pdfn Cloud archival compliance pipeline\n if (standard) {\n res.status(400).json({\n error: `PDF/A archival compliance requires the pdfn Cloud compliance pipeline.\n\nLayout is identical in local dev. Compliance does not change layout or rendering — it only adds validation and archival metadata.\n\nTo generate ${standard}-compliant PDFs:\n Set: PDFN_API_KEY=pdfn_live_...\n Get key at: https://console.pdfn.dev\n\nTo preview layout without compliance:\n Remove the 'standard' option`,\n });\n return;\n }\n\n // Return HTML directly if format=html (for debugging)\n if (format === \"html\") {\n res.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n res.send(html);\n return;\n }\n\n try {\n const result = await browserManager.withPage(async (page) => {\n return generatePdf(page, html, {\n ...pdfOptions,\n timeout: pdfOptions?.timeout ?? timeout,\n });\n });\n\n // Set response headers with metrics\n res.setHeader(\"Content-Type\", \"application/pdf\");\n res.setHeader(\"X-PDFN-Total-Time\", result.metrics.total.toString());\n res.setHeader(\"X-PDFN-Content-Load\", result.metrics.contentLoad.toString());\n res.setHeader(\"X-PDFN-PagedJs-Time\", result.metrics.pagedJs.toString());\n res.setHeader(\"X-PDFN-PDF-Capture\", result.metrics.pdfCapture.toString());\n res.setHeader(\"X-PDFN-Page-Count\", result.metrics.pageCount.toString());\n res.setHeader(\"X-PDFN-PDF-Size\", result.metrics.pdfSize.toString());\n\n // Attach result for logging middleware (used by serve command)\n (res as any)._pdfResult = result;\n\n // Call success callback if provided\n onSuccess?.(result);\n\n res.send(result.buffer);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n\n // Call error callback if provided\n onError?.(message);\n\n if (message.includes(\"Server busy\")) {\n res.status(503).json({ error: message });\n return;\n }\n\n res.status(500).json({ error: message });\n }\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { config } from \"dotenv\";\n\n/**\n * Load environment variables.\n * Always loads: .env → .env.local\n * With mode: also loads .env.[mode] → .env.[mode].local\n *\n * @param mode - Optional environment mode (e.g., \"production\")\n */\nexport function loadEnv(mode?: string): void {\n const cwd = process.cwd();\n\n // Always load base env files\n const envFiles: string[] = [\".env\", \".env.local\"];\n\n // Optionally load mode-specific files\n if (mode) {\n envFiles.push(`.env.${mode}`, `.env.${mode}.local`);\n }\n\n // Deduplicate (e.g. --mode local would produce .env.local twice)\n const unique = [...new Set(envFiles)];\n\n for (const file of unique) {\n const filePath = resolve(cwd, file);\n if (existsSync(filePath)) {\n // quiet: true suppresses dotenv's verbose output in v17+\n config({ path: filePath, override: true, quiet: true });\n }\n }\n}\n","import { Command } from \"commander\";\nimport { existsSync, mkdirSync, copyFileSync, readFileSync } from \"fs\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport chalk from \"chalk\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Available templates with descriptions\nconst TEMPLATES: Record<string, { name: string; description: string; pageSize: string }> = {\n invoice: {\n name: \"Invoice\",\n description: \"Professional invoice with itemized billing\",\n pageSize: \"A4\",\n },\n letter: {\n name: \"Business Letter\",\n description: \"US business correspondence\",\n pageSize: \"Letter\",\n },\n contract: {\n name: \"Contract\",\n description: \"Legal service agreement with terms\",\n pageSize: \"Legal\",\n },\n ticket: {\n name: \"Event Ticket\",\n description: \"Admission ticket with QR placeholder\",\n pageSize: \"A5\",\n },\n poster: {\n name: \"Poster\",\n description: \"Event poster (landscape)\",\n pageSize: \"Tabloid\",\n },\n report: {\n name: \"Sales Report\",\n description: \"Report with Recharts (npm install recharts)\",\n pageSize: \"A4\",\n },\n};\n\ntype TemplateStyle = \"inline\" | \"tailwind\";\n\nfunction getTemplatesDir(style: TemplateStyle): string {\n // After build, cli.js is in dist/, templates are in templates/\n // So from dist/ we go up one level to package root, then into templates/<style>/\n return join(__dirname, \"..\", \"templates\", style);\n}\n\n/**\n * Check if @pdfn/tailwind is installed in user's project\n * Checks both package.json and node_modules for compatibility with npm, pnpm, yarn\n */\nfunction isTailwindInstalled(cwd: string): boolean {\n // First check package.json for explicit dependency\n try {\n const pkgPath = join(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.dependencies?.[\"@pdfn/tailwind\"] || pkg.devDependencies?.[\"@pdfn/tailwind\"]) {\n return true;\n }\n }\n } catch {\n // Ignore parse errors\n }\n\n // Fall back to checking node_modules (for linked packages, global installs, etc.)\n const tailwindPath = join(cwd, \"node_modules\", \"@pdfn\", \"tailwind\");\n return existsSync(tailwindPath);\n}\n\nexport const addCommand = new Command(\"add\")\n .description(\"Add a starter template to your project\")\n .argument(\"[template]\", \"Template name (e.g., invoice, letter, contract)\")\n .option(\"--list\", \"List available templates\")\n .option(\"--tailwind\", \"Use Tailwind CSS styling (requires @pdfn/tailwind)\")\n .option(\"--inline\", \"Use inline styles (default)\")\n .option(\"--force\", \"Overwrite existing files\")\n .action(async (template, options) => {\n const cwd = process.cwd();\n const outputDir = \"./pdfn-templates\";\n\n // List templates\n if (options.list || !template) {\n console.log(chalk.bold(\"\\nAvailable templates:\\n\"));\n\n for (const [id, info] of Object.entries(TEMPLATES)) {\n console.log(` ${chalk.cyan(id.padEnd(12))} ${info.description} ${chalk.dim(`(${info.pageSize})`)}`);\n }\n\n console.log(chalk.dim(\"\\nUsage: pdfn add <template> [--tailwind]\"));\n console.log(chalk.dim(\"Example: pdfn add invoice\"));\n console.log(chalk.dim(\"Example: pdfn add invoice --tailwind\\n\"));\n console.log(chalk.bold(\"Options:\"));\n console.log(chalk.dim(\" --inline Use inline styles (default)\"));\n console.log(chalk.dim(\" --tailwind Use Tailwind CSS (requires @pdfn/tailwind)\\n\"));\n return;\n }\n\n // Validate template\n if (!TEMPLATES[template]) {\n console.error(chalk.red(`\\nError: Unknown template \"${template}\"`));\n console.log(chalk.dim(\"Run 'pdfn add --list' to see available templates\\n\"));\n process.exit(1);\n }\n\n // Determine style (default to inline)\n const style: TemplateStyle = options.tailwind ? \"tailwind\" : \"inline\";\n\n // Check for @pdfn/tailwind if tailwind style requested\n if (style === \"tailwind\" && !isTailwindInstalled(cwd)) {\n console.error(chalk.yellow(`\\n⚠ @pdfn/tailwind is not installed.`));\n console.log(chalk.dim(\"Install it first to use Tailwind templates:\\n\"));\n console.log(chalk.cyan(\" npm install @pdfn/tailwind\\n\"));\n console.log(chalk.dim(\"Or use inline styles (default):\\n\"));\n console.log(chalk.cyan(` pdfn add ${template}\\n`));\n process.exit(1);\n }\n\n const templatesDir = getTemplatesDir(style);\n const sourceFile = join(templatesDir, `${template}.tsx`);\n const outputFile = join(outputDir, `${template}.tsx`);\n\n // Check if source template exists\n if (!existsSync(sourceFile)) {\n console.error(chalk.red(`\\nError: Template file not found: ${sourceFile}`));\n console.log(chalk.dim(\"This may be a package installation issue.\\n\"));\n process.exit(1);\n }\n\n // Create output directory\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n console.log(chalk.dim(`Created ${outputDir}/`));\n }\n\n // Check if file already exists\n if (existsSync(outputFile) && !options.force) {\n console.error(chalk.yellow(`\\nFile already exists: ${outputFile}`));\n console.log(chalk.dim(\"Use --force to overwrite\\n\"));\n process.exit(1);\n }\n\n // Copy template\n try {\n copyFileSync(sourceFile, outputFile);\n\n const info = TEMPLATES[template];\n const styleLabel = style === \"tailwind\" ? chalk.cyan(\" (Tailwind)\") : chalk.dim(\" (inline styles)\");\n console.log(chalk.green(`\\n✓ Added ${info.name} template`) + styleLabel);\n console.log(chalk.dim(` ${outputFile}\\n`));\n\n // Show next steps\n console.log(chalk.bold(\"Next steps:\"));\n console.log(chalk.dim(` 1. Edit ${outputFile} to customize`));\n console.log(chalk.dim(` 2. Run 'npx pdfn dev' to preview\\n`));\n\n // Additional info for tailwind\n if (style === \"tailwind\") {\n console.log(chalk.dim(\"Note: Tailwind templates require @pdfn/tailwind to be installed.\\n\"));\n }\n } catch (error) {\n console.error(chalk.red(`\\nError copying template: ${error}`));\n process.exit(1);\n }\n });\n\n// Also export the template info for use by dev server\nexport { TEMPLATES };\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA0vBD,UAAU,SAAS,IACf,eACA,uFACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAqDM,iBACI,kGACA,+GACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAmGmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAkBf,iBAAiB,IAAI,cAAc,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAqjBrE,iBAAiB,gBAAgB,cAAc,QAAQ,EAAE;AAAA;AAAA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","names":["Command","existsSync","resolve","puppeteer","startTime","existsSync","config","resolve","pdfn","options","results","puppeteer","Command","existsSync","readFileSync","join","chalk","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdfn",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "Dev server and PDF generation for print-ready PDFs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"vite": "^7.3.0",
|
|
50
50
|
"ws": "^8.18.3",
|
|
51
51
|
"@pdfn/client": "0.1.1",
|
|
52
|
-
"@pdfn/
|
|
53
|
-
"@pdfn/
|
|
52
|
+
"@pdfn/vite": "1.0.0",
|
|
53
|
+
"@pdfn/react": "1.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/express": "^5.0.6",
|