@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/dist/index.d.ts CHANGED
@@ -1,301 +1,17 @@
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
- import * as zod_to_json_schema from 'zod-to-json-schema';
4
- import { z } from 'zod';
1
+ import { C as ChartSpecValue, B as BrandPalette, a as ChartSvgRenderer } from './images-C90NCxYd.js';
2
+ export { 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, 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, A as LayoutCategory, 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, a9 as resolveBrand, aa as speakerNotes, ab as validateBrandProfile, ac as validateDeck } from './images-C90NCxYd.js';
3
+ export { AUTHORING_GUIDE, EditableDeck, EditorField, LOGO_SVG, LayoutEditorInfo, PALETTE, PLACEHOLDER_IMAGE, RUNTIME_SCRIPT_HASH, SlideRef, 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 } from './core.js';
4
+ export { r as renderDeck } from './render-default-B-QkReoS.js';
5
+ import 'zod';
6
+ import 'zod-to-json-schema';
5
7
 
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
8
  /**
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.
9
+ * The built-in chart renderer: ECharts server-side SVG, in process. ECharts
10
+ * is imported lazily (echarts-render.ts) so decks without charts never load
11
+ * it. Only the main entry reaches this module; `/core` callers inject their
12
+ * own `ChartSvgRenderer`.
112
13
  */
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
14
 
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
- /**
213
- * Chart specs for the `chart` layout. Authors describe data, never styling;
214
- * this module turns a spec into an ECharts option themed from the brand, and
215
- * renders it to static SVG (server-side rendering, no runtime JS) so charts
216
- * work offline, in print, in Workers and in the browser alike.
217
- *
218
- * ECharts is imported lazily (echarts-render.ts) so decks without charts
219
- * never load it.
220
- */
221
-
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
- type ChartType = (typeof CHART_TYPES)[number];
224
- /** Types that only read the first series. */
225
- declare const SINGLE_SERIES_CHARTS: readonly ChartType[];
226
- declare const ChartSpec: z.ZodObject<{
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"]>;
228
- labels: z.ZodArray<z.ZodString, "many">;
229
- series: z.ZodArray<z.ZodObject<{
230
- name: z.ZodString;
231
- values: z.ZodArray<z.ZodNumber, "many">;
232
- as: z.ZodOptional<z.ZodEnum<["bar", "line"]>>;
233
- axis: z.ZodOptional<z.ZodEnum<["primary", "secondary"]>>;
234
- }, "strip", z.ZodTypeAny, {
235
- values: number[];
236
- name: string;
237
- as?: "bar" | "line" | undefined;
238
- axis?: "primary" | "secondary" | undefined;
239
- }, {
240
- values: number[];
241
- name: string;
242
- as?: "bar" | "line" | undefined;
243
- axis?: "primary" | "secondary" | undefined;
244
- }>, "many">;
245
- format: z.ZodDefault<z.ZodEnum<["number", "percent", "currency", "compact"]>>;
246
- decimals: z.ZodDefault<z.ZodNumber>;
247
- unit: z.ZodOptional<z.ZodString>;
248
- legend: z.ZodDefault<z.ZodEnum<["auto", "show", "hide"]>>;
249
- labelsOnData: z.ZodDefault<z.ZodBoolean>;
250
- highlight: z.ZodOptional<z.ZodString>;
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>;
255
- }, "strict", z.ZodTypeAny, {
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";
257
- labels: string[];
258
- series: {
259
- values: number[];
260
- name: string;
261
- as?: "bar" | "line" | undefined;
262
- axis?: "primary" | "secondary" | undefined;
263
- }[];
264
- format: "number" | "percent" | "currency" | "compact";
265
- decimals: number;
266
- legend: "auto" | "show" | "hide";
267
- labelsOnData: boolean;
268
- unit?: string | undefined;
269
- highlight?: string | undefined;
270
- max?: number | undefined;
271
- secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
272
- totals?: string[] | undefined;
273
- perIcon?: number | undefined;
274
- }, {
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";
276
- labels: string[];
277
- series: {
278
- values: number[];
279
- name: string;
280
- as?: "bar" | "line" | undefined;
281
- axis?: "primary" | "secondary" | undefined;
282
- }[];
283
- format?: "number" | "percent" | "currency" | "compact" | undefined;
284
- decimals?: number | undefined;
285
- unit?: string | undefined;
286
- legend?: "auto" | "show" | "hide" | undefined;
287
- labelsOnData?: boolean | undefined;
288
- highlight?: string | undefined;
289
- max?: number | undefined;
290
- secondaryFormat?: "number" | "percent" | "currency" | "compact" | undefined;
291
- totals?: string[] | undefined;
292
- perIcon?: number | undefined;
293
- }>;
294
- type ChartSpecInput = z.input<typeof ChartSpec>;
295
- type ChartSpecValue = z.output<typeof ChartSpec>;
296
- declare function formatValue(v: number, spec: Pick<ChartSpecValue, 'format' | 'decimals' | 'unit'>): string;
297
- /** Builds the ECharts option for a spec. Pure; exported for tests and custom renderers. */
298
- declare function chartOption(spec: ChartSpecValue, palette?: BrandPalette): Record<string, unknown>;
299
15
  /** Renders one chart spec to an accessible, responsive SVG string. */
300
16
  declare function renderChartSvg(spec: ChartSpecValue, opts?: {
301
17
  width?: number;
@@ -303,30 +19,7 @@ declare function renderChartSvg(spec: ChartSpecValue, opts?: {
303
19
  palette?: BrandPalette;
304
20
  title?: string;
305
21
  }): Promise<string>;
306
- /** Plain-language summary for screen readers. */
307
- declare function describeChart(spec: ChartSpecValue): string;
308
-
309
- /** YouTube / Vimeo URL parsing for the video layout. */
310
- interface VideoRef {
311
- provider: 'youtube' | 'vimeo';
312
- id: string;
313
- /** Canonical watch URL (used for QR codes and "open" links). */
314
- watchUrl: string;
315
- /** Poster image URL, when the provider exposes a predictable one. */
316
- poster?: string;
317
- }
318
- declare function parseVideoUrl(url: string): VideoRef | undefined;
319
- interface EmbedOptions {
320
- start?: number;
321
- end?: number;
322
- muted?: boolean;
323
- captions?: boolean;
324
- autoplay?: boolean;
325
- }
326
- /** Privacy-friendly player URL (youtube-nocookie, Vimeo with dnt). */
327
- declare function videoEmbedUrl(ref: VideoRef, o: EmbedOptions): string;
328
-
329
- /** CSP source for the inline presenter runtime: script-src '<hash>'. */
330
- declare const RUNTIME_SCRIPT_HASH = "sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=";
22
+ /** `ChartSvgRenderer` over `renderChartSvg`: the default for `renderDeck`. */
23
+ declare const renderChartSvgs: ChartSvgRenderer;
331
24
 
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 };
25
+ export { BrandPalette, ChartSpecValue, ChartSvgRenderer, renderChartSvg, renderChartSvgs };
package/dist/index.js CHANGED
@@ -1,9 +1,17 @@
1
+ import {
2
+ RUNTIME_SCRIPT_HASH
3
+ } from "./chunk-4NSKDQ5V.js";
1
4
  import {
2
5
  AUTHORING_GUIDE,
3
6
  deckJsonSchema,
4
7
  describeLayout,
5
8
  listLayouts
6
- } from "./chunk-ORNJ4CTQ.js";
9
+ } from "./chunk-QOKQWHHD.js";
10
+ import {
11
+ renderChartSvg,
12
+ renderChartSvgs,
13
+ renderDeck
14
+ } from "./chunk-6ARF37DO.js";
7
15
  import {
8
16
  BrandPaletteSchema,
9
17
  BrandProfileSchema,
@@ -39,6 +47,7 @@ import {
39
47
  extractDeckSource,
40
48
  fieldMarkers,
41
49
  findSlide,
50
+ finishChartSvg,
42
51
  formatIssues,
43
52
  formatPath,
44
53
  formatValue,
@@ -57,8 +66,6 @@ import {
57
66
  parseVideoUrl,
58
67
  prose,
59
68
  removeSlide,
60
- renderChartSvg,
61
- renderDeck,
62
69
  resolveBrand,
63
70
  slideKey,
64
71
  speakerNotes,
@@ -67,10 +74,7 @@ import {
67
74
  validateBrandProfile,
68
75
  validateDeck,
69
76
  videoEmbedUrl
70
- } from "./chunk-HI5ZCAHO.js";
71
-
72
- // src/generated/runtime-hash.ts
73
- var RUNTIME_SCRIPT_HASH = "sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=";
77
+ } from "./chunk-MCG5BFJN.js";
74
78
  export {
75
79
  AUTHORING_GUIDE,
76
80
  BrandPaletteSchema,
@@ -110,6 +114,7 @@ export {
110
114
  extractDeckSource,
111
115
  fieldMarkers,
112
116
  findSlide,
117
+ finishChartSvg,
113
118
  formatIssues,
114
119
  formatPath,
115
120
  formatValue,
@@ -130,6 +135,7 @@ export {
130
135
  prose,
131
136
  removeSlide,
132
137
  renderChartSvg,
138
+ renderChartSvgs,
133
139
  renderDeck,
134
140
  resolveBrand,
135
141
  slideKey,
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":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -2,13 +2,14 @@ import {
2
2
  AUTHORING_GUIDE,
3
3
  describeLayout,
4
4
  listLayouts
5
- } from "./chunk-ORNJ4CTQ.js";
5
+ } from "./chunk-QOKQWHHD.js";
6
6
  import {
7
7
  buildDeckFile,
8
8
  confinedPath,
9
9
  loadDeckFile,
10
10
  saveDeckFile
11
- } from "./chunk-UKRX7A2V.js";
11
+ } from "./chunk-4LBQOMMI.js";
12
+ import "./chunk-6ARF37DO.js";
12
13
  import {
13
14
  addSlide,
14
15
  formatIssues,
@@ -18,7 +19,7 @@ import {
18
19
  stringifyDeck,
19
20
  updateSlide,
20
21
  validateDeck
21
- } from "./chunk-HI5ZCAHO.js";
22
+ } from "./chunk-MCG5BFJN.js";
22
23
 
23
24
  // src/node/mcp.ts
24
25
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -320,4 +321,4 @@ export {
320
321
  createSlidesMcpServer,
321
322
  runStdio
322
323
  };
323
- //# sourceMappingURL=mcp-MG2E7J3I.js.map
324
+ //# sourceMappingURL=mcp-ZBYNCCHN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/node/mcp.ts"],"sourcesContent":["/**\n * Local MCP server (stdio) for building decks on disk with an AI agent.\n *\n * Tools work slide-by-slide on a YAML deck file and validate before every\n * save, so an agent can never leave a deck in a broken state. File access is\n * confined to the root directory the server was started in.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { access } from 'node:fs/promises';\nimport { relative, resolve, sep } from 'node:path';\nimport { z } from 'zod';\nimport { confinedPath } from './paths.js';\nimport { AUTHORING_GUIDE, describeLayout, listLayouts } from '../catalog.js';\nimport {\n addSlide,\n moveSlide,\n outline,\n removeSlide,\n updateSlide,\n type EditableDeck,\n} from '../edit.js';\nimport { stringifyDeck } from '../parse.js';\nimport { formatIssues, validateDeck } from '../validate.js';\nimport { buildDeckFile, loadDeckFile, saveDeckFile } from './index.js';\n\nconst VERSION = '0.1.0';\n\ntype ToolResult = { content: { type: 'text'; text: string }[]; isError?: boolean };\nconst text = (t: string, isError = false): ToolResult => ({\n content: [{ type: 'text', text: t }],\n ...(isError ? { isError } : {}),\n});\n\nexport function createSlidesMcpServer(rootDir = process.cwd()): McpServer {\n const root = resolve(rootDir);\n const server = new McpServer({ name: 'classic-slides', version: VERSION });\n\n function safePath(p: string): string {\n const abs = resolve(root, p);\n if (abs !== root && !abs.startsWith(root + sep))\n throw new Error(`Path \"${p}\" is outside the deck root ${root}.`);\n if (!/\\.(ya?ml|json)$/i.test(abs))\n throw new Error('Deck files must end in .yaml, .yml or .json.');\n return confinedPath(root, abs);\n }\n\n async function load(p: string): Promise<{ abs: string; deck: EditableDeck }> {\n const abs = safePath(p);\n const deck = (await loadDeckFile(abs)) as EditableDeck;\n if (!deck || !Array.isArray(deck.slides))\n throw new Error(`${p} is not a deck file (no slides list).`);\n return { abs, deck };\n }\n\n /** Validates, then saves only when there are no errors. */\n async function commit(abs: string, deck: EditableDeck, done: string): Promise<ToolResult> {\n const v = validateDeck(deck);\n if (!v.ok) {\n return text(\n `Not saved — the change would make the deck invalid:\\n${formatIssues(v.issues)}\\n\\nFix the fields and try again.`,\n true\n );\n }\n await saveDeckFile(confinedPath(root, abs), deck);\n const warnings = v.issues.filter((i) => i.level === 'warning');\n return text(\n `${done}\\n\\nOutline:\\n${outline(deck).join('\\n')}${warnings.length ? `\\n\\nWarnings (saved anyway):\\n${formatIssues(warnings)}` : ''}`\n );\n }\n\n const run =\n <A>(fn: (args: A) => Promise<ToolResult>) =>\n async (args: A): Promise<ToolResult> => {\n try {\n return await fn(args);\n } catch (e) {\n return text(e instanceof Error ? e.message : String(e), true);\n }\n };\n\n const Path = z.string().describe(`Deck file path, relative to ${root}.`);\n const Ref = z\n .union([z.string(), z.number().int().positive()])\n .describe('Slide id, or 1-based slide number.');\n const SlideObj = z\n .record(z.unknown())\n .describe('A slide object: { layout, ...fields }. See get_layout for fields.');\n\n server.tool(\n 'list_layouts',\n 'List every slide layout with what it is for. Start here.',\n {},\n async () =>\n text(\n listLayouts()\n .map((l) => `- **${l.name}** — ${l.description}\\n Use for: ${l.whenToUse}`)\n .join('\\n')\n )\n );\n\n server.tool(\n 'get_layout',\n 'Full field reference (JSON Schema) and a working example for one layout.',\n { name: z.string() },\n run(async ({ name }) => {\n const d = describeLayout(name);\n if (!d) return text(`Unknown layout \"${name}\". Call list_layouts.`, true);\n return text(JSON.stringify(d, null, 2));\n })\n );\n\n server.tool(\n 'get_authoring_guide',\n 'Brand and writing rules for Classic Homes decks. Read before writing slides.',\n {},\n async () => text(AUTHORING_GUIDE)\n );\n\n server.tool(\n 'create_deck',\n 'Create a new deck file. Optionally include the first slides.',\n {\n path: Path,\n title: z.string(),\n description: z.string().optional(),\n journeySteps: z\n .array(z.string())\n .optional()\n .describe('Numbered agenda steps; enables journey features.'),\n slides: z.array(SlideObj).optional(),\n overwrite: z.boolean().default(false),\n },\n run(async ({ path, title, description, journeySteps, slides, overwrite }) => {\n const abs = safePath(path);\n if (!overwrite) {\n const exists = await access(abs).then(\n () => true,\n () => false\n );\n if (exists)\n return text(\n `${path} already exists. Use read_deck and edit it, or pass overwrite: true.`,\n true\n );\n }\n const deck: EditableDeck = {\n title,\n ...(description ? { description } : {}),\n ...(journeySteps?.length ? { journey: { steps: journeySteps } } : {}),\n slides: slides?.length ? slides : [{ layout: 'title', title }],\n };\n return commit(abs, deck, `Created ${path}.`);\n })\n );\n\n server.tool(\n 'read_deck',\n 'Read a deck: outline plus the full YAML source.',\n { path: Path },\n run(async ({ path }) => {\n const { deck } = await load(path);\n return text(\n `Outline:\\n${outline(deck).join('\\n')}\\n\\nSource:\\n\\`\\`\\`yaml\\n${stringifyDeck(deck)}\\`\\`\\``\n );\n })\n );\n\n server.tool(\n 'update_deck',\n 'Change deck-level fields (title, description, author, date, journey). Set a field to null to remove it.',\n { path: Path, fields: z.record(z.unknown()) },\n run(async ({ path, fields }) => {\n const { abs, deck } = await load(path);\n for (const [k, v] of Object.entries(fields)) {\n if (k === 'slides') return text('Use the slide tools to change slides.', true);\n if (v === null) delete deck[k];\n else deck[k] = v;\n }\n return commit(abs, deck, 'Deck fields updated.');\n })\n );\n\n server.tool(\n 'add_slide',\n 'Insert a slide. position is 1-based; omit to append.',\n { path: Path, slide: SlideObj, position: z.number().int().positive().optional() },\n run(async ({ path, slide, position }) => {\n const { abs, deck } = await load(path);\n const at = addSlide(deck, slide, position);\n return commit(abs, deck, `Added slide ${at}.`);\n })\n );\n\n server.tool(\n 'add_slides',\n 'Append several slides at once (e.g. a whole section). Saved only if all are valid.',\n { path: Path, slides: z.array(SlideObj).min(1) },\n run(async ({ path, slides }) => {\n const { abs, deck } = await load(path);\n slides.forEach((s) => addSlide(deck, s));\n return commit(abs, deck, `Added ${slides.length} slides.`);\n })\n );\n\n server.tool(\n 'update_slide',\n 'Merge fields into a slide. Set a field to null to remove it. Changing layout replaces the slide.',\n { path: Path, slide: Ref, fields: z.record(z.unknown()) },\n run(async ({ path, slide, fields }) => {\n const { abs, deck } = await load(path);\n const at = updateSlide(deck, slide, fields);\n return commit(abs, deck, `Updated slide ${at}.`);\n })\n );\n\n server.tool(\n 'move_slide',\n 'Move a slide to a new 1-based position.',\n { path: Path, slide: Ref, to: z.number().int().positive() },\n run(async ({ path, slide, to }) => {\n const { abs, deck } = await load(path);\n const at = moveSlide(deck, slide, to);\n return commit(abs, deck, `Moved slide to ${at}.`);\n })\n );\n\n server.tool(\n 'delete_slide',\n 'Delete a slide.',\n { path: Path, slide: Ref },\n run(async ({ path, slide }) => {\n const { abs, deck } = await load(path);\n const removed = removeSlide(deck, slide);\n return commit(abs, deck, `Deleted [${removed.layout}] slide.`);\n })\n );\n\n server.tool(\n 'validate_deck',\n 'Check a deck for errors and crowded slides. Fix every error and most warnings before building.',\n { path: Path },\n run(async ({ path }) => {\n const { deck } = await load(path);\n const v = validateDeck(deck);\n if (!v.issues.length) return text('Deck is valid with no warnings.');\n return text(\n `${v.ok ? 'Valid, with warnings' : 'Invalid'}:\\n${formatIssues(v.issues)}`,\n !v.ok\n );\n })\n );\n\n server.tool(\n 'build_deck',\n 'Render the deck to one self-contained HTML file (images and fonts inlined). Returns the output path.',\n {\n path: Path,\n out: z.string().optional().describe('Output .html path; defaults next to the deck.'),\n },\n run(async ({ path, out }) => {\n const abs = safePath(path);\n let outAbs: string | undefined;\n if (out) {\n outAbs = resolve(root, out);\n if (!outAbs.startsWith(root + sep) || !outAbs.endsWith('.html'))\n throw new Error('out must be an .html path inside the deck root.');\n }\n const warnings: string[] = [];\n const r = await buildDeckFile(abs, {\n out: outAbs,\n rootDir: root,\n onWarning: (w) => warnings.push(w),\n });\n return text(\n `Built ${relative(root, r.out)} — ${r.slideCount} slides, ${(r.bytes / 1024 / 1024).toFixed(2)} MB. Open it in a browser to present (N = notes, F = fullscreen).${\n warnings.length ? `\\n\\nImage warnings:\\n- ${warnings.join('\\n- ')}` : ''\n }${r.warnings.length ? `\\n\\nContent warnings:\\n${formatIssues(r.warnings)}` : ''}`\n );\n })\n );\n\n server.resource(\n 'authoring-guide',\n 'slides://guide',\n { mimeType: 'text/markdown' },\n async (uri) => ({\n contents: [{ uri: uri.href, mimeType: 'text/markdown', text: AUTHORING_GUIDE }],\n })\n );\n\n server.prompt(\n 'build_deck_from_outline',\n 'Turn a topic and rough outline into a finished, validated deck.',\n {\n path: z.string().describe('Where to save the deck, e.g. decks/closing-day.yaml'),\n topic: z.string(),\n audience: z.string(),\n outline: z.string().optional().describe('Bullet outline, notes, or pasted source material.'),\n },\n ({ path, topic, audience, outline: src }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a Classic Homes presentation.\n\nTopic: ${topic}\nAudience: ${audience}\nSave to: ${path}\n${src ? `\\nSource material:\\n${src}\\n` : ''}\nSteps:\n1. Call get_authoring_guide and list_layouts. Call get_layout for each layout you plan to use.\n2. Plan the slide list (one idea per slide) before writing.\n3. create_deck, then add_slides one section at a time. Put detail in each slide's notes.\n4. validate_deck and fix every error and crowding warning.\n5. build_deck and report the output path with a one-line summary per section.`,\n },\n },\n ],\n })\n );\n\n return server;\n}\n\nexport async function runStdio(rootDir?: string): Promise<void> {\n const server = createSlidesMcpServer(rootDir);\n await server.connect(new StdioServerTransport());\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/node/mcp.ts"],"sourcesContent":["/**\n * Local MCP server (stdio) for building decks on disk with an AI agent.\n *\n * Tools work slide-by-slide on a YAML deck file and validate before every\n * save, so an agent can never leave a deck in a broken state. File access is\n * confined to the root directory the server was started in.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { access } from 'node:fs/promises';\nimport { relative, resolve, sep } from 'node:path';\nimport { z } from 'zod';\nimport { confinedPath } from './paths.js';\nimport { AUTHORING_GUIDE, describeLayout, listLayouts } from '../catalog.js';\nimport {\n addSlide,\n moveSlide,\n outline,\n removeSlide,\n updateSlide,\n type EditableDeck,\n} from '../edit.js';\nimport { stringifyDeck } from '../parse.js';\nimport { formatIssues, validateDeck } from '../validate.js';\nimport { buildDeckFile, loadDeckFile, saveDeckFile } from './index.js';\n\nconst VERSION = '0.1.0';\n\ntype ToolResult = { content: { type: 'text'; text: string }[]; isError?: boolean };\nconst text = (t: string, isError = false): ToolResult => ({\n content: [{ type: 'text', text: t }],\n ...(isError ? { isError } : {}),\n});\n\nexport function createSlidesMcpServer(rootDir = process.cwd()): McpServer {\n const root = resolve(rootDir);\n const server = new McpServer({ name: 'classic-slides', version: VERSION });\n\n function safePath(p: string): string {\n const abs = resolve(root, p);\n if (abs !== root && !abs.startsWith(root + sep))\n throw new Error(`Path \"${p}\" is outside the deck root ${root}.`);\n if (!/\\.(ya?ml|json)$/i.test(abs))\n throw new Error('Deck files must end in .yaml, .yml or .json.');\n return confinedPath(root, abs);\n }\n\n async function load(p: string): Promise<{ abs: string; deck: EditableDeck }> {\n const abs = safePath(p);\n const deck = (await loadDeckFile(abs)) as EditableDeck;\n if (!deck || !Array.isArray(deck.slides))\n throw new Error(`${p} is not a deck file (no slides list).`);\n return { abs, deck };\n }\n\n /** Validates, then saves only when there are no errors. */\n async function commit(abs: string, deck: EditableDeck, done: string): Promise<ToolResult> {\n const v = validateDeck(deck);\n if (!v.ok) {\n return text(\n `Not saved — the change would make the deck invalid:\\n${formatIssues(v.issues)}\\n\\nFix the fields and try again.`,\n true\n );\n }\n await saveDeckFile(confinedPath(root, abs), deck);\n const warnings = v.issues.filter((i) => i.level === 'warning');\n return text(\n `${done}\\n\\nOutline:\\n${outline(deck).join('\\n')}${warnings.length ? `\\n\\nWarnings (saved anyway):\\n${formatIssues(warnings)}` : ''}`\n );\n }\n\n const run =\n <A>(fn: (args: A) => Promise<ToolResult>) =>\n async (args: A): Promise<ToolResult> => {\n try {\n return await fn(args);\n } catch (e) {\n return text(e instanceof Error ? e.message : String(e), true);\n }\n };\n\n const Path = z.string().describe(`Deck file path, relative to ${root}.`);\n const Ref = z\n .union([z.string(), z.number().int().positive()])\n .describe('Slide id, or 1-based slide number.');\n const SlideObj = z\n .record(z.unknown())\n .describe('A slide object: { layout, ...fields }. See get_layout for fields.');\n\n server.tool(\n 'list_layouts',\n 'List every slide layout with what it is for. Start here.',\n {},\n async () =>\n text(\n listLayouts()\n .map((l) => `- **${l.name}** — ${l.description}\\n Use for: ${l.whenToUse}`)\n .join('\\n')\n )\n );\n\n server.tool(\n 'get_layout',\n 'Full field reference (JSON Schema) and a working example for one layout.',\n { name: z.string() },\n run(async ({ name }) => {\n const d = describeLayout(name);\n if (!d) return text(`Unknown layout \"${name}\". Call list_layouts.`, true);\n return text(JSON.stringify(d, null, 2));\n })\n );\n\n server.tool(\n 'get_authoring_guide',\n 'Brand and writing rules for Classic Homes decks. Read before writing slides.',\n {},\n async () => text(AUTHORING_GUIDE)\n );\n\n server.tool(\n 'create_deck',\n 'Create a new deck file. Optionally include the first slides.',\n {\n path: Path,\n title: z.string(),\n description: z.string().optional(),\n journeySteps: z\n .array(z.string())\n .optional()\n .describe('Numbered agenda steps; enables journey features.'),\n slides: z.array(SlideObj).optional(),\n overwrite: z.boolean().default(false),\n },\n run(async ({ path, title, description, journeySteps, slides, overwrite }) => {\n const abs = safePath(path);\n if (!overwrite) {\n const exists = await access(abs).then(\n () => true,\n () => false\n );\n if (exists)\n return text(\n `${path} already exists. Use read_deck and edit it, or pass overwrite: true.`,\n true\n );\n }\n const deck: EditableDeck = {\n title,\n ...(description ? { description } : {}),\n ...(journeySteps?.length ? { journey: { steps: journeySteps } } : {}),\n slides: slides?.length ? slides : [{ layout: 'title', title }],\n };\n return commit(abs, deck, `Created ${path}.`);\n })\n );\n\n server.tool(\n 'read_deck',\n 'Read a deck: outline plus the full YAML source.',\n { path: Path },\n run(async ({ path }) => {\n const { deck } = await load(path);\n return text(\n `Outline:\\n${outline(deck).join('\\n')}\\n\\nSource:\\n\\`\\`\\`yaml\\n${stringifyDeck(deck)}\\`\\`\\``\n );\n })\n );\n\n server.tool(\n 'update_deck',\n 'Change deck-level fields (title, description, author, date, journey). Set a field to null to remove it.',\n { path: Path, fields: z.record(z.unknown()) },\n run(async ({ path, fields }) => {\n const { abs, deck } = await load(path);\n for (const [k, v] of Object.entries(fields)) {\n if (k === 'slides') return text('Use the slide tools to change slides.', true);\n if (v === null) delete deck[k];\n else deck[k] = v;\n }\n return commit(abs, deck, 'Deck fields updated.');\n })\n );\n\n server.tool(\n 'add_slide',\n 'Insert a slide. position is 1-based; omit to append.',\n { path: Path, slide: SlideObj, position: z.number().int().positive().optional() },\n run(async ({ path, slide, position }) => {\n const { abs, deck } = await load(path);\n const at = addSlide(deck, slide, position);\n return commit(abs, deck, `Added slide ${at}.`);\n })\n );\n\n server.tool(\n 'add_slides',\n 'Append several slides at once (e.g. a whole section). Saved only if all are valid.',\n { path: Path, slides: z.array(SlideObj).min(1) },\n run(async ({ path, slides }) => {\n const { abs, deck } = await load(path);\n slides.forEach((s) => addSlide(deck, s));\n return commit(abs, deck, `Added ${slides.length} slides.`);\n })\n );\n\n server.tool(\n 'update_slide',\n 'Merge fields into a slide. Set a field to null to remove it. Changing layout replaces the slide.',\n { path: Path, slide: Ref, fields: z.record(z.unknown()) },\n run(async ({ path, slide, fields }) => {\n const { abs, deck } = await load(path);\n const at = updateSlide(deck, slide, fields);\n return commit(abs, deck, `Updated slide ${at}.`);\n })\n );\n\n server.tool(\n 'move_slide',\n 'Move a slide to a new 1-based position.',\n { path: Path, slide: Ref, to: z.number().int().positive() },\n run(async ({ path, slide, to }) => {\n const { abs, deck } = await load(path);\n const at = moveSlide(deck, slide, to);\n return commit(abs, deck, `Moved slide to ${at}.`);\n })\n );\n\n server.tool(\n 'delete_slide',\n 'Delete a slide.',\n { path: Path, slide: Ref },\n run(async ({ path, slide }) => {\n const { abs, deck } = await load(path);\n const removed = removeSlide(deck, slide);\n return commit(abs, deck, `Deleted [${removed.layout}] slide.`);\n })\n );\n\n server.tool(\n 'validate_deck',\n 'Check a deck for errors and crowded slides. Fix every error and most warnings before building.',\n { path: Path },\n run(async ({ path }) => {\n const { deck } = await load(path);\n const v = validateDeck(deck);\n if (!v.issues.length) return text('Deck is valid with no warnings.');\n return text(\n `${v.ok ? 'Valid, with warnings' : 'Invalid'}:\\n${formatIssues(v.issues)}`,\n !v.ok\n );\n })\n );\n\n server.tool(\n 'build_deck',\n 'Render the deck to one self-contained HTML file (images and fonts inlined). Returns the output path.',\n {\n path: Path,\n out: z.string().optional().describe('Output .html path; defaults next to the deck.'),\n },\n run(async ({ path, out }) => {\n const abs = safePath(path);\n let outAbs: string | undefined;\n if (out) {\n outAbs = resolve(root, out);\n if (!outAbs.startsWith(root + sep) || !outAbs.endsWith('.html'))\n throw new Error('out must be an .html path inside the deck root.');\n }\n const warnings: string[] = [];\n const r = await buildDeckFile(abs, {\n out: outAbs,\n rootDir: root,\n onWarning: (w) => warnings.push(w),\n });\n return text(\n `Built ${relative(root, r.out)} — ${r.slideCount} slides, ${(r.bytes / 1024 / 1024).toFixed(2)} MB. Open it in a browser to present (N = notes, F = fullscreen).${\n warnings.length ? `\\n\\nImage warnings:\\n- ${warnings.join('\\n- ')}` : ''\n }${r.warnings.length ? `\\n\\nContent warnings:\\n${formatIssues(r.warnings)}` : ''}`\n );\n })\n );\n\n server.resource(\n 'authoring-guide',\n 'slides://guide',\n { mimeType: 'text/markdown' },\n async (uri) => ({\n contents: [{ uri: uri.href, mimeType: 'text/markdown', text: AUTHORING_GUIDE }],\n })\n );\n\n server.prompt(\n 'build_deck_from_outline',\n 'Turn a topic and rough outline into a finished, validated deck.',\n {\n path: z.string().describe('Where to save the deck, e.g. decks/closing-day.yaml'),\n topic: z.string(),\n audience: z.string(),\n outline: z.string().optional().describe('Bullet outline, notes, or pasted source material.'),\n },\n ({ path, topic, audience, outline: src }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a Classic Homes presentation.\n\nTopic: ${topic}\nAudience: ${audience}\nSave to: ${path}\n${src ? `\\nSource material:\\n${src}\\n` : ''}\nSteps:\n1. Call get_authoring_guide and list_layouts. Call get_layout for each layout you plan to use.\n2. Plan the slide list (one idea per slide) before writing.\n3. create_deck, then add_slides one section at a time. Put detail in each slide's notes.\n4. validate_deck and fix every error and crowding warning.\n5. build_deck and report the output path with a one-line summary per section.`,\n },\n },\n ],\n })\n );\n\n return server;\n}\n\nexport async function runStdio(rootDir?: string): Promise<void> {\n const server = createSlidesMcpServer(rootDir);\n await server.connect(new StdioServerTransport());\n}\n"],"mappings":"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","names":[]}
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-rkJZtnjl.js';
2
- export { Y as renderDeck } from './images-rkJZtnjl.js';
1
+ import { R as RenderOptions, J as RenderResult, w as InlineImagesResult, u as ImageResolver } from './images-C90NCxYd.js';
2
+ export { r as renderDeck } from './render-default-B-QkReoS.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,11 @@ import {
5
5
  fileImageResolver,
6
6
  loadDeckFile,
7
7
  saveDeckFile
8
- } from "./chunk-UKRX7A2V.js";
8
+ } from "./chunk-4LBQOMMI.js";
9
9
  import {
10
10
  renderDeck
11
- } from "./chunk-HI5ZCAHO.js";
11
+ } from "./chunk-6ARF37DO.js";
12
+ import "./chunk-MCG5BFJN.js";
12
13
  export {
13
14
  IMAGE_WARN_BYTES,
14
15
  buildDeckFile,
@@ -0,0 +1,5 @@
1
+ import { R as RenderOptions, J as RenderResult } from './images-C90NCxYd.js';
2
+
3
+ declare function renderDeck(input: unknown, options?: RenderOptions): Promise<RenderResult>;
4
+
5
+ export { renderDeck as r };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/slides",
3
- "version": "0.7.0",
3
+ "version": "0.8.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",
@@ -13,6 +13,10 @@
13
13
  "types": "./dist/index.d.ts",
14
14
  "default": "./dist/index.js"
15
15
  },
16
+ "./core": {
17
+ "types": "./dist/core.d.ts",
18
+ "default": "./dist/core.js"
19
+ },
16
20
  "./node": {
17
21
  "types": "./dist/node.d.ts",
18
22
  "default": "./dist/node.js"