@classic-homes/slides 0.5.12 → 0.7.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.
@@ -6,7 +6,7 @@ import {
6
6
  LAYOUT_CATEGORIES,
7
7
  baseSlide,
8
8
  getLayout
9
- } from "./chunk-25NZMEQG.js";
9
+ } from "./chunk-HI5ZCAHO.js";
10
10
 
11
11
  // src/catalog.ts
12
12
  import { z } from "zod";
@@ -89,9 +89,22 @@ set colours, fonts or sizes directly, which is what keeps every deck on-brand.
89
89
  - **Wrap-up:** \`qr\` (send people somewhere), \`contacts\`, closing \`statement\`.
90
90
 
91
91
  ## Charts
92
- \`chart.labels\` are the categories; each series has one value per label. Pie, donut, funnel and
93
- gauge use the first series; gauge needs \`max\`; scatter uses two series as x and y. Use
94
- \`highlight\` to emphasise one category and \`format\`/\`unit\` instead of typing symbols into numbers.
92
+ \`chart.labels\` are the categories; each series has one value per label. Use \`highlight\` to
93
+ emphasise one category and \`format\`/\`unit\` instead of typing symbols into numbers.
94
+ - **Compare:** \`column\`/\`bar\`; \`dumbbell\` (2+ series as dots per row, e.g. last year vs this
95
+ year); \`bullet\` (series 1 actual, series 2 target; \`max\` sets the track).
96
+ - **Part of whole:** \`stacked-*\` (absolute), \`percent-column\`/\`percent-bar\` (each row = 100%),
97
+ \`pie\`/\`donut\` (\u2264 7 slices), \`treemap\` (many categories).
98
+ - **Change:** \`line\`/\`area\`; \`combo\` (series 1 bars, the rest lines; set a series' \`as\` to
99
+ override and \`axis: secondary\` + \`secondaryFormat\` for a second scale, e.g. closings and average
100
+ price); \`waterfall\` (series 1 is signed changes; list running-total rows in \`totals\`).
101
+ - **Single value:** \`gauge\` (one value, needs \`max\`); \`ring\` (up to 4 progress rings, one per label;
102
+ \`max\`, or \`format: percent\` for 100); \`pictogram\` (house icons per row; \`perIcon\` sets the step).
103
+ - **Grid & flow:** \`heatmap\` (labels are rows, series names are columns); \`sankey\` (labels are
104
+ sources, series names are destinations, each cell a flow; a destination that is also a label chains
105
+ stages; no loops).
106
+ - **Other:** \`funnel\` (drop-off, first series), \`scatter\` (two series as x and y), \`radar\`.
107
+ Pie, donut, funnel, gauge, ring, treemap, waterfall and pictogram read the first series only.
95
108
 
96
109
  ## Video & embeds
97
110
  They need internet while presenting. Offline, in print and in PDF they show a poster (or
@@ -130,4 +143,4 @@ export {
130
143
  deckJsonSchema,
131
144
  AUTHORING_GUIDE
132
145
  };
133
- //# sourceMappingURL=chunk-42YDHMNB.js.map
146
+ //# sourceMappingURL=chunk-ORNJ4CTQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["import { z } from 'zod';\nimport { ignoreOverride, zodToJsonSchema } from 'zod-to-json-schema';\nimport {\n LAYOUT_CATEGORIES,\n LAYOUTS,\n getLayout,\n type LayoutCategory,\n type LayoutName,\n} from './layouts/index.js';\nimport { baseSlide, Image, Item } from './schema/common.js';\nimport { DeckSchema } from './schema/deck.js';\n\n/**\n * Item and Image accept a bare string as shorthand (\"Q4 best\" → { text }, \"a.jpg\" → { src }).\n * zod-to-json-schema only sees the object behind the preprocess, so advertise both forms\n * or agents reading the schema think the shorthand in our own examples is invalid.\n */\nconst SHORTHAND = new Map<unknown, string>([\n [(Item._def as { schema: unknown }).schema, 'Shorthand for { text }.'],\n [(Image._def as { schema: unknown }).schema, 'Shorthand for { src }.'],\n]);\n\nfunction toJsonSchema(schema: z.ZodTypeAny, name?: string) {\n return zodToJsonSchema(schema, {\n ...(name ? { name } : {}),\n $refStrategy: 'none',\n override: (def) => {\n const inner = (def as { schema?: unknown }).schema;\n const isEffects = (def as { typeName?: string }).typeName === 'ZodEffects';\n const hint = isEffects ? SHORTHAND.get(inner) : undefined;\n if (!hint) return ignoreOverride;\n const object = zodToJsonSchema(inner as z.ZodTypeAny, {\n $refStrategy: 'none',\n }) as Record<string, unknown>;\n delete object.$schema;\n return {\n anyOf: [{ type: 'string', description: hint }, object],\n ...((def as { description?: string }).description\n ? { description: (def as { description?: string }).description }\n : {}),\n };\n },\n });\n}\n\nexport interface LayoutSummary {\n name: string;\n category: LayoutCategory;\n description: string;\n whenToUse: string;\n}\n\nexport function listLayouts(): LayoutSummary[] {\n return LAYOUTS.map((l) => ({\n name: l.name,\n category: LAYOUT_CATEGORIES[l.name as LayoutName],\n description: l.description,\n whenToUse: l.whenToUse,\n }));\n}\n\n/** Full description of one layout: fields as JSON Schema plus a working example. */\nexport function describeLayout(name: string) {\n const layout = getLayout(name);\n if (!layout) return undefined;\n const schema = z.object({ layout: z.literal(layout.name), ...layout.shape, ...baseSlide });\n return {\n name: layout.name,\n description: layout.description,\n whenToUse: layout.whenToUse,\n defaults: layout.defaults ?? {},\n schema: toJsonSchema(schema),\n example: layout.example,\n };\n}\n\n/** JSON Schema for a whole deck (editor tooling, `$schema` in deck files). */\nexport function deckJsonSchema() {\n return toJsonSchema(DeckSchema, 'ClassicDeck');\n}\n\n/** Authoring guidance shared by the CLI, MCP server and docs. */\nexport 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 — 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 — 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–6 headline figures, optional trend + sparkline), \\`chart\\` (a real chart —\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×2), \\`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. Pie, donut, funnel and\ngauge use the first series; gauge needs \\`max\\`; scatter uses two series as x and y. Use\n\\`highlight\\` to emphasise one category and \\`format\\`/\\`unit\\` instead of typing symbols into numbers.\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 — the \"Open ↗\" 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 · P presenter window\n(current + next slide, notes, timer) · O overview · F fullscreen · \\`?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://…)\\`. 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\\` → 2–5 content slides\n4. \\`qr\\` (where to go next) → \\`contacts\\` → closing \\`statement\\`\n`;\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,SAAS;AAClB,SAAS,gBAAgB,uBAAuB;AAgBhD,IAAM,YAAY,oBAAI,IAAqB;AAAA,EACzC,CAAE,KAAK,KAA6B,QAAQ,yBAAyB;AAAA,EACrE,CAAE,MAAM,KAA6B,QAAQ,wBAAwB;AACvE,CAAC;AAED,SAAS,aAAa,QAAsB,MAAe;AACzD,SAAO,gBAAgB,QAAQ;AAAA,IAC7B,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACvB,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AACjB,YAAM,QAAS,IAA6B;AAC5C,YAAM,YAAa,IAA8B,aAAa;AAC9D,YAAM,OAAO,YAAY,UAAU,IAAI,KAAK,IAAI;AAChD,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,SAAS,gBAAgB,OAAuB;AAAA,QACpD,cAAc;AAAA,MAChB,CAAC;AACD,aAAO,OAAO;AACd,aAAO;AAAA,QACL,OAAO,CAAC,EAAE,MAAM,UAAU,aAAa,KAAK,GAAG,MAAM;AAAA,QACrD,GAAK,IAAiC,cAClC,EAAE,aAAc,IAAiC,YAAY,IAC7D,CAAC;AAAA,MACP;AAAA,IACF;AAAA,EACF,CAAC;AACH;AASO,SAAS,cAA+B;AAC7C,SAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,IACzB,MAAM,EAAE;AAAA,IACR,UAAU,kBAAkB,EAAE,IAAkB;AAAA,IAChD,aAAa,EAAE;AAAA,IACf,WAAW,EAAE;AAAA,EACf,EAAE;AACJ;AAGO,SAAS,eAAe,MAAc;AAC3C,QAAM,SAAS,UAAU,IAAI;AAC7B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,OAAO,IAAI,GAAG,GAAG,OAAO,OAAO,GAAG,UAAU,CAAC;AACzF,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,IAClB,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,QAAQ,aAAa,MAAM;AAAA,IAC3B,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,iBAAiB;AAC/B,SAAO,aAAa,YAAY,aAAa;AAC/C;AAGO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["import { z } from 'zod';\nimport { ignoreOverride, zodToJsonSchema } from 'zod-to-json-schema';\nimport {\n LAYOUT_CATEGORIES,\n LAYOUTS,\n getLayout,\n type LayoutCategory,\n type LayoutName,\n} from './layouts/index.js';\nimport { baseSlide, Image, Item } from './schema/common.js';\nimport { DeckSchema } from './schema/deck.js';\n\n/**\n * Item and Image accept a bare string as shorthand (\"Q4 best\" → { text }, \"a.jpg\" → { src }).\n * zod-to-json-schema only sees the object behind the preprocess, so advertise both forms\n * or agents reading the schema think the shorthand in our own examples is invalid.\n */\nconst SHORTHAND = new Map<unknown, string>([\n [(Item._def as { schema: unknown }).schema, 'Shorthand for { text }.'],\n [(Image._def as { schema: unknown }).schema, 'Shorthand for { src }.'],\n]);\n\nfunction toJsonSchema(schema: z.ZodTypeAny, name?: string) {\n return zodToJsonSchema(schema, {\n ...(name ? { name } : {}),\n $refStrategy: 'none',\n override: (def) => {\n const inner = (def as { schema?: unknown }).schema;\n const isEffects = (def as { typeName?: string }).typeName === 'ZodEffects';\n const hint = isEffects ? SHORTHAND.get(inner) : undefined;\n if (!hint) return ignoreOverride;\n const object = zodToJsonSchema(inner as z.ZodTypeAny, {\n $refStrategy: 'none',\n }) as Record<string, unknown>;\n delete object.$schema;\n return {\n anyOf: [{ type: 'string', description: hint }, object],\n ...((def as { description?: string }).description\n ? { description: (def as { description?: string }).description }\n : {}),\n };\n },\n });\n}\n\nexport interface LayoutSummary {\n name: string;\n category: LayoutCategory;\n description: string;\n whenToUse: string;\n}\n\nexport function listLayouts(): LayoutSummary[] {\n return LAYOUTS.map((l) => ({\n name: l.name,\n category: LAYOUT_CATEGORIES[l.name as LayoutName],\n description: l.description,\n whenToUse: l.whenToUse,\n }));\n}\n\n/** Full description of one layout: fields as JSON Schema plus a working example. */\nexport function describeLayout(name: string) {\n const layout = getLayout(name);\n if (!layout) return undefined;\n const schema = z.object({ layout: z.literal(layout.name), ...layout.shape, ...baseSlide });\n return {\n name: layout.name,\n description: layout.description,\n whenToUse: layout.whenToUse,\n defaults: layout.defaults ?? {},\n schema: toJsonSchema(schema),\n example: layout.example,\n };\n}\n\n/** JSON Schema for a whole deck (editor tooling, `$schema` in deck files). */\nexport function deckJsonSchema() {\n return toJsonSchema(DeckSchema, 'ClassicDeck');\n}\n\n/** Authoring guidance shared by the CLI, MCP server and docs. */\nexport 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 — 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 — 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–6 headline figures, optional trend + sparkline), \\`chart\\` (a real chart —\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×2), \\`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\\` (≤ 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 — the \"Open ↗\" 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 · P presenter window\n(current + next slide, notes, timer) · O overview · F fullscreen · \\`?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://…)\\`. 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\\` → 2–5 content slides\n4. \\`qr\\` (where to go next) → \\`contacts\\` → closing \\`statement\\`\n`;\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,SAAS;AAClB,SAAS,gBAAgB,uBAAuB;AAgBhD,IAAM,YAAY,oBAAI,IAAqB;AAAA,EACzC,CAAE,KAAK,KAA6B,QAAQ,yBAAyB;AAAA,EACrE,CAAE,MAAM,KAA6B,QAAQ,wBAAwB;AACvE,CAAC;AAED,SAAS,aAAa,QAAsB,MAAe;AACzD,SAAO,gBAAgB,QAAQ;AAAA,IAC7B,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACvB,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AACjB,YAAM,QAAS,IAA6B;AAC5C,YAAM,YAAa,IAA8B,aAAa;AAC9D,YAAM,OAAO,YAAY,UAAU,IAAI,KAAK,IAAI;AAChD,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,SAAS,gBAAgB,OAAuB;AAAA,QACpD,cAAc;AAAA,MAChB,CAAC;AACD,aAAO,OAAO;AACd,aAAO;AAAA,QACL,OAAO,CAAC,EAAE,MAAM,UAAU,aAAa,KAAK,GAAG,MAAM;AAAA,QACrD,GAAK,IAAiC,cAClC,EAAE,aAAc,IAAiC,YAAY,IAC7D,CAAC;AAAA,MACP;AAAA,IACF;AAAA,EACF,CAAC;AACH;AASO,SAAS,cAA+B;AAC7C,SAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,IACzB,MAAM,EAAE;AAAA,IACR,UAAU,kBAAkB,EAAE,IAAkB;AAAA,IAChD,aAAa,EAAE;AAAA,IACf,WAAW,EAAE;AAAA,EACf,EAAE;AACJ;AAGO,SAAS,eAAe,MAAc;AAC3C,QAAM,SAAS,UAAU,IAAI;AAC7B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,OAAO,IAAI,GAAG,GAAG,OAAO,OAAO,GAAG,UAAU,CAAC;AACzF,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,IAClB,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,QAAQ,aAAa,MAAM;AAAA,IAC3B,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,iBAAiB;AAC/B,SAAO,aAAa,YAAY,aAAa;AAC/C;AAGO,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  parseDeckSource,
4
4
  renderDeck,
5
5
  stringifyDeck
6
- } from "./chunk-25NZMEQG.js";
6
+ } from "./chunk-HI5ZCAHO.js";
7
7
 
8
8
  // src/node/index.ts
9
9
  import { readFile, stat, writeFile } from "fs/promises";
@@ -181,4 +181,4 @@ export {
181
181
  buildDeckFile,
182
182
  exportDeckFile
183
183
  };
184
- //# sourceMappingURL=chunk-6CYZI27I.js.map
184
+ //# sourceMappingURL=chunk-UKRX7A2V.js.map
package/dist/cli.js CHANGED
@@ -4,19 +4,19 @@ import {
4
4
  deckJsonSchema,
5
5
  describeLayout,
6
6
  listLayouts
7
- } from "./chunk-42YDHMNB.js";
7
+ } from "./chunk-ORNJ4CTQ.js";
8
8
  import {
9
9
  buildDeckFile,
10
10
  exportDeckFile,
11
11
  fileImageResolver,
12
12
  loadDeckFile
13
- } from "./chunk-6CYZI27I.js";
13
+ } from "./chunk-UKRX7A2V.js";
14
14
  import {
15
15
  DeckError,
16
16
  formatIssues,
17
17
  renderDeck,
18
18
  validateDeck
19
- } from "./chunk-25NZMEQG.js";
19
+ } from "./chunk-HI5ZCAHO.js";
20
20
 
21
21
  // src/node/cli.ts
22
22
  import { watch } from "fs";
@@ -130,7 +130,7 @@ ${"".padEnd(12)} Use for: ${l.whenToUse}
130
130
  case "mcp": {
131
131
  let mcp;
132
132
  try {
133
- mcp = await import("./mcp-P3CPIO3H.js");
133
+ mcp = await import("./mcp-MG2E7J3I.js");
134
134
  } catch (e) {
135
135
  if (e.code === "ERR_MODULE_NOT_FOUND") {
136
136
  throw new Error(
@@ -4,25 +4,39 @@ import {
4
4
  BarChart,
5
5
  FunnelChart,
6
6
  GaugeChart,
7
+ HeatmapChart,
7
8
  LineChart,
9
+ PictorialBarChart,
8
10
  PieChart,
9
11
  RadarChart,
10
- ScatterChart
12
+ SankeyChart,
13
+ ScatterChart,
14
+ TreemapChart
11
15
  } from "echarts/charts";
12
- import { GridComponent, LegendComponent, RadarComponent } from "echarts/components";
16
+ import {
17
+ GridComponent,
18
+ LegendComponent,
19
+ RadarComponent,
20
+ VisualMapContinuousComponent
21
+ } from "echarts/components";
13
22
  import * as echarts from "echarts/core";
14
23
  import { SVGRenderer } from "echarts/renderers";
15
24
  echarts.use([
16
25
  BarChart,
17
26
  FunnelChart,
18
27
  GaugeChart,
28
+ HeatmapChart,
19
29
  LineChart,
30
+ PictorialBarChart,
20
31
  PieChart,
21
32
  RadarChart,
33
+ SankeyChart,
22
34
  ScatterChart,
35
+ TreemapChart,
23
36
  GridComponent,
24
37
  LegendComponent,
25
38
  RadarComponent,
39
+ VisualMapContinuousComponent,
26
40
  SVGRenderer
27
41
  ]);
28
42
  echarts.registerTheme("classic-slides", lightTheme);
@@ -47,4 +61,4 @@ function createRenderer() {
47
61
  export {
48
62
  createRenderer
49
63
  };
50
- //# sourceMappingURL=echarts-render-2QVSBZ6V.js.map
64
+ //# sourceMappingURL=echarts-render-GDCT5QAF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/echarts-render.ts"],"sourcesContent":["/** ECharts server-side SVG renderer. Loaded lazily by charts.ts. */\nimport { lightTheme } from '@classic-homes/charts-core/theme';\nimport {\n BarChart,\n FunnelChart,\n GaugeChart,\n HeatmapChart,\n LineChart,\n PictorialBarChart,\n PieChart,\n RadarChart,\n SankeyChart,\n ScatterChart,\n TreemapChart,\n} from 'echarts/charts';\nimport {\n GridComponent,\n LegendComponent,\n RadarComponent,\n VisualMapContinuousComponent,\n} from 'echarts/components';\nimport * as echarts from 'echarts/core';\nimport { SVGRenderer } from 'echarts/renderers';\nimport type { ChartRenderer } from './charts.js';\n\necharts.use([\n BarChart,\n FunnelChart,\n GaugeChart,\n HeatmapChart,\n LineChart,\n PictorialBarChart,\n PieChart,\n RadarChart,\n SankeyChart,\n ScatterChart,\n TreemapChart,\n GridComponent,\n LegendComponent,\n RadarComponent,\n VisualMapContinuousComponent,\n SVGRenderer,\n]);\necharts.registerTheme('classic-slides', lightTheme as unknown as object);\n\nexport function createRenderer(): ChartRenderer {\n return {\n render(option, width, height) {\n const chart = echarts.init(null, 'classic-slides', {\n renderer: 'svg',\n ssr: true,\n width,\n height,\n });\n try {\n chart.setOption(option);\n return chart.renderToSVGString();\n } finally {\n chart.dispose();\n }\n },\n };\n}\n"],"mappings":";AACA,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,YAAY,aAAa;AACzB,SAAS,mBAAmB;AAGpB,YAAI;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACO,sBAAc,kBAAkB,UAA+B;AAEhE,SAAS,iBAAgC;AAC9C,SAAO;AAAA,IACL,OAAO,QAAQ,OAAO,QAAQ;AAC5B,YAAM,QAAgB,aAAK,MAAM,kBAAkB;AAAA,QACjD,UAAU;AAAA,QACV,KAAK;AAAA,QACL;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI;AACF,cAAM,UAAU,MAAM;AACtB,eAAO,MAAM,kBAAkB;AAAA,MACjC,UAAE;AACA,cAAM,QAAQ;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -491,13 +491,13 @@ declare const DeckSettingsSchema: z.ZodObject<{
491
491
  autoAdvance: z.ZodOptional<z.ZodNumber>;
492
492
  loop: z.ZodDefault<z.ZodBoolean>;
493
493
  }, "strict", z.ZodTypeAny, {
494
- logo: "title" | "all" | "none";
494
+ logo: "all" | "title" | "none";
495
495
  slideNumbers: boolean;
496
496
  loop: boolean;
497
497
  confidential?: string | undefined;
498
498
  autoAdvance?: number | undefined;
499
499
  }, {
500
- logo?: "title" | "all" | "none" | undefined;
500
+ logo?: "all" | "title" | "none" | undefined;
501
501
  slideNumbers?: boolean | undefined;
502
502
  confidential?: string | undefined;
503
503
  autoAdvance?: number | undefined;
@@ -598,6 +598,35 @@ interface RenderResult {
598
598
  /** Collects every image src in the deck (any object with a string `src`). */
599
599
  declare function collectImageSources(value: unknown, out?: Set<string>): Set<string>;
600
600
  declare function renderDeck(input: unknown, options?: RenderOptions): Promise<RenderResult>;
601
+ interface SpeakerNotesOptions {
602
+ /** Brand profiles available to `deck.brand.profile` (needed to validate decks that name one). */
603
+ brands?: BrandProfile[];
604
+ /** List slides marked `hidden` too. Default false, matching a presentation. */
605
+ includeHidden?: boolean;
606
+ }
607
+ /** One presented slide's speaker notes. */
608
+ interface SpeakerNote {
609
+ /** 1-based slide number as presented (a walkthrough counts once per stage). */
610
+ number: number;
611
+ /** 0-based index of the deck slide it came from. */
612
+ deckIndex: number;
613
+ slideId?: string;
614
+ /** Stage of a slide that expands into several (a walkthrough), else absent. */
615
+ part?: {
616
+ index: number;
617
+ count: number;
618
+ };
619
+ /** Timing cue. */
620
+ time?: string;
621
+ /** Notes as HTML (paragraphs, bullets, inline marks), escaped; '' when there are none. */
622
+ html: string;
623
+ }
624
+ /**
625
+ * Every presented slide's speaker notes, numbered as `renderDeck` numbers
626
+ * them, for a printed notes handout. Walks the slides the way `renderDeck`
627
+ * does but skips images, QR codes and charts, so nothing is fetched or drawn.
628
+ */
629
+ declare function speakerNotes(input: unknown, options?: SpeakerNotesOptions): SpeakerNote[];
601
630
  /** Pulls the embedded deck source back out of a rendered HTML file. */
602
631
  declare function extractDeckSource(html: string): unknown | undefined;
603
632
 
@@ -638,4 +667,4 @@ declare function inlineDeckImages(input: unknown, resolve: ImageResolver, option
638
667
  */
639
668
  declare function externalizeDeckImages(input: unknown, upload: (dataUri: string) => string | Promise<string>, options?: MapImagesOptions): Promise<unknown>;
640
669
 
641
- export { type ValidationResult as A, type BrandPalette as B, CLASSIC_HOMES_BRAND as C, type Deck as D, checkPalette as E, type FieldMarker as F, collectImageSources as G, externalizeDeckImages as H, type ImageInput as I, extractDeckSource as J, fieldMarkers as K, type LayoutCategory as L, type MapImagesOptions as M, formatIssues as N, formatPath as O, PREVIEW_SIZE as P, getLayout as Q, type RenderOptions as R, type SlideData as S, inlineDeckImages as T, mapDeckImages as U, type ValidateOptions as V, renderDeck as W, resolveBrand as X, validateBrandProfile as Y, validateDeck as Z, BrandPaletteSchema as a, type BrandProfile as b, type BrandProfileInput as c, BrandProfileSchema as d, CLASSIC_HOMES_PALETTE as e, type DeckBrand as f, DeckBrandSchema as g, DeckError as h, type DeckInput as i, DeckSchema as j, type DeckSettings as k, DeckSettingsSchema as l, type ImageResolver as m, type ImageValue as n, type InlineImagesResult as o, type ItemValue as p, LAYOUTS as q, LAYOUT_CATEGORIES as r, LAYOUT_CATEGORY_ORDER as s, type LayoutDef as t, type LayoutIssue as u, type LayoutName as v, type RenderResult as w, type ResolvedBrand as x, type SlideInput as y, SlideSchema as z };
670
+ export { validateBrandProfile as $, type SpeakerNote as A, type BrandPalette as B, CLASSIC_HOMES_BRAND as C, type Deck as D, type SpeakerNotesOptions as E, type FieldMarker as F, type ValidationResult as G, checkPalette as H, type ImageInput as I, collectImageSources as J, externalizeDeckImages as K, type LayoutCategory as L, type MapImagesOptions as M, extractDeckSource as N, fieldMarkers as O, PREVIEW_SIZE as P, formatIssues as Q, type RenderOptions as R, type SlideData as S, formatPath as T, getLayout as U, type ValidateOptions as V, inlineDeckImages as W, mapDeckImages as X, renderDeck as Y, resolveBrand as Z, speakerNotes as _, BrandPaletteSchema as a, validateDeck as a0, type BrandProfile as b, type BrandProfileInput as c, BrandProfileSchema as d, CLASSIC_HOMES_PALETTE as e, type DeckBrand as f, DeckBrandSchema as g, DeckError as h, type DeckInput as i, DeckSchema as j, type DeckSettings as k, DeckSettingsSchema as l, type ImageResolver as m, type ImageValue as n, type InlineImagesResult as o, type ItemValue as p, LAYOUTS as q, LAYOUT_CATEGORIES as r, LAYOUT_CATEGORY_ORDER as s, type LayoutDef as t, type LayoutIssue as u, type LayoutName as v, type RenderResult as w, type ResolvedBrand as x, type SlideInput as y, SlideSchema as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LayoutCategory, B as BrandPalette } from './images-8N30SsCW.js';
2
- export { a as BrandPaletteSchema, b as BrandProfile, c as BrandProfileInput, d as BrandProfileSchema, C as CLASSIC_HOMES_BRAND, e as CLASSIC_HOMES_PALETTE, D as Deck, f as DeckBrand, g as DeckBrandSchema, h as DeckError, i as DeckInput, j as DeckSchema, k as DeckSettings, l as DeckSettingsSchema, F as FieldMarker, I as ImageInput, m as ImageResolver, n as ImageValue, o as InlineImagesResult, p as ItemValue, q as LAYOUTS, r as LAYOUT_CATEGORIES, s as LAYOUT_CATEGORY_ORDER, t as LayoutDef, u as LayoutIssue, v as LayoutName, M as MapImagesOptions, P as PREVIEW_SIZE, R as RenderOptions, w as RenderResult, x as ResolvedBrand, S as SlideData, y as SlideInput, z as SlideSchema, V as ValidateOptions, A as ValidationResult, E as checkPalette, G as collectImageSources, H as externalizeDeckImages, J as extractDeckSource, K as fieldMarkers, N as formatIssues, O as formatPath, Q as getLayout, T as inlineDeckImages, U as mapDeckImages, W as renderDeck, X as resolveBrand, Y as validateBrandProfile, Z as validateDeck } from './images-8N30SsCW.js';
1
+ import { L as LayoutCategory, B as BrandPalette } from './images-rkJZtnjl.js';
2
+ export { a as BrandPaletteSchema, b as BrandProfile, c as BrandProfileInput, d as BrandProfileSchema, C as CLASSIC_HOMES_BRAND, e as CLASSIC_HOMES_PALETTE, D as Deck, f as DeckBrand, g as DeckBrandSchema, h as DeckError, i as DeckInput, j as DeckSchema, k as DeckSettings, l as DeckSettingsSchema, F as FieldMarker, I as ImageInput, m as ImageResolver, n as ImageValue, o as InlineImagesResult, p as ItemValue, q as LAYOUTS, r as LAYOUT_CATEGORIES, s as LAYOUT_CATEGORY_ORDER, t as LayoutDef, u as LayoutIssue, v as LayoutName, M as MapImagesOptions, P as PREVIEW_SIZE, R as RenderOptions, w as RenderResult, x as ResolvedBrand, S as SlideData, y as SlideInput, z as SlideSchema, A as SpeakerNote, E as SpeakerNotesOptions, V as ValidateOptions, G as ValidationResult, H as checkPalette, J as collectImageSources, K as externalizeDeckImages, N as extractDeckSource, O as fieldMarkers, Q as formatIssues, T as formatPath, U as getLayout, W as inlineDeckImages, X as mapDeckImages, Y as renderDeck, Z as resolveBrand, _ as speakerNotes, $ as validateBrandProfile, a0 as validateDeck } from './images-rkJZtnjl.js';
3
3
  import * as zod_to_json_schema from 'zod-to-json-schema';
4
4
  import { z } from 'zod';
5
5
 
@@ -35,7 +35,7 @@ declare function deckJsonSchema(): zod_to_json_schema.JsonSchema7Type & {
35
35
  } | undefined;
36
36
  };
37
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. Pie, donut, funnel and\ngauge use the first series; gauge needs `max`; scatter uses two series as x and y. Use\n`highlight` to emphasise one category and `format`/`unit` instead of typing symbols into numbers.\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";
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
39
 
40
40
  /** Parses deck source text. YAML is a superset of JSON, so both work. */
41
41
  declare function parseDeckSource(text: string): unknown;
@@ -219,20 +219,28 @@ declare function blankSlide(name: string): Record<string, unknown> | undefined;
219
219
  * never load it.
220
220
  */
221
221
 
222
- declare const CHART_TYPES: readonly ["column", "bar", "stacked-column", "stacked-bar", "line", "area", "pie", "donut", "funnel", "gauge", "scatter", "radar"];
222
+ 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"];
223
223
  type ChartType = (typeof CHART_TYPES)[number];
224
+ /** Types that only read the first series. */
225
+ declare const SINGLE_SERIES_CHARTS: readonly ChartType[];
224
226
  declare const ChartSpec: z.ZodObject<{
225
- type: z.ZodEnum<["column", "bar", "stacked-column", "stacked-bar", "line", "area", "pie", "donut", "funnel", "gauge", "scatter", "radar"]>;
227
+ 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"]>;
226
228
  labels: z.ZodArray<z.ZodString, "many">;
227
229
  series: z.ZodArray<z.ZodObject<{
228
230
  name: z.ZodString;
229
231
  values: z.ZodArray<z.ZodNumber, "many">;
232
+ as: z.ZodOptional<z.ZodEnum<["bar", "line"]>>;
233
+ axis: z.ZodOptional<z.ZodEnum<["primary", "secondary"]>>;
230
234
  }, "strip", z.ZodTypeAny, {
231
235
  values: number[];
232
236
  name: string;
237
+ as?: "bar" | "line" | undefined;
238
+ axis?: "primary" | "secondary" | undefined;
233
239
  }, {
234
240
  values: number[];
235
241
  name: string;
242
+ as?: "bar" | "line" | undefined;
243
+ axis?: "primary" | "secondary" | undefined;
236
244
  }>, "many">;
237
245
  format: z.ZodDefault<z.ZodEnum<["number", "percent", "currency", "compact"]>>;
238
246
  decimals: z.ZodDefault<z.ZodNumber>;
@@ -241,12 +249,17 @@ declare const ChartSpec: z.ZodObject<{
241
249
  labelsOnData: z.ZodDefault<z.ZodBoolean>;
242
250
  highlight: z.ZodOptional<z.ZodString>;
243
251
  max: z.ZodOptional<z.ZodNumber>;
252
+ secondaryFormat: z.ZodOptional<z.ZodEnum<["number", "percent", "currency", "compact"]>>;
253
+ totals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
254
+ perIcon: z.ZodOptional<z.ZodNumber>;
244
255
  }, "strict", z.ZodTypeAny, {
245
- type: "column" | "bar" | "stacked-column" | "stacked-bar" | "line" | "area" | "pie" | "donut" | "funnel" | "gauge" | "scatter" | "radar";
256
+ 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";
246
257
  labels: string[];
247
258
  series: {
248
259
  values: number[];
249
260
  name: string;
261
+ as?: "bar" | "line" | undefined;
262
+ axis?: "primary" | "secondary" | undefined;
250
263
  }[];
251
264
  format: "number" | "percent" | "currency" | "compact";
252
265
  decimals: number;
@@ -255,12 +268,17 @@ declare const ChartSpec: z.ZodObject<{
255
268
  unit?: string | undefined;
256
269
  highlight?: string | undefined;
257
270
  max?: number | undefined;
271
+ secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
272
+ totals?: string[] | undefined;
273
+ perIcon?: number | undefined;
258
274
  }, {
259
- type: "column" | "bar" | "stacked-column" | "stacked-bar" | "line" | "area" | "pie" | "donut" | "funnel" | "gauge" | "scatter" | "radar";
275
+ 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";
260
276
  labels: string[];
261
277
  series: {
262
278
  values: number[];
263
279
  name: string;
280
+ as?: "bar" | "line" | undefined;
281
+ axis?: "primary" | "secondary" | undefined;
264
282
  }[];
265
283
  format?: "number" | "percent" | "currency" | "compact" | undefined;
266
284
  decimals?: number | undefined;
@@ -269,6 +287,9 @@ declare const ChartSpec: z.ZodObject<{
269
287
  labelsOnData?: boolean | undefined;
270
288
  highlight?: string | undefined;
271
289
  max?: number | undefined;
290
+ secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
291
+ totals?: string[] | undefined;
292
+ perIcon?: number | undefined;
272
293
  }>;
273
294
  type ChartSpecInput = z.input<typeof ChartSpec>;
274
295
  type ChartSpecValue = z.output<typeof ChartSpec>;
@@ -308,4 +329,4 @@ declare function videoEmbedUrl(ref: VideoRef, o: EmbedOptions): string;
308
329
  /** CSP source for the inline presenter runtime: script-src '<hash>'. */
309
330
  declare const RUNTIME_SCRIPT_HASH = "sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=";
310
331
 
311
- export { AUTHORING_GUIDE, BrandPalette, CHART_TYPES, ChartSpec, type ChartSpecInput, type ChartSpecValue, type ChartType, type EditableDeck, type EditorField, LOGO_SVG, LayoutCategory, type LayoutEditorInfo, PALETTE, PLACEHOLDER_IMAGE, RUNTIME_SCRIPT_HASH, type SlideRef, type VideoRef, addSlide, allLayoutEditorInfo, blankSlide, chartOption, commonSlideFields, deckEditorFields, deckJsonSchema, describeChart, describeLayout, ensureSlideIds, escapeHtml, findSlide, formatValue, inline, isPlaceholderImage, layoutEditorInfo, layoutGalleryDeck, listLayouts, logoLockup, moveSlide, newSlideId, outline, parseDeckSource, parseVideoUrl, prose, removeSlide, renderChartSvg, slideKey, stringifyDeck, updateSlide, videoEmbedUrl };
332
+ export { AUTHORING_GUIDE, BrandPalette, CHART_TYPES, ChartSpec, type ChartSpecInput, type ChartSpecValue, type ChartType, type EditableDeck, type EditorField, LOGO_SVG, LayoutCategory, type LayoutEditorInfo, PALETTE, PLACEHOLDER_IMAGE, RUNTIME_SCRIPT_HASH, SINGLE_SERIES_CHARTS, type SlideRef, type VideoRef, addSlide, allLayoutEditorInfo, blankSlide, chartOption, commonSlideFields, deckEditorFields, deckJsonSchema, describeChart, describeLayout, ensureSlideIds, escapeHtml, findSlide, formatValue, inline, isPlaceholderImage, layoutEditorInfo, layoutGalleryDeck, listLayouts, logoLockup, moveSlide, newSlideId, outline, parseDeckSource, parseVideoUrl, prose, removeSlide, renderChartSvg, slideKey, stringifyDeck, updateSlide, videoEmbedUrl };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  deckJsonSchema,
4
4
  describeLayout,
5
5
  listLayouts
6
- } from "./chunk-42YDHMNB.js";
6
+ } from "./chunk-ORNJ4CTQ.js";
7
7
  import {
8
8
  BrandPaletteSchema,
9
9
  BrandProfileSchema,
@@ -22,6 +22,7 @@ import {
22
22
  PALETTE,
23
23
  PLACEHOLDER_IMAGE,
24
24
  PREVIEW_SIZE,
25
+ SINGLE_SERIES_CHARTS,
25
26
  SlideSchema,
26
27
  addSlide,
27
28
  allLayoutEditorInfo,
@@ -60,12 +61,13 @@ import {
60
61
  renderDeck,
61
62
  resolveBrand,
62
63
  slideKey,
64
+ speakerNotes,
63
65
  stringifyDeck,
64
66
  updateSlide,
65
67
  validateBrandProfile,
66
68
  validateDeck,
67
69
  videoEmbedUrl
68
- } from "./chunk-25NZMEQG.js";
70
+ } from "./chunk-HI5ZCAHO.js";
69
71
 
70
72
  // src/generated/runtime-hash.ts
71
73
  var RUNTIME_SCRIPT_HASH = "sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=";
@@ -89,6 +91,7 @@ export {
89
91
  PLACEHOLDER_IMAGE,
90
92
  PREVIEW_SIZE,
91
93
  RUNTIME_SCRIPT_HASH,
94
+ SINGLE_SERIES_CHARTS,
92
95
  SlideSchema,
93
96
  addSlide,
94
97
  allLayoutEditorInfo,
@@ -130,6 +133,7 @@ export {
130
133
  renderDeck,
131
134
  resolveBrand,
132
135
  slideKey,
136
+ speakerNotes,
133
137
  stringifyDeck,
134
138
  updateSlide,
135
139
  validateBrandProfile,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/runtime-hash.ts"],"sourcesContent":["// Generated by scripts/embed-fonts.ts. Do not edit.\n/** CSP source for the inline presenter runtime: script-src '<hash>'. */\nexport const RUNTIME_SCRIPT_HASH = 'sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,sBAAsB;","names":[]}
1
+ {"version":3,"sources":["../src/generated/runtime-hash.ts"],"sourcesContent":["// Generated by scripts/embed-fonts.ts. Do not edit.\n/** CSP source for the inline presenter runtime: script-src '<hash>'. */\nexport const RUNTIME_SCRIPT_HASH = 'sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,sBAAsB;","names":[]}
@@ -2,13 +2,13 @@ import {
2
2
  AUTHORING_GUIDE,
3
3
  describeLayout,
4
4
  listLayouts
5
- } from "./chunk-42YDHMNB.js";
5
+ } from "./chunk-ORNJ4CTQ.js";
6
6
  import {
7
7
  buildDeckFile,
8
8
  confinedPath,
9
9
  loadDeckFile,
10
10
  saveDeckFile
11
- } from "./chunk-6CYZI27I.js";
11
+ } from "./chunk-UKRX7A2V.js";
12
12
  import {
13
13
  addSlide,
14
14
  formatIssues,
@@ -18,7 +18,7 @@ import {
18
18
  stringifyDeck,
19
19
  updateSlide,
20
20
  validateDeck
21
- } from "./chunk-25NZMEQG.js";
21
+ } from "./chunk-HI5ZCAHO.js";
22
22
 
23
23
  // src/node/mcp.ts
24
24
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -320,4 +320,4 @@ export {
320
320
  createSlidesMcpServer,
321
321
  runStdio
322
322
  };
323
- //# sourceMappingURL=mcp-P3CPIO3H.js.map
323
+ //# sourceMappingURL=mcp-MG2E7J3I.js.map
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RenderOptions, w as RenderResult, o as InlineImagesResult, m as ImageResolver } from './images-8N30SsCW.js';
2
- export { W as renderDeck } from './images-8N30SsCW.js';
1
+ import { R as RenderOptions, w as RenderResult, o as InlineImagesResult, m as ImageResolver } from './images-rkJZtnjl.js';
2
+ export { Y as renderDeck } from './images-rkJZtnjl.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
@@ -5,10 +5,10 @@ import {
5
5
  fileImageResolver,
6
6
  loadDeckFile,
7
7
  saveDeckFile
8
- } from "./chunk-6CYZI27I.js";
8
+ } from "./chunk-UKRX7A2V.js";
9
9
  import {
10
10
  renderDeck
11
- } from "./chunk-25NZMEQG.js";
11
+ } from "./chunk-HI5ZCAHO.js";
12
12
  export {
13
13
  IMAGE_WARN_BYTES,
14
14
  buildDeckFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/slides",
3
- "version": "0.5.12",
3
+ "version": "0.7.0",
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",