@classic-homes/slides 0.2.0 → 0.3.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.js CHANGED
@@ -1,190 +1,129 @@
1
- import {
2
- addSlide,
3
- findSlide,
4
- moveSlide,
5
- outline,
6
- removeSlide,
7
- updateSlide
8
- } from "./chunk-VONVGYRL.js";
9
1
  import {
10
2
  AUTHORING_GUIDE,
11
3
  deckJsonSchema,
12
4
  describeLayout,
13
5
  listLayouts
14
- } from "./chunk-TXNQKBWJ.js";
6
+ } from "./chunk-YI7A4EWX.js";
15
7
  import {
8
+ BrandPaletteSchema,
9
+ BrandProfileSchema,
10
+ CHART_TYPES,
11
+ CLASSIC_HOMES_BRAND,
12
+ CLASSIC_HOMES_PALETTE,
13
+ ChartSpec,
14
+ DeckBrandSchema,
16
15
  DeckError,
17
16
  DeckSchema,
18
- Image,
19
- Item,
17
+ DeckSettingsSchema,
20
18
  LAYOUTS,
19
+ LAYOUT_CATEGORIES,
20
+ LAYOUT_CATEGORY_ORDER,
21
21
  LOGO_SVG,
22
22
  PALETTE,
23
+ PLACEHOLDER_IMAGE,
24
+ PREVIEW_SIZE,
23
25
  SlideSchema,
24
- Text,
25
- baseSlide,
26
+ addSlide,
27
+ allLayoutEditorInfo,
28
+ blankSlide,
29
+ chartOption,
30
+ checkPalette,
26
31
  collectImageSources,
32
+ commonSlideFields,
33
+ deckEditorFields,
34
+ describeChart,
35
+ ensureSlideIds,
27
36
  escapeHtml,
28
37
  extractDeckSource,
38
+ findSlide,
29
39
  formatIssues,
30
40
  formatPath,
41
+ formatValue,
31
42
  getLayout,
32
43
  inline,
44
+ isPlaceholderImage,
45
+ layoutEditorInfo,
46
+ logoLockup,
47
+ moveSlide,
48
+ newSlideId,
49
+ outline,
33
50
  parseDeckSource,
51
+ parseVideoUrl,
34
52
  prose,
53
+ removeSlide,
54
+ renderChartSvg,
35
55
  renderDeck,
56
+ resolveBrand,
57
+ slideKey,
36
58
  stringifyDeck,
37
- validateDeck
38
- } from "./chunk-TM3TNVHB.js";
59
+ updateSlide,
60
+ validateBrandProfile,
61
+ validateDeck,
62
+ videoEmbedUrl
63
+ } from "./chunk-GXGDPBAR.js";
39
64
 
40
- // src/editor.ts
41
- import { z } from "zod";
42
- var MULTILINE = /* @__PURE__ */ new Set(["lead", "body", "text", "quote", "footnote", "sub", "notes", "caption"]);
43
- var LABELS = {
44
- eyebrowSub: "Eyebrow subtitle",
45
- showJourney: "Show journey strip",
46
- stepDone: "Step completed",
47
- mediaSide: "Photo side",
48
- imageAspect: "Photo shape",
49
- numberFrom: "First badge number",
50
- url: "URL",
51
- id: "Slide id"
52
- };
53
- function labelFor(key) {
54
- if (LABELS[key]) return LABELS[key];
55
- const words = key.replace(/([A-Z])/g, " $1").toLowerCase();
56
- return words.charAt(0).toUpperCase() + words.slice(1);
57
- }
58
- function unwrap(schema) {
59
- let s = schema;
60
- let required = true;
61
- let dflt;
62
- let description = schema.description;
63
- for (; ; ) {
64
- description ??= s.description;
65
- if (s instanceof z.ZodOptional || s instanceof z.ZodNullable) {
66
- required = false;
67
- s = s.unwrap();
68
- } else if (s instanceof z.ZodDefault) {
69
- required = false;
70
- dflt = s._def.defaultValue();
71
- s = s._def.innerType;
72
- } else break;
73
- }
74
- return { inner: s, required, default: dflt, description };
75
- }
76
- function isText(s) {
77
- return s === Text || s instanceof z.ZodString && s.description === Text.description;
78
- }
79
- function describeField(key, schema) {
80
- const { inner, required, default: dflt, description } = unwrap(schema);
81
- const base = {
82
- key,
83
- label: labelFor(key),
84
- required,
85
- ...description ? { description } : {},
86
- ...dflt !== void 0 ? { default: dflt } : {},
87
- ...MULTILINE.has(key) ? { multiline: true } : {}
88
- };
89
- if (inner === Image) return { ...base, kind: "image" };
90
- if (inner === Item) return { ...base, kind: "item" };
91
- if (isText(inner)) return { ...base, kind: "inline" };
92
- if (inner instanceof z.ZodString) return { ...base, kind: "string" };
93
- if (inner instanceof z.ZodBoolean) return { ...base, kind: "boolean" };
94
- if (inner instanceof z.ZodEnum) return { ...base, kind: "enum", options: [...inner.options] };
95
- if (inner instanceof z.ZodLiteral) return { ...base, kind: "enum", options: [inner.value] };
96
- if (inner instanceof z.ZodNumber) {
97
- return {
98
- ...base,
99
- kind: "number",
100
- integer: inner.isInt,
101
- ...inner.minValue !== null ? { min: inner.minValue } : {},
102
- ...inner.maxValue !== null ? { max: inner.maxValue } : {}
103
- };
104
- }
105
- if (inner instanceof z.ZodUnion) {
106
- const opts = inner.options;
107
- if (opts.every((o) => o instanceof z.ZodLiteral)) {
108
- return {
109
- ...base,
110
- kind: "enum",
111
- options: opts.map((o) => o.value)
112
- };
113
- }
114
- return { ...base, kind: "string" };
115
- }
116
- if (inner instanceof z.ZodArray) {
117
- const el = inner.element;
118
- return {
119
- ...base,
120
- kind: "list",
121
- of: { ...describeField("", el), label: labelFor(key.replace(/s$/, "")) },
122
- ...inner._def.minLength ? { min: inner._def.minLength.value } : {},
123
- ...inner._def.maxLength ? { max: inner._def.maxLength.value } : {}
124
- };
125
- }
126
- if (inner instanceof z.ZodObject) {
127
- return {
128
- ...base,
129
- kind: "group",
130
- fields: Object.entries(inner.shape).map(
131
- ([k, v]) => describeField(k, v)
132
- )
133
- };
134
- }
135
- return { ...base, kind: "string" };
136
- }
137
- function commonSlideFields() {
138
- return Object.entries(baseSlide).map(([k, v]) => describeField(k, v));
139
- }
140
- function layoutEditorInfo(name) {
141
- const layout = getLayout(name);
142
- if (!layout) return void 0;
143
- return {
144
- name: layout.name,
145
- description: layout.description,
146
- whenToUse: layout.whenToUse,
147
- defaults: layout.defaults ?? {},
148
- fields: Object.entries(layout.shape).map(
149
- ([k, v]) => describeField(k, v)
150
- ),
151
- example: layout.example
152
- };
153
- }
154
- function allLayoutEditorInfo() {
155
- return LAYOUTS.map((l) => layoutEditorInfo(l.name));
156
- }
65
+ // src/generated/runtime-hash.ts
66
+ var RUNTIME_SCRIPT_HASH = "sha256-oKFaVecqTMnaoeBzefA8f7poigISWe5hotztD1XgJP8=";
157
67
  export {
158
68
  AUTHORING_GUIDE,
69
+ BrandPaletteSchema,
70
+ BrandProfileSchema,
71
+ CHART_TYPES,
72
+ CLASSIC_HOMES_BRAND,
73
+ CLASSIC_HOMES_PALETTE,
74
+ ChartSpec,
75
+ DeckBrandSchema,
159
76
  DeckError,
160
77
  DeckSchema,
78
+ DeckSettingsSchema,
161
79
  LAYOUTS,
80
+ LAYOUT_CATEGORIES,
81
+ LAYOUT_CATEGORY_ORDER,
162
82
  LOGO_SVG,
163
83
  PALETTE,
84
+ PLACEHOLDER_IMAGE,
85
+ PREVIEW_SIZE,
86
+ RUNTIME_SCRIPT_HASH,
164
87
  SlideSchema,
165
88
  addSlide,
166
89
  allLayoutEditorInfo,
90
+ blankSlide,
91
+ chartOption,
92
+ checkPalette,
167
93
  collectImageSources,
168
94
  commonSlideFields,
95
+ deckEditorFields,
169
96
  deckJsonSchema,
97
+ describeChart,
170
98
  describeLayout,
99
+ ensureSlideIds,
171
100
  escapeHtml,
172
101
  extractDeckSource,
173
102
  findSlide,
174
103
  formatIssues,
175
104
  formatPath,
105
+ formatValue,
176
106
  getLayout,
177
107
  inline,
108
+ isPlaceholderImage,
178
109
  layoutEditorInfo,
179
110
  listLayouts,
111
+ logoLockup,
180
112
  moveSlide,
113
+ newSlideId,
181
114
  outline,
182
115
  parseDeckSource,
116
+ parseVideoUrl,
183
117
  prose,
184
118
  removeSlide,
119
+ renderChartSvg,
185
120
  renderDeck,
121
+ resolveBrand,
122
+ slideKey,
186
123
  stringifyDeck,
187
124
  updateSlide,
188
- validateDeck
125
+ validateBrandProfile,
126
+ validateDeck,
127
+ videoEmbedUrl
189
128
  };
190
129
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["/**\n * Form metadata for editors. Walks each layout's zod shape and describes its\n * fields as simple widget descriptors, so a UI can build a form for any\n * layout without understanding zod or JSON Schema.\n */\nimport { z } from 'zod';\nimport { getLayout, LAYOUTS } from './layouts/index.js';\nimport { baseSlide, Image, Item, Text } from './schema/common.js';\n\nexport type EditorField =\n | FieldBase<'inline'> // formatted text (**bold**, *highlight*, links)\n | FieldBase<'string'>\n | (FieldBase<'enum'> & { options: (string | number)[] })\n | FieldBase<'boolean'>\n | (FieldBase<'number'> & { min?: number; max?: number; integer: boolean })\n | FieldBase<'image'>\n | FieldBase<'item'> // { text, sub? } bullet\n | (FieldBase<'list'> & { of: EditorField; min?: number; max?: number })\n | (FieldBase<'group'> & { fields: EditorField[] });\n\ninterface FieldBase<K extends string> {\n kind: K;\n /** Property name ('' for list elements). */\n key: string;\n label: string;\n description?: string;\n required: boolean;\n default?: unknown;\n /** Suggest a textarea rather than a one-line input. */\n multiline?: boolean;\n}\n\nconst MULTILINE = new Set(['lead', 'body', 'text', 'quote', 'footnote', 'sub', 'notes', 'caption']);\nconst LABELS: Record<string, string> = {\n eyebrowSub: 'Eyebrow subtitle',\n showJourney: 'Show journey strip',\n stepDone: 'Step completed',\n mediaSide: 'Photo side',\n imageAspect: 'Photo shape',\n numberFrom: 'First badge number',\n url: 'URL',\n id: 'Slide id',\n};\n\nfunction labelFor(key: string): string {\n if (LABELS[key]) return LABELS[key];\n const words = key.replace(/([A-Z])/g, ' $1').toLowerCase();\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\ntype AnyZod = z.ZodTypeAny;\n\nfunction unwrap(schema: AnyZod): {\n inner: AnyZod;\n required: boolean;\n default?: unknown;\n description?: string;\n} {\n let s = schema;\n let required = true;\n let dflt: unknown;\n let description = schema.description;\n for (;;) {\n description ??= s.description;\n if (s instanceof z.ZodOptional || s instanceof z.ZodNullable) {\n required = false;\n s = s.unwrap();\n } else if (s instanceof z.ZodDefault) {\n required = false;\n dflt = s._def.defaultValue();\n s = s._def.innerType;\n } else break;\n }\n return { inner: s, required, default: dflt, description };\n}\n\nfunction isText(s: AnyZod): boolean {\n // Text may have been re-described (.describe clones), so compare by checks too.\n return s === Text || (s instanceof z.ZodString && s.description === Text.description);\n}\n\nfunction describeField(key: string, schema: AnyZod): EditorField {\n const { inner, required, default: dflt, description } = unwrap(schema);\n const base = {\n key,\n label: labelFor(key),\n required,\n ...(description ? { description } : {}),\n ...(dflt !== undefined ? { default: dflt } : {}),\n ...(MULTILINE.has(key) ? { multiline: true } : {}),\n };\n\n if (inner === Image) return { ...base, kind: 'image' };\n if (inner === Item) return { ...base, kind: 'item' };\n if (isText(inner)) return { ...base, kind: 'inline' };\n if (inner instanceof z.ZodString) return { ...base, kind: 'string' };\n if (inner instanceof z.ZodBoolean) return { ...base, kind: 'boolean' };\n if (inner instanceof z.ZodEnum) return { ...base, kind: 'enum', options: [...inner.options] };\n if (inner instanceof z.ZodLiteral) return { ...base, kind: 'enum', options: [inner.value] };\n if (inner instanceof z.ZodNumber) {\n return {\n ...base,\n kind: 'number',\n integer: inner.isInt,\n ...(inner.minValue !== null ? { min: inner.minValue } : {}),\n ...(inner.maxValue !== null ? { max: inner.maxValue } : {}),\n };\n }\n if (inner instanceof z.ZodUnion) {\n const opts = inner.options as AnyZod[];\n if (opts.every((o) => o instanceof z.ZodLiteral)) {\n return {\n ...base,\n kind: 'enum',\n options: opts.map((o) => (o as z.ZodLiteral<string | number>).value),\n };\n }\n return { ...base, kind: 'string' };\n }\n if (inner instanceof z.ZodArray) {\n const el = inner.element as AnyZod;\n return {\n ...base,\n kind: 'list',\n of: { ...describeField('', el), label: labelFor(key.replace(/s$/, '')) },\n ...(inner._def.minLength ? { min: inner._def.minLength.value } : {}),\n ...(inner._def.maxLength ? { max: inner._def.maxLength.value } : {}),\n };\n }\n if (inner instanceof z.ZodObject) {\n return {\n ...base,\n kind: 'group',\n fields: Object.entries(inner.shape as Record<string, AnyZod>).map(([k, v]) =>\n describeField(k, v)\n ),\n };\n }\n return { ...base, kind: 'string' };\n}\n\nexport interface LayoutEditorInfo {\n name: string;\n description: string;\n whenToUse: string;\n defaults: { background?: string; track?: string };\n /** Layout-specific fields, in declaration order. */\n fields: EditorField[];\n /** A valid starting slide for this layout. */\n example: Record<string, unknown>;\n}\n\n/** Fields shared by every slide (track, background, notes, step…). */\nexport function commonSlideFields(): EditorField[] {\n return Object.entries(baseSlide).map(([k, v]) => describeField(k, v));\n}\n\nexport function layoutEditorInfo(name: string): LayoutEditorInfo | undefined {\n const layout = getLayout(name);\n if (!layout) return undefined;\n return {\n name: layout.name,\n description: layout.description,\n whenToUse: layout.whenToUse,\n defaults: layout.defaults ?? {},\n fields: Object.entries(layout.shape as Record<string, AnyZod>).map(([k, v]) =>\n describeField(k, v)\n ),\n example: layout.example,\n };\n}\n\nexport function allLayoutEditorInfo(): LayoutEditorInfo[] {\n return LAYOUTS.map((l) => layoutEditorInfo(l.name)!);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../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-oKFaVecqTMnaoeBzefA8f7poigISWe5hotztD1XgJP8=';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,sBAAsB;","names":[]}
@@ -1,25 +1,24 @@
1
- import {
2
- addSlide,
3
- moveSlide,
4
- outline,
5
- removeSlide,
6
- updateSlide
7
- } from "./chunk-VONVGYRL.js";
8
1
  import {
9
2
  AUTHORING_GUIDE,
10
3
  describeLayout,
11
4
  listLayouts
12
- } from "./chunk-TXNQKBWJ.js";
5
+ } from "./chunk-YI7A4EWX.js";
13
6
  import {
14
7
  buildDeckFile,
8
+ confinedPath,
15
9
  loadDeckFile,
16
10
  saveDeckFile
17
- } from "./chunk-CKVKX2ZD.js";
11
+ } from "./chunk-2IPLZ3SA.js";
18
12
  import {
13
+ addSlide,
19
14
  formatIssues,
15
+ moveSlide,
16
+ outline,
17
+ removeSlide,
20
18
  stringifyDeck,
19
+ updateSlide,
21
20
  validateDeck
22
- } from "./chunk-TM3TNVHB.js";
21
+ } from "./chunk-GXGDPBAR.js";
23
22
 
24
23
  // src/node/mcp.ts
25
24
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -41,7 +40,7 @@ function createSlidesMcpServer(rootDir = process.cwd()) {
41
40
  throw new Error(`Path "${p}" is outside the deck root ${root}.`);
42
41
  if (!/\.(ya?ml|json)$/i.test(abs))
43
42
  throw new Error("Deck files must end in .yaml, .yml or .json.");
44
- return abs;
43
+ return confinedPath(root, abs);
45
44
  }
46
45
  async function load(p) {
47
46
  const abs = safePath(p);
@@ -61,7 +60,7 @@ Fix the fields and try again.`,
61
60
  true
62
61
  );
63
62
  }
64
- await saveDeckFile(abs, deck);
63
+ await saveDeckFile(confinedPath(root, abs), deck);
65
64
  const warnings = v.issues.filter((i) => i.level === "warning");
66
65
  return text(
67
66
  `${done}
@@ -252,7 +251,11 @@ ${formatIssues(v.issues)}`,
252
251
  throw new Error("out must be an .html path inside the deck root.");
253
252
  }
254
253
  const warnings = [];
255
- const r = await buildDeckFile(abs, { out: outAbs, onWarning: (w) => warnings.push(w) });
254
+ const r = await buildDeckFile(abs, {
255
+ out: outAbs,
256
+ rootDir: root,
257
+ onWarning: (w) => warnings.push(w)
258
+ });
256
259
  return text(
257
260
  `Built ${relative(root, r.out)} \u2014 ${r.slideCount} slides, ${(r.bytes / 1024 / 1024).toFixed(2)} MB. Open it in a browser to present (N = notes, F = fullscreen).${warnings.length ? `
258
261
 
@@ -317,4 +320,4 @@ export {
317
320
  createSlidesMcpServer,
318
321
  runStdio
319
322
  };
320
- //# sourceMappingURL=mcp-5R6YD3QW.js.map
323
+ //# sourceMappingURL=mcp-PUA45IS7.js.map
@@ -0,0 +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.union([z.string(), z.number()]).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,10 +1,12 @@
1
- import { R as RenderOptions, g as RenderResult, I as ImageResolver } from './render-CZ4g2fa9.js';
2
- export { r as renderDeck } from './render-CZ4g2fa9.js';
1
+ import { R as RenderOptions, v as RenderResult, m as ImageResolver } from './render-DcyKuBRD.js';
2
+ export { K as renderDeck } from './render-DcyKuBRD.js';
3
3
  import 'zod';
4
4
 
5
5
  /** Largest single image we inline without warning. Big photos bloat the deck file. */
6
6
  declare const IMAGE_WARN_BYTES: number;
7
7
  interface FileResolverOptions {
8
+ /** Restrict local image reads and build paths to this root (used by the MCP server). */
9
+ rootDir?: string;
8
10
  /** Fetch https images and inline them so the deck works offline. Default true. */
9
11
  inlineRemote?: boolean;
10
12
  onWarning?: (message: string) => void;
package/dist/node.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  fileImageResolver,
5
5
  loadDeckFile,
6
6
  saveDeckFile
7
- } from "./chunk-CKVKX2ZD.js";
7
+ } from "./chunk-2IPLZ3SA.js";
8
8
  import {
9
9
  renderDeck
10
- } from "./chunk-TM3TNVHB.js";
10
+ } from "./chunk-GXGDPBAR.js";
11
11
  export {
12
12
  IMAGE_WARN_BYTES,
13
13
  buildDeckFile,