@tycoworks/tycoslide 0.8.0 → 0.10.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.
Files changed (54) hide show
  1. package/README.md +12 -9
  2. package/SKILL.md +6 -7
  3. package/dist/cli.js +39 -59
  4. package/dist/engine/fillers/filler.d.ts +21 -13
  5. package/dist/engine/fillers/filler.js +21 -24
  6. package/dist/engine/generate.d.ts +23 -17
  7. package/dist/engine/generate.js +157 -70
  8. package/dist/engine/index.d.ts +1 -1
  9. package/dist/engine/types.d.ts +39 -9
  10. package/dist/index.d.ts +15 -21
  11. package/dist/index.js +63 -88
  12. package/dist/manifest.js +17 -25
  13. package/dist/markdown/blocks/code.d.ts +17 -0
  14. package/dist/markdown/blocks/code.js +65 -0
  15. package/dist/markdown/blocks/image.d.ts +2 -0
  16. package/dist/markdown/blocks/image.js +9 -0
  17. package/dist/markdown/blocks/mermaid.d.ts +15 -0
  18. package/dist/markdown/blocks/mermaid.js +227 -0
  19. package/dist/markdown/{resolvers → blocks}/mermaidTheme.d.ts +1 -1
  20. package/dist/markdown/{resolvers → blocks}/mermaidTheme.js +1 -1
  21. package/dist/markdown/blocks/registry.d.ts +16 -0
  22. package/dist/markdown/blocks/registry.js +44 -0
  23. package/dist/markdown/blocks/table.d.ts +2 -0
  24. package/dist/markdown/blocks/table.js +23 -0
  25. package/dist/markdown/blocks/text.d.ts +12 -0
  26. package/dist/markdown/blocks/text.js +90 -0
  27. package/dist/markdown/deckCompiler.d.ts +17 -20
  28. package/dist/markdown/deckCompiler.js +143 -113
  29. package/dist/markdown/index.d.ts +11 -11
  30. package/dist/markdown/index.js +9 -8
  31. package/dist/markdown/inline.d.ts +26 -0
  32. package/dist/markdown/inline.js +136 -0
  33. package/dist/markdown/mdast.d.ts +25 -0
  34. package/dist/markdown/mdast.js +49 -0
  35. package/dist/markdown/schema/deckSchema.d.ts +30 -0
  36. package/dist/markdown/schema/deckSchema.js +51 -0
  37. package/dist/markdown/schema/strict.d.ts +9 -0
  38. package/dist/markdown/schema/strict.js +18 -0
  39. package/dist/markdown/schema/themeConfigSchema.d.ts +106 -0
  40. package/dist/markdown/schema/themeConfigSchema.js +147 -0
  41. package/dist/markdown/types.d.ts +194 -134
  42. package/dist/markdown/types.js +34 -23
  43. package/dist/skillZip.d.ts +17 -0
  44. package/dist/skillZip.js +35 -0
  45. package/package.json +7 -3
  46. package/syntax.md +1 -1
  47. package/dist/markdown/parsers.d.ts +0 -32
  48. package/dist/markdown/parsers.js +0 -233
  49. package/dist/markdown/resolvers/code.d.ts +0 -17
  50. package/dist/markdown/resolvers/code.js +0 -44
  51. package/dist/markdown/resolvers/mermaid.d.ts +0 -14
  52. package/dist/markdown/resolvers/mermaid.js +0 -81
  53. package/dist/markdown/resolvers/resolver.d.ts +0 -42
  54. package/dist/markdown/resolvers/resolver.js +0 -52
@@ -1,5 +1,6 @@
1
- import { type ImageFill, type TableFill, type TemplateFill, type TextFill } from "../engine/index.js";
2
- import type { MermaidConfig } from "./resolvers/mermaidTheme.js";
1
+ import type { RootContent } from "mdast";
2
+ import { type Frame, type ImageFill, type TableFill, type TemplateFill, type TextFill } from "../engine/index.js";
3
+ import type { MermaidConfig } from "./blocks/mermaidTheme.js";
3
4
  /**
4
5
  * An asset's scaling/cropping tolerance, declared in the theme catalog. The
5
6
  * compiler maps it to the engine's object-fit `fit`. `icon`: never enlarge,
@@ -26,18 +27,6 @@ export type AssetEntry = {
26
27
  };
27
28
  /** Two-level catalog: `{ category: { name: AssetEntry } }`. */
28
29
  export type AssetCatalog = Record<string, Record<string, AssetEntry>>;
29
- /**
30
- * Discriminator strings for the compiler-internal fence shapes produced by
31
- * deckCompiler.ts. `Code` and `Mermaid` are resolved away entirely (syntax
32
- * highlighting → TextFill, PNG rendering → ImageFill), so neither reaches the
33
- * engine. Prose has no FenceType: it becomes a `TextFill` (`{ paragraphs }`),
34
- * one of the engine's own fill shapes.
35
- */
36
- export declare const FenceType: {
37
- readonly Code: "code";
38
- readonly Mermaid: "mermaid";
39
- };
40
- export type FenceType = (typeof FenceType)[keyof typeof FenceType];
41
30
  /**
42
31
  * Discriminator for a layout's frontmatter *parameters* — inputs the author
43
32
  * writes as a single `key: value` line. Two kinds: `Template` (fills a styled
@@ -51,113 +40,117 @@ export declare const ParameterType: {
51
40
  };
52
41
  export type ParameterType = (typeof ParameterType)[keyof typeof ParameterType];
53
42
  /**
54
- * Compiler-facing slot type for a layout's *slots* multi-line body regions
55
- * (the default body region, or a `::name::` region). Narrowed to four kinds;
56
- * `Template` and `Image` left this list to become parameters. `Code` and `Mermaid`
57
- * are compiler-only, resolved (syntax highlighting, mermaid PNG rendering)
58
- * before the engine sees the deck. Required on every CompilerSlot — no silent
59
- * default.
43
+ * The engine content types a slot may `accept`. A subset of the engine's
44
+ * `SlotType`: `Template` is parameter-only, never a body block. There is no
45
+ * `code` / `mermaid` accept type a code fence folds into a `Text`-accepting
46
+ * slot (syntax highlighting → TextFill) and a mermaid fence into an
47
+ * `Image`-accepting slot (PNG render ImageFill), the same fold the engine
48
+ * already performs. The author's markdown shape selects which accepted type a
49
+ * region routes to.
60
50
  */
61
- export declare const CompilerSlotType: {
51
+ export declare const AcceptType: {
62
52
  readonly Text: "text";
63
53
  readonly Table: "table";
64
- readonly Code: "code";
65
- readonly Mermaid: "mermaid";
54
+ readonly Image: "image";
66
55
  };
67
- export type CompilerSlotType = (typeof CompilerSlotType)[keyof typeof CompilerSlotType];
56
+ export type AcceptType = (typeof AcceptType)[keyof typeof AcceptType];
68
57
  /**
69
- * A fenced code block awaiting syntax highlighting. Produced by deckCompiler,
70
- * consumed by CodeResolver. Never crosses the engine boundary.
58
+ * The tonal surface a layout sits on. A code layout on a light panel needs a
59
+ * light Shiki theme (dark syntax would wash out); one on a dark panel needs a
60
+ * dark theme. A layout declares its `variant`; the code compile resolves a
61
+ * `{ light, dark }` `codeTheme` pair against it (a pair with no `variant` throws).
71
62
  */
72
- export type CodeFence = {
73
- type: typeof FenceType.Code;
74
- language: string;
75
- source: string;
63
+ export declare const Variant: {
64
+ readonly Light: "light";
65
+ readonly Dark: "dark";
76
66
  };
67
+ export type Variant = (typeof Variant)[keyof typeof Variant];
77
68
  /**
78
- * A mermaid code fence awaiting rendering. Produced by deckCompiler, consumed
79
- * by MermaidResolver the resulting PNG is wrapped as an ImageFill in
80
- * step.content, so the engine never learns mermaid exists.
69
+ * The four engine content shapes a slot's compiled content can be — the value
70
+ * `compile` produces and the engine fills. ImageFill carries a `.type`
71
+ * discriminator; the other three TextFill, TableFill, TemplateFill — are
72
+ * identified structurally by their signature field.
81
73
  */
82
- export type MermaidFence = {
83
- type: typeof FenceType.Mermaid;
84
- definition: string;
85
- };
74
+ export type EngineFill = TextFill | TableFill | ImageFill | TemplateFill;
86
75
  /**
87
- * The full set of value shapes a slot may hold during compilation, before code
88
- * fences are highlighted (→ TextFill) and mermaid fences are rendered
89
- * (→ ImageFill). CodeFence, MermaidFence, and ImageFill carry a `.type`
90
- * discriminator; the three engine fills without one — TextFill, TableFill,
91
- * TemplateFill — are identified structurally by their signature field.
76
+ * The three engine content shapes a block handler can produce. A subset of
77
+ * `EngineFill` `TemplateFill` is parameter-only (`AcceptType` excludes
78
+ * `Template`), so no block handler ever compiles one.
92
79
  */
93
- export type MarkdownBlock = TextFill | TableFill | CodeFence | MermaidFence | ImageFill | TemplateFill;
80
+ export type BlockFill = TextFill | TableFill | ImageFill;
94
81
  /**
95
- * A DeckStep as compileDeck produces it content values may still be
96
- * unresolved (CodeFence, MermaidFence) at this point. `resolveFences` narrows
97
- * these into StyledParagraph[] / ImageFill before the engine sees the deck.
82
+ * Everything a block handler needs to compile a node into its engine fill: the
83
+ * asset resolver, the diagnostic context to name the offending layout/slide/slot
84
+ * when a region's markdown shape is illegal (a stray standalone block mixed into
85
+ * prose), and the theme-level `config` — from which code/mermaid compile read
86
+ * their one-per-theme style (`codeTheme`, `mermaid`, `mermaidVariant`,
87
+ * `outputDir`), not the slot.
88
+ *
89
+ * Lives in types.ts (not in `blocks/registry.ts`, which re-exports it) so a
90
+ * per-kind block file can import it without importing the registry — the registry
91
+ * imports the per-kind handlers, so the reverse would cycle.
98
92
  */
99
- export type CompilerDeckStep = {
100
- layout: string;
101
- content?: Record<string, MarkdownBlock>;
102
- /** Slide-level speaker notes, stripped from frontmatter. Plain text. */
103
- notes?: string;
93
+ export type BlockContext = {
94
+ resolveAssetRef: (ref: string) => ImageFill;
95
+ layoutName: string;
96
+ slideIdx: number;
97
+ source: string;
98
+ config: CompilerConfig;
99
+ /**
100
+ * The current layout's tonal surface, threaded from its `variant`. The code
101
+ * compile reads it to pick the arm of a `{ light, dark }` `codeTheme` pair;
102
+ * a pair with no `variant` throws (no default).
103
+ */
104
+ layoutVariant?: Variant;
104
105
  };
105
106
  /**
106
- * The intermediate deck shape produced by compileDeck. buildDeck runs the
107
- * resolvers and hands the resulting engine-shaped Deck to generate().
107
+ * A block handler recognizes one content kind at the region's top level, folds
108
+ * it to the engine `AcceptType` it fills, and compiles the node straight into the
109
+ * matching engine fill — highlighting a code fence to a TextFill, rendering a
110
+ * mermaid fence to an ImageFill. Recognition (`match`), type (`acceptType`), and
111
+ * build (`compile`) live together — a new content kind is one file, one registry
112
+ * row. Non-generic like the engine's `Filler`: the array can't correlate a
113
+ * per-element type guard with `compile`'s param, so each handler narrows the node
114
+ * internally with a single-hop cast. Mirrors the old sdk's `SyntaxHandler`.
108
115
  */
109
- export type CompilerDeck = {
110
- theme: string;
111
- output?: string;
112
- steps: CompilerDeckStep[];
116
+ export type BlockHandler = {
117
+ match(node: RootContent): boolean;
118
+ acceptType: AcceptType;
119
+ compile(node: RootContent, ctx: BlockContext): Promise<BlockFill>;
113
120
  };
114
121
  /**
115
- * A DeckStep whose content values have been narrowed post-resolution no
116
- * CodeFence or MermaidFence remains, only shapes the engine understands.
117
- * Structurally equivalent to the engine's DeckStep.
122
+ * A DeckStep as the compiler produces it. Content is already engine-shaped
123
+ * code fences highlighted to TextFill and mermaid fences rendered to ImageFill
124
+ * during `compile` — so this is structurally equivalent to the engine's DeckStep
125
+ * and needs no further resolution.
118
126
  */
119
- export type ResolvedCompilerDeckStep = {
127
+ export type CompilerDeckStep = {
120
128
  layout: string;
121
- content?: Record<string, TextFill | TableFill | ImageFill | TemplateFill>;
122
- /** Slide-level speaker notes, threaded through to the engine. Plain text. */
129
+ content?: Record<string, EngineFill>;
130
+ /** Slide-level speaker notes, stripped from frontmatter. Plain text. */
123
131
  notes?: string;
124
132
  };
125
133
  /**
126
- * A CompilerDeck whose steps have been passed through the resolvers via
127
- * `resolveFences`. Structurally equivalent to the engine's Deck deck
128
- * resolution returns this shape so `generate` can consume it without any cast.
134
+ * The deck shape the compiler produces. `output` may be unset until a caller
135
+ * (the CLI, or a programmatic caller) assigns one; `buildDeck` fails fast if it
136
+ * is still missing. Once `output` is present this is structurally equivalent to
137
+ * the engine's Deck, so `generate` consumes it without any cast.
129
138
  */
130
- export type ResolvedCompilerDeck = {
139
+ export type CompilerDeck = {
131
140
  theme: string;
132
- output: string;
133
- steps: ResolvedCompilerDeckStep[];
134
- };
135
- /**
136
- * Fields common to every text shape, image parameter, and slot. A text shape is
137
- * addressed by `shapeName` and owns a `template` (its placeholders are the
138
- * fillable keys); image parameters and slots additionally carry a single `key`.
139
- */
140
- type CompilerShapeBase = {
141
- shapeName: string;
142
- limit?: {
143
- maxChars?: number;
144
- maxLines?: number;
145
- maxItems?: number;
146
- };
147
- /**
148
- * Whether the parameter/slot may be omitted from a slide. Optional (defaults
149
- * to false): a required one with no value causes the compiler to throw with
150
- * layout + key names.
151
- */
152
- required?: boolean;
141
+ output?: string;
142
+ steps: CompilerDeckStep[];
153
143
  };
154
144
  /**
155
- * Shape base plus a single `key` the addressing model for image parameters
156
- * and every slot. Template parameters do NOT extend this: their fillable keys are
157
- * the placeholders in their `template`, not a single top-level key.
145
+ * Markdown-flavored measurement hints on a slot or template parameter: caps on
146
+ * expanded run text, line count, or list items. All optional a missing cap is
147
+ * "no limit." Advisory metadata surfaced in the manifest; the fill never enforces
148
+ * it.
158
149
  */
159
- type CompilerSlotBase = CompilerShapeBase & {
160
- key: string;
150
+ export type Limit = {
151
+ maxChars?: number;
152
+ maxLines?: number;
153
+ maxItems?: number;
161
154
  };
162
155
  /**
163
156
  * Template parameter: a styled text shape filled by expanding a `template` into the
@@ -167,14 +160,31 @@ type CompilerSlotBase = CompilerShapeBase & {
167
160
  * placeholders in its template. The shape carries no top-level `key`; its
168
161
  * placeholders are the keys the author fills in frontmatter.
169
162
  */
170
- export type CompilerTemplateParameter = CompilerShapeBase & {
163
+ export type CompilerTemplateParameter = {
164
+ shapeName: string;
165
+ limit?: Limit;
166
+ /**
167
+ * Whether the parameter may be omitted from a slide. Optional (defaults to
168
+ * false): a required one with no value causes the compiler to throw with
169
+ * layout + key names.
170
+ */
171
+ required?: boolean;
171
172
  type: typeof ParameterType.Template;
172
173
  /** The shape's text as one template with `{key}` placeholders; newlines are line breaks. */
173
174
  template: string;
174
175
  };
175
176
  /** Image parameter: one frontmatter path filled by fillImage. Sizing/crop
176
- * behaviour comes from the resolved asset's `type`, not the slot. */
177
- export type CompilerImageParameter = CompilerSlotBase & {
177
+ * behaviour comes from the resolved asset's `type`, not the slot. Carries no
178
+ * `limit` measuring an image path against char/line caps is meaningless. */
179
+ export type CompilerImageParameter = {
180
+ shapeName: string;
181
+ /**
182
+ * Whether the parameter may be omitted from a slide. Optional (defaults to
183
+ * false): a required one with no value causes the compiler to throw with
184
+ * layout + key names.
185
+ */
186
+ required?: boolean;
187
+ key: string;
178
188
  type: typeof ParameterType.Image;
179
189
  };
180
190
  /**
@@ -182,58 +192,90 @@ export type CompilerImageParameter = CompilerSlotBase & {
182
192
  * single `key: value` line, resolved against the layout's `parameters` list.
183
193
  */
184
194
  export type CompilerParameter = CompilerTemplateParameter | CompilerImageParameter;
185
- /** Multi-paragraph body filled by fillText (specimen-style rebuild). */
186
- export type CompilerTextSlot = CompilerSlotBase & {
187
- type: typeof CompilerSlotType.Text;
188
- /** Leave the first N specimen paragraphs untouched. */
189
- startAt?: number;
190
- };
191
- /** Table shape backed by an `<a:tbl>` with header + data specimen rows. */
192
- export type CompilerTableSlot = CompilerSlotBase & {
193
- type: typeof CompilerSlotType.Table;
194
- };
195
195
  /**
196
- * Text slot that consumes a fenced code block. The resolver (`CodeResolver`)
197
- * uses `codeTheme` to Shiki-highlight the source into StyledParagraph[]; the
198
- * engine sees the projected Text slot.
196
+ * Reserved keys in a deck's frontmatter global (theme, output) and per-slide
197
+ * (layout, body). Exported so callers (e.g. cli.ts) reference the constants
198
+ * instead of literal strings.
199
199
  */
200
- export type CompilerCodeSlot = CompilerSlotBase & {
201
- type: typeof CompilerSlotType.Code;
202
- /** Shiki theme id (required — no silent default). */
203
- codeTheme: string;
200
+ export declare const RESERVED_KEY: {
201
+ readonly LAYOUT: "layout";
202
+ readonly BODY: "body";
203
+ readonly OUTPUT: "output";
204
+ readonly THEME: "theme";
205
+ readonly NOTES: "notes";
204
206
  };
205
207
  /**
206
- * Slot that consumes a mermaid code fence. The resolver (`MermaidResolver`)
207
- * uses `mermaidVariant` to select colors and renders a PNG; the engine sees the
208
- * projected Image slot with `contain` fit. Mermaid slots do NOT declare `fit` —
209
- * mermaid diagrams are always contained.
208
+ * A kind of content a slot accepts, and the real template shape that realizes
209
+ * it the compiler mirror of the engine's `Block`. `type` is an engine content
210
+ * type (`text` | `table` | `image`); `shapeName` names the shape on
211
+ * `sourceSlide` carrying the specimen styling. When `sourceSlide` equals the
212
+ * layout's `slideNumber` the shape is already on the cloned slide (fill in
213
+ * place); otherwise it is transplanted into the slot's `frame`. `startAt` is a
214
+ * text-specimen concern (leave the first N specimen paragraphs untouched), only
215
+ * meaningful on a text block. Named `CompilerBlock` to stay distinct from the
216
+ * engine's `Block` and the compiler's `BlockHandler`.
210
217
  */
211
- export type CompilerMermaidSlot = CompilerSlotBase & {
212
- type: typeof CompilerSlotType.Mermaid;
213
- /** Name of the mermaid variant (required — no silent default). */
214
- mermaidVariant: string;
218
+ export type CompilerBlock = {
219
+ type: AcceptType;
220
+ sourceSlide: number;
221
+ shapeName: string;
222
+ startAt?: number;
215
223
  };
216
224
  /**
217
- * Compiler-facing slot. Discriminated union on `type` each variant declares
218
- * ONLY the fields legal for that slot type. A layout's body regions (the
219
- * default region, or a `::name::` region), resolved against the layout's
220
- * `slots` list. Adds markdown-flavored concepts (limit hints, code-fence theme
221
- * selection, mermaid variant naming) on top of the engine's minimal Slot.
222
- * Compiler-only types (Code, Mermaid) are projected down to engine types (Text,
223
- * Image) at the engine boundary before the engine sees the layout.
225
+ * Compiler-facing slot. A layout's body region (the default region, or a
226
+ * `::name::` region), no longer welded to one shape+type: it `accepts` a set of
227
+ * `CompilerBlock`s and owns a `frame` (real observed EMU coordinates, never
228
+ * computed). The author's markdown shape selects which accepted block a region
229
+ * routes to; a type the slot does not accept fails fast. `frame` is required
230
+ * only when a slot has a transplant block (a block whose `sourceSlide` differs
231
+ * from the layout's `slideNumber`); a base-only slot fills in place and needs
232
+ * none. Adds markdown-flavored `limit` hints on top of the engine's minimal
233
+ * Slot; per-slot `codeTheme` / `mermaidVariant` moved to the theme level.
224
234
  */
225
- export type CompilerSlot = CompilerTextSlot | CompilerTableSlot | CompilerCodeSlot | CompilerMermaidSlot;
235
+ export type CompilerSlot = {
236
+ key: string;
237
+ accepts: CompilerBlock[];
238
+ frame?: Frame;
239
+ limit?: Limit;
240
+ /**
241
+ * Whether the slot may be omitted from a slide. Optional (defaults to false):
242
+ * a required slot with no content throws with layout + key names.
243
+ */
244
+ required?: boolean;
245
+ };
226
246
  export type CompilerLayout = {
227
247
  name: string;
228
248
  slideNumber: number;
229
- description: string;
230
- whenToUse: string;
231
- whenNotToUse: string;
249
+ /** Optional prose — a layout is a shape, not a purpose (agent-guidance descoped). */
250
+ description?: string;
251
+ whenToUse?: string;
252
+ whenNotToUse?: string;
253
+ /**
254
+ * The layout's tonal surface. Selects the arm of a `{ light, dark }` `codeTheme`
255
+ * pair for code fences on this layout. Required when `codeTheme` is a pair.
256
+ */
257
+ variant?: Variant;
232
258
  /** Frontmatter inputs (template, image) — one value per `key: value` line. */
233
259
  parameters: CompilerParameter[];
234
- /** Body regions (text, table, code, mermaid) — the body or `::name::` regions. */
260
+ /** Body regions — the default body or `::name::` regions; each `accepts` blocks. */
235
261
  slots: CompilerSlot[];
236
262
  };
263
+ /**
264
+ * A brand font the theme hands the compiler for HTML-based rendering (currently
265
+ * mermaid). Purely compiler-facing — the engine fills PPTX shapes, whose fonts
266
+ * live in the template, and never sees this. `path` is a module specifier
267
+ * (`@fontsource/inter/files/inter-latin-400-normal.woff2`) resolved from the
268
+ * theme's directory, or a `./`- / `/`-prefixed filesystem path resolved against
269
+ * `rootDir`; `family` is the CSS `font-family` name the mermaid variant's
270
+ * `fontFamily` must match. Register weight 400 + 700 to avoid synthetic bold on
271
+ * node labels. The mechanism is generic — the theme names its own fonts.
272
+ */
273
+ export type ThemeFont = {
274
+ family: string;
275
+ path: string;
276
+ /** OpenType weight (default 400). */
277
+ weight?: number;
278
+ };
237
279
  /**
238
280
  * Compiler-facing theme configuration. Mirrors the engine's ThemeConfig but
239
281
  * carries markdown-flavored fields (mermaid variants) that live outside the
@@ -246,8 +288,26 @@ export type CompilerThemeConfig = {
246
288
  template: string;
247
289
  outputDir?: string;
248
290
  mermaid?: MermaidConfig;
291
+ /**
292
+ * Brand fonts injected as `@font-face` when rendering mermaid, so diagram text
293
+ * uses the theme font instead of a Chromium fallback. Optional — with none
294
+ * declared, mermaid renders in whatever the OS substitutes.
295
+ */
296
+ fonts?: ThemeFont[];
297
+ /**
298
+ * Theme-level defaults for the two content types the compiler resolves before
299
+ * the engine sees them. One code style and one mermaid style per theme (the
300
+ * design-system framing) — they can't sit on a multi-type slot. `codeTheme` is
301
+ * a Shiki theme id, or a `{ light, dark }` pair when the theme has both light
302
+ * and dark code layouts (each layout's `variant` picks the arm);
303
+ * `mermaidVariant` names an entry in `mermaid`.
304
+ */
305
+ codeTheme?: string | {
306
+ light: string;
307
+ dark: string;
308
+ };
309
+ mermaidVariant?: string;
249
310
  };
250
311
  export type CompilerConfig = CompilerThemeConfig & {
251
312
  rootDir: string;
252
313
  };
253
- export {};
@@ -1,4 +1,4 @@
1
- import { SlotType } from "../engine/index.js";
1
+ import { SlotType, } from "../engine/index.js";
2
2
  // ── Asset catalog (compiler / theme-metadata only) ───────────────────────────
3
3
  /**
4
4
  * An asset's scaling/cropping tolerance, declared in the theme catalog. The
@@ -10,18 +10,6 @@ export const AssetType = {
10
10
  Image: "image",
11
11
  Background: "background",
12
12
  };
13
- // ── FenceType discriminator ───────────────────────────────────────────────────
14
- /**
15
- * Discriminator strings for the compiler-internal fence shapes produced by
16
- * deckCompiler.ts. `Code` and `Mermaid` are resolved away entirely (syntax
17
- * highlighting → TextFill, PNG rendering → ImageFill), so neither reaches the
18
- * engine. Prose has no FenceType: it becomes a `TextFill` (`{ paragraphs }`),
19
- * one of the engine's own fill shapes.
20
- */
21
- export const FenceType = {
22
- Code: "code",
23
- Mermaid: "mermaid",
24
- };
25
13
  // ── ParameterType discriminator (frontmatter, one value) ──────────────────────
26
14
  /**
27
15
  * Discriminator for a layout's frontmatter *parameters* — inputs the author
@@ -34,18 +22,41 @@ export const ParameterType = {
34
22
  Template: SlotType.Template,
35
23
  Image: SlotType.Image,
36
24
  };
37
- // ── CompilerSlotType discriminator (body region, multi-line) ──────────────────
25
+ // ── AcceptType discriminator (what a slot accepts) ────────────────────────────
38
26
  /**
39
- * Compiler-facing slot type for a layout's *slots* multi-line body regions
40
- * (the default body region, or a `::name::` region). Narrowed to four kinds;
41
- * `Template` and `Image` left this list to become parameters. `Code` and `Mermaid`
42
- * are compiler-only, resolved (syntax highlighting, mermaid PNG rendering)
43
- * before the engine sees the deck. Required on every CompilerSlot — no silent
44
- * default.
27
+ * The engine content types a slot may `accept`. A subset of the engine's
28
+ * `SlotType`: `Template` is parameter-only, never a body block. There is no
29
+ * `code` / `mermaid` accept type a code fence folds into a `Text`-accepting
30
+ * slot (syntax highlighting → TextFill) and a mermaid fence into an
31
+ * `Image`-accepting slot (PNG render ImageFill), the same fold the engine
32
+ * already performs. The author's markdown shape selects which accepted type a
33
+ * region routes to.
45
34
  */
46
- export const CompilerSlotType = {
35
+ export const AcceptType = {
47
36
  Text: SlotType.Text,
48
37
  Table: SlotType.Table,
49
- Code: FenceType.Code,
50
- Mermaid: FenceType.Mermaid,
38
+ Image: SlotType.Image,
39
+ };
40
+ // ── Variant discriminator (a layout's light/dark surface) ─────────────────────
41
+ /**
42
+ * The tonal surface a layout sits on. A code layout on a light panel needs a
43
+ * light Shiki theme (dark syntax would wash out); one on a dark panel needs a
44
+ * dark theme. A layout declares its `variant`; the code compile resolves a
45
+ * `{ light, dark }` `codeTheme` pair against it (a pair with no `variant` throws).
46
+ */
47
+ export const Variant = {
48
+ Light: "light",
49
+ Dark: "dark",
50
+ };
51
+ /**
52
+ * Reserved keys in a deck's frontmatter — global (theme, output) and per-slide
53
+ * (layout, body). Exported so callers (e.g. cli.ts) reference the constants
54
+ * instead of literal strings.
55
+ */
56
+ export const RESERVED_KEY = {
57
+ LAYOUT: "layout",
58
+ BODY: "body",
59
+ OUTPUT: "output",
60
+ THEME: "theme",
61
+ NOTES: "notes",
51
62
  };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Rewrite the `name:` value in a skill.md's leading YAML frontmatter so the
3
+ * packaged skill is named after the consuming theme, not the source template.
4
+ * Only the leading `---`…`---` block is touched; the body is left byte-for-byte.
5
+ * Throws if there is no frontmatter or no `name:` line — the caller names the file.
6
+ */
7
+ export declare function renameSkill(md: string, name: string): string;
8
+ /**
9
+ * Build an uploadable Agent Skill zip whose entries live under a single root
10
+ * folder (e.g. `mz-slides/skill.md`), matching Anthropic's custom-skill format.
11
+ * Each entry is written from an explicit in-memory list rather than a directory
12
+ * scan, so the zip contains exactly the files the caller passes — nothing else.
13
+ */
14
+ export declare function zipSkill(folderName: string, files: {
15
+ name: string;
16
+ content: string | Buffer;
17
+ }[]): Promise<Buffer>;
@@ -0,0 +1,35 @@
1
+ import JSZip from "jszip";
2
+ const FRONTMATTER = /^---\n([\s\S]*?)\n---/;
3
+ const NAME_LINE = /^name:[ \t]*.*$/m;
4
+ /**
5
+ * Rewrite the `name:` value in a skill.md's leading YAML frontmatter so the
6
+ * packaged skill is named after the consuming theme, not the source template.
7
+ * Only the leading `---`…`---` block is touched; the body is left byte-for-byte.
8
+ * Throws if there is no frontmatter or no `name:` line — the caller names the file.
9
+ */
10
+ export function renameSkill(md, name) {
11
+ const block = md.match(FRONTMATTER);
12
+ if (!block)
13
+ throw new Error("skill.md has no YAML frontmatter block");
14
+ if (!NAME_LINE.test(block[1]))
15
+ throw new Error('skill.md frontmatter has no "name:" line');
16
+ return md.replace(block[0], block[0].replace(NAME_LINE, `name: ${name}`));
17
+ }
18
+ /**
19
+ * Build an uploadable Agent Skill zip whose entries live under a single root
20
+ * folder (e.g. `mz-slides/skill.md`), matching Anthropic's custom-skill format.
21
+ * Each entry is written from an explicit in-memory list rather than a directory
22
+ * scan, so the zip contains exactly the files the caller passes — nothing else.
23
+ */
24
+ export async function zipSkill(folderName, files) {
25
+ if (files.length === 0)
26
+ throw new Error(`No files to zip for skill folder: ${folderName}`);
27
+ const zip = new JSZip();
28
+ const folder = zip.folder(folderName);
29
+ if (!folder)
30
+ throw new Error(`Failed to create zip folder: ${folderName}`);
31
+ for (const file of files) {
32
+ folder.file(file.name, file.content);
33
+ }
34
+ return zip.generateAsync({ type: "nodebuffer" });
35
+ }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@tycoworks/tycoslide",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
+ "description": "Create editable, on-brand PowerPoint slides from markdown.",
4
5
  "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
@@ -22,17 +23,20 @@
22
23
  "format": "biome format --write ."
23
24
  },
24
25
  "dependencies": {
25
- "@mermaid-js/mermaid-cli": "^11.15.0",
26
26
  "@xmldom/xmldom": "^0.9.10",
27
27
  "commander": "^15.0.0",
28
28
  "image-size": "^2.0.2",
29
+ "jszip": "^3.10.1",
30
+ "mermaid": "^11.16.1",
31
+ "playwright": "^1.58.2",
29
32
  "pptx-automizer": "^0.8.2",
30
33
  "remark-gfm": "^4.0.1",
31
34
  "remark-ins": "^1.2.5",
32
35
  "remark-parse": "^11.0.0",
33
36
  "shiki": "^4.3.0",
34
37
  "unified": "^11.0.0",
35
- "yaml": "^2.0.0"
38
+ "yaml": "^2.0.0",
39
+ "zod": "^4.4.3"
36
40
  },
37
41
  "devDependencies": {
38
42
  "@biomejs/biome": "^2.5.1",
package/syntax.md CHANGED
@@ -238,7 +238,7 @@ notes: |
238
238
  ---
239
239
  ```
240
240
 
241
- To build with all speaker notes omitted, pass `--no-notes` to `tycoslide build` (see [README](README.md#cli)).
241
+ To build with all speaker notes omitted, pass `--no-notes` to `npx tycoslide build` (see [README](README.md#cli)).
242
242
 
243
243
  ---
244
244
 
@@ -1,32 +0,0 @@
1
- import type { StyledParagraph, TableFill, TextRun } from "../engine/index.js";
2
- /**
3
- * Parse inline markdown formatting in a single line of text into TextRun arrays.
4
- * Handles **bold**, *italic*, ***bold italic***, ~~strikethrough~~, ++underline++,
5
- * [link](url), and `inline code`.
6
- */
7
- export declare function parseInlineRuns(text: string): TextRun[];
8
- /**
9
- * Parse a raw prose line into a StyledParagraph with bullet detection and inline
10
- * formatting. The caller provides the raw line including leading whitespace and
11
- * bullet markers.
12
- */
13
- export declare function parseStyledParagraph(raw: string): StyledParagraph;
14
- /**
15
- * Parse a single line of prose into its structural components.
16
- *
17
- * Lines beginning with `- ` or `* ` (optionally preceded by spaces) are
18
- * recognized as bullet items. Every 2 spaces of leading indent on a bullet
19
- * line increase its level by 1. A leading dash without a trailing space is
20
- * NOT a bullet (e.g. `-foo`).
21
- */
22
- export declare function parseProseLine(raw: string): {
23
- text: string;
24
- bullet: boolean;
25
- level: number;
26
- };
27
- /**
28
- * Parse a GFM table into TableFill (StyledParagraph[] cells). Returns null
29
- * for non-table text. Cells carry rich runs so inline formatting works
30
- * inside tables for free.
31
- */
32
- export declare function parseGfmTable(text: string): TableFill | null;