@classic-homes/slides 0.7.0 → 0.8.0
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 +21 -3
- package/dist/{chunk-UKRX7A2V.js → chunk-4LBQOMMI.js} +5 -3
- package/dist/chunk-4LBQOMMI.js.map +1 -0
- package/dist/chunk-4NSKDQ5V.js +7 -0
- package/dist/chunk-4NSKDQ5V.js.map +1 -0
- package/dist/chunk-6ARF37DO.js +38 -0
- package/dist/chunk-6ARF37DO.js.map +1 -0
- package/dist/{chunk-HI5ZCAHO.js → chunk-MCG5BFJN.js} +85 -84
- package/dist/chunk-MCG5BFJN.js.map +1 -0
- package/dist/{chunk-ORNJ4CTQ.js → chunk-QOKQWHHD.js} +2 -2
- package/dist/cli.js +7 -5
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +235 -0
- package/dist/core.js +143 -0
- package/dist/core.js.map +1 -0
- package/dist/{images-rkJZtnjl.d.ts → images-C90NCxYd.d.ts} +118 -1
- package/dist/index.d.ts +13 -320
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/dist/{mcp-MG2E7J3I.js → mcp-ZBYNCCHN.js} +5 -4
- package/dist/{mcp-MG2E7J3I.js.map → mcp-ZBYNCCHN.js.map} +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -2
- package/dist/render-default-B-QkReoS.d.ts +5 -0
- package/package.json +5 -1
- package/dist/chunk-HI5ZCAHO.js.map +0 -1
- package/dist/chunk-UKRX7A2V.js.map +0 -1
- /package/dist/{chunk-ORNJ4CTQ.js.map → chunk-QOKQWHHD.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
LAYOUT_CATEGORIES,
|
|
7
7
|
baseSlide,
|
|
8
8
|
getLayout
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MCG5BFJN.js";
|
|
10
10
|
|
|
11
11
|
// src/catalog.ts
|
|
12
12
|
import { z } from "zod";
|
|
@@ -143,4 +143,4 @@ export {
|
|
|
143
143
|
deckJsonSchema,
|
|
144
144
|
AUTHORING_GUIDE
|
|
145
145
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-QOKQWHHD.js.map
|
package/dist/cli.js
CHANGED
|
@@ -4,19 +4,21 @@ import {
|
|
|
4
4
|
deckJsonSchema,
|
|
5
5
|
describeLayout,
|
|
6
6
|
listLayouts
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QOKQWHHD.js";
|
|
8
8
|
import {
|
|
9
9
|
buildDeckFile,
|
|
10
10
|
exportDeckFile,
|
|
11
11
|
fileImageResolver,
|
|
12
12
|
loadDeckFile
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4LBQOMMI.js";
|
|
14
|
+
import {
|
|
15
|
+
renderDeck
|
|
16
|
+
} from "./chunk-6ARF37DO.js";
|
|
14
17
|
import {
|
|
15
18
|
DeckError,
|
|
16
19
|
formatIssues,
|
|
17
|
-
renderDeck,
|
|
18
20
|
validateDeck
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-MCG5BFJN.js";
|
|
20
22
|
|
|
21
23
|
// src/node/cli.ts
|
|
22
24
|
import { watch } from "fs";
|
|
@@ -130,7 +132,7 @@ ${"".padEnd(12)} Use for: ${l.whenToUse}
|
|
|
130
132
|
case "mcp": {
|
|
131
133
|
let mcp;
|
|
132
134
|
try {
|
|
133
|
-
mcp = await import("./mcp-
|
|
135
|
+
mcp = await import("./mcp-ZBYNCCHN.js");
|
|
134
136
|
} catch (e) {
|
|
135
137
|
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
136
138
|
throw new Error(
|
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, readdir } 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, exportDeckFile, 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 export <file.yaml> [-o out.json] Portable JSON with images inlined, for import\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\n/** Size above which hosted deck imports are likely to be refused. */\nconst IMPORT_WARN_MB = 10;\n\n// dist/cli.js when built; src/node/cli.ts when run from source (tests, tsx).\nconst here = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = resolve(here, basename(here) === 'node' ? '../..' : '..');\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 // Only the bundled starters, checked before anything is created on disk.\n const starters = (await readdir(join(pkgRoot, 'starters')))\n .filter((f) => f.endsWith('.yaml'))\n .map((f) => f.slice(0, -'.yaml'.length));\n if (!starters.includes(values.starter))\n throw new Error(`Unknown starter \"${values.starter}\". Use one of: ${starters.join(', ')}.`);\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 'export': {\n if (!arg) throw new Error('Usage: classic-slides export <file.yaml>');\n const r = await exportDeckFile(arg, {\n out: values.out,\n onWarning: (w) => console.warn(`⚠ ${w}`),\n });\n for (const w of r.warnings) console.warn(`⚠ Not inlined: ${w.src} — ${w.message}`);\n const mb = r.bytes / 1024 / 1024;\n console.log(`✔ ${r.out} — ${mb.toFixed(2)} MB`);\n if (mb > IMPORT_WARN_MB)\n console.warn(\n `⚠ Over ${IMPORT_WARN_MB} MB; the deck editor may refuse it. Resize large photos (~2000px wide).`\n );\n process.exitCode = r.warnings.length ? 1 : 0;\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, '&')\n .replace(/</g, '<')}</pre>${reload}`;\n\n // Bound to loopback only: the preview inlines local images and speaker notes.\n const server = 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 // Only the deck itself is served; anything else (favicon, probes) would re-render it.\n const path = (req.url ?? '/').split('?')[0];\n if (path !== '/' && path !== '/index.html') {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('Not found');\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 });\n server.on('error', (e: NodeJS.ErrnoException) => {\n console.error(\n e.code === 'EADDRINUSE'\n ? `✖ Port ${port} is already in use. Try --port ${port + 1}.`\n : `✖ Preview server failed: ${e.message}`\n );\n process.exit(1);\n });\n server.listen(port, '127.0.0.1', () =>\n console.log(`Previewing ${file} at http://127.0.0.1:${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, readdir } 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-default.js';\nimport { DeckError, formatIssues, validateDeck } from '../validate.js';\nimport { buildDeckFile, exportDeckFile, 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 export <file.yaml> [-o out.json] Portable JSON with images inlined, for import\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\n/** Size above which hosted deck imports are likely to be refused. */\nconst IMPORT_WARN_MB = 10;\n\n// dist/cli.js when built; src/node/cli.ts when run from source (tests, tsx).\nconst here = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = resolve(here, basename(here) === 'node' ? '../..' : '..');\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 // Only the bundled starters, checked before anything is created on disk.\n const starters = (await readdir(join(pkgRoot, 'starters')))\n .filter((f) => f.endsWith('.yaml'))\n .map((f) => f.slice(0, -'.yaml'.length));\n if (!starters.includes(values.starter))\n throw new Error(`Unknown starter \"${values.starter}\". Use one of: ${starters.join(', ')}.`);\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 'export': {\n if (!arg) throw new Error('Usage: classic-slides export <file.yaml>');\n const r = await exportDeckFile(arg, {\n out: values.out,\n onWarning: (w) => console.warn(`⚠ ${w}`),\n });\n for (const w of r.warnings) console.warn(`⚠ Not inlined: ${w.src} — ${w.message}`);\n const mb = r.bytes / 1024 / 1024;\n console.log(`✔ ${r.out} — ${mb.toFixed(2)} MB`);\n if (mb > IMPORT_WARN_MB)\n console.warn(\n `⚠ Over ${IMPORT_WARN_MB} MB; the deck editor may refuse it. Resize large photos (~2000px wide).`\n );\n process.exitCode = r.warnings.length ? 1 : 0;\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, '&')\n .replace(/</g, '<')}</pre>${reload}`;\n\n // Bound to loopback only: the preview inlines local images and speaker notes.\n const server = 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 // Only the deck itself is served; anything else (favicon, probes) would re-render it.\n const path = (req.url ?? '/').split('?')[0];\n if (path !== '/' && path !== '/index.html') {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('Not found');\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 });\n server.on('error', (e: NodeJS.ErrnoException) => {\n console.error(\n e.code === 'EADDRINUSE'\n ? `✖ Port ${port} is already in use. Try --port ${port + 1}.`\n : `✖ Preview server failed: ${e.message}`\n );\n process.exit(1);\n });\n server.listen(port, '127.0.0.1', () =>\n console.log(`Previewing ${file} at http://127.0.0.1:${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":[]}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { A as LayoutCategory } from './images-C90NCxYd.js';
|
|
2
|
+
export { B as BrandPalette, b as BrandPaletteSchema, c as BrandProfile, d as BrandProfileInput, e as BrandProfileSchema, f as CHART_TYPES, g as CLASSIC_HOMES_BRAND, h as CLASSIC_HOMES_PALETTE, i as ChartRenderer, j as ChartSpec, k as ChartSpecInput, C as ChartSpecValue, a as ChartSvgRenderer, l as ChartSvgRequest, m as ChartType, D as Deck, n as DeckBrand, o as DeckBrandSchema, p as DeckError, q as DeckInput, r as DeckSchema, s as DeckSettings, t as DeckSettingsSchema, F as FieldMarker, I as ImageInput, u as ImageResolver, v as ImageValue, w as InlineImagesResult, x as ItemValue, L as LAYOUTS, y as LAYOUT_CATEGORIES, z as LAYOUT_CATEGORY_ORDER, E as LayoutDef, G as LayoutIssue, H as LayoutName, M as MapImagesOptions, P as PREVIEW_SIZE, R as RenderOptions, J as RenderResult, K as ResolvedBrand, S as SINGLE_SERIES_CHARTS, N as SlideData, O as SlideInput, Q as SlideSchema, T as SpeakerNote, U as SpeakerNotesOptions, V as ValidateOptions, W as ValidationResult, X as chartOption, Y as checkPalette, Z as collectImageSources, _ as describeChart, $ as externalizeDeckImages, a0 as extractDeckSource, a1 as fieldMarkers, a2 as finishChartSvg, a3 as formatIssues, a4 as formatPath, a5 as formatValue, a6 as getLayout, a7 as inlineDeckImages, a8 as mapDeckImages, ad as renderDeck, a9 as resolveBrand, aa as speakerNotes, ab as validateBrandProfile, ac as validateDeck } from './images-C90NCxYd.js';
|
|
3
|
+
import * as zod_to_json_schema from 'zod-to-json-schema';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
interface LayoutSummary {
|
|
7
|
+
name: string;
|
|
8
|
+
category: LayoutCategory;
|
|
9
|
+
description: string;
|
|
10
|
+
whenToUse: string;
|
|
11
|
+
}
|
|
12
|
+
declare function listLayouts(): LayoutSummary[];
|
|
13
|
+
/** Full description of one layout: fields as JSON Schema plus a working example. */
|
|
14
|
+
declare function describeLayout(name: string): {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
whenToUse: string;
|
|
18
|
+
defaults: {
|
|
19
|
+
background?: string;
|
|
20
|
+
track?: "value" | "build" | "neutral";
|
|
21
|
+
};
|
|
22
|
+
schema: zod_to_json_schema.JsonSchema7Type & {
|
|
23
|
+
$schema?: string | undefined;
|
|
24
|
+
definitions?: {
|
|
25
|
+
[key: string]: zod_to_json_schema.JsonSchema7Type;
|
|
26
|
+
} | undefined;
|
|
27
|
+
};
|
|
28
|
+
example: Record<string, unknown>;
|
|
29
|
+
} | undefined;
|
|
30
|
+
/** JSON Schema for a whole deck (editor tooling, `$schema` in deck files). */
|
|
31
|
+
declare function deckJsonSchema(): zod_to_json_schema.JsonSchema7Type & {
|
|
32
|
+
$schema?: string | undefined;
|
|
33
|
+
definitions?: {
|
|
34
|
+
[key: string]: zod_to_json_schema.JsonSchema7Type;
|
|
35
|
+
} | undefined;
|
|
36
|
+
};
|
|
37
|
+
/** Authoring guidance shared by the CLI, MCP server and docs. */
|
|
38
|
+
declare const AUTHORING_GUIDE = "# Writing a Classic Homes deck\n\nA deck is a YAML (or JSON) file: deck-level fields plus a list of slides. Each slide\npicks a **layout** and fills its fields. Layouts own all styling \u2014 there is no way to\nset colours, fonts or sizes directly, which is what keeps every deck on-brand.\n\n## Principles\n- **One idea per slide.** The headline states the point. Detail goes in `notes`.\n- **Short copy.** Titles under ~70 characters, bullets a few words, at most 6 bullets.\n `validate` warns when a slide gets crowded \u2014 split it rather than shrinking text.\n- **Speaker notes on every content slide.** Presenters press N (or P for presenter view).\n- **Real photos over decoration.** Every image needs `alt` text.\n- **Tracks** colour the accent: `value` (red, default) for the homeowner/value story,\n `build` (teal) for construction/process, `neutral` (navy) for framing slides.\n- **Backgrounds**: `paper` (default) for content; solid `navy`/`red`/`teal`/`plum`/`grey`/`stone`\n for dividers. `backgroundImage` + `overlay` puts a photo behind title/section/statement slides.\n\n## Picking a layout\n- **Numbers:** `stats` (2\u20136 headline figures, optional trend + sparkline), `chart` (a real chart \u2014\n put the takeaway in the title), `table` (exact figures people compare).\n- **Processes & structure:** `flow` (ordered steps with arrows), `timeline` (dated milestones, phases),\n `cycle` (things that repeat), `hierarchy` (org chart / tree), `matrix` (2\u00D72), `tiers`\n (funnel or pyramid), `walkthrough` (photo per stage), `journey` (deck agenda).\n- **Media:** `split` (text + photos), `photos`, `image` (with numbered `hotspots` for floor plans\n and site maps), `compare` (before/after slider), `video` (YouTube/Vimeo), `embed` (any https page:\n maps, dashboards, 3D tours, forms).\n- **Words:** `statement`, `bullets`, `cards`, `comparison`, `quote`.\n- **Wrap-up:** `qr` (send people somewhere), `contacts`, closing `statement`.\n\n## Charts\n`chart.labels` are the categories; each series has one value per label. Use `highlight` to\nemphasise one category and `format`/`unit` instead of typing symbols into numbers.\n- **Compare:** `column`/`bar`; `dumbbell` (2+ series as dots per row, e.g. last year vs this\n year); `bullet` (series 1 actual, series 2 target; `max` sets the track).\n- **Part of whole:** `stacked-*` (absolute), `percent-column`/`percent-bar` (each row = 100%),\n `pie`/`donut` (\u2264 7 slices), `treemap` (many categories).\n- **Change:** `line`/`area`; `combo` (series 1 bars, the rest lines; set a series' `as` to\n override and `axis: secondary` + `secondaryFormat` for a second scale, e.g. closings and average\n price); `waterfall` (series 1 is signed changes; list running-total rows in `totals`).\n- **Single value:** `gauge` (one value, needs `max`); `ring` (up to 4 progress rings, one per label;\n `max`, or `format: percent` for 100); `pictogram` (house icons per row; `perIcon` sets the step).\n- **Grid & flow:** `heatmap` (labels are rows, series names are columns); `sankey` (labels are\n sources, series names are destinations, each cell a flow; a destination that is also a label chains\n stages; no loops).\n- **Other:** `funnel` (drop-off, first series), `scatter` (two series as x and y), `radar`.\nPie, donut, funnel, gauge, ring, treemap, waterfall and pictogram read the first series only.\n\n## Video & embeds\nThey need internet while presenting. Offline, in print and in PDF they show a poster (or\n`fallback` screenshot) with a QR code. Some sites refuse to be framed \u2014 the \"Open \u2197\" link\nalways works; add a `fallback` image for anything critical.\n\n## Brand\n`brand.profile` picks who the deck speaks for (default `classic-homes`). You may override\n`name`, `tagline`, `logo`, `logoOnDark`, `favicon` and `footer`; colours come from the profile.\n`settings` controls slide numbers, logo placement, a confidential label and kiosk auto-advance.\n\n## Presenting\nArrow keys / Space advance (and step through `build: true` reveals). N notes \u00B7 P presenter window\n(current + next slide, notes, timer) \u00B7 O overview \u00B7 F fullscreen \u00B7 `?kiosk=15` on the URL loops\nunattended. `hidden: true` skips a slide without deleting it.\n\n## Inline text\n`**bold**`, `*highlight*` (accent colour), `\\`code\\``, `[label](https://\u2026)`. No HTML.\n\n## Journey decks\nSet `journey.steps` at deck level to get a numbered agenda. Then:\n- a `journey` slide shows the full map,\n- `step: 3` on any slide draws the vertical STEP rail,\n- `showJourney: true` adds the compact \"you are here\" strip (great on `section` slides).\n\n## Typical shape\n1. `title`\n2. `statement` or `journey` (why we're here / the agenda)\n3. per part: `section` \u2192 2\u20135 content slides\n4. `qr` (where to go next) \u2192 `contacts` \u2192 closing `statement`\n";
|
|
39
|
+
|
|
40
|
+
/** Parses deck source text. YAML is a superset of JSON, so both work. */
|
|
41
|
+
declare function parseDeckSource(text: string): unknown;
|
|
42
|
+
/** Serializes a deck back to YAML, the human-editable format. */
|
|
43
|
+
declare function stringifyDeck(deck: unknown): string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Inline text formatting for slide copy and speaker notes.
|
|
47
|
+
*
|
|
48
|
+
* Deck text is never raw HTML: authors and agents write a tiny markdown subset
|
|
49
|
+
* and everything else is escaped. That keeps decks on-brand (no inline styles)
|
|
50
|
+
* and makes rendered output safe to publish.
|
|
51
|
+
*
|
|
52
|
+
* **bold** → <b> (strong ink colour)
|
|
53
|
+
* *highlight* → <em class="hl"> (track accent colour)
|
|
54
|
+
* `code` → <code>
|
|
55
|
+
* [label](url) → <a> (http/https/mailto/tel only)
|
|
56
|
+
* single newline → <br>
|
|
57
|
+
*/
|
|
58
|
+
declare function escapeHtml(value: string): string;
|
|
59
|
+
/** Formats one run of inline text. Newlines become line breaks. */
|
|
60
|
+
declare function inline(text: string | undefined | null): string;
|
|
61
|
+
/**
|
|
62
|
+
* Formats a block of prose (speaker notes, long captions): blank lines split
|
|
63
|
+
* paragraphs, lines starting with "- " become a bullet list.
|
|
64
|
+
*/
|
|
65
|
+
declare function prose(text: string | undefined | null): string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Slide palette. Mostly brand tokens used as-is; the few surface tints
|
|
69
|
+
* (paper, soft accents) are deck-specific and derived here.
|
|
70
|
+
*/
|
|
71
|
+
declare const PALETTE: {
|
|
72
|
+
readonly red: "#a91c22";
|
|
73
|
+
readonly redLogo: "#e02726";
|
|
74
|
+
readonly teal: "#236265";
|
|
75
|
+
readonly tealFill: "#236265";
|
|
76
|
+
readonly navy: "#002e3f";
|
|
77
|
+
readonly plum: "#5e2c41";
|
|
78
|
+
readonly sage: "#969a82";
|
|
79
|
+
readonly stone: "#676d69";
|
|
80
|
+
readonly grey: "#4f4f4f";
|
|
81
|
+
readonly ink: "#002e3f";
|
|
82
|
+
readonly inkSoft: "#4f4f4f";
|
|
83
|
+
readonly inkFaint: "#7a7a7a";
|
|
84
|
+
};
|
|
85
|
+
/** Horizontal Classic Homes lockup (mark + wordmark), mirrors LogoMain in theme-svelte. Colours follow CSS. */
|
|
86
|
+
declare const LOGO_SVG: string;
|
|
87
|
+
/**
|
|
88
|
+
* Logo lockup from a name. Classic Homes brands (divisions, studios) keep the
|
|
89
|
+
* flower mark; partner brands without a logo file get a clean wordmark so
|
|
90
|
+
* they are never shown under the Classic Homes mark.
|
|
91
|
+
*/
|
|
92
|
+
declare function logoLockup(name: string, withMark?: boolean): string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Slide-level edits on a deck object. Pure functions shared by the MCP
|
|
96
|
+
* server and (later) the hosted editor, so agents change one slide at a
|
|
97
|
+
* time instead of rewriting whole files.
|
|
98
|
+
*/
|
|
99
|
+
interface EditableDeck {
|
|
100
|
+
slides: Record<string, unknown>[];
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
}
|
|
103
|
+
/** A slide reference: its `id`, or its 1-based position. */
|
|
104
|
+
type SlideRef = string | number;
|
|
105
|
+
declare function findSlide(deck: EditableDeck, ref: SlideRef): number;
|
|
106
|
+
/** A short random slide id such as "s-k3f9x2" (valid for the slide `id` field). */
|
|
107
|
+
declare function newSlideId(taken?: ReadonlySet<string>): string;
|
|
108
|
+
/**
|
|
109
|
+
* Gives every slide without an `id` a stable random one, so comments and
|
|
110
|
+
* analytics can anchor to slides across edits and reorders. Idempotent;
|
|
111
|
+
* returns how many ids were added.
|
|
112
|
+
*/
|
|
113
|
+
declare function ensureSlideIds(deck: EditableDeck): number;
|
|
114
|
+
/** Stable key for a slide: its id, or "#<position>" for decks saved before ids existed. */
|
|
115
|
+
declare function slideKey(slide: {
|
|
116
|
+
id?: unknown;
|
|
117
|
+
}, index: number): string;
|
|
118
|
+
/** Inserts a slide. `position` is 1-based; omitted appends. Returns the new slide's position. */
|
|
119
|
+
declare function addSlide(deck: EditableDeck, slide: Record<string, unknown>, position?: number): number;
|
|
120
|
+
/** Shallow-merges fields into a slide. A field set to null is removed. Changing `layout` replaces the slide. */
|
|
121
|
+
declare function updateSlide(deck: EditableDeck, ref: SlideRef, patch: Record<string, unknown>): number;
|
|
122
|
+
declare function moveSlide(deck: EditableDeck, ref: SlideRef, to: number): number;
|
|
123
|
+
declare function removeSlide(deck: EditableDeck, ref: SlideRef): Record<string, unknown>;
|
|
124
|
+
/** One-line outline of each slide, for agents and CLIs. */
|
|
125
|
+
declare function outline(deck: EditableDeck): string[];
|
|
126
|
+
|
|
127
|
+
type EditorField = FieldBase<'inline'> | FieldBase<'string'> | (FieldBase<'enum'> & {
|
|
128
|
+
options: (string | number)[];
|
|
129
|
+
}) | FieldBase<'boolean'> | (FieldBase<'number'> & {
|
|
130
|
+
min?: number;
|
|
131
|
+
max?: number;
|
|
132
|
+
integer: boolean;
|
|
133
|
+
}) | FieldBase<'image'> | FieldBase<'item'> | (FieldBase<'list'> & {
|
|
134
|
+
of: EditorField;
|
|
135
|
+
min?: number;
|
|
136
|
+
max?: number;
|
|
137
|
+
}) | (FieldBase<'group'> & {
|
|
138
|
+
fields: EditorField[];
|
|
139
|
+
});
|
|
140
|
+
interface FieldBase<K extends string> {
|
|
141
|
+
kind: K;
|
|
142
|
+
/** Property name ('' for list elements). */
|
|
143
|
+
key: string;
|
|
144
|
+
label: string;
|
|
145
|
+
description?: string;
|
|
146
|
+
required: boolean;
|
|
147
|
+
default?: unknown;
|
|
148
|
+
/** Suggest a textarea rather than a one-line input. */
|
|
149
|
+
multiline?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Suggests a richer widget; editors that don't know a hint can fall back to `kind`.
|
|
152
|
+
* - url: https link · video-url: YouTube/Vimeo link
|
|
153
|
+
* - chart: ChartSpec group (edit labels × series as a grid)
|
|
154
|
+
* - grid: list of rows of cells (table layout)
|
|
155
|
+
* - point: {x, y} in % of an image (hotspot picker)
|
|
156
|
+
*/
|
|
157
|
+
hint?: 'url' | 'video-url' | 'chart' | 'grid' | 'point';
|
|
158
|
+
/** Rarely changed; editors should tuck it behind an "Advanced" section. */
|
|
159
|
+
advanced?: boolean;
|
|
160
|
+
}
|
|
161
|
+
/** Deck-level fields for a settings panel: brand overrides and presentation settings. */
|
|
162
|
+
declare function deckEditorFields(): {
|
|
163
|
+
brand: EditorField[];
|
|
164
|
+
settings: EditorField[];
|
|
165
|
+
};
|
|
166
|
+
interface LayoutEditorInfo {
|
|
167
|
+
name: string;
|
|
168
|
+
category: LayoutCategory;
|
|
169
|
+
description: string;
|
|
170
|
+
whenToUse: string;
|
|
171
|
+
defaults: {
|
|
172
|
+
background?: string;
|
|
173
|
+
track?: string;
|
|
174
|
+
};
|
|
175
|
+
/** Layout-specific fields, in declaration order. */
|
|
176
|
+
fields: EditorField[];
|
|
177
|
+
/** A valid starting slide for this layout. */
|
|
178
|
+
example: Record<string, unknown>;
|
|
179
|
+
}
|
|
180
|
+
/** Fields shared by every slide (track, background, notes, step…). */
|
|
181
|
+
declare function commonSlideFields(): EditorField[];
|
|
182
|
+
declare function layoutEditorInfo(name: string): LayoutEditorInfo | undefined;
|
|
183
|
+
declare function allLayoutEditorInfo(): LayoutEditorInfo[];
|
|
184
|
+
/**
|
|
185
|
+
* One deck holding every layout's starting slide, in palette order, for
|
|
186
|
+
* rendering layout thumbnails once (render it, then show slide N of the
|
|
187
|
+
* result per layout). `slideFor[layout]` is that layout's slide index.
|
|
188
|
+
*/
|
|
189
|
+
declare function layoutGalleryDeck(): {
|
|
190
|
+
deck: {
|
|
191
|
+
title: string;
|
|
192
|
+
journey: {
|
|
193
|
+
steps: string[];
|
|
194
|
+
};
|
|
195
|
+
slides: Record<string, unknown>[];
|
|
196
|
+
};
|
|
197
|
+
slideFor: Record<string, number>;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Stand-in image for new slides: a neutral frame reading "Add a photo".
|
|
201
|
+
* It validates (so a fresh slide previews immediately) but lint warns until
|
|
202
|
+
* it is replaced.
|
|
203
|
+
*/
|
|
204
|
+
declare const PLACEHOLDER_IMAGE: string;
|
|
205
|
+
declare function isPlaceholderImage(src: string | undefined): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* A new slide for a layout, ready to edit: the layout's example with sample
|
|
208
|
+
* image paths swapped for {@link PLACEHOLDER_IMAGE}. Always passes validation.
|
|
209
|
+
*/
|
|
210
|
+
declare function blankSlide(name: string): Record<string, unknown> | undefined;
|
|
211
|
+
|
|
212
|
+
/** YouTube / Vimeo URL parsing for the video layout. */
|
|
213
|
+
interface VideoRef {
|
|
214
|
+
provider: 'youtube' | 'vimeo';
|
|
215
|
+
id: string;
|
|
216
|
+
/** Canonical watch URL (used for QR codes and "open" links). */
|
|
217
|
+
watchUrl: string;
|
|
218
|
+
/** Poster image URL, when the provider exposes a predictable one. */
|
|
219
|
+
poster?: string;
|
|
220
|
+
}
|
|
221
|
+
declare function parseVideoUrl(url: string): VideoRef | undefined;
|
|
222
|
+
interface EmbedOptions {
|
|
223
|
+
start?: number;
|
|
224
|
+
end?: number;
|
|
225
|
+
muted?: boolean;
|
|
226
|
+
captions?: boolean;
|
|
227
|
+
autoplay?: boolean;
|
|
228
|
+
}
|
|
229
|
+
/** Privacy-friendly player URL (youtube-nocookie, Vimeo with dnt). */
|
|
230
|
+
declare function videoEmbedUrl(ref: VideoRef, o: EmbedOptions): string;
|
|
231
|
+
|
|
232
|
+
/** CSP source for the inline presenter runtime: script-src '<hash>'. */
|
|
233
|
+
declare const RUNTIME_SCRIPT_HASH = "sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=";
|
|
234
|
+
|
|
235
|
+
export { AUTHORING_GUIDE, type EditableDeck, type EditorField, LOGO_SVG, LayoutCategory, type LayoutEditorInfo, PALETTE, PLACEHOLDER_IMAGE, RUNTIME_SCRIPT_HASH, type SlideRef, type VideoRef, addSlide, allLayoutEditorInfo, blankSlide, commonSlideFields, deckEditorFields, deckJsonSchema, describeLayout, ensureSlideIds, escapeHtml, findSlide, inline, isPlaceholderImage, layoutEditorInfo, layoutGalleryDeck, listLayouts, logoLockup, moveSlide, newSlideId, outline, parseDeckSource, parseVideoUrl, prose, removeSlide, slideKey, stringifyDeck, updateSlide, videoEmbedUrl };
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RUNTIME_SCRIPT_HASH
|
|
3
|
+
} from "./chunk-4NSKDQ5V.js";
|
|
4
|
+
import {
|
|
5
|
+
AUTHORING_GUIDE,
|
|
6
|
+
deckJsonSchema,
|
|
7
|
+
describeLayout,
|
|
8
|
+
listLayouts
|
|
9
|
+
} from "./chunk-QOKQWHHD.js";
|
|
10
|
+
import {
|
|
11
|
+
BrandPaletteSchema,
|
|
12
|
+
BrandProfileSchema,
|
|
13
|
+
CHART_TYPES,
|
|
14
|
+
CLASSIC_HOMES_BRAND,
|
|
15
|
+
CLASSIC_HOMES_PALETTE,
|
|
16
|
+
ChartSpec,
|
|
17
|
+
DeckBrandSchema,
|
|
18
|
+
DeckError,
|
|
19
|
+
DeckSchema,
|
|
20
|
+
DeckSettingsSchema,
|
|
21
|
+
LAYOUTS,
|
|
22
|
+
LAYOUT_CATEGORIES,
|
|
23
|
+
LAYOUT_CATEGORY_ORDER,
|
|
24
|
+
LOGO_SVG,
|
|
25
|
+
PALETTE,
|
|
26
|
+
PLACEHOLDER_IMAGE,
|
|
27
|
+
PREVIEW_SIZE,
|
|
28
|
+
SINGLE_SERIES_CHARTS,
|
|
29
|
+
SlideSchema,
|
|
30
|
+
addSlide,
|
|
31
|
+
allLayoutEditorInfo,
|
|
32
|
+
blankSlide,
|
|
33
|
+
chartOption,
|
|
34
|
+
checkPalette,
|
|
35
|
+
collectImageSources,
|
|
36
|
+
commonSlideFields,
|
|
37
|
+
deckEditorFields,
|
|
38
|
+
describeChart,
|
|
39
|
+
ensureSlideIds,
|
|
40
|
+
escapeHtml,
|
|
41
|
+
externalizeDeckImages,
|
|
42
|
+
extractDeckSource,
|
|
43
|
+
fieldMarkers,
|
|
44
|
+
findSlide,
|
|
45
|
+
finishChartSvg,
|
|
46
|
+
formatIssues,
|
|
47
|
+
formatPath,
|
|
48
|
+
formatValue,
|
|
49
|
+
getLayout,
|
|
50
|
+
inline,
|
|
51
|
+
inlineDeckImages,
|
|
52
|
+
isPlaceholderImage,
|
|
53
|
+
layoutEditorInfo,
|
|
54
|
+
layoutGalleryDeck,
|
|
55
|
+
logoLockup,
|
|
56
|
+
mapDeckImages,
|
|
57
|
+
moveSlide,
|
|
58
|
+
newSlideId,
|
|
59
|
+
outline,
|
|
60
|
+
parseDeckSource,
|
|
61
|
+
parseVideoUrl,
|
|
62
|
+
prose,
|
|
63
|
+
removeSlide,
|
|
64
|
+
renderDeck,
|
|
65
|
+
resolveBrand,
|
|
66
|
+
slideKey,
|
|
67
|
+
speakerNotes,
|
|
68
|
+
stringifyDeck,
|
|
69
|
+
updateSlide,
|
|
70
|
+
validateBrandProfile,
|
|
71
|
+
validateDeck,
|
|
72
|
+
videoEmbedUrl
|
|
73
|
+
} from "./chunk-MCG5BFJN.js";
|
|
74
|
+
export {
|
|
75
|
+
AUTHORING_GUIDE,
|
|
76
|
+
BrandPaletteSchema,
|
|
77
|
+
BrandProfileSchema,
|
|
78
|
+
CHART_TYPES,
|
|
79
|
+
CLASSIC_HOMES_BRAND,
|
|
80
|
+
CLASSIC_HOMES_PALETTE,
|
|
81
|
+
ChartSpec,
|
|
82
|
+
DeckBrandSchema,
|
|
83
|
+
DeckError,
|
|
84
|
+
DeckSchema,
|
|
85
|
+
DeckSettingsSchema,
|
|
86
|
+
LAYOUTS,
|
|
87
|
+
LAYOUT_CATEGORIES,
|
|
88
|
+
LAYOUT_CATEGORY_ORDER,
|
|
89
|
+
LOGO_SVG,
|
|
90
|
+
PALETTE,
|
|
91
|
+
PLACEHOLDER_IMAGE,
|
|
92
|
+
PREVIEW_SIZE,
|
|
93
|
+
RUNTIME_SCRIPT_HASH,
|
|
94
|
+
SINGLE_SERIES_CHARTS,
|
|
95
|
+
SlideSchema,
|
|
96
|
+
addSlide,
|
|
97
|
+
allLayoutEditorInfo,
|
|
98
|
+
blankSlide,
|
|
99
|
+
chartOption,
|
|
100
|
+
checkPalette,
|
|
101
|
+
collectImageSources,
|
|
102
|
+
commonSlideFields,
|
|
103
|
+
deckEditorFields,
|
|
104
|
+
deckJsonSchema,
|
|
105
|
+
describeChart,
|
|
106
|
+
describeLayout,
|
|
107
|
+
ensureSlideIds,
|
|
108
|
+
escapeHtml,
|
|
109
|
+
externalizeDeckImages,
|
|
110
|
+
extractDeckSource,
|
|
111
|
+
fieldMarkers,
|
|
112
|
+
findSlide,
|
|
113
|
+
finishChartSvg,
|
|
114
|
+
formatIssues,
|
|
115
|
+
formatPath,
|
|
116
|
+
formatValue,
|
|
117
|
+
getLayout,
|
|
118
|
+
inline,
|
|
119
|
+
inlineDeckImages,
|
|
120
|
+
isPlaceholderImage,
|
|
121
|
+
layoutEditorInfo,
|
|
122
|
+
layoutGalleryDeck,
|
|
123
|
+
listLayouts,
|
|
124
|
+
logoLockup,
|
|
125
|
+
mapDeckImages,
|
|
126
|
+
moveSlide,
|
|
127
|
+
newSlideId,
|
|
128
|
+
outline,
|
|
129
|
+
parseDeckSource,
|
|
130
|
+
parseVideoUrl,
|
|
131
|
+
prose,
|
|
132
|
+
removeSlide,
|
|
133
|
+
renderDeck,
|
|
134
|
+
resolveBrand,
|
|
135
|
+
slideKey,
|
|
136
|
+
speakerNotes,
|
|
137
|
+
stringifyDeck,
|
|
138
|
+
updateSlide,
|
|
139
|
+
validateBrandProfile,
|
|
140
|
+
validateDeck,
|
|
141
|
+
videoEmbedUrl
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -350,6 +350,117 @@ declare function validateBrandProfile(input: unknown): {
|
|
|
350
350
|
}[];
|
|
351
351
|
};
|
|
352
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Chart specs for the `chart` layout. Authors describe data, never styling;
|
|
355
|
+
* this module turns a spec into an ECharts option themed from the brand, and
|
|
356
|
+
* renders it to static SVG (server-side rendering, no runtime JS) so charts
|
|
357
|
+
* work offline, in print, in Workers and in the browser alike.
|
|
358
|
+
*
|
|
359
|
+
* This module never imports ECharts: chart-render.ts draws with it (loaded
|
|
360
|
+
* lazily), and the `/core` entry leaves it out for callers that inject their
|
|
361
|
+
* own `ChartSvgRenderer`.
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
declare const CHART_TYPES: readonly ["column", "bar", "stacked-column", "stacked-bar", "line", "area", "pie", "donut", "funnel", "gauge", "scatter", "radar", "percent-column", "percent-bar", "combo", "waterfall", "heatmap", "dumbbell", "bullet", "treemap", "ring", "sankey", "pictogram"];
|
|
365
|
+
type ChartType = (typeof CHART_TYPES)[number];
|
|
366
|
+
/** Types that only read the first series. */
|
|
367
|
+
declare const SINGLE_SERIES_CHARTS: readonly ChartType[];
|
|
368
|
+
declare const ChartSpec: z.ZodObject<{
|
|
369
|
+
type: z.ZodEnum<["column", "bar", "stacked-column", "stacked-bar", "line", "area", "pie", "donut", "funnel", "gauge", "scatter", "radar", "percent-column", "percent-bar", "combo", "waterfall", "heatmap", "dumbbell", "bullet", "treemap", "ring", "sankey", "pictogram"]>;
|
|
370
|
+
labels: z.ZodArray<z.ZodString, "many">;
|
|
371
|
+
series: z.ZodArray<z.ZodObject<{
|
|
372
|
+
name: z.ZodString;
|
|
373
|
+
values: z.ZodArray<z.ZodNumber, "many">;
|
|
374
|
+
as: z.ZodOptional<z.ZodEnum<["bar", "line"]>>;
|
|
375
|
+
axis: z.ZodOptional<z.ZodEnum<["primary", "secondary"]>>;
|
|
376
|
+
}, "strip", z.ZodTypeAny, {
|
|
377
|
+
values: number[];
|
|
378
|
+
name: string;
|
|
379
|
+
as?: "bar" | "line" | undefined;
|
|
380
|
+
axis?: "primary" | "secondary" | undefined;
|
|
381
|
+
}, {
|
|
382
|
+
values: number[];
|
|
383
|
+
name: string;
|
|
384
|
+
as?: "bar" | "line" | undefined;
|
|
385
|
+
axis?: "primary" | "secondary" | undefined;
|
|
386
|
+
}>, "many">;
|
|
387
|
+
format: z.ZodDefault<z.ZodEnum<["number", "percent", "currency", "compact"]>>;
|
|
388
|
+
decimals: z.ZodDefault<z.ZodNumber>;
|
|
389
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
390
|
+
legend: z.ZodDefault<z.ZodEnum<["auto", "show", "hide"]>>;
|
|
391
|
+
labelsOnData: z.ZodDefault<z.ZodBoolean>;
|
|
392
|
+
highlight: z.ZodOptional<z.ZodString>;
|
|
393
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
394
|
+
secondaryFormat: z.ZodOptional<z.ZodEnum<["number", "percent", "currency", "compact"]>>;
|
|
395
|
+
totals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
396
|
+
perIcon: z.ZodOptional<z.ZodNumber>;
|
|
397
|
+
}, "strict", z.ZodTypeAny, {
|
|
398
|
+
type: "column" | "bar" | "stacked-column" | "stacked-bar" | "line" | "area" | "pie" | "donut" | "funnel" | "gauge" | "scatter" | "radar" | "percent-column" | "percent-bar" | "combo" | "waterfall" | "heatmap" | "dumbbell" | "bullet" | "treemap" | "ring" | "sankey" | "pictogram";
|
|
399
|
+
labels: string[];
|
|
400
|
+
series: {
|
|
401
|
+
values: number[];
|
|
402
|
+
name: string;
|
|
403
|
+
as?: "bar" | "line" | undefined;
|
|
404
|
+
axis?: "primary" | "secondary" | undefined;
|
|
405
|
+
}[];
|
|
406
|
+
format: "number" | "percent" | "currency" | "compact";
|
|
407
|
+
decimals: number;
|
|
408
|
+
legend: "auto" | "show" | "hide";
|
|
409
|
+
labelsOnData: boolean;
|
|
410
|
+
unit?: string | undefined;
|
|
411
|
+
highlight?: string | undefined;
|
|
412
|
+
max?: number | undefined;
|
|
413
|
+
secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
|
|
414
|
+
totals?: string[] | undefined;
|
|
415
|
+
perIcon?: number | undefined;
|
|
416
|
+
}, {
|
|
417
|
+
type: "column" | "bar" | "stacked-column" | "stacked-bar" | "line" | "area" | "pie" | "donut" | "funnel" | "gauge" | "scatter" | "radar" | "percent-column" | "percent-bar" | "combo" | "waterfall" | "heatmap" | "dumbbell" | "bullet" | "treemap" | "ring" | "sankey" | "pictogram";
|
|
418
|
+
labels: string[];
|
|
419
|
+
series: {
|
|
420
|
+
values: number[];
|
|
421
|
+
name: string;
|
|
422
|
+
as?: "bar" | "line" | undefined;
|
|
423
|
+
axis?: "primary" | "secondary" | undefined;
|
|
424
|
+
}[];
|
|
425
|
+
format?: "number" | "percent" | "currency" | "compact" | undefined;
|
|
426
|
+
decimals?: number | undefined;
|
|
427
|
+
unit?: string | undefined;
|
|
428
|
+
legend?: "auto" | "show" | "hide" | undefined;
|
|
429
|
+
labelsOnData?: boolean | undefined;
|
|
430
|
+
highlight?: string | undefined;
|
|
431
|
+
max?: number | undefined;
|
|
432
|
+
secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
|
|
433
|
+
totals?: string[] | undefined;
|
|
434
|
+
perIcon?: number | undefined;
|
|
435
|
+
}>;
|
|
436
|
+
type ChartSpecInput = z.input<typeof ChartSpec>;
|
|
437
|
+
type ChartSpecValue = z.output<typeof ChartSpec>;
|
|
438
|
+
declare function formatValue(v: number, spec: Pick<ChartSpecValue, 'format' | 'decimals' | 'unit'>): string;
|
|
439
|
+
/** Builds the ECharts option for a spec. Pure; exported for tests and custom renderers. */
|
|
440
|
+
declare function chartOption(spec: ChartSpecValue, palette?: BrandPalette): Record<string, unknown>;
|
|
441
|
+
interface ChartRenderer {
|
|
442
|
+
render(option: Record<string, unknown>, width: number, height: number): string;
|
|
443
|
+
}
|
|
444
|
+
/** One chart for a `ChartSvgRenderer` to draw. Plain data, so it can cross a process boundary. */
|
|
445
|
+
interface ChartSvgRequest {
|
|
446
|
+
spec: ChartSpecValue;
|
|
447
|
+
width: number;
|
|
448
|
+
height: number;
|
|
449
|
+
/** Brand palette; omitted for the stock Classic Homes brand. */
|
|
450
|
+
palette?: BrandPalette;
|
|
451
|
+
title?: string;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Draws charts to finished SVG strings (`renderChartSvg` output), one per
|
|
455
|
+
* request, in request order. `renderDeck` takes one as `renderCharts` so
|
|
456
|
+
* ECharts can run somewhere else, such as a separate Worker.
|
|
457
|
+
*/
|
|
458
|
+
type ChartSvgRenderer = (charts: ChartSvgRequest[]) => Promise<string[]>;
|
|
459
|
+
/** Labels a raw ECharts SVG for screen readers and makes it responsive. */
|
|
460
|
+
declare function finishChartSvg(svg: string, spec: ChartSpecValue, title?: string): string;
|
|
461
|
+
/** Plain-language summary for screen readers. */
|
|
462
|
+
declare function describeChart(spec: ChartSpecValue): string;
|
|
463
|
+
|
|
353
464
|
/** The deck-level fields a layout may read while rendering. */
|
|
354
465
|
interface DeckContext {
|
|
355
466
|
title: string;
|
|
@@ -571,6 +682,12 @@ interface RenderOptions {
|
|
|
571
682
|
* stop navigating. Off by default; never use it for shared or exported decks.
|
|
572
683
|
*/
|
|
573
684
|
editorMarkers?: boolean;
|
|
685
|
+
/**
|
|
686
|
+
* Draws the deck's charts. The main entry defaults to ECharts in process;
|
|
687
|
+
* the `/core` entry has no default, so a deck with charts needs one there
|
|
688
|
+
* (for example, a call to a Worker that runs ECharts).
|
|
689
|
+
*/
|
|
690
|
+
renderCharts?: ChartSvgRenderer;
|
|
574
691
|
}
|
|
575
692
|
/** A field the editor runtime can match a clicked element to. */
|
|
576
693
|
interface FieldMarker {
|
|
@@ -667,4 +784,4 @@ declare function inlineDeckImages(input: unknown, resolve: ImageResolver, option
|
|
|
667
784
|
*/
|
|
668
785
|
declare function externalizeDeckImages(input: unknown, upload: (dataUri: string) => string | Promise<string>, options?: MapImagesOptions): Promise<unknown>;
|
|
669
786
|
|
|
670
|
-
export {
|
|
787
|
+
export { externalizeDeckImages as $, type LayoutCategory as A, type BrandPalette as B, type ChartSpecValue as C, type Deck as D, type LayoutDef as E, type FieldMarker as F, type LayoutIssue as G, type LayoutName as H, type ImageInput as I, type RenderResult as J, type ResolvedBrand as K, LAYOUTS as L, type MapImagesOptions as M, type SlideData as N, type SlideInput as O, PREVIEW_SIZE as P, SlideSchema as Q, type RenderOptions as R, SINGLE_SERIES_CHARTS as S, type SpeakerNote as T, type SpeakerNotesOptions as U, type ValidateOptions as V, type ValidationResult as W, chartOption as X, checkPalette as Y, collectImageSources as Z, describeChart as _, type ChartSvgRenderer as a, extractDeckSource as a0, fieldMarkers as a1, finishChartSvg as a2, formatIssues as a3, formatPath as a4, formatValue as a5, getLayout as a6, inlineDeckImages as a7, mapDeckImages as a8, resolveBrand as a9, speakerNotes as aa, validateBrandProfile as ab, validateDeck as ac, renderDeck as ad, BrandPaletteSchema as b, type BrandProfile as c, type BrandProfileInput as d, BrandProfileSchema as e, CHART_TYPES as f, CLASSIC_HOMES_BRAND as g, CLASSIC_HOMES_PALETTE as h, type ChartRenderer as i, ChartSpec as j, type ChartSpecInput as k, type ChartSvgRequest as l, type ChartType as m, type DeckBrand as n, DeckBrandSchema as o, DeckError as p, type DeckInput as q, DeckSchema as r, type DeckSettings as s, DeckSettingsSchema as t, type ImageResolver as u, type ImageValue as v, type InlineImagesResult as w, type ItemValue as x, LAYOUT_CATEGORIES as y, LAYOUT_CATEGORY_ORDER as z };
|