@tycoworks/tycoslide 0.17.0 → 0.17.1
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/agents/commands.js +2 -2
- package/dist/agents/inventory.d.ts +2 -2
- package/dist/agents/inventory.js +9 -6
- package/dist/agents/skill.d.ts +7 -5
- package/dist/agents/skill.js +19 -6
- package/dist/markdown/slideParser.js +1 -1
- package/docs/markdown.md +3 -3
- package/package.json +1 -1
- package/theme-package/SKILL.template.md +2 -2
package/dist/agents/commands.js
CHANGED
|
@@ -4,7 +4,7 @@ import { loadThemeConfig } from "../index.js";
|
|
|
4
4
|
import { extractTemplate, summarizeExtraction } from "./extract.js";
|
|
5
5
|
import { ASSETS_DIR, ASSETS_FILE, MANIFEST_FILE, PACKAGE_JSON, SKILL_FILE, SKILL_TEMPLATE_FILE, SKILL_ZIP_EXT, TEMPLATE_FILE, THEME_CONFIG, THEME_PACKAGE_DIR, } from "./files.js";
|
|
6
6
|
import { generateManifest } from "./manifest.js";
|
|
7
|
-
import { loadAssetCatalog,
|
|
7
|
+
import { loadAssetCatalog, personalizeSkill, skillPackageJson, zipDir } from "./skill.js";
|
|
8
8
|
/** The CLI's line for something written, with an optional note on what it holds. */
|
|
9
9
|
function wrote(target, detail) {
|
|
10
10
|
console.log(detail ? `WROTE ${target} ${detail}` : `WROTE ${target}`);
|
|
@@ -27,7 +27,7 @@ export function registerAgentCommands(program, tool) {
|
|
|
27
27
|
const skillName = basename(themePkg.name);
|
|
28
28
|
let skillMd;
|
|
29
29
|
try {
|
|
30
|
-
skillMd =
|
|
30
|
+
skillMd = personalizeSkill(readFileSync(skillMdPath, "utf-8"), skillName, themePkg.description);
|
|
31
31
|
}
|
|
32
32
|
catch (err) {
|
|
33
33
|
throw new Error(`${skillMdPath}: ${err.message}`);
|
|
@@ -52,8 +52,8 @@ export type InventoryShape = {
|
|
|
52
52
|
rows?: number;
|
|
53
53
|
cols?: number;
|
|
54
54
|
/**
|
|
55
|
-
* The
|
|
56
|
-
*
|
|
55
|
+
* The shape's text, with `¶` between paragraphs and `↵` for line breaks,
|
|
56
|
+
* either of which a parameter template writes as `\n`.
|
|
57
57
|
*/
|
|
58
58
|
text: string;
|
|
59
59
|
};
|
package/dist/agents/inventory.js
CHANGED
|
@@ -300,7 +300,6 @@ const SHAPE_ELEMENTS = new Set([
|
|
|
300
300
|
const GROUP_SEPARATOR = "/";
|
|
301
301
|
const PARAGRAPH_MARK = "¶";
|
|
302
302
|
const LINE_BREAK_MARK = "↵";
|
|
303
|
-
const TEXT_PREVIEW_CHARS = 80;
|
|
304
303
|
const TEXT_BOX_ON = "1";
|
|
305
304
|
/** A placeholder with no `type` is a body placeholder. */
|
|
306
305
|
const DEFAULT_PLACEHOLDER_TYPE = "body";
|
|
@@ -327,14 +326,14 @@ async function readShapes(presentation, parts) {
|
|
|
327
326
|
}
|
|
328
327
|
}
|
|
329
328
|
}
|
|
330
|
-
// Set fields only, in a fixed order
|
|
329
|
+
// Set fields only, in a fixed order.
|
|
331
330
|
return found.map(({ shape: { name, kind, frame, frameFrom, rows, cols, text } }) => ({
|
|
332
331
|
name,
|
|
333
332
|
kind,
|
|
334
333
|
...(frame && { frame }),
|
|
335
334
|
...(frameFrom && { frameFrom }),
|
|
336
335
|
...(rows !== undefined && { rows, cols }),
|
|
337
|
-
text
|
|
336
|
+
text,
|
|
338
337
|
}));
|
|
339
338
|
}
|
|
340
339
|
function walkShapes(container, prefix) {
|
|
@@ -373,13 +372,17 @@ function shapeKind(el, table) {
|
|
|
373
372
|
return ShapeKind.Other;
|
|
374
373
|
}
|
|
375
374
|
}
|
|
376
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* The shape's text, `¶` between paragraphs and `↵` for line breaks, whitespace
|
|
377
|
+
* collapsed. Runs join as PowerPoint shows them, with no space added between: a
|
|
378
|
+
* run is a stretch of one style, not a word.
|
|
379
|
+
*/
|
|
377
380
|
function shapeText(el) {
|
|
378
381
|
const paragraphs = collectElements(el, Tag.PARAGRAPH).map((paragraph) => elementChildren(paragraph)
|
|
379
382
|
.flatMap((child) => child.tagName === Tag.LINE_BREAK
|
|
380
|
-
? [LINE_BREAK_MARK]
|
|
383
|
+
? [` ${LINE_BREAK_MARK} `]
|
|
381
384
|
: collectElements(child, Tag.TEXT).map((text) => text.textContent ?? ""))
|
|
382
|
-
.join("
|
|
385
|
+
.join("")
|
|
383
386
|
.split(/\s+/)
|
|
384
387
|
.filter(Boolean)
|
|
385
388
|
.join(" "));
|
package/dist/agents/skill.d.ts
CHANGED
|
@@ -18,12 +18,14 @@ export declare function loadAssetCatalog(themeDir: string): AssetCatalog;
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function packAssets(paths: string[], read: (rel: string) => Buffer): Promise<Buffer>;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* Make the skill template the theme's own: its frontmatter `name:` and its title
|
|
22
|
+
* heading become the theme's name, and the theme's `description`, when it has
|
|
23
|
+
* one, leads the skill's description, so an agent with several theme skills can
|
|
24
|
+
* tell them apart. The rest of the body is left byte-for-byte. Throws if there is
|
|
25
|
+
* no frontmatter, no `name:` line, or no folded description to lead — the caller
|
|
26
|
+
* names the file.
|
|
25
27
|
*/
|
|
26
|
-
export declare function
|
|
28
|
+
export declare function personalizeSkill(md: string, name: string, description?: string): string;
|
|
27
29
|
/**
|
|
28
30
|
* The `package.json` a packaged skill installs from — deliberately NOT the theme's
|
|
29
31
|
* own. A theme repo's manifest is a development document: it may carry scripts,
|
package/dist/agents/skill.js
CHANGED
|
@@ -47,19 +47,32 @@ export async function packAssets(paths, read) {
|
|
|
47
47
|
}
|
|
48
48
|
const FRONTMATTER = /^---\n([\s\S]*?)\n---/;
|
|
49
49
|
const NAME_LINE = /^name:[ \t]*.*$/m;
|
|
50
|
+
/** The folded `description: >` opener; the description's lines follow it, indented. */
|
|
51
|
+
const FOLDED_DESCRIPTION_LINE = /^description:[ \t]*>[ \t]*$/m;
|
|
52
|
+
const DESCRIPTION_INDENT = " ";
|
|
53
|
+
const TITLE_HEADING = /^# .*$/m;
|
|
50
54
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
+
* Make the skill template the theme's own: its frontmatter `name:` and its title
|
|
56
|
+
* heading become the theme's name, and the theme's `description`, when it has
|
|
57
|
+
* one, leads the skill's description, so an agent with several theme skills can
|
|
58
|
+
* tell them apart. The rest of the body is left byte-for-byte. Throws if there is
|
|
59
|
+
* no frontmatter, no `name:` line, or no folded description to lead — the caller
|
|
60
|
+
* names the file.
|
|
55
61
|
*/
|
|
56
|
-
export function
|
|
62
|
+
export function personalizeSkill(md, name, description) {
|
|
57
63
|
const block = md.match(FRONTMATTER);
|
|
58
64
|
if (!block)
|
|
59
65
|
throw new Error("SKILL.md has no YAML frontmatter block");
|
|
60
66
|
if (!NAME_LINE.test(block[1]))
|
|
61
67
|
throw new Error('SKILL.md frontmatter has no "name:" line');
|
|
62
|
-
|
|
68
|
+
let frontmatter = block[0].replace(NAME_LINE, `name: ${name}`);
|
|
69
|
+
if (description) {
|
|
70
|
+
if (!FOLDED_DESCRIPTION_LINE.test(frontmatter)) {
|
|
71
|
+
throw new Error('SKILL.md frontmatter has no folded "description: >" to lead');
|
|
72
|
+
}
|
|
73
|
+
frontmatter = frontmatter.replace(FOLDED_DESCRIPTION_LINE, (line) => `${line}\n${DESCRIPTION_INDENT}${description}`);
|
|
74
|
+
}
|
|
75
|
+
return frontmatter + md.slice(block[0].length).replace(TITLE_HEADING, `# ${name}`);
|
|
63
76
|
}
|
|
64
77
|
/**
|
|
65
78
|
* Files a packaged skill needs beyond the theme's own declarations. Only the
|
|
@@ -136,7 +136,7 @@ function buildSlide(index, fmString, rawContent) {
|
|
|
136
136
|
export class FrontmatterParseError extends Error {
|
|
137
137
|
constructor(slideIndex, yamlSource, cause) {
|
|
138
138
|
const preview = yamlSource.length > 80 ? `${yamlSource.slice(0, 80)}...` : yamlSource;
|
|
139
|
-
super(`
|
|
139
|
+
super(`Slide ${slideIndex + 1}: invalid YAML in frontmatter:\n${preview}`);
|
|
140
140
|
this.name = "FrontmatterParseError";
|
|
141
141
|
this.cause = cause;
|
|
142
142
|
}
|
package/docs/markdown.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This document covers the detailed syntax for writing slide content in tycoslide deck files.
|
|
4
4
|
|
|
5
|
-
> **Every layout, parameter, and slot name in the examples below is a placeholder.** Your theme's real names live in `theme.json` — read it first, and never assume a name shown here (`Body`, `TwoColumn`, `hero`, `::left::`, etc.) exists in your theme.
|
|
5
|
+
> **Every layout, parameter, and slot name in the examples below is a placeholder.** Your theme's real names live in its `theme.json`, and in `manifest.json` when the theme is packaged as a skill — read it first, and never assume a name shown here (`Body`, `TwoColumn`, `hero`, `::left::`, etc.) exists in your theme.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -216,7 +216,7 @@ The theme owns all styling. These directives are rejected at build time:
|
|
|
216
216
|
Each parameter or slot in the layout definition may declare:
|
|
217
217
|
- **`accepts`** (slots, required) -- an array of `text`, `table`, `image`.
|
|
218
218
|
- **`required: true`** -- the slide has no usable default and the build fails if the parameter/slot has no value.
|
|
219
|
-
- **optional (the default)** -- a parameter or slot you leave unfilled is dropped from the slide (its shape is removed), so a layout with numbered slots (e.g. up to six sections, up to four stats) renders only the ones you fill.
|
|
219
|
+
- **optional (the default)** -- a parameter or slot you leave unfilled is dropped from the slide (its shape is removed), so a layout with numbered slots (e.g. up to six sections, up to four stats) renders only the ones you fill. A shape the theme doesn't map, such as a card drawn behind a stat, stays, so an unfilled one can leave an empty card.
|
|
220
220
|
|
|
221
221
|
Each layout also declares a `slideNumber` pointing at the physical slide in the theme's template -- unique per layout (one layout maps to one physical slide).
|
|
222
222
|
|
|
@@ -268,7 +268,7 @@ Common errors and fixes:
|
|
|
268
268
|
|
|
269
269
|
| Error | Fix |
|
|
270
270
|
|-------|-----|
|
|
271
|
-
| `unknown layout "xyz"` | Check layout names in `theme.json` |
|
|
271
|
+
| `unknown layout "xyz"` | Check layout names in `theme.json` (or `manifest.json`) |
|
|
272
272
|
| A parameter or slot didn't fill | Use the key names the layout declares -- parameters in frontmatter, slots as body regions |
|
|
273
273
|
| YAML parse error | Fix the YAML syntax in the slide's frontmatter |
|
|
274
274
|
| `image "…" not found at …` | Check the path; it is relative to the deck file |
|
package/package.json
CHANGED
|
@@ -39,7 +39,7 @@ This skill builds decks from a markdown deck file. The theme provides slide layo
|
|
|
39
39
|
|
|
40
40
|
Before writing anything, read `manifest.json`. It lists the theme's **layouts** -- for each: `name`, `description`, `parameters` (frontmatter inputs) and `slots` (body regions). A layout is identified by its `name`; every parameter and slot by its `key`. Slots carry `accepts`, and either may be `required`. A slot that accepts an image also shows its `fit`. A `cover` slot crops the image to fill the slot, a `contain` slot shows the whole image, and a `scale-down` slot shows the whole image without ever enlarging it.
|
|
41
41
|
|
|
42
|
-
A layout's inputs split two ways, parameters and slots; see [markdown.md](node_modules/@tycoworks/tycoslide/docs/markdown.md#parameters-and-slots) for how to fill each. Unfilled ones are dropped, so fill as many of a layout's numbered slots (sections, stats, columns) as you have content for.
|
|
42
|
+
A layout's inputs split two ways, parameters and slots; see [markdown.md](node_modules/@tycoworks/tycoslide/docs/markdown.md#parameters-and-slots) for how to fill each. Unfilled ones are dropped, so fill as many of a layout's numbered slots (sections, stats, columns) as you have content for. A card or panel drawn behind an unfilled one stays, so choose a layout with no more of them than you can fill.
|
|
43
43
|
|
|
44
44
|
Study each layout's `slots` before writing any slides.
|
|
45
45
|
|
|
@@ -63,7 +63,7 @@ If your deck is in the theme folder, the file is already there, so skip the copy
|
|
|
63
63
|
|
|
64
64
|
## Creating Slides
|
|
65
65
|
|
|
66
|
-
Write a deck file in markdown, as [markdown.md](node_modules/@tycoworks/tycoslide/docs/markdown.md) describes (see its [full example](node_modules/@tycoworks/tycoslide/docs/markdown.md#full-example)), and build it as its [Build](node_modules/@tycoworks/tycoslide/docs/markdown.md#build) section shows.
|
|
66
|
+
Write a deck file in markdown, as [markdown.md](node_modules/@tycoworks/tycoslide/docs/markdown.md) describes (see its [full example](node_modules/@tycoworks/tycoslide/docs/markdown.md#full-example)), and build it as its [Build](node_modules/@tycoworks/tycoslide/docs/markdown.md#build) section shows. The deck's `theme:` is the path from the deck to this folder's `theme.json`: `./theme.json` when the deck sits in this folder, and the full path to it when the deck is anywhere else.
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|