@tycoworks/tycoslide 0.7.0 → 0.9.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 (62) hide show
  1. package/README.md +6 -6
  2. package/SKILL.md +2 -1
  3. package/dist/cli.js +8 -107
  4. package/dist/engine/dom.d.ts +5 -0
  5. package/dist/engine/dom.js +19 -3
  6. package/dist/engine/fillers/filler.d.ts +21 -13
  7. package/dist/engine/fillers/filler.js +21 -27
  8. package/dist/engine/fillers/image.d.ts +46 -7
  9. package/dist/engine/fillers/image.js +78 -36
  10. package/dist/engine/fillers/table.d.ts +4 -3
  11. package/dist/engine/fillers/table.js +58 -9
  12. package/dist/engine/generate.d.ts +34 -18
  13. package/dist/engine/generate.js +234 -65
  14. package/dist/engine/index.d.ts +3 -2
  15. package/dist/engine/index.js +1 -1
  16. package/dist/engine/notes.d.ts +76 -0
  17. package/dist/engine/notes.js +313 -0
  18. package/dist/engine/types.d.ts +57 -24
  19. package/dist/engine/types.js +11 -7
  20. package/dist/index.d.ts +19 -25
  21. package/dist/index.js +65 -92
  22. package/dist/manifest.js +19 -29
  23. package/dist/markdown/blocks/code.d.ts +15 -0
  24. package/dist/markdown/blocks/code.js +50 -0
  25. package/dist/markdown/blocks/image.d.ts +2 -0
  26. package/dist/markdown/blocks/image.js +9 -0
  27. package/dist/markdown/blocks/mermaid.d.ts +15 -0
  28. package/dist/markdown/blocks/mermaid.js +227 -0
  29. package/dist/markdown/{resolvers → blocks}/mermaidTheme.d.ts +1 -1
  30. package/dist/markdown/{resolvers → blocks}/mermaidTheme.js +1 -1
  31. package/dist/markdown/blocks/registry.d.ts +16 -0
  32. package/dist/markdown/blocks/registry.js +44 -0
  33. package/dist/markdown/blocks/table.d.ts +2 -0
  34. package/dist/markdown/blocks/table.js +23 -0
  35. package/dist/markdown/blocks/text.d.ts +12 -0
  36. package/dist/markdown/blocks/text.js +90 -0
  37. package/dist/markdown/deckCompiler.d.ts +18 -30
  38. package/dist/markdown/deckCompiler.js +167 -122
  39. package/dist/markdown/index.d.ts +11 -11
  40. package/dist/markdown/index.js +9 -8
  41. package/dist/markdown/inline.d.ts +26 -0
  42. package/dist/markdown/inline.js +136 -0
  43. package/dist/markdown/mdast.d.ts +25 -0
  44. package/dist/markdown/mdast.js +49 -0
  45. package/dist/markdown/schema/deckSchema.d.ts +30 -0
  46. package/dist/markdown/schema/deckSchema.js +51 -0
  47. package/dist/markdown/schema/strict.d.ts +9 -0
  48. package/dist/markdown/schema/strict.js +18 -0
  49. package/dist/markdown/schema/themeConfigSchema.d.ts +99 -0
  50. package/dist/markdown/schema/themeConfigSchema.js +145 -0
  51. package/dist/markdown/types.d.ts +184 -137
  52. package/dist/markdown/types.js +30 -19
  53. package/package.json +7 -3
  54. package/syntax.md +25 -5
  55. package/dist/markdown/parsers.d.ts +0 -32
  56. package/dist/markdown/parsers.js +0 -233
  57. package/dist/markdown/resolvers/code.d.ts +0 -17
  58. package/dist/markdown/resolvers/code.js +0 -44
  59. package/dist/markdown/resolvers/mermaid.d.ts +0 -14
  60. package/dist/markdown/resolvers/mermaid.js +0 -89
  61. package/dist/markdown/resolvers/resolver.d.ts +0 -42
  62. package/dist/markdown/resolvers/resolver.js +0 -52
@@ -1,5 +1,17 @@
1
- import { type FitMode, 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";
4
+ /**
5
+ * An asset's scaling/cropping tolerance, declared in the theme catalog. The
6
+ * compiler maps it to the engine's object-fit `fit`. `icon`: never enlarge,
7
+ * never crop. `image`: never crop, may scale. `background`: crop and scale freely.
8
+ */
9
+ export declare const AssetType: {
10
+ readonly Icon: "icon";
11
+ readonly Image: "image";
12
+ readonly Background: "background";
13
+ };
14
+ export type AssetType = (typeof AssetType)[keyof typeof AssetType];
3
15
  /**
4
16
  * A theme's declaration of a reusable image asset. Purely compiler-facing —
5
17
  * the engine never sees this type; the compiler resolves an entry's `path`
@@ -8,23 +20,13 @@ import type { MermaidConfig } from "./resolvers/mermaidTheme.js";
8
20
  */
9
21
  export type AssetEntry = {
10
22
  path: string;
23
+ /** Required — a missing type is a fail-fast error. */
24
+ type: AssetType;
11
25
  description: string;
12
26
  whenToUse?: string;
13
27
  };
14
28
  /** Two-level catalog: `{ category: { name: AssetEntry } }`. */
15
29
  export type AssetCatalog = Record<string, Record<string, AssetEntry>>;
16
- /**
17
- * Discriminator strings for the compiler-internal fence shapes produced by
18
- * deckCompiler.ts. `Code` and `Mermaid` are resolved away entirely (syntax
19
- * highlighting → TextFill, PNG rendering → ImageFill), so neither reaches the
20
- * engine. Prose has no FenceType: it becomes a `TextFill` (`{ paragraphs }`),
21
- * one of the engine's own fill shapes.
22
- */
23
- export declare const FenceType: {
24
- readonly Code: "code";
25
- readonly Mermaid: "mermaid";
26
- };
27
- export type FenceType = (typeof FenceType)[keyof typeof FenceType];
28
30
  /**
29
31
  * Discriminator for a layout's frontmatter *parameters* — inputs the author
30
32
  * writes as a single `key: value` line. Two kinds: `Template` (fills a styled
@@ -38,58 +40,84 @@ export declare const ParameterType: {
38
40
  };
39
41
  export type ParameterType = (typeof ParameterType)[keyof typeof ParameterType];
40
42
  /**
41
- * Compiler-facing slot type for a layout's *slots* multi-line body regions
42
- * (the default body region, or a `::name::` region). Narrowed to four kinds;
43
- * `Template` and `Image` left this list to become parameters. `Code` and `Mermaid`
44
- * are compiler-only, resolved (syntax highlighting, mermaid PNG rendering)
45
- * before the engine sees the deck. Required on every CompilerSlot — no silent
46
- * 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.
47
50
  */
48
- export declare const CompilerSlotType: {
51
+ export declare const AcceptType: {
49
52
  readonly Text: "text";
50
53
  readonly Table: "table";
51
- readonly Code: "code";
52
- readonly Mermaid: "mermaid";
54
+ readonly Image: "image";
53
55
  };
54
- export type CompilerSlotType = (typeof CompilerSlotType)[keyof typeof CompilerSlotType];
56
+ export type AcceptType = (typeof AcceptType)[keyof typeof AcceptType];
55
57
  /**
56
- * A fenced code block awaiting syntax highlighting. Produced by deckCompiler,
57
- * consumed by CodeResolver. Never crosses the engine boundary.
58
+ * The four engine content shapes a slot's compiled content can be — the value
59
+ * `compile` produces and the engine fills. ImageFill carries a `.type`
60
+ * discriminator; the other three — TextFill, TableFill, TemplateFill — are
61
+ * identified structurally by their signature field.
58
62
  */
59
- export type CodeFence = {
60
- type: typeof FenceType.Code;
61
- language: string;
62
- source: string;
63
- };
63
+ export type EngineFill = TextFill | TableFill | ImageFill | TemplateFill;
64
+ /**
65
+ * The three engine content shapes a block handler can produce. A subset of
66
+ * `EngineFill` — `TemplateFill` is parameter-only (`AcceptType` excludes
67
+ * `Template`), so no block handler ever compiles one.
68
+ */
69
+ export type BlockFill = TextFill | TableFill | ImageFill;
64
70
  /**
65
- * A mermaid code fence awaiting rendering. Produced by deckCompiler, consumed
66
- * by MermaidResolver the resulting PNG is wrapped as an ImageFill in
67
- * step.content, so the engine never learns mermaid exists.
71
+ * Everything a block handler needs to compile a node into its engine fill: the
72
+ * asset resolver, the diagnostic context to name the offending layout/slide/slot
73
+ * when a region's markdown shape is illegal (a stray standalone block mixed into
74
+ * prose), and the theme-level `config` — from which code/mermaid compile read
75
+ * their one-per-theme style (`codeTheme`, `mermaid`, `mermaidVariant`,
76
+ * `outputDir`), not the slot.
77
+ *
78
+ * Lives in types.ts (not in `blocks/registry.ts`, which re-exports it) so a
79
+ * per-kind block file can import it without importing the registry — the registry
80
+ * imports the per-kind handlers, so the reverse would cycle.
68
81
  */
69
- export type MermaidFence = {
70
- type: typeof FenceType.Mermaid;
71
- definition: string;
82
+ export type BlockContext = {
83
+ resolveAssetRef: (ref: string) => ImageFill;
84
+ layoutName: string;
85
+ slideIdx: number;
86
+ source: string;
87
+ config: CompilerConfig;
72
88
  };
73
89
  /**
74
- * The full set of value shapes a slot may hold during compilation, before code
75
- * fences are highlighted (→ TextFill) and mermaid fences are rendered
76
- * (→ ImageFill). CodeFence, MermaidFence, and ImageFill carry a `.type`
77
- * discriminator; the three engine fills without one TextFill, TableFill,
78
- * TemplateFillare identified structurally by their signature field.
90
+ * A block handler recognizes one content kind at the region's top level, folds
91
+ * it to the engine `AcceptType` it fills, and compiles the node straight into the
92
+ * matching engine fill highlighting a code fence to a TextFill, rendering a
93
+ * mermaid fence to an ImageFill. Recognition (`match`), type (`acceptType`), and
94
+ * build (`compile`) live together a new content kind is one file, one registry
95
+ * row. Non-generic like the engine's `Filler`: the array can't correlate a
96
+ * per-element type guard with `compile`'s param, so each handler narrows the node
97
+ * internally with a single-hop cast. Mirrors the old sdk's `SyntaxHandler`.
79
98
  */
80
- export type MarkdownBlock = TextFill | TableFill | CodeFence | MermaidFence | ImageFill | TemplateFill;
99
+ export type BlockHandler = {
100
+ match(node: RootContent): boolean;
101
+ acceptType: AcceptType;
102
+ compile(node: RootContent, ctx: BlockContext): Promise<BlockFill>;
103
+ };
81
104
  /**
82
- * A DeckStep as compileDeck produces it content values may still be
83
- * unresolved (CodeFence, MermaidFence) at this point. `resolveFences` narrows
84
- * these into StyledParagraph[] / ImageFill before the engine sees the deck.
105
+ * A DeckStep as the compiler produces it. Content is already engine-shaped
106
+ * code fences highlighted to TextFill and mermaid fences rendered to ImageFill
107
+ * during `compile` so this is structurally equivalent to the engine's DeckStep
108
+ * and needs no further resolution.
85
109
  */
86
110
  export type CompilerDeckStep = {
87
111
  layout: string;
88
- content?: Record<string, MarkdownBlock>;
112
+ content?: Record<string, EngineFill>;
113
+ /** Slide-level speaker notes, stripped from frontmatter. Plain text. */
114
+ notes?: string;
89
115
  };
90
116
  /**
91
- * The intermediate deck shape produced by compileDeck. buildDeck runs the
92
- * resolvers and hands the resulting engine-shaped Deck to generate().
117
+ * The deck shape the compiler produces. `output` may be unset until a caller
118
+ * (the CLI, or a programmatic caller) assigns one; `buildDeck` fails fast if it
119
+ * is still missing. Once `output` is present this is structurally equivalent to
120
+ * the engine's Deck, so `generate` consumes it without any cast.
93
121
  */
94
122
  export type CompilerDeck = {
95
123
  theme: string;
@@ -97,50 +125,15 @@ export type CompilerDeck = {
97
125
  steps: CompilerDeckStep[];
98
126
  };
99
127
  /**
100
- * A DeckStep whose content values have been narrowed post-resolution no
101
- * CodeFence or MermaidFence remains, only shapes the engine understands.
102
- * Structurally equivalent to the engine's DeckStep.
128
+ * Markdown-flavored measurement hints on a slot or template parameter: caps on
129
+ * expanded run text, line count, or list items. All optional — a missing cap is
130
+ * "no limit." Advisory metadata surfaced in the manifest; the fill never enforces
131
+ * it.
103
132
  */
104
- export type ResolvedCompilerDeckStep = {
105
- layout: string;
106
- content?: Record<string, TextFill | TableFill | ImageFill | TemplateFill>;
107
- };
108
- /**
109
- * A CompilerDeck whose steps have been passed through the resolvers via
110
- * `resolveFences`. Structurally equivalent to the engine's Deck — deck
111
- * resolution returns this shape so `generate` can consume it without any cast.
112
- */
113
- export type ResolvedCompilerDeck = {
114
- theme: string;
115
- output: string;
116
- steps: ResolvedCompilerDeckStep[];
117
- };
118
- /**
119
- * Fields common to every text shape, image parameter, and slot. A text shape is
120
- * addressed by `shapeName` and owns a `template` (its placeholders are the
121
- * fillable keys); image parameters and slots additionally carry a single `key`.
122
- */
123
- type CompilerShapeBase = {
124
- shapeName: string;
125
- limit?: {
126
- maxChars?: number;
127
- maxLines?: number;
128
- maxItems?: number;
129
- };
130
- /**
131
- * Whether the parameter/slot may be omitted from a slide. Optional (defaults
132
- * to false): a required one with no value causes the compiler to throw with
133
- * layout + key names.
134
- */
135
- required?: boolean;
136
- };
137
- /**
138
- * Shape base plus a single `key` — the addressing model for image parameters
139
- * and every slot. Template parameters do NOT extend this: their fillable keys are
140
- * the placeholders in their `template`, not a single top-level key.
141
- */
142
- type CompilerSlotBase = CompilerShapeBase & {
143
- key: string;
133
+ export type Limit = {
134
+ maxChars?: number;
135
+ maxLines?: number;
136
+ maxItems?: number;
144
137
  };
145
138
  /**
146
139
  * Template parameter: a styled text shape filled by expanding a `template` into the
@@ -150,76 +143,117 @@ type CompilerSlotBase = CompilerShapeBase & {
150
143
  * placeholders in its template. The shape carries no top-level `key`; its
151
144
  * placeholders are the keys the author fills in frontmatter.
152
145
  */
153
- export type CompilerTemplateParameter = CompilerShapeBase & {
146
+ export type CompilerTemplateParameter = {
147
+ shapeName: string;
148
+ limit?: Limit;
149
+ /**
150
+ * Whether the parameter may be omitted from a slide. Optional (defaults to
151
+ * false): a required one with no value causes the compiler to throw with
152
+ * layout + key names.
153
+ */
154
+ required?: boolean;
154
155
  type: typeof ParameterType.Template;
155
156
  /** The shape's text as one template with `{key}` placeholders; newlines are line breaks. */
156
157
  template: string;
157
158
  };
158
- /** Image parameter: one frontmatter path filled by fillImage. */
159
- export type CompilerImageParameter = CompilerSlotBase & {
159
+ /** Image parameter: one frontmatter path filled by fillImage. Sizing/crop
160
+ * behaviour comes from the resolved asset's `type`, not the slot. Carries no
161
+ * `limit` — measuring an image path against char/line caps is meaningless. */
162
+ export type CompilerImageParameter = {
163
+ shapeName: string;
164
+ /**
165
+ * Whether the parameter may be omitted from a slide. Optional (defaults to
166
+ * false): a required one with no value causes the compiler to throw with
167
+ * layout + key names.
168
+ */
169
+ required?: boolean;
170
+ key: string;
160
171
  type: typeof ParameterType.Image;
161
- /** How the picture scales inside its frame (required — no silent default). */
162
- fit: FitMode;
163
172
  };
164
173
  /**
165
174
  * Compiler-facing parameter. A layout's frontmatter inputs — each written as a
166
175
  * single `key: value` line, resolved against the layout's `parameters` list.
167
176
  */
168
177
  export type CompilerParameter = CompilerTemplateParameter | CompilerImageParameter;
169
- /** Multi-paragraph body filled by fillText (specimen-style rebuild). */
170
- export type CompilerTextSlot = CompilerSlotBase & {
171
- type: typeof CompilerSlotType.Text;
172
- /** Leave the first N specimen paragraphs untouched. */
173
- startAt?: number;
174
- };
175
- /** Table shape backed by an `<a:tbl>` with header + data specimen rows. */
176
- export type CompilerTableSlot = CompilerSlotBase & {
177
- type: typeof CompilerSlotType.Table;
178
- /** Enforced column count. */
179
- columns?: number;
180
- };
181
178
  /**
182
- * Text slot that consumes a fenced code block. The resolver (`CodeResolver`)
183
- * uses `codeTheme` to Shiki-highlight the source into StyledParagraph[]; the
184
- * engine sees the projected Text slot.
179
+ * Reserved keys in a deck's frontmatter global (theme, output) and per-slide
180
+ * (layout, body). Exported so callers (e.g. cli.ts) reference the constants
181
+ * instead of literal strings.
185
182
  */
186
- export type CompilerCodeSlot = CompilerSlotBase & {
187
- type: typeof CompilerSlotType.Code;
188
- /** Shiki theme id (required — no silent default). */
189
- codeTheme: string;
183
+ export declare const RESERVED_KEY: {
184
+ readonly LAYOUT: "layout";
185
+ readonly BODY: "body";
186
+ readonly OUTPUT: "output";
187
+ readonly THEME: "theme";
188
+ readonly NOTES: "notes";
190
189
  };
191
190
  /**
192
- * Slot that consumes a mermaid code fence. The resolver (`MermaidResolver`)
193
- * uses `mermaidVariant` to select colors and renders a PNG; the engine sees the
194
- * projected Image slot with `contain` fit. Mermaid slots do NOT declare `fit` —
195
- * mermaid diagrams are always contained.
191
+ * A kind of content a slot accepts, and the real template shape that realizes
192
+ * it the compiler mirror of the engine's `Block`. `type` is an engine content
193
+ * type (`text` | `table` | `image`); `shapeName` names the shape on
194
+ * `sourceSlide` carrying the specimen styling. When `sourceSlide` equals the
195
+ * layout's `slideNumber` the shape is already on the cloned slide (fill in
196
+ * place); otherwise it is transplanted into the slot's `frame`. `startAt` is a
197
+ * text-specimen concern (leave the first N specimen paragraphs untouched), only
198
+ * meaningful on a text block. Named `CompilerBlock` to stay distinct from the
199
+ * engine's `Block` and the compiler's `BlockHandler`.
196
200
  */
197
- export type CompilerMermaidSlot = CompilerSlotBase & {
198
- type: typeof CompilerSlotType.Mermaid;
199
- /** Name of the mermaid variant (required — no silent default). */
200
- mermaidVariant: string;
201
+ export type CompilerBlock = {
202
+ type: AcceptType;
203
+ sourceSlide: number;
204
+ shapeName: string;
205
+ startAt?: number;
201
206
  };
202
207
  /**
203
- * Compiler-facing slot. Discriminated union on `type` each variant declares
204
- * ONLY the fields legal for that slot type. A layout's body regions (the
205
- * default region, or a `::name::` region), resolved against the layout's
206
- * `slots` list. Adds markdown-flavored concepts (limit hints, code-fence theme
207
- * selection, mermaid variant naming) on top of the engine's minimal Slot.
208
- * Compiler-only types (Code, Mermaid) are projected down to engine types (Text,
209
- * Image) at the engine boundary before the engine sees the layout.
208
+ * Compiler-facing slot. A layout's body region (the default region, or a
209
+ * `::name::` region), no longer welded to one shape+type: it `accepts` a set of
210
+ * `CompilerBlock`s and owns a `frame` (real observed EMU coordinates, never
211
+ * computed). The author's markdown shape selects which accepted block a region
212
+ * routes to; a type the slot does not accept fails fast. `frame` is required
213
+ * only when a slot has a transplant block (a block whose `sourceSlide` differs
214
+ * from the layout's `slideNumber`); a base-only slot fills in place and needs
215
+ * none. Adds markdown-flavored `limit` hints on top of the engine's minimal
216
+ * Slot; per-slot `codeTheme` / `mermaidVariant` moved to the theme level.
210
217
  */
211
- export type CompilerSlot = CompilerTextSlot | CompilerTableSlot | CompilerCodeSlot | CompilerMermaidSlot;
218
+ export type CompilerSlot = {
219
+ key: string;
220
+ accepts: CompilerBlock[];
221
+ frame?: Frame;
222
+ limit?: Limit;
223
+ /**
224
+ * Whether the slot may be omitted from a slide. Optional (defaults to false):
225
+ * a required slot with no content throws with layout + key names.
226
+ */
227
+ required?: boolean;
228
+ };
212
229
  export type CompilerLayout = {
213
230
  name: string;
214
231
  slideNumber: number;
215
- description: string;
216
- whenToUse: string;
217
- whenNotToUse: string;
232
+ /** Optional prose — a layout is a shape, not a purpose (agent-guidance descoped). */
233
+ description?: string;
234
+ whenToUse?: string;
235
+ whenNotToUse?: string;
218
236
  /** Frontmatter inputs (template, image) — one value per `key: value` line. */
219
237
  parameters: CompilerParameter[];
220
- /** Body regions (text, table, code, mermaid) — the body or `::name::` regions. */
238
+ /** Body regions — the default body or `::name::` regions; each `accepts` blocks. */
221
239
  slots: CompilerSlot[];
222
240
  };
241
+ /**
242
+ * A brand font the theme hands the compiler for HTML-based rendering (currently
243
+ * mermaid). Purely compiler-facing — the engine fills PPTX shapes, whose fonts
244
+ * live in the template, and never sees this. `path` is a module specifier
245
+ * (`@fontsource/inter/files/inter-latin-400-normal.woff2`) resolved from the
246
+ * theme's directory, or a `./`- / `/`-prefixed filesystem path resolved against
247
+ * `rootDir`; `family` is the CSS `font-family` name the mermaid variant's
248
+ * `fontFamily` must match. Register weight 400 + 700 to avoid synthetic bold on
249
+ * node labels. The mechanism is generic — the theme names its own fonts.
250
+ */
251
+ export type ThemeFont = {
252
+ family: string;
253
+ path: string;
254
+ /** OpenType weight (default 400). */
255
+ weight?: number;
256
+ };
223
257
  /**
224
258
  * Compiler-facing theme configuration. Mirrors the engine's ThemeConfig but
225
259
  * carries markdown-flavored fields (mermaid variants) that live outside the
@@ -232,8 +266,21 @@ export type CompilerThemeConfig = {
232
266
  template: string;
233
267
  outputDir?: string;
234
268
  mermaid?: MermaidConfig;
269
+ /**
270
+ * Brand fonts injected as `@font-face` when rendering mermaid, so diagram text
271
+ * uses the theme font instead of a Chromium fallback. Optional — with none
272
+ * declared, mermaid renders in whatever the OS substitutes.
273
+ */
274
+ fonts?: ThemeFont[];
275
+ /**
276
+ * Theme-level defaults for the two content types the compiler resolves before
277
+ * the engine sees them. One code style and one mermaid style per theme (the
278
+ * design-system framing) — they can't sit on a multi-type slot. `codeTheme` is
279
+ * a Shiki theme id; `mermaidVariant` names an entry in `mermaid`.
280
+ */
281
+ codeTheme?: string;
282
+ mermaidVariant?: string;
235
283
  };
236
284
  export type CompilerConfig = CompilerThemeConfig & {
237
285
  rootDir: string;
238
286
  };
239
- export {};
@@ -1,15 +1,14 @@
1
1
  import { SlotType, } from "../engine/index.js";
2
- // ── FenceType discriminator ───────────────────────────────────────────────────
2
+ // ── Asset catalog (compiler / theme-metadata only) ───────────────────────────
3
3
  /**
4
- * Discriminator strings for the compiler-internal fence shapes produced by
5
- * deckCompiler.ts. `Code` and `Mermaid` are resolved away entirely (syntax
6
- * highlighting TextFill, PNG rendering ImageFill), so neither reaches the
7
- * engine. Prose has no FenceType: it becomes a `TextFill` (`{ paragraphs }`),
8
- * one of the engine's own fill shapes.
4
+ * An asset's scaling/cropping tolerance, declared in the theme catalog. The
5
+ * compiler maps it to the engine's object-fit `fit`. `icon`: never enlarge,
6
+ * never crop. `image`: never crop, may scale. `background`: crop and scale freely.
9
7
  */
10
- export const FenceType = {
11
- Code: "code",
12
- Mermaid: "mermaid",
8
+ export const AssetType = {
9
+ Icon: "icon",
10
+ Image: "image",
11
+ Background: "background",
13
12
  };
14
13
  // ── ParameterType discriminator (frontmatter, one value) ──────────────────────
15
14
  /**
@@ -23,18 +22,30 @@ export const ParameterType = {
23
22
  Template: SlotType.Template,
24
23
  Image: SlotType.Image,
25
24
  };
26
- // ── CompilerSlotType discriminator (body region, multi-line) ──────────────────
25
+ // ── AcceptType discriminator (what a slot accepts) ────────────────────────────
27
26
  /**
28
- * Compiler-facing slot type for a layout's *slots* multi-line body regions
29
- * (the default body region, or a `::name::` region). Narrowed to four kinds;
30
- * `Template` and `Image` left this list to become parameters. `Code` and `Mermaid`
31
- * are compiler-only, resolved (syntax highlighting, mermaid PNG rendering)
32
- * before the engine sees the deck. Required on every CompilerSlot — no silent
33
- * 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.
34
34
  */
35
- export const CompilerSlotType = {
35
+ export const AcceptType = {
36
36
  Text: SlotType.Text,
37
37
  Table: SlotType.Table,
38
- Code: FenceType.Code,
39
- Mermaid: FenceType.Mermaid,
38
+ Image: SlotType.Image,
39
+ };
40
+ /**
41
+ * Reserved keys in a deck's frontmatter — global (theme, output) and per-slide
42
+ * (layout, body). Exported so callers (e.g. cli.ts) reference the constants
43
+ * instead of literal strings.
44
+ */
45
+ export const RESERVED_KEY = {
46
+ LAYOUT: "layout",
47
+ BODY: "body",
48
+ OUTPUT: "output",
49
+ THEME: "theme",
50
+ NOTES: "notes",
40
51
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tycoworks/tycoslide",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,20 +22,24 @@
22
22
  "format": "biome format --write ."
23
23
  },
24
24
  "dependencies": {
25
+ "@xmldom/xmldom": "^0.9.10",
25
26
  "commander": "^15.0.0",
26
27
  "image-size": "^2.0.2",
28
+ "mermaid": "^11.16.1",
29
+ "playwright": "^1.58.2",
27
30
  "pptx-automizer": "^0.8.2",
28
31
  "remark-gfm": "^4.0.1",
29
32
  "remark-ins": "^1.2.5",
30
33
  "remark-parse": "^11.0.0",
31
34
  "shiki": "^4.3.0",
32
35
  "unified": "^11.0.0",
33
- "yaml": "^2.0.0"
36
+ "yaml": "^2.0.0",
37
+ "zod": "^4.4.3"
34
38
  },
35
39
  "devDependencies": {
36
40
  "@biomejs/biome": "^2.5.1",
37
41
  "@types/node": "^22.20.0",
38
- "@xmldom/xmldom": "^0.9.10",
42
+ "jszip": "^3.10.1",
39
43
  "typescript": "^5.8.0"
40
44
  }
41
45
  }
package/syntax.md CHANGED
@@ -91,7 +91,7 @@ A layout advertises two kinds of author-facing input, split by one rule: **a par
91
91
  "parameters": [
92
92
  { "key": "title", "type": "template" },
93
93
  { "key": "subtitle", "type": "template" },
94
- { "key": "logo", "type": "image", "fit": "contain", "required": true }
94
+ { "key": "logo", "type": "image", "required": true }
95
95
  ],
96
96
  "slots": [
97
97
  { "key": "body", "type": "text" },
@@ -114,7 +114,7 @@ Fill a parameter by putting a value under its key in the slide's frontmatter.
114
114
  jobTitle: CEO, Acme Corp
115
115
  ```
116
116
  The engine substitutes each value into the run that carries its style, so if the designer made the name bold and the job title grey, the filled name stays bold and the filled title stays grey.
117
- - **`image`** -- a picture placeholder. Set it in frontmatter with the image path (from an asset catalog entry, or an absolute path). The parameter declares a `fit`: `contain` shows the whole image, `cover` fills the frame and center-crops overflow.
117
+ - **`image`** -- a picture placeholder. Set it in frontmatter with the image path (from an asset catalog entry, or an absolute path). How it is scaled and cropped is set by the **asset's `type`** in the catalog (`icon` never enlarges past native and never crops; `image` fits the whole picture without cropping; `background` fills the frame and center-crops).
118
118
  ```yaml
119
119
  hero: assets/diagrams/architecture.png
120
120
  ```
@@ -136,13 +136,13 @@ Fill a slot by writing a region in the body: the default (unmarked) region maps
136
136
  ```
137
137
  ````
138
138
  The language tag (e.g. `sql`, `python`, `typescript`) is required -- it drives syntax highlighting. Colors are applied as native text runs in the output, not images.
139
- - **`mermaid`** -- a mermaid diagram rendered as a themed PNG (see below). Written as a fenced `mermaid` region; the resulting PNG fills the slot with `contain` fit.
139
+ - **`mermaid`** -- a mermaid diagram rendered as a themed PNG (see below). Written as a fenced `mermaid` region; the resulting PNG is shown contained (in its entirety).
140
140
 
141
141
  ---
142
142
 
143
143
  ## Mermaid diagrams
144
144
 
145
- Mermaid diagrams are rendered as themed PNGs and delivered to any slot declared with `type: mermaid`. Write a fenced code block with the `mermaid` language tag in a named slot whose layout declares that slot as `type: mermaid` (with a `mermaidVariant` naming the theme's color variant). The resulting PNG behaves like any other image in the slot -- always shown in its entirety (`contain` fit).
145
+ Mermaid diagrams are rendered as themed PNGs and delivered to any slot declared with `type: mermaid`. Write a fenced code block with the `mermaid` language tag in a named slot whose layout declares that slot as `type: mermaid` (with a `mermaidVariant` naming the theme's color variant). The resulting PNG behaves like any other image in the slot -- always shown in its entirety.
146
146
 
147
147
  To let the same physical slide accept either an image or a diagram, the theme author declares two layouts with the same `slideNumber` -- one exposing the fill as an `image` parameter (frontmatter path), one as a `mermaid` slot (a fenced region). Authors pick between them by naming the layout in frontmatter; the compiler routes content based on the layout's declaration, so there is no ambiguity.
148
148
 
@@ -214,7 +214,7 @@ hero: assets/diagrams/architecture.png
214
214
  Each parameter or slot in the layout definition may declare:
215
215
  - **`type`** (required) -- parameters: `template`, `image`; slots: `text`, `table`, `code`, `mermaid`.
216
216
  - **`required: true`** -- the slide has no usable default and the build fails if the parameter/slot has no value (e.g. team-member photos, icon-grid icons, the quote logo). If you don't have a suitable image, ask the user for one.
217
- - **`fit`** -- image parameters only (required): `contain` shows the whole image inside the frame (letterboxed); `cover` fills the frame and center-crops overflow. Fit is a layout-designer decision baked into the parameter -- callers never override it per slide. Mermaid slots don't declare `fit`; mermaid always renders contained.
217
+ - **image sizing** -- each catalog asset declares a `type` (`icon` | `image` | `background`) that determines how it is scaled and cropped: `icon` never enlarges past native and never crops; `image` fits the whole picture (no crop, may scale); `background` fills and center-crops. Mermaid renders as `image` (contained).
218
218
  - **`codeTheme`** -- code slots only (required): the Shiki theme id used to syntax-highlight fenced code that lands in this slot (e.g. `"github-dark"`).
219
219
  - **`mermaidVariant`** -- mermaid slots only (required): names the color variant from `theme.mermaid` (e.g. `"dark"`). See [Mermaid diagrams](#mermaid-diagrams) above.
220
220
 
@@ -222,6 +222,26 @@ Each layout also declares a `slideNumber` pointing at the physical slide in the
222
222
 
223
223
  ---
224
224
 
225
+ ## Speaker notes
226
+
227
+ Any slide may carry a `notes:` key in its frontmatter -- a plain-text speaker-notes block attached to that slide's notes page. It is slide-level metadata, not a parameter or a slot: it is never routed to a shape and never appears on the slide face, only in the presenter/notes view.
228
+
229
+ Write multiple lines with a YAML block scalar (`|`); each line becomes one notes paragraph. Blank template notes on the underlying slide are always stripped, so only what you author here shows up.
230
+
231
+ ```yaml
232
+ ---
233
+ layout: Body
234
+ title: Key Achievements
235
+ notes: |
236
+ Open by thanking the regional teams.
237
+ Land the 23% number, then pause before the churn stat.
238
+ ---
239
+ ```
240
+
241
+ To build with all speaker notes omitted, pass `--no-notes` to `tycoslide build` (see [README](README.md#cli)).
242
+
243
+ ---
244
+
225
245
  ## Slides with no body
226
246
 
227
247
  Slides that have all their content in frontmatter (common for title slides, section dividers) need no body:
@@ -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;