@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.
- package/README.md +12 -9
- package/SKILL.md +6 -7
- package/dist/cli.js +39 -59
- package/dist/engine/fillers/filler.d.ts +21 -13
- package/dist/engine/fillers/filler.js +21 -24
- package/dist/engine/generate.d.ts +23 -17
- package/dist/engine/generate.js +157 -70
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/types.d.ts +39 -9
- package/dist/index.d.ts +15 -21
- package/dist/index.js +63 -88
- package/dist/manifest.js +17 -25
- package/dist/markdown/blocks/code.d.ts +17 -0
- package/dist/markdown/blocks/code.js +65 -0
- package/dist/markdown/blocks/image.d.ts +2 -0
- package/dist/markdown/blocks/image.js +9 -0
- package/dist/markdown/blocks/mermaid.d.ts +15 -0
- package/dist/markdown/blocks/mermaid.js +227 -0
- package/dist/markdown/{resolvers → blocks}/mermaidTheme.d.ts +1 -1
- package/dist/markdown/{resolvers → blocks}/mermaidTheme.js +1 -1
- package/dist/markdown/blocks/registry.d.ts +16 -0
- package/dist/markdown/blocks/registry.js +44 -0
- package/dist/markdown/blocks/table.d.ts +2 -0
- package/dist/markdown/blocks/table.js +23 -0
- package/dist/markdown/blocks/text.d.ts +12 -0
- package/dist/markdown/blocks/text.js +90 -0
- package/dist/markdown/deckCompiler.d.ts +17 -20
- package/dist/markdown/deckCompiler.js +143 -113
- package/dist/markdown/index.d.ts +11 -11
- package/dist/markdown/index.js +9 -8
- package/dist/markdown/inline.d.ts +26 -0
- package/dist/markdown/inline.js +136 -0
- package/dist/markdown/mdast.d.ts +25 -0
- package/dist/markdown/mdast.js +49 -0
- package/dist/markdown/schema/deckSchema.d.ts +30 -0
- package/dist/markdown/schema/deckSchema.js +51 -0
- package/dist/markdown/schema/strict.d.ts +9 -0
- package/dist/markdown/schema/strict.js +18 -0
- package/dist/markdown/schema/themeConfigSchema.d.ts +106 -0
- package/dist/markdown/schema/themeConfigSchema.js +147 -0
- package/dist/markdown/types.d.ts +194 -134
- package/dist/markdown/types.js +34 -23
- package/dist/skillZip.d.ts +17 -0
- package/dist/skillZip.js +35 -0
- package/package.json +7 -3
- package/syntax.md +1 -1
- package/dist/markdown/parsers.d.ts +0 -32
- package/dist/markdown/parsers.js +0 -233
- package/dist/markdown/resolvers/code.d.ts +0 -17
- package/dist/markdown/resolvers/code.js +0 -44
- package/dist/markdown/resolvers/mermaid.d.ts +0 -14
- package/dist/markdown/resolvers/mermaid.js +0 -81
- package/dist/markdown/resolvers/resolver.d.ts +0 -42
- package/dist/markdown/resolvers/resolver.js +0 -52
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# tycoslide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create editable, on-brand PowerPoint slides from markdown.
|
|
4
4
|
|
|
5
5
|
> **Early release** — tycoslide is under active development.
|
|
6
6
|
|
|
7
7
|
## How it works
|
|
8
8
|
|
|
9
|
-
1. **tycoslide wraps
|
|
10
|
-
2. **
|
|
11
|
-
3. **tycoslide builds
|
|
9
|
+
1. **tycoslide wraps existing PowerPoint files as reusable templates.**
|
|
10
|
+
2. **You (or an agent) write slides in markdown.**
|
|
11
|
+
3. **tycoslide builds new PowerPoint files.**
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
@@ -42,16 +42,15 @@ title: Highlights
|
|
|
42
42
|
Build:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
tycoslide build deck.md
|
|
45
|
+
npx tycoslide build deck.md # → deck.pptx
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## CLI
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
tycoslide build deck.md # markdown → PPTX (theme resolved from deck frontmatter)
|
|
52
|
-
tycoslide build deck.md --no-notes # omit speaker notes from the output
|
|
53
|
-
tycoslide
|
|
54
|
-
tycoslide manifest # print layout + asset catalog to stdout
|
|
51
|
+
npx tycoslide build deck.md # markdown → PPTX (theme resolved from deck frontmatter)
|
|
52
|
+
npx tycoslide build deck.md --no-notes # omit speaker notes from the output
|
|
53
|
+
npx tycoslide package # generate the Agent Skill (skill.md, syntax.md, manifest.json at the theme root) + an uploadable <package-name>.zip
|
|
55
54
|
```
|
|
56
55
|
|
|
57
56
|
## Theme Structure
|
|
@@ -65,6 +64,10 @@ my-theme/
|
|
|
65
64
|
assets/icons/
|
|
66
65
|
theme.json
|
|
67
66
|
package.json
|
|
67
|
+
skill.md # generated by `tycoslide package`
|
|
68
|
+
syntax.md # generated by `tycoslide package`
|
|
69
|
+
manifest.json # generated by `tycoslide package`
|
|
70
|
+
<package-name>.zip # uploadable Agent Skill bundle
|
|
68
71
|
```
|
|
69
72
|
|
|
70
73
|
**Template** — the PPTX file with named shapes that tycoslide fills.
|
package/SKILL.md
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: slides
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"presentation," "pitch," or .pptx output. Also trigger when the user says "build me a deck,"
|
|
7
|
-
"make slides about X," or "turn this into a presentation."
|
|
4
|
+
Build branded slides, presentations, pitch decks, or sales collateral as a .pptx.
|
|
5
|
+
Trigger on "deck," "slides," "presentation," "pitch," ".pptx," or "build me a deck."
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
# slides
|
|
11
9
|
|
|
12
10
|
## Setup
|
|
13
11
|
|
|
14
|
-
Before first use, install dependencies from the
|
|
12
|
+
Before first use, install dependencies from the theme root:
|
|
15
13
|
|
|
16
14
|
```bash
|
|
17
15
|
npm install
|
|
@@ -48,7 +46,7 @@ A layout's inputs split two ways (see [syntax.md](syntax.md) for details):
|
|
|
48
46
|
|
|
49
47
|
A single physical slide may back multiple layouts. When two manifest entries share the same `slideNumber`, they render into the same underlying PPTX shapes but declare their fill differently -- e.g. one layout exposing the fill as an `image` parameter and a sibling exposing it as a `mermaid` slot. Pick between them by naming the layout you want in frontmatter (`layout: Full bleed diagram` vs `layout: Full bleed image`); the declaration is unambiguous per layout, so the compiler always knows how to interpret the content you provide.
|
|
50
48
|
|
|
51
|
-
Study each layout's `
|
|
49
|
+
Study each layout's `slots` and `limit`s before writing any slides.
|
|
52
50
|
|
|
53
51
|
---
|
|
54
52
|
|
|
@@ -131,7 +129,7 @@ The deck is written to your current working directory (not inside the skill).
|
|
|
131
129
|
|
|
132
130
|
### For Each Slide
|
|
133
131
|
|
|
134
|
-
**Every slide communicates one idea.** If you're writing more than 5 bullets or 3 paragraphs, split into two slides.
|
|
132
|
+
**Every slide communicates one idea.** If you're writing more than 5 bullets or 3 paragraphs, split into two slides. Put the takeaway in the headline, then support it.
|
|
135
133
|
|
|
136
134
|
Check each layout's `limit` in the manifest for content density constraints. When content overflows, split across slides.
|
|
137
135
|
|
|
@@ -143,6 +141,7 @@ Check each layout's `limit` in the manifest for content density constraints. Whe
|
|
|
143
141
|
- **Don't open with a body/content layout** -- use the Title layout for impact
|
|
144
142
|
- **Don't skip section dividers** -- for decks over 5 slides, use Section title layouts to group sections
|
|
145
143
|
- **Don't restyle the layout** -- the theme owns all design; you only fill slots
|
|
144
|
+
- **Don't use an image that's wrong for the slot** -- a small slot wants a simple icon, not a dense illustration; if the build warns an image shrank to a small %, swap it for a simpler one
|
|
146
145
|
- **Don't invent layout or asset names** -- only use what exists in the manifest
|
|
147
146
|
- **Don't leave required parameters or slots empty** -- and don't leave a placeholder logo or dummy text in an image parameter you care about
|
|
148
147
|
- **Don't mix dark and light** -- keep one variant across the deck
|
package/dist/cli.js
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { buildDeck } from "./index.js";
|
|
6
6
|
import { generateManifest } from "./manifest.js";
|
|
7
|
-
import { compileDeck, parseSlideDocument, RESERVED_KEY
|
|
7
|
+
import { compileDeck, loadThemeConfig, parseSlideDocument, RESERVED_KEY } from "./markdown/index.js";
|
|
8
|
+
import { renameSkill, zipSkill } from "./skillZip.js";
|
|
8
9
|
const DEFAULT_CONFIG = "theme.json";
|
|
9
|
-
const SKILL_DIR = "skills/slides";
|
|
10
|
-
const PLUGIN_DIR = ".claude-plugin";
|
|
11
|
-
const PLUGIN_FILE = "plugin.json";
|
|
12
10
|
const MANIFEST_FILE = "manifest.json";
|
|
13
|
-
|
|
11
|
+
// The theme skill is written as lowercase skill.md (copied from tycoslide's own
|
|
12
|
+
// SKILL.md), so the skill folder can live at the theme repo root.
|
|
13
|
+
const SKILL_FILE = "skill.md";
|
|
14
14
|
const SYNTAX_FILE = "syntax.md";
|
|
15
15
|
const BUILD_COMMAND = "npx tycoslide build";
|
|
16
16
|
const sdkDir = dirname(fileURLToPath(import.meta.url));
|
|
17
|
-
const skillMdPath = resolve(sdkDir, "..",
|
|
17
|
+
const skillMdPath = resolve(sdkDir, "..", "SKILL.md");
|
|
18
18
|
const syntaxMdPath = resolve(sdkDir, "..", SYNTAX_FILE);
|
|
19
|
-
function loadConfig(absPath) {
|
|
20
|
-
let raw;
|
|
21
|
-
try {
|
|
22
|
-
raw = JSON.parse(readFileSync(absPath, "utf-8"));
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
throw new Error(`Config file not found or invalid JSON: ${absPath}`);
|
|
26
|
-
}
|
|
27
|
-
return { ...raw, rootDir: dirname(absPath) };
|
|
28
|
-
}
|
|
29
19
|
const pkg = JSON.parse(readFileSync(resolve(sdkDir, "..", "package.json"), "utf-8"));
|
|
30
20
|
const program = new Command().name("tycoslide").description("PPTX template engine CLI").version(pkg.version);
|
|
31
21
|
program
|
|
@@ -53,56 +43,46 @@ program
|
|
|
53
43
|
if (!absConfigPath) {
|
|
54
44
|
throw new Error(`${basename(deckPath)}: missing required "${RESERVED_KEY.THEME}" in global frontmatter`);
|
|
55
45
|
}
|
|
56
|
-
const config =
|
|
57
|
-
const deck = compileDeck(doc, config
|
|
46
|
+
const config = loadThemeConfig(absConfigPath);
|
|
47
|
+
const deck = await compileDeck(doc, config);
|
|
58
48
|
if (!deck.output)
|
|
59
49
|
deck.output = basename(deckPath).replace(/\.md$/, ".pptx");
|
|
60
50
|
await buildDeck(deck, config, { excludeNotes: !opts.notes });
|
|
61
51
|
});
|
|
62
52
|
program
|
|
63
|
-
.command("
|
|
64
|
-
.description("Generate manifest.json
|
|
53
|
+
.command("package")
|
|
54
|
+
.description("Generate the Agent Skill (manifest.json, SKILL.md, syntax.md) for AI agents")
|
|
65
55
|
.option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
|
|
66
|
-
.option(`-o, --out <file>`, "write to file instead of stdout")
|
|
67
56
|
.action(async (opts) => {
|
|
68
|
-
const config =
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
const config = loadThemeConfig(resolve(process.cwd(), opts.config));
|
|
58
|
+
const themePkg = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf-8"));
|
|
59
|
+
if (!themePkg.name) {
|
|
60
|
+
throw new Error('Cannot name the skill: the theme\'s package.json has no "name" field.');
|
|
61
|
+
}
|
|
62
|
+
// basename drops any npm scope, e.g. "@acme/mz-slides" -> "mz-slides".
|
|
63
|
+
const skillName = basename(themePkg.name);
|
|
64
|
+
const manifestJson = `${generateManifest(config, { build: { command: BUILD_COMMAND } })}\n`;
|
|
65
|
+
writeFileSync(resolve(process.cwd(), MANIFEST_FILE), manifestJson);
|
|
66
|
+
console.log(`WROTE ${MANIFEST_FILE}`);
|
|
67
|
+
let skillMd;
|
|
68
|
+
try {
|
|
69
|
+
skillMd = renameSkill(readFileSync(skillMdPath, "utf-8"), skillName);
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
catch (err) {
|
|
72
|
+
throw new Error(`${skillMdPath}: ${err.message}`);
|
|
76
73
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
.
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
skills: "./skills",
|
|
91
|
-
};
|
|
92
|
-
if (pkg.author)
|
|
93
|
-
pluginMeta.author = pkg.author;
|
|
94
|
-
const pluginDir = resolve(cwd, PLUGIN_DIR);
|
|
95
|
-
mkdirSync(pluginDir, { recursive: true });
|
|
96
|
-
writeFileSync(resolve(pluginDir, PLUGIN_FILE), `${JSON.stringify(pluginMeta, null, 2)}\n`);
|
|
97
|
-
console.log(`WROTE ${PLUGIN_DIR}/${PLUGIN_FILE}`);
|
|
98
|
-
const skillDir = resolve(cwd, SKILL_DIR);
|
|
99
|
-
mkdirSync(skillDir, { recursive: true });
|
|
100
|
-
const json = generateManifest(config, { build: { command: BUILD_COMMAND } });
|
|
101
|
-
writeFileSync(resolve(skillDir, MANIFEST_FILE), `${json}\n`);
|
|
102
|
-
console.log(`WROTE ${SKILL_DIR}/${MANIFEST_FILE}`);
|
|
103
|
-
copyFileSync(skillMdPath, resolve(skillDir, SKILL_FILE));
|
|
104
|
-
console.log(`WROTE ${SKILL_DIR}/${SKILL_FILE}`);
|
|
105
|
-
copyFileSync(syntaxMdPath, resolve(skillDir, SYNTAX_FILE));
|
|
106
|
-
console.log(`WROTE ${SKILL_DIR}/${SYNTAX_FILE}`);
|
|
74
|
+
writeFileSync(resolve(process.cwd(), SKILL_FILE), skillMd);
|
|
75
|
+
console.log(`WROTE ${SKILL_FILE}`);
|
|
76
|
+
const syntaxMd = readFileSync(syntaxMdPath, "utf-8");
|
|
77
|
+
writeFileSync(resolve(process.cwd(), SYNTAX_FILE), syntaxMd);
|
|
78
|
+
console.log(`WROTE ${SYNTAX_FILE}`);
|
|
79
|
+
const zipFile = `${skillName}.zip`;
|
|
80
|
+
const zipBuf = await zipSkill(skillName, [
|
|
81
|
+
{ name: SKILL_FILE, content: skillMd },
|
|
82
|
+
{ name: SYNTAX_FILE, content: syntaxMd },
|
|
83
|
+
{ name: MANIFEST_FILE, content: manifestJson },
|
|
84
|
+
]);
|
|
85
|
+
writeFileSync(resolve(process.cwd(), zipFile), zipBuf);
|
|
86
|
+
console.log(`WROTE ${zipFile}`);
|
|
107
87
|
});
|
|
108
88
|
await program.parseAsync(process.argv);
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `Filler` strategy registry — one plain-object strategy per SlotType, each
|
|
3
|
-
* pairing a value discriminator with
|
|
4
|
-
* slot type, so a strategy carries no redundant
|
|
5
|
-
*
|
|
6
|
-
* value shape, then `fill` applies it to the slide.
|
|
3
|
+
* pairing a value discriminator with the element-level modify callbacks that
|
|
4
|
+
* apply it. The record key IS the slot type, so a strategy carries no redundant
|
|
5
|
+
* `type` field.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* `callbacks(value, target)` returns the `(element, relation)` callbacks that
|
|
8
|
+
* fill one shape. They are deliberately shape-name-agnostic beyond the `target`
|
|
9
|
+
* so they can be applied two ways: `slide.modifyElement(name, callbacks)` for a
|
|
10
|
+
* shape already on the cloned base slide, or `slide.addElement(alias, n, name,
|
|
11
|
+
* callbacks)` for a shape transplanted from another slide — pptx-automizer runs
|
|
12
|
+
* an appended shape's callbacks against the imported element itself, so the same
|
|
13
|
+
* callbacks refill a transplant.
|
|
14
|
+
*
|
|
15
|
+
* Element-level geometry lives in the `fillX` primitives; cross-shape concerns
|
|
16
|
+
* (media pre-swap for images) live in the callbacks here.
|
|
11
17
|
*/
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
import { SlotType } from "../types.js";
|
|
19
|
+
/** The shape a filler targets, plus its slot-level options (startAt for text). */
|
|
20
|
+
export type FillTarget = {
|
|
21
|
+
shapeName: string;
|
|
22
|
+
startAt?: number;
|
|
15
23
|
};
|
|
24
|
+
/** A pptx-automizer element-modify callback: `(element, relation) => void`. */
|
|
25
|
+
export type ShapeCallback = (element: any, relation: any) => unknown;
|
|
16
26
|
export interface Filler<T> {
|
|
17
27
|
matches(v: unknown): v is T;
|
|
18
|
-
|
|
19
|
-
label: string;
|
|
20
|
-
fill(slide: any, slot: Slot, value: T, ctx: FillContext): void;
|
|
28
|
+
callbacks(value: T, target: FillTarget): ShapeCallback[];
|
|
21
29
|
}
|
|
22
30
|
export declare const FILLERS: Record<SlotType, Filler<any>>;
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `Filler` strategy registry — one plain-object strategy per SlotType, each
|
|
3
|
-
* pairing a value discriminator with
|
|
4
|
-
* slot type, so a strategy carries no redundant
|
|
5
|
-
*
|
|
6
|
-
* value shape, then `fill` applies it to the slide.
|
|
3
|
+
* pairing a value discriminator with the element-level modify callbacks that
|
|
4
|
+
* apply it. The record key IS the slot type, so a strategy carries no redundant
|
|
5
|
+
* `type` field.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* `callbacks(value, target)` returns the `(element, relation)` callbacks that
|
|
8
|
+
* fill one shape. They are deliberately shape-name-agnostic beyond the `target`
|
|
9
|
+
* so they can be applied two ways: `slide.modifyElement(name, callbacks)` for a
|
|
10
|
+
* shape already on the cloned base slide, or `slide.addElement(alias, n, name,
|
|
11
|
+
* callbacks)` for a shape transplanted from another slide — pptx-automizer runs
|
|
12
|
+
* an appended shape's callbacks against the imported element itself, so the same
|
|
13
|
+
* callbacks refill a transplant.
|
|
14
|
+
*
|
|
15
|
+
* Element-level geometry lives in the `fillX` primitives; cross-shape concerns
|
|
16
|
+
* (media pre-swap for images) live in the callbacks here.
|
|
11
17
|
*/
|
|
12
18
|
import { basename } from "node:path";
|
|
13
19
|
import { ModifyImageHelper } from "pptx-automizer";
|
|
@@ -19,32 +25,23 @@ import { fillText, isTextFill } from "./text.js";
|
|
|
19
25
|
export const FILLERS = {
|
|
20
26
|
[SlotType.Template]: {
|
|
21
27
|
matches: isTemplateFill,
|
|
22
|
-
|
|
23
|
-
fill: (slide, slot, v) => slide.modifyElement(slot.shapeName, [(el) => fillTemplate(el, v, slot.shapeName)]),
|
|
28
|
+
callbacks: (v, t) => [(el) => fillTemplate(el, v, t.shapeName)],
|
|
24
29
|
},
|
|
25
30
|
[SlotType.Text]: {
|
|
26
31
|
matches: isTextFill,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
slide.modifyElement(slot.shapeName, [
|
|
31
|
-
(el, relation) => fillText(el, v, { startAt, relation, shapeName: slot.shapeName }),
|
|
32
|
-
]);
|
|
33
|
-
},
|
|
32
|
+
callbacks: (v, t) => [
|
|
33
|
+
(el, relation) => fillText(el, v, { startAt: t.startAt ?? 0, relation, shapeName: t.shapeName }),
|
|
34
|
+
],
|
|
34
35
|
},
|
|
35
36
|
[SlotType.Table]: {
|
|
36
37
|
matches: isTableFill,
|
|
37
|
-
|
|
38
|
-
fill: (slide, slot, v) => {
|
|
39
|
-
slide.modifyElement(slot.shapeName, [(el) => fillTable(el, v, slot.shapeName)]);
|
|
40
|
-
},
|
|
38
|
+
callbacks: (v, t) => [(el) => fillTable(el, v, t.shapeName)],
|
|
41
39
|
},
|
|
42
40
|
[SlotType.Image]: {
|
|
43
41
|
matches: isImageFill,
|
|
44
|
-
|
|
45
|
-
fill: (slide, slot, v) => slide.modifyElement(slot.shapeName, [
|
|
42
|
+
callbacks: (v, t) => [
|
|
46
43
|
ModifyImageHelper.setRelationTarget(basename(v.path)),
|
|
47
|
-
(el) => fillImage(el, v,
|
|
48
|
-
]
|
|
44
|
+
(el) => fillImage(el, v, t.shapeName),
|
|
45
|
+
],
|
|
49
46
|
},
|
|
50
47
|
};
|
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
* file (registered by generate(), swapped by the ImageFiller) and adjusts
|
|
19
19
|
* geometry for the chosen fit. See fillers/image.ts.
|
|
20
20
|
*
|
|
21
|
-
* generate() loads the template, registers media, and for each DeckStep
|
|
22
|
-
* the
|
|
23
|
-
*
|
|
21
|
+
* generate() loads the template, registers media, and for each DeckStep clones
|
|
22
|
+
* the layout's base slide and calls `fillSlide`. Each value in `step.content`
|
|
23
|
+
* selects, by its own shape, the `Block` in the slot's `accepts` whose type it
|
|
24
|
+
* matches: a base-slide block fills in place; any other block is transplanted
|
|
25
|
+
* from its source slide onto the clone, then filled with the same callbacks.
|
|
26
|
+
*
|
|
27
|
+
* `generate()` is first below; its helpers follow (function declarations hoist).
|
|
24
28
|
*/
|
|
25
|
-
import type { Config, Deck,
|
|
29
|
+
import type { Config, Deck, DeckStep, Layout } from "./types.js";
|
|
26
30
|
/** Options for `generate` / `buildDeck`. */
|
|
27
31
|
export type GenerateOptions = {
|
|
28
32
|
/**
|
|
@@ -40,22 +44,24 @@ export type GenerateOptions = {
|
|
|
40
44
|
* 1. Load the template; register it twice (as root and under an alias).
|
|
41
45
|
* 2. Pre-register every image's media buffer with pptx-automizer, walking
|
|
42
46
|
* the unified `step.content` for ImageFill values.
|
|
43
|
-
* 3. For each deck step, clone the layout's
|
|
44
|
-
* addSlide callback
|
|
45
|
-
*
|
|
47
|
+
* 3. For each deck step, clone the layout's base slide, then within the
|
|
48
|
+
* addSlide callback call `fillSlide` to dispatch each content value to the
|
|
49
|
+
* matching `Block` (fill in place, or transplant + fill).
|
|
46
50
|
* 4. Write the output PPTX.
|
|
47
51
|
*/
|
|
48
52
|
export declare function generate(deck: Deck, config: Config, options?: GenerateOptions): Promise<void>;
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* Fill one cloned slide. Per slot the step supplies a value for: resolve WHICH
|
|
55
|
+
* shape realizes it (`resolveBlock`), build WHAT to write (`FILLERS[…].callbacks`),
|
|
56
|
+
* and place it WHERE/HOW (`applyBlock`). Every ambiguity fails fast, naming layout
|
|
57
|
+
* + slot.
|
|
53
58
|
*
|
|
54
|
-
* Exported for tests;
|
|
59
|
+
* Exported for tests; `generate()` calls it inside the `addSlide` callback.
|
|
60
|
+
*/
|
|
61
|
+
export declare function fillSlide(slide: any, layout: Layout, step: DeckStep, sourceAlias: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Reject a slot whose `accepts` lists two blocks of the same type — the
|
|
64
|
+
* value→block lookup would silently pick the first. Called once per layout at
|
|
65
|
+
* build start.
|
|
55
66
|
*/
|
|
56
|
-
export declare function
|
|
57
|
-
layout: string;
|
|
58
|
-
content?: Record<string, unknown>;
|
|
59
|
-
}, tpl: {
|
|
60
|
-
slots: Slot[];
|
|
61
|
-
}): void;
|
|
67
|
+
export declare function assertSlotsWellFormed(layout: Layout): void;
|