@classic-homes/slides 0.2.0 → 0.2.1

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.
@@ -2,7 +2,7 @@ import {
2
2
  parseDeckSource,
3
3
  renderDeck,
4
4
  stringifyDeck
5
- } from "./chunk-TM3TNVHB.js";
5
+ } from "./chunk-7FROOJCY.js";
6
6
 
7
7
  // src/node/index.ts
8
8
  import { readFile, stat, writeFile } from "fs/promises";
@@ -73,4 +73,4 @@ export {
73
73
  saveDeckFile,
74
74
  buildDeckFile
75
75
  };
76
- //# sourceMappingURL=chunk-CKVKX2ZD.js.map
76
+ //# sourceMappingURL=chunk-MYY5GZC2.js.map
package/dist/cli.js CHANGED
@@ -4,18 +4,18 @@ import {
4
4
  deckJsonSchema,
5
5
  describeLayout,
6
6
  listLayouts
7
- } from "./chunk-TXNQKBWJ.js";
7
+ } from "./chunk-33EPZMT6.js";
8
8
  import {
9
9
  buildDeckFile,
10
10
  fileImageResolver,
11
11
  loadDeckFile
12
- } from "./chunk-CKVKX2ZD.js";
12
+ } from "./chunk-MYY5GZC2.js";
13
13
  import {
14
14
  DeckError,
15
15
  formatIssues,
16
16
  renderDeck,
17
17
  validateDeck
18
- } from "./chunk-TM3TNVHB.js";
18
+ } from "./chunk-7FROOJCY.js";
19
19
 
20
20
  // src/node/cli.ts
21
21
  import { watch } from "fs";
@@ -105,8 +105,18 @@ ${"".padEnd(12)} Use for: ${l.whenToUse}
105
105
  case "schema":
106
106
  return void console.log(JSON.stringify(deckJsonSchema(), null, 2));
107
107
  case "mcp": {
108
- const { runStdio } = await import("./mcp-5R6YD3QW.js");
109
- return runStdio(values.root);
108
+ let mcp;
109
+ try {
110
+ mcp = await import("./mcp-DGMQTIQI.js");
111
+ } catch (e) {
112
+ if (e.code === "ERR_MODULE_NOT_FOUND") {
113
+ throw new Error(
114
+ "The MCP server needs @modelcontextprotocol/sdk. Run it with:\n npx -y -p @classic-homes/slides -p @modelcontextprotocol/sdk classic-slides mcp"
115
+ );
116
+ }
117
+ throw e;
118
+ }
119
+ return mcp.runStdio(values.root);
110
120
  }
111
121
  default:
112
122
  throw new Error(`Unknown command "${cmd}".
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/node/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * classic-slides — build branded Classic Homes decks from YAML.\n */\nimport { watch } from 'node:fs';\nimport { access, copyFile, mkdir } from 'node:fs/promises';\nimport { createServer } from 'node:http';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { AUTHORING_GUIDE, deckJsonSchema, describeLayout, listLayouts } from '../catalog.js';\nimport { renderDeck } from '../render.js';\nimport { DeckError, formatIssues, validateDeck } from '../validate.js';\nimport { buildDeckFile, fileImageResolver, loadDeckFile } from './index.js';\n\nconst HELP = `classic-slides — branded Classic Homes presentations\n\nUsage\n classic-slides new <file.yaml> [--starter basic|journey] Start a deck from a starter\n classic-slides build <file.yaml> [-o out.html] Render one self-contained HTML file\n classic-slides dev <file.yaml> [--port 4321] Live preview; reloads on save\n classic-slides validate <file.yaml> Check for errors and crowded slides\n classic-slides layouts [name] List layouts, or show one in detail\n classic-slides guide Print the authoring guide\n classic-slides schema Print the deck JSON Schema\n classic-slides mcp [--root dir] Run the MCP server on stdio\n`;\n\nconst pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\n\nasync function main() {\n const { positionals, values } = parseArgs({\n allowPositionals: true,\n options: {\n out: { type: 'string', short: 'o' },\n port: { type: 'string', default: '4321' },\n starter: { type: 'string', default: 'basic' },\n root: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const [cmd, arg] = positionals;\n\n if (!cmd || values.help) return void process.stdout.write(HELP);\n\n switch (cmd) {\n case 'new': {\n if (!arg) throw new Error('Usage: classic-slides new <file.yaml>');\n const target = resolve(arg.endsWith('.yaml') || arg.endsWith('.yml') ? arg : `${arg}.yaml`);\n if (\n await access(target).then(\n () => true,\n () => false\n )\n )\n throw new Error(`${target} already exists.`);\n const starter = join(pkgRoot, 'starters', `${values.starter}.yaml`);\n await mkdir(dirname(target), { recursive: true });\n await copyFile(starter, target);\n console.log(`Created ${target}\\nNext: classic-slides dev ${basename(target)}`);\n return;\n }\n case 'build': {\n if (!arg) throw new Error('Usage: classic-slides build <file.yaml>');\n const r = await buildDeckFile(arg, {\n out: values.out,\n onWarning: (w) => console.warn(`⚠ ${w}`),\n });\n if (r.warnings.length) console.warn(formatIssues(r.warnings));\n console.log(`✔ ${r.out} — ${r.slideCount} slides, ${(r.bytes / 1024 / 1024).toFixed(2)} MB`);\n return;\n }\n case 'validate': {\n if (!arg) throw new Error('Usage: classic-slides validate <file.yaml>');\n const v = validateDeck(await loadDeckFile(resolve(arg)));\n console.log(v.issues.length ? formatIssues(v.issues) : '✔ Valid, no warnings');\n process.exitCode = v.ok ? 0 : 1;\n return;\n }\n case 'dev':\n return dev(arg, Number(values.port));\n case 'layouts': {\n if (arg) {\n const d = describeLayout(arg);\n if (!d) throw new Error(`Unknown layout \"${arg}\".`);\n console.log(JSON.stringify(d, null, 2));\n } else {\n for (const l of listLayouts())\n console.log(\n `${l.name.padEnd(12)} ${l.description}\\n${''.padEnd(12)} Use for: ${l.whenToUse}\\n`\n );\n }\n return;\n }\n case 'guide':\n return void console.log(AUTHORING_GUIDE);\n case 'schema':\n return void console.log(JSON.stringify(deckJsonSchema(), null, 2));\n case 'mcp': {\n const { runStdio } = await import('./mcp.js');\n return runStdio(values.root);\n }\n default:\n throw new Error(`Unknown command \"${cmd}\".\\n\\n${HELP}`);\n }\n}\n\n/** Preview server: renders on every request and reloads the page when the deck folder changes. */\nasync function dev(file: string | undefined, port: number) {\n if (!file) throw new Error('Usage: classic-slides dev <file.yaml>');\n const abs = resolve(file);\n const dir = dirname(abs);\n const clients = new Set<import('node:http').ServerResponse>();\n const reload = `<script>new EventSource('/__reload').onmessage=()=>location.reload()</script>`;\n\n const errorPage = (msg: string) =>\n `<!doctype html><meta charset=\"utf-8\"><title>Deck error</title><body style=\"font:15px/1.5 ui-monospace,monospace;padding:40px;background:#fdf2f2;color:#7f1d1d\"><h2 style=\"font-family:system-ui\">Deck has problems</h2><pre style=\"white-space:pre-wrap\">${msg\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')}</pre>${reload}`;\n\n createServer(async (req, res) => {\n if (req.url === '/__reload') {\n res.writeHead(200, {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n });\n res.write(': ok\\n\\n');\n clients.add(res);\n req.on('close', () => clients.delete(res));\n return;\n }\n let html: string;\n try {\n const deck = await loadDeckFile(abs);\n const r = await renderDeck(deck, {\n resolveImage: fileImageResolver(dir),\n embedSource: false,\n });\n html = r.html.replace('</body>', `${reload}</body>`);\n if (r.warnings.length) console.warn(formatIssues(r.warnings));\n } catch (e) {\n html = errorPage(\n e instanceof DeckError ? formatIssues(e.issues) : e instanceof Error ? e.message : String(e)\n );\n }\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' });\n res.end(html);\n }).listen(port, () =>\n console.log(`Previewing ${file} at http://localhost:${port} (Ctrl+C to stop)`)\n );\n\n let timer: NodeJS.Timeout | undefined;\n watch(dir, { recursive: true }, (_, name) => {\n if (name && /\\.html$/.test(name)) return;\n clearTimeout(timer);\n timer = setTimeout(() => clients.forEach((c) => c.write('data: reload\\n\\n')), 120);\n });\n}\n\nmain().catch((e) => {\n console.error(e instanceof DeckError ? e.message : `✖ ${e instanceof Error ? e.message : e}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/node/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * classic-slides — build branded Classic Homes decks from YAML.\n */\nimport { watch } from 'node:fs';\nimport { access, copyFile, mkdir } from 'node:fs/promises';\nimport { createServer } from 'node:http';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { AUTHORING_GUIDE, deckJsonSchema, describeLayout, listLayouts } from '../catalog.js';\nimport { renderDeck } from '../render.js';\nimport { DeckError, formatIssues, validateDeck } from '../validate.js';\nimport { buildDeckFile, fileImageResolver, loadDeckFile } from './index.js';\n\nconst HELP = `classic-slides — branded Classic Homes presentations\n\nUsage\n classic-slides new <file.yaml> [--starter basic|journey] Start a deck from a starter\n classic-slides build <file.yaml> [-o out.html] Render one self-contained HTML file\n classic-slides dev <file.yaml> [--port 4321] Live preview; reloads on save\n classic-slides validate <file.yaml> Check for errors and crowded slides\n classic-slides layouts [name] List layouts, or show one in detail\n classic-slides guide Print the authoring guide\n classic-slides schema Print the deck JSON Schema\n classic-slides mcp [--root dir] Run the MCP server on stdio\n`;\n\nconst pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\n\nasync function main() {\n const { positionals, values } = parseArgs({\n allowPositionals: true,\n options: {\n out: { type: 'string', short: 'o' },\n port: { type: 'string', default: '4321' },\n starter: { type: 'string', default: 'basic' },\n root: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const [cmd, arg] = positionals;\n\n if (!cmd || values.help) return void process.stdout.write(HELP);\n\n switch (cmd) {\n case 'new': {\n if (!arg) throw new Error('Usage: classic-slides new <file.yaml>');\n const target = resolve(arg.endsWith('.yaml') || arg.endsWith('.yml') ? arg : `${arg}.yaml`);\n if (\n await access(target).then(\n () => true,\n () => false\n )\n )\n throw new Error(`${target} already exists.`);\n const starter = join(pkgRoot, 'starters', `${values.starter}.yaml`);\n await mkdir(dirname(target), { recursive: true });\n await copyFile(starter, target);\n console.log(`Created ${target}\\nNext: classic-slides dev ${basename(target)}`);\n return;\n }\n case 'build': {\n if (!arg) throw new Error('Usage: classic-slides build <file.yaml>');\n const r = await buildDeckFile(arg, {\n out: values.out,\n onWarning: (w) => console.warn(`⚠ ${w}`),\n });\n if (r.warnings.length) console.warn(formatIssues(r.warnings));\n console.log(`✔ ${r.out} — ${r.slideCount} slides, ${(r.bytes / 1024 / 1024).toFixed(2)} MB`);\n return;\n }\n case 'validate': {\n if (!arg) throw new Error('Usage: classic-slides validate <file.yaml>');\n const v = validateDeck(await loadDeckFile(resolve(arg)));\n console.log(v.issues.length ? formatIssues(v.issues) : '✔ Valid, no warnings');\n process.exitCode = v.ok ? 0 : 1;\n return;\n }\n case 'dev':\n return dev(arg, Number(values.port));\n case 'layouts': {\n if (arg) {\n const d = describeLayout(arg);\n if (!d) throw new Error(`Unknown layout \"${arg}\".`);\n console.log(JSON.stringify(d, null, 2));\n } else {\n for (const l of listLayouts())\n console.log(\n `${l.name.padEnd(12)} ${l.description}\\n${''.padEnd(12)} Use for: ${l.whenToUse}\\n`\n );\n }\n return;\n }\n case 'guide':\n return void console.log(AUTHORING_GUIDE);\n case 'schema':\n return void console.log(JSON.stringify(deckJsonSchema(), null, 2));\n case 'mcp': {\n // The MCP SDK is an optional peer so apps that only render decks don't install it.\n let mcp: typeof import('./mcp.js');\n try {\n mcp = await import('./mcp.js');\n } catch (e) {\n if ((e as { code?: string }).code === 'ERR_MODULE_NOT_FOUND') {\n throw new Error(\n 'The MCP server needs @modelcontextprotocol/sdk. Run it with:\\n npx -y -p @classic-homes/slides -p @modelcontextprotocol/sdk classic-slides mcp'\n );\n }\n throw e;\n }\n return mcp.runStdio(values.root);\n }\n default:\n throw new Error(`Unknown command \"${cmd}\".\\n\\n${HELP}`);\n }\n}\n\n/** Preview server: renders on every request and reloads the page when the deck folder changes. */\nasync function dev(file: string | undefined, port: number) {\n if (!file) throw new Error('Usage: classic-slides dev <file.yaml>');\n const abs = resolve(file);\n const dir = dirname(abs);\n const clients = new Set<import('node:http').ServerResponse>();\n const reload = `<script>new EventSource('/__reload').onmessage=()=>location.reload()</script>`;\n\n const errorPage = (msg: string) =>\n `<!doctype html><meta charset=\"utf-8\"><title>Deck error</title><body style=\"font:15px/1.5 ui-monospace,monospace;padding:40px;background:#fdf2f2;color:#7f1d1d\"><h2 style=\"font-family:system-ui\">Deck has problems</h2><pre style=\"white-space:pre-wrap\">${msg\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')}</pre>${reload}`;\n\n createServer(async (req, res) => {\n if (req.url === '/__reload') {\n res.writeHead(200, {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n });\n res.write(': ok\\n\\n');\n clients.add(res);\n req.on('close', () => clients.delete(res));\n return;\n }\n let html: string;\n try {\n const deck = await loadDeckFile(abs);\n const r = await renderDeck(deck, {\n resolveImage: fileImageResolver(dir),\n embedSource: false,\n });\n html = r.html.replace('</body>', `${reload}</body>`);\n if (r.warnings.length) console.warn(formatIssues(r.warnings));\n } catch (e) {\n html = errorPage(\n e instanceof DeckError ? formatIssues(e.issues) : e instanceof Error ? e.message : String(e)\n );\n }\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' });\n res.end(html);\n }).listen(port, () =>\n console.log(`Previewing ${file} at http://localhost:${port} (Ctrl+C to stop)`)\n );\n\n let timer: NodeJS.Timeout | undefined;\n watch(dir, { recursive: true }, (_, name) => {\n if (name && /\\.html$/.test(name)) return;\n clearTimeout(timer);\n timer = setTimeout(() => clients.forEach((c) => c.write('data: reload\\n\\n')), 120);\n });\n}\n\nmain().catch((e) => {\n console.error(e instanceof DeckError ? e.message : `✖ ${e instanceof Error ? e.message : e}`);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAIA,SAAS,aAAa;AACtB,SAAS,QAAQ,UAAU,aAAa;AACxC,SAAS,oBAAoB;AAC7B,SAAS,UAAU,SAAS,MAAM,eAAe;AACjD,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAM1B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAab,IAAM,UAAU,QAAQ,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,IAAI;AAErE,eAAe,OAAO;AACpB,QAAM,EAAE,aAAa,OAAO,IAAI,UAAU;AAAA,IACxC,kBAAkB;AAAA,IAClB,SAAS;AAAA,MACP,KAAK,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,MAClC,MAAM,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,MACxC,SAAS,EAAE,MAAM,UAAU,SAAS,QAAQ;AAAA,MAC5C,MAAM,EAAE,MAAM,SAAS;AAAA,MACvB,MAAM,EAAE,MAAM,WAAW,OAAO,IAAI;AAAA,IACtC;AAAA,EACF,CAAC;AACD,QAAM,CAAC,KAAK,GAAG,IAAI;AAEnB,MAAI,CAAC,OAAO,OAAO,KAAM,QAAO,KAAK,QAAQ,OAAO,MAAM,IAAI;AAE9D,UAAQ,KAAK;AAAA,IACX,KAAK,OAAO;AACV,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,YAAM,SAAS,QAAQ,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG,OAAO;AAC1F,UACE,MAAM,OAAO,MAAM,EAAE;AAAA,QACnB,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAEA,cAAM,IAAI,MAAM,GAAG,MAAM,kBAAkB;AAC7C,YAAM,UAAU,KAAK,SAAS,YAAY,GAAG,OAAO,OAAO,OAAO;AAClE,YAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,YAAM,SAAS,SAAS,MAAM;AAC9B,cAAQ,IAAI,WAAW,MAAM;AAAA,2BAA8B,SAAS,MAAM,CAAC,EAAE;AAC7E;AAAA,IACF;AAAA,IACA,KAAK,SAAS;AACZ,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yCAAyC;AACnE,YAAM,IAAI,MAAM,cAAc,KAAK;AAAA,QACjC,KAAK,OAAO;AAAA,QACZ,WAAW,CAAC,MAAM,QAAQ,KAAK,UAAK,CAAC,EAAE;AAAA,MACzC,CAAC;AACD,UAAI,EAAE,SAAS,OAAQ,SAAQ,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC5D,cAAQ,IAAI,UAAK,EAAE,GAAG,WAAM,EAAE,UAAU,aAAa,EAAE,QAAQ,OAAO,MAAM,QAAQ,CAAC,CAAC,KAAK;AAC3F;AAAA,IACF;AAAA,IACA,KAAK,YAAY;AACf,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,YAAM,IAAI,aAAa,MAAM,aAAa,QAAQ,GAAG,CAAC,CAAC;AACvD,cAAQ,IAAI,EAAE,OAAO,SAAS,aAAa,EAAE,MAAM,IAAI,2BAAsB;AAC7E,cAAQ,WAAW,EAAE,KAAK,IAAI;AAC9B;AAAA,IACF;AAAA,IACA,KAAK;AACH,aAAO,IAAI,KAAK,OAAO,OAAO,IAAI,CAAC;AAAA,IACrC,KAAK,WAAW;AACd,UAAI,KAAK;AACP,cAAM,IAAI,eAAe,GAAG;AAC5B,YAAI,CAAC,EAAG,OAAM,IAAI,MAAM,mBAAmB,GAAG,IAAI;AAClD,gBAAQ,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AAAA,MACxC,OAAO;AACL,mBAAW,KAAK,YAAY;AAC1B,kBAAQ;AAAA,YACN,GAAG,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW;AAAA,EAAK,GAAG,OAAO,EAAE,CAAC,aAAa,EAAE,SAAS;AAAA;AAAA,UACjF;AAAA,MACJ;AACA;AAAA,IACF;AAAA,IACA,KAAK;AACH,aAAO,KAAK,QAAQ,IAAI,eAAe;AAAA,IACzC,KAAK;AACH,aAAO,KAAK,QAAQ,IAAI,KAAK,UAAU,eAAe,GAAG,MAAM,CAAC,CAAC;AAAA,IACnE,KAAK,OAAO;AAEV,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,OAAO,mBAAU;AAAA,MAC/B,SAAS,GAAG;AACV,YAAK,EAAwB,SAAS,wBAAwB;AAC5D,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,cAAM;AAAA,MACR;AACA,aAAO,IAAI,SAAS,OAAO,IAAI;AAAA,IACjC;AAAA,IACA;AACE,YAAM,IAAI,MAAM,oBAAoB,GAAG;AAAA;AAAA,EAAS,IAAI,EAAE;AAAA,EAC1D;AACF;AAGA,eAAe,IAAI,MAA0B,MAAc;AACzD,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,uCAAuC;AAClE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,MAAM,QAAQ,GAAG;AACvB,QAAM,UAAU,oBAAI,IAAwC;AAC5D,QAAM,SAAS;AAEf,QAAM,YAAY,CAAC,QACjB,4PAA4P,IACzP,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,CAAC,SAAS,MAAM;AAEzC,eAAa,OAAO,KAAK,QAAQ;AAC/B,QAAI,IAAI,QAAQ,aAAa;AAC3B,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,YAAY;AAAA,MACd,CAAC;AACD,UAAI,MAAM,UAAU;AACpB,cAAQ,IAAI,GAAG;AACf,UAAI,GAAG,SAAS,MAAM,QAAQ,OAAO,GAAG,CAAC;AACzC;AAAA,IACF;AACA,QAAI;AACJ,QAAI;AACF,YAAM,OAAO,MAAM,aAAa,GAAG;AACnC,YAAM,IAAI,MAAM,WAAW,MAAM;AAAA,QAC/B,cAAc,kBAAkB,GAAG;AAAA,QACnC,aAAa;AAAA,MACf,CAAC;AACD,aAAO,EAAE,KAAK,QAAQ,WAAW,GAAG,MAAM,SAAS;AACnD,UAAI,EAAE,SAAS,OAAQ,SAAQ,KAAK,aAAa,EAAE,QAAQ,CAAC;AAAA,IAC9D,SAAS,GAAG;AACV,aAAO;AAAA,QACL,aAAa,YAAY,aAAa,EAAE,MAAM,IAAI,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,MAC7F;AAAA,IACF;AACA,QAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,iBAAiB,WAAW,CAAC;AAC9F,QAAI,IAAI,IAAI;AAAA,EACd,CAAC,EAAE;AAAA,IAAO;AAAA,IAAM,MACd,QAAQ,IAAI,cAAc,IAAI,wBAAwB,IAAI,oBAAoB;AAAA,EAChF;AAEA,MAAI;AACJ,QAAM,KAAK,EAAE,WAAW,KAAK,GAAG,CAAC,GAAG,SAAS;AAC3C,QAAI,QAAQ,UAAU,KAAK,IAAI,EAAG;AAClC,iBAAa,KAAK;AAClB,YAAQ,WAAW,MAAM,QAAQ,QAAQ,CAAC,MAAM,EAAE,MAAM,kBAAkB,CAAC,GAAG,GAAG;AAAA,EACnF,CAAC;AACH;AAEA,KAAK,EAAE,MAAM,CAAC,MAAM;AAClB,UAAQ,MAAM,aAAa,YAAY,EAAE,UAAU,UAAK,aAAa,QAAQ,EAAE,UAAU,CAAC,EAAE;AAC5F,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { D as Deck, a as DeckError, b as DeckInput, c as DeckSchema, I as ImageResolver, L as LAYOUTS, d as LayoutDef, e as LayoutIssue, f as LayoutName, R as RenderOptions, g as RenderResult, S as SlideData, h as SlideInput, i as SlideSchema, V as ValidationResult, j as collectImageSources, k as extractDeckSource, l as formatIssues, m as formatPath, n as getLayout, r as renderDeck, v as validateDeck } from './render-CZ4g2fa9.js';
1
+ export { D as Deck, a as DeckError, b as DeckInput, c as DeckSchema, I as ImageResolver, L as LAYOUTS, d as LayoutDef, e as LayoutIssue, f as LayoutName, P as PREVIEW_SIZE, R as RenderOptions, g as RenderResult, S as SlideData, h as SlideInput, i as SlideSchema, V as ValidationResult, j as collectImageSources, k as extractDeckSource, l as formatIssues, m as formatPath, n as getLayout, r as renderDeck, v as validateDeck } from './render-DTz7tLKM.js';
2
2
  import * as zod_to_json_schema from 'zod-to-json-schema';
3
3
  import 'zod';
4
4
 
@@ -145,5 +145,17 @@ interface LayoutEditorInfo {
145
145
  declare function commonSlideFields(): EditorField[];
146
146
  declare function layoutEditorInfo(name: string): LayoutEditorInfo | undefined;
147
147
  declare function allLayoutEditorInfo(): LayoutEditorInfo[];
148
+ /**
149
+ * Stand-in image for new slides: a neutral frame reading "Add a photo".
150
+ * It validates (so a fresh slide previews immediately) but lint warns until
151
+ * it is replaced.
152
+ */
153
+ declare const PLACEHOLDER_IMAGE: string;
154
+ declare function isPlaceholderImage(src: string | undefined): boolean;
155
+ /**
156
+ * A new slide for a layout, ready to edit: the layout's example with sample
157
+ * image paths swapped for {@link PLACEHOLDER_IMAGE}. Always passes validation.
158
+ */
159
+ declare function blankSlide(name: string): Record<string, unknown> | undefined;
148
160
 
149
- export { AUTHORING_GUIDE, type EditableDeck, type EditorField, LOGO_SVG, type LayoutEditorInfo, PALETTE, type SlideRef, addSlide, allLayoutEditorInfo, commonSlideFields, deckJsonSchema, describeLayout, escapeHtml, findSlide, inline, layoutEditorInfo, listLayouts, moveSlide, outline, parseDeckSource, prose, removeSlide, stringifyDeck, updateSlide };
161
+ export { AUTHORING_GUIDE, type EditableDeck, type EditorField, LOGO_SVG, type LayoutEditorInfo, PALETTE, PLACEHOLDER_IMAGE, type SlideRef, addSlide, allLayoutEditorInfo, blankSlide, commonSlideFields, deckJsonSchema, describeLayout, escapeHtml, findSlide, inline, isPlaceholderImage, layoutEditorInfo, listLayouts, moveSlide, outline, parseDeckSource, prose, removeSlide, stringifyDeck, updateSlide };
package/dist/index.js CHANGED
@@ -11,149 +11,34 @@ import {
11
11
  deckJsonSchema,
12
12
  describeLayout,
13
13
  listLayouts
14
- } from "./chunk-TXNQKBWJ.js";
14
+ } from "./chunk-33EPZMT6.js";
15
15
  import {
16
16
  DeckError,
17
17
  DeckSchema,
18
- Image,
19
- Item,
20
18
  LAYOUTS,
21
19
  LOGO_SVG,
22
20
  PALETTE,
21
+ PLACEHOLDER_IMAGE,
22
+ PREVIEW_SIZE,
23
23
  SlideSchema,
24
- Text,
25
- baseSlide,
24
+ allLayoutEditorInfo,
25
+ blankSlide,
26
26
  collectImageSources,
27
+ commonSlideFields,
27
28
  escapeHtml,
28
29
  extractDeckSource,
29
30
  formatIssues,
30
31
  formatPath,
31
32
  getLayout,
32
33
  inline,
34
+ isPlaceholderImage,
35
+ layoutEditorInfo,
33
36
  parseDeckSource,
34
37
  prose,
35
38
  renderDeck,
36
39
  stringifyDeck,
37
40
  validateDeck
38
- } from "./chunk-TM3TNVHB.js";
39
-
40
- // src/editor.ts
41
- import { z } from "zod";
42
- var MULTILINE = /* @__PURE__ */ new Set(["lead", "body", "text", "quote", "footnote", "sub", "notes", "caption"]);
43
- var LABELS = {
44
- eyebrowSub: "Eyebrow subtitle",
45
- showJourney: "Show journey strip",
46
- stepDone: "Step completed",
47
- mediaSide: "Photo side",
48
- imageAspect: "Photo shape",
49
- numberFrom: "First badge number",
50
- url: "URL",
51
- id: "Slide id"
52
- };
53
- function labelFor(key) {
54
- if (LABELS[key]) return LABELS[key];
55
- const words = key.replace(/([A-Z])/g, " $1").toLowerCase();
56
- return words.charAt(0).toUpperCase() + words.slice(1);
57
- }
58
- function unwrap(schema) {
59
- let s = schema;
60
- let required = true;
61
- let dflt;
62
- let description = schema.description;
63
- for (; ; ) {
64
- description ??= s.description;
65
- if (s instanceof z.ZodOptional || s instanceof z.ZodNullable) {
66
- required = false;
67
- s = s.unwrap();
68
- } else if (s instanceof z.ZodDefault) {
69
- required = false;
70
- dflt = s._def.defaultValue();
71
- s = s._def.innerType;
72
- } else break;
73
- }
74
- return { inner: s, required, default: dflt, description };
75
- }
76
- function isText(s) {
77
- return s === Text || s instanceof z.ZodString && s.description === Text.description;
78
- }
79
- function describeField(key, schema) {
80
- const { inner, required, default: dflt, description } = unwrap(schema);
81
- const base = {
82
- key,
83
- label: labelFor(key),
84
- required,
85
- ...description ? { description } : {},
86
- ...dflt !== void 0 ? { default: dflt } : {},
87
- ...MULTILINE.has(key) ? { multiline: true } : {}
88
- };
89
- if (inner === Image) return { ...base, kind: "image" };
90
- if (inner === Item) return { ...base, kind: "item" };
91
- if (isText(inner)) return { ...base, kind: "inline" };
92
- if (inner instanceof z.ZodString) return { ...base, kind: "string" };
93
- if (inner instanceof z.ZodBoolean) return { ...base, kind: "boolean" };
94
- if (inner instanceof z.ZodEnum) return { ...base, kind: "enum", options: [...inner.options] };
95
- if (inner instanceof z.ZodLiteral) return { ...base, kind: "enum", options: [inner.value] };
96
- if (inner instanceof z.ZodNumber) {
97
- return {
98
- ...base,
99
- kind: "number",
100
- integer: inner.isInt,
101
- ...inner.minValue !== null ? { min: inner.minValue } : {},
102
- ...inner.maxValue !== null ? { max: inner.maxValue } : {}
103
- };
104
- }
105
- if (inner instanceof z.ZodUnion) {
106
- const opts = inner.options;
107
- if (opts.every((o) => o instanceof z.ZodLiteral)) {
108
- return {
109
- ...base,
110
- kind: "enum",
111
- options: opts.map((o) => o.value)
112
- };
113
- }
114
- return { ...base, kind: "string" };
115
- }
116
- if (inner instanceof z.ZodArray) {
117
- const el = inner.element;
118
- return {
119
- ...base,
120
- kind: "list",
121
- of: { ...describeField("", el), label: labelFor(key.replace(/s$/, "")) },
122
- ...inner._def.minLength ? { min: inner._def.minLength.value } : {},
123
- ...inner._def.maxLength ? { max: inner._def.maxLength.value } : {}
124
- };
125
- }
126
- if (inner instanceof z.ZodObject) {
127
- return {
128
- ...base,
129
- kind: "group",
130
- fields: Object.entries(inner.shape).map(
131
- ([k, v]) => describeField(k, v)
132
- )
133
- };
134
- }
135
- return { ...base, kind: "string" };
136
- }
137
- function commonSlideFields() {
138
- return Object.entries(baseSlide).map(([k, v]) => describeField(k, v));
139
- }
140
- function layoutEditorInfo(name) {
141
- const layout = getLayout(name);
142
- if (!layout) return void 0;
143
- return {
144
- name: layout.name,
145
- description: layout.description,
146
- whenToUse: layout.whenToUse,
147
- defaults: layout.defaults ?? {},
148
- fields: Object.entries(layout.shape).map(
149
- ([k, v]) => describeField(k, v)
150
- ),
151
- example: layout.example
152
- };
153
- }
154
- function allLayoutEditorInfo() {
155
- return LAYOUTS.map((l) => layoutEditorInfo(l.name));
156
- }
41
+ } from "./chunk-7FROOJCY.js";
157
42
  export {
158
43
  AUTHORING_GUIDE,
159
44
  DeckError,
@@ -161,9 +46,12 @@ export {
161
46
  LAYOUTS,
162
47
  LOGO_SVG,
163
48
  PALETTE,
49
+ PLACEHOLDER_IMAGE,
50
+ PREVIEW_SIZE,
164
51
  SlideSchema,
165
52
  addSlide,
166
53
  allLayoutEditorInfo,
54
+ blankSlide,
167
55
  collectImageSources,
168
56
  commonSlideFields,
169
57
  deckJsonSchema,
@@ -175,6 +63,7 @@ export {
175
63
  formatPath,
176
64
  getLayout,
177
65
  inline,
66
+ isPlaceholderImage,
178
67
  layoutEditorInfo,
179
68
  listLayouts,
180
69
  moveSlide,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["/**\n * Form metadata for editors. Walks each layout's zod shape and describes its\n * fields as simple widget descriptors, so a UI can build a form for any\n * layout without understanding zod or JSON Schema.\n */\nimport { z } from 'zod';\nimport { getLayout, LAYOUTS } from './layouts/index.js';\nimport { baseSlide, Image, Item, Text } from './schema/common.js';\n\nexport type EditorField =\n | FieldBase<'inline'> // formatted text (**bold**, *highlight*, links)\n | FieldBase<'string'>\n | (FieldBase<'enum'> & { options: (string | number)[] })\n | FieldBase<'boolean'>\n | (FieldBase<'number'> & { min?: number; max?: number; integer: boolean })\n | FieldBase<'image'>\n | FieldBase<'item'> // { text, sub? } bullet\n | (FieldBase<'list'> & { of: EditorField; min?: number; max?: number })\n | (FieldBase<'group'> & { fields: EditorField[] });\n\ninterface FieldBase<K extends string> {\n kind: K;\n /** Property name ('' for list elements). */\n key: string;\n label: string;\n description?: string;\n required: boolean;\n default?: unknown;\n /** Suggest a textarea rather than a one-line input. */\n multiline?: boolean;\n}\n\nconst MULTILINE = new Set(['lead', 'body', 'text', 'quote', 'footnote', 'sub', 'notes', 'caption']);\nconst LABELS: Record<string, string> = {\n eyebrowSub: 'Eyebrow subtitle',\n showJourney: 'Show journey strip',\n stepDone: 'Step completed',\n mediaSide: 'Photo side',\n imageAspect: 'Photo shape',\n numberFrom: 'First badge number',\n url: 'URL',\n id: 'Slide id',\n};\n\nfunction labelFor(key: string): string {\n if (LABELS[key]) return LABELS[key];\n const words = key.replace(/([A-Z])/g, ' $1').toLowerCase();\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\ntype AnyZod = z.ZodTypeAny;\n\nfunction unwrap(schema: AnyZod): {\n inner: AnyZod;\n required: boolean;\n default?: unknown;\n description?: string;\n} {\n let s = schema;\n let required = true;\n let dflt: unknown;\n let description = schema.description;\n for (;;) {\n description ??= s.description;\n if (s instanceof z.ZodOptional || s instanceof z.ZodNullable) {\n required = false;\n s = s.unwrap();\n } else if (s instanceof z.ZodDefault) {\n required = false;\n dflt = s._def.defaultValue();\n s = s._def.innerType;\n } else break;\n }\n return { inner: s, required, default: dflt, description };\n}\n\nfunction isText(s: AnyZod): boolean {\n // Text may have been re-described (.describe clones), so compare by checks too.\n return s === Text || (s instanceof z.ZodString && s.description === Text.description);\n}\n\nfunction describeField(key: string, schema: AnyZod): EditorField {\n const { inner, required, default: dflt, description } = unwrap(schema);\n const base = {\n key,\n label: labelFor(key),\n required,\n ...(description ? { description } : {}),\n ...(dflt !== undefined ? { default: dflt } : {}),\n ...(MULTILINE.has(key) ? { multiline: true } : {}),\n };\n\n if (inner === Image) return { ...base, kind: 'image' };\n if (inner === Item) return { ...base, kind: 'item' };\n if (isText(inner)) return { ...base, kind: 'inline' };\n if (inner instanceof z.ZodString) return { ...base, kind: 'string' };\n if (inner instanceof z.ZodBoolean) return { ...base, kind: 'boolean' };\n if (inner instanceof z.ZodEnum) return { ...base, kind: 'enum', options: [...inner.options] };\n if (inner instanceof z.ZodLiteral) return { ...base, kind: 'enum', options: [inner.value] };\n if (inner instanceof z.ZodNumber) {\n return {\n ...base,\n kind: 'number',\n integer: inner.isInt,\n ...(inner.minValue !== null ? { min: inner.minValue } : {}),\n ...(inner.maxValue !== null ? { max: inner.maxValue } : {}),\n };\n }\n if (inner instanceof z.ZodUnion) {\n const opts = inner.options as AnyZod[];\n if (opts.every((o) => o instanceof z.ZodLiteral)) {\n return {\n ...base,\n kind: 'enum',\n options: opts.map((o) => (o as z.ZodLiteral<string | number>).value),\n };\n }\n return { ...base, kind: 'string' };\n }\n if (inner instanceof z.ZodArray) {\n const el = inner.element as AnyZod;\n return {\n ...base,\n kind: 'list',\n of: { ...describeField('', el), label: labelFor(key.replace(/s$/, '')) },\n ...(inner._def.minLength ? { min: inner._def.minLength.value } : {}),\n ...(inner._def.maxLength ? { max: inner._def.maxLength.value } : {}),\n };\n }\n if (inner instanceof z.ZodObject) {\n return {\n ...base,\n kind: 'group',\n fields: Object.entries(inner.shape as Record<string, AnyZod>).map(([k, v]) =>\n describeField(k, v)\n ),\n };\n }\n return { ...base, kind: 'string' };\n}\n\nexport interface LayoutEditorInfo {\n name: string;\n description: string;\n whenToUse: string;\n defaults: { background?: string; track?: string };\n /** Layout-specific fields, in declaration order. */\n fields: EditorField[];\n /** A valid starting slide for this layout. */\n example: Record<string, unknown>;\n}\n\n/** Fields shared by every slide (track, background, notes, step…). */\nexport function commonSlideFields(): EditorField[] {\n return Object.entries(baseSlide).map(([k, v]) => describeField(k, v));\n}\n\nexport function layoutEditorInfo(name: string): LayoutEditorInfo | undefined {\n const layout = getLayout(name);\n if (!layout) return undefined;\n return {\n name: layout.name,\n description: layout.description,\n whenToUse: layout.whenToUse,\n defaults: layout.defaults ?? {},\n fields: Object.entries(layout.shape as Record<string, AnyZod>).map(([k, v]) =>\n describeField(k, v)\n ),\n example: layout.example,\n };\n}\n\nexport function allLayoutEditorInfo(): LayoutEditorInfo[] {\n return LAYOUTS.map((l) => layoutEditorInfo(l.name)!);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -9,17 +9,17 @@ import {
9
9
  AUTHORING_GUIDE,
10
10
  describeLayout,
11
11
  listLayouts
12
- } from "./chunk-TXNQKBWJ.js";
12
+ } from "./chunk-33EPZMT6.js";
13
13
  import {
14
14
  buildDeckFile,
15
15
  loadDeckFile,
16
16
  saveDeckFile
17
- } from "./chunk-CKVKX2ZD.js";
17
+ } from "./chunk-MYY5GZC2.js";
18
18
  import {
19
19
  formatIssues,
20
20
  stringifyDeck,
21
21
  validateDeck
22
- } from "./chunk-TM3TNVHB.js";
22
+ } from "./chunk-7FROOJCY.js";
23
23
 
24
24
  // src/node/mcp.ts
25
25
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -317,4 +317,4 @@ export {
317
317
  createSlidesMcpServer,
318
318
  runStdio
319
319
  };
320
- //# sourceMappingURL=mcp-5R6YD3QW.js.map
320
+ //# sourceMappingURL=mcp-DGMQTIQI.js.map
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RenderOptions, g as RenderResult, I as ImageResolver } from './render-CZ4g2fa9.js';
2
- export { r as renderDeck } from './render-CZ4g2fa9.js';
1
+ import { R as RenderOptions, g as RenderResult, I as ImageResolver } from './render-DTz7tLKM.js';
2
+ export { r as renderDeck } from './render-DTz7tLKM.js';
3
3
  import 'zod';
4
4
 
5
5
  /** Largest single image we inline without warning. Big photos bloat the deck file. */
package/dist/node.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  fileImageResolver,
5
5
  loadDeckFile,
6
6
  saveDeckFile
7
- } from "./chunk-CKVKX2ZD.js";
7
+ } from "./chunk-MYY5GZC2.js";
8
8
  import {
9
9
  renderDeck
10
- } from "./chunk-TM3TNVHB.js";
10
+ } from "./chunk-7FROOJCY.js";
11
11
  export {
12
12
  IMAGE_WARN_BYTES,
13
13
  buildDeckFile,
@@ -110,6 +110,15 @@ declare class DeckError extends Error {
110
110
  constructor(issues: LayoutIssue[]);
111
111
  }
112
112
 
113
+ /**
114
+ * Size to lay a preview out at before scaling it down. Slides are responsive
115
+ * and switch to a stacked layout below ~840px wide, so small previews should
116
+ * render at this size and use a CSS transform.
117
+ */
118
+ declare const PREVIEW_SIZE: {
119
+ readonly width: 1280;
120
+ readonly height: 720;
121
+ };
113
122
  type ImageResolver = (src: string) => Promise<string> | string;
114
123
  interface RenderOptions {
115
124
  /**
@@ -120,11 +129,15 @@ interface RenderOptions {
120
129
  resolveImage?: ImageResolver;
121
130
  /** Embed the deck source (JSON, without image data) so the HTML can be re-opened for editing. Default true. */
122
131
  embedSource?: boolean;
132
+ /** 0-based rendered slide to open on (ignored when the URL has #slide-N). Handy for editor previews. */
133
+ initialSlide?: number;
123
134
  }
124
135
  interface RenderResult {
125
136
  html: string;
126
137
  deck: Deck;
127
138
  slideCount: number;
139
+ /** For each deck slide, the 0-based indices of the rendered slides it produced (a walkthrough yields several). */
140
+ slideMap: number[][];
128
141
  warnings: ReturnType<typeof validateDeck>['issues'];
129
142
  }
130
143
  /** Collects every image src in the deck (any object with a string `src`). */
@@ -133,4 +146,4 @@ declare function renderDeck(input: unknown, options?: RenderOptions): Promise<Re
133
146
  /** Pulls the embedded deck source back out of a rendered HTML file. */
134
147
  declare function extractDeckSource(html: string): unknown | undefined;
135
148
 
136
- export { type Deck as D, type ImageResolver as I, LAYOUTS as L, type RenderOptions as R, type SlideData as S, type ValidationResult as V, DeckError as a, type DeckInput as b, DeckSchema as c, type LayoutDef as d, type LayoutIssue as e, type LayoutName as f, type RenderResult as g, type SlideInput as h, SlideSchema as i, collectImageSources as j, extractDeckSource as k, formatIssues as l, formatPath as m, getLayout as n, renderDeck as r, validateDeck as v };
149
+ export { type Deck as D, type ImageResolver as I, LAYOUTS as L, PREVIEW_SIZE as P, type RenderOptions as R, type SlideData as S, type ValidationResult as V, DeckError as a, type DeckInput as b, DeckSchema as c, type LayoutDef as d, type LayoutIssue as e, type LayoutName as f, type RenderResult as g, type SlideInput as h, SlideSchema as i, collectImageSources as j, extractDeckSource as k, formatIssues as l, formatPath as m, getLayout as n, renderDeck as r, validateDeck as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/slides",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Branded Classic Homes presentations from structured deck files: schema, layouts, renderer, CLI and MCP server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,18 +52,26 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@classic-homes/theme-tokens": "^0.4.0",
55
- "@modelcontextprotocol/sdk": "^1.11.0",
56
55
  "qrcode": "^1.5.4",
57
56
  "yaml": "^2.6.0",
58
57
  "zod": "^3.25.0",
59
58
  "zod-to-json-schema": "^3.24.0"
60
59
  },
60
+ "peerDependencies": {
61
+ "@modelcontextprotocol/sdk": "^1.11.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@modelcontextprotocol/sdk": {
65
+ "optional": true
66
+ }
67
+ },
61
68
  "devDependencies": {
62
69
  "@types/node": "^22.0.0",
63
70
  "@types/qrcode": "^1.5.5",
64
71
  "tsup": "^8.0.0",
65
72
  "tsx": "^4.0.0",
66
73
  "typescript": "^5.3.0",
67
- "vitest": "^4.1.10"
74
+ "vitest": "^4.1.10",
75
+ "@modelcontextprotocol/sdk": "^1.11.0"
68
76
  }
69
77
  }