@tycoworks/tycoslide 0.15.1 → 0.16.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/README.md +9 -5
- package/dist/agents/catalog.d.ts +32 -0
- package/dist/agents/catalog.js +37 -0
- package/dist/agents/commands.d.ts +9 -0
- package/dist/agents/commands.js +57 -0
- package/dist/agents/extract.d.ts +22 -0
- package/dist/agents/extract.js +54 -0
- package/dist/{files.d.ts → agents/files.d.ts} +14 -3
- package/dist/{files.js → agents/files.js} +20 -5
- package/dist/agents/inventory.d.ts +83 -0
- package/dist/agents/inventory.js +568 -0
- package/dist/agents/manifest.d.ts +3 -0
- package/dist/{manifest.js → agents/manifest.js} +3 -14
- package/dist/agents/media.d.ts +38 -0
- package/dist/agents/media.js +83 -0
- package/dist/agents/pptx.d.ts +48 -0
- package/dist/agents/pptx.js +123 -0
- package/dist/agents/skill.d.ts +43 -0
- package/dist/agents/skill.js +134 -0
- package/dist/cli.js +8 -44
- package/dist/engine/dom.d.ts +3 -0
- package/dist/engine/dom.js +4 -0
- package/dist/engine/fillers/image.d.ts +6 -5
- package/dist/engine/fillers/image.js +24 -7
- package/dist/engine/generate.d.ts +2 -2
- package/dist/engine/generate.js +2 -2
- package/dist/engine/index.d.ts +3 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/notes.js +20 -62
- package/dist/engine/ooxml.d.ts +36 -0
- package/dist/engine/ooxml.js +56 -0
- package/dist/engine/types.d.ts +4 -0
- package/dist/index.d.ts +4 -11
- package/dist/index.js +2 -15
- package/dist/markdown/blocks/code.js +1 -2
- package/dist/markdown/blocks/image.d.ts +23 -0
- package/dist/markdown/blocks/image.js +65 -3
- package/dist/markdown/blocks/mermaid.d.ts +1 -1
- package/dist/markdown/blocks/mermaid.js +6 -4
- package/dist/markdown/blocks/table.js +7 -6
- package/dist/markdown/blocks/text.js +9 -8
- package/dist/markdown/deckCompiler.d.ts +2 -13
- package/dist/markdown/deckCompiler.js +7 -78
- package/dist/markdown/index.d.ts +4 -2
- package/dist/markdown/index.js +3 -1
- package/dist/markdown/inline.d.ts +2 -0
- package/dist/markdown/inline.js +4 -3
- package/dist/markdown/schema/themeConfigSchema.d.ts +2 -11
- package/dist/markdown/schema/themeConfigSchema.js +6 -16
- package/dist/markdown/slideParser.d.ts +2 -0
- package/dist/markdown/slideParser.js +6 -3
- package/dist/markdown/types.d.ts +4 -29
- package/dist/markdown/types.js +0 -11
- package/{theme-package/syntax.md → docs/markdown.md} +60 -28
- package/docs/theme.md +144 -0
- package/package.json +5 -4
- package/theme-package/SKILL.template.md +161 -0
- package/dist/engine/rels.d.ts +0 -18
- package/dist/engine/rels.js +0 -55
- package/dist/manifest.d.ts +0 -5
- package/dist/skillZip.d.ts +0 -49
- package/dist/skillZip.js +0 -177
- package/theme-package/SKILL.md +0 -226
|
@@ -1,23 +1,12 @@
|
|
|
1
|
-
import { type ImageFill } from "../engine/index.js";
|
|
2
1
|
import type { ParsedDocument } from "./slideParser.js";
|
|
3
|
-
import {
|
|
4
|
-
/**
|
|
5
|
-
* Wrap an absolute image path as an ImageFill, expanding the resolved asset
|
|
6
|
-
* `type` into the engine's scaling constraints. Callers resolve the path (see
|
|
7
|
-
* `resolveImagePath`) and the type (from the catalog) first.
|
|
8
|
-
*/
|
|
9
|
-
export declare function toImageFill(path: string, type: AssetType): ImageFill;
|
|
2
|
+
import { type CompilerConfig, type CompilerDeck } from "./types.js";
|
|
10
3
|
/**
|
|
11
4
|
* Compile a parsed deck document against a theme `config`. Each slide's content
|
|
12
5
|
* is compiled straight into engine fills — prose/tables/images plus highlighted
|
|
13
6
|
* code (Shiki) and rendered mermaid (PNG) — so the returned deck is
|
|
14
7
|
* engine-shaped, ready for `buildDeck`.
|
|
15
8
|
*
|
|
16
|
-
*
|
|
17
|
-
* relative image paths. When empty, image paths are returned unchanged — callers
|
|
18
|
-
* that already produce absolute paths (or don't need resolution, e.g. unit tests)
|
|
19
|
-
* rely on the pass-through. When set, relative paths are resolved to absolute via
|
|
20
|
-
* `path.resolve(rootDir, path)`; absolute paths pass through. `config.codeTheme` /
|
|
9
|
+
* Image paths resolve against `config.deckDir`. `config.codeTheme` /
|
|
21
10
|
* `config.mermaid` / `config.mermaidVariant` feed the code and mermaid compiles.
|
|
22
11
|
*/
|
|
23
12
|
export declare function compileDeck(doc: ParsedDocument, config: CompilerConfig): Promise<CompilerDeck>;
|
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { ImageFit, SlotType } from "../engine/index.js";
|
|
3
1
|
import { parseSlotContent } from "./blocks/registry.js";
|
|
4
2
|
import { validateSlideFrontmatter } from "./schema/deckSchema.js";
|
|
5
3
|
import { templateKeys, templateToSegments } from "./textTemplate.js";
|
|
6
|
-
import {
|
|
7
|
-
/** Map each semantic asset type to the engine's object-fit directive. */
|
|
8
|
-
const FIT_FOR = {
|
|
9
|
-
[AssetType.Icon]: ImageFit.ScaleDown,
|
|
10
|
-
[AssetType.Image]: ImageFit.Contain,
|
|
11
|
-
[AssetType.Background]: ImageFit.Cover,
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Wrap an absolute image path as an ImageFill, expanding the resolved asset
|
|
15
|
-
* `type` into the engine's scaling constraints. Callers resolve the path (see
|
|
16
|
-
* `resolveImagePath`) and the type (from the catalog) first.
|
|
17
|
-
*/
|
|
18
|
-
export function toImageFill(path, type) {
|
|
19
|
-
return { type: SlotType.Image, path, fit: FIT_FOR[type] };
|
|
20
|
-
}
|
|
4
|
+
import { RESERVED_KEY, } from "./types.js";
|
|
21
5
|
const KNOWN_GLOBAL_KEYS = new Set([RESERVED_KEY.THEME]);
|
|
22
|
-
// Anchored whole-field reference: the entire value is `$category.name` or it is
|
|
23
|
-
// not a reference at all. Anchored ⇒ no escaping concerns.
|
|
24
|
-
const ASSET_REF_RE = /^\$([a-zA-Z]\w*)\.([a-zA-Z]\w*)$/;
|
|
25
6
|
/**
|
|
26
7
|
* Assert that a region's parsed block folds to a type the slot `accepts`. The
|
|
27
8
|
* folded type comes straight from `parseSlotContent` (which returns it beside
|
|
@@ -36,17 +17,6 @@ function assertSlotRegion(slot, got, layoutName, slideNo, source) {
|
|
|
36
17
|
`(from ${source}); it accepts: ${accepted}.`);
|
|
37
18
|
}
|
|
38
19
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Resolve a catalog image path against the deck's root directory.
|
|
41
|
-
* When `rootDir` is empty, the path is returned unchanged so callers that
|
|
42
|
-
* already produce absolute paths (or callers that don't care about
|
|
43
|
-
* resolution) can opt out. Absolute paths always pass through.
|
|
44
|
-
*/
|
|
45
|
-
function resolveImagePath(rootDir, path) {
|
|
46
|
-
if (!rootDir || path.startsWith("/"))
|
|
47
|
-
return path;
|
|
48
|
-
return resolve(rootDir, path);
|
|
49
|
-
}
|
|
50
20
|
/**
|
|
51
21
|
* Assert no two layouts sample the same base slide. In the sampled-composition
|
|
52
22
|
* model one layout is one sampled base slide's region-arrangement, so a shared
|
|
@@ -111,7 +81,7 @@ function validateLayout(layout) {
|
|
|
111
81
|
claimContentKey(slot.key, "slot");
|
|
112
82
|
}
|
|
113
83
|
}
|
|
114
|
-
async function compileStep(slide, config
|
|
84
|
+
async function compileStep(slide, config) {
|
|
115
85
|
const { layouts } = config;
|
|
116
86
|
const { frontmatter, slots, index } = slide;
|
|
117
87
|
// Slide numbers in errors are 1-based, matching how an author counts slides in
|
|
@@ -192,10 +162,10 @@ async function compileStep(slide, config, resolveAssetRef) {
|
|
|
192
162
|
}
|
|
193
163
|
const source = `::${name}::`;
|
|
194
164
|
const parsed = parseSlotContent(text, {
|
|
195
|
-
resolveAssetRef,
|
|
196
165
|
layoutName,
|
|
197
166
|
slideNo: slideNo,
|
|
198
167
|
source,
|
|
168
|
+
region: `Slide ${slideNo}: layout "${layoutName}" slot content (from ${source})`,
|
|
199
169
|
config,
|
|
200
170
|
layoutVariant: layoutDef.variant,
|
|
201
171
|
});
|
|
@@ -222,15 +192,11 @@ async function compileStep(slide, config, resolveAssetRef) {
|
|
|
222
192
|
* code (Shiki) and rendered mermaid (PNG) — so the returned deck is
|
|
223
193
|
* engine-shaped, ready for `buildDeck`.
|
|
224
194
|
*
|
|
225
|
-
*
|
|
226
|
-
* relative image paths. When empty, image paths are returned unchanged — callers
|
|
227
|
-
* that already produce absolute paths (or don't need resolution, e.g. unit tests)
|
|
228
|
-
* rely on the pass-through. When set, relative paths are resolved to absolute via
|
|
229
|
-
* `path.resolve(rootDir, path)`; absolute paths pass through. `config.codeTheme` /
|
|
195
|
+
* Image paths resolve against `config.deckDir`. `config.codeTheme` /
|
|
230
196
|
* `config.mermaid` / `config.mermaidVariant` feed the code and mermaid compiles.
|
|
231
197
|
*/
|
|
232
198
|
export async function compileDeck(doc, config) {
|
|
233
|
-
const { layouts
|
|
199
|
+
const { layouts } = config;
|
|
234
200
|
const theme = doc.global[RESERVED_KEY.THEME];
|
|
235
201
|
if (theme === undefined) {
|
|
236
202
|
throw new Error(`Missing required "${RESERVED_KEY.THEME}" in global frontmatter`);
|
|
@@ -245,48 +211,11 @@ export async function compileDeck(doc, config) {
|
|
|
245
211
|
assertUniqueSlideNumbers(layouts);
|
|
246
212
|
for (const layout of layouts)
|
|
247
213
|
validateLayout(layout);
|
|
248
|
-
// A body image names a picture one of two ways, and both end as an absolute
|
|
249
|
-
// path plus a fit: `$category.name` looks the picture up in the theme's
|
|
250
|
-
// catalog (which carries its declared type), while anything else is a file
|
|
251
|
-
// path relative to the deck (typed `image`, since nothing declares it). Only
|
|
252
|
-
// the lookup differs; the wrapping is shared.
|
|
253
|
-
const fromCatalog = (ref) => {
|
|
254
|
-
const match = ASSET_REF_RE.exec(ref);
|
|
255
|
-
if (!match) {
|
|
256
|
-
throw new Error(`Asset reference "${ref}" must be in the form $category.name (e.g. $logos.primary).`);
|
|
257
|
-
}
|
|
258
|
-
const [, category, name] = match;
|
|
259
|
-
const entry = assets[category]?.[name];
|
|
260
|
-
if (!entry) {
|
|
261
|
-
// Every asset in the catalog is far too many to read: a theme's icon set
|
|
262
|
-
// alone can run to thousands.
|
|
263
|
-
// A known category narrows it to that category's names, which is what the
|
|
264
|
-
// author is choosing between; an unknown one lists the categories instead.
|
|
265
|
-
const group = assets[category];
|
|
266
|
-
const available = group
|
|
267
|
-
? Object.keys(group)
|
|
268
|
-
.map((n) => `$${category}.${n}`)
|
|
269
|
-
.join(", ")
|
|
270
|
-
: Object.keys(assets)
|
|
271
|
-
.map((c) => `$${c}.*`)
|
|
272
|
-
.join(", ");
|
|
273
|
-
throw new Error(`Unknown asset reference "${ref}". Available: ${available}`);
|
|
274
|
-
}
|
|
275
|
-
return { path: resolveImagePath(rootDir, entry.path), type: entry.type };
|
|
276
|
-
};
|
|
277
|
-
const fromDeck = (ref) => ({
|
|
278
|
-
path: resolveImagePath(config.deckDir, ref),
|
|
279
|
-
type: AssetType.Image,
|
|
280
|
-
});
|
|
281
|
-
const resolveAssetRef = (ref) => {
|
|
282
|
-
const { path, type } = ref.startsWith("$") ? fromCatalog(ref) : fromDeck(ref);
|
|
283
|
-
return toImageFill(path, type);
|
|
284
|
-
};
|
|
285
214
|
// Slides compile in order: a slide's structural errors (unknown layout/key,
|
|
286
|
-
//
|
|
215
|
+
// accept-type mismatch) fire before its own content is rendered.
|
|
287
216
|
const steps = [];
|
|
288
217
|
for (const slide of doc.slides) {
|
|
289
|
-
steps.push(await compileStep(slide, config
|
|
218
|
+
steps.push(await compileStep(slide, config));
|
|
290
219
|
}
|
|
291
220
|
return { theme: String(theme), steps };
|
|
292
221
|
}
|
package/dist/markdown/index.d.ts
CHANGED
|
@@ -6,8 +6,10 @@ export { type BlockContext, type BlockHandler, parseSlotContent } from "./blocks
|
|
|
6
6
|
export { compileDeck } from "./deckCompiler.js";
|
|
7
7
|
export { parseInlineRuns } from "./inline.js";
|
|
8
8
|
export { parseRegion } from "./mdast.js";
|
|
9
|
+
export { strict } from "./schema/strict.js";
|
|
9
10
|
export { loadThemeConfig, parseThemeConfig, ThemeConfigSchema } from "./schema/themeConfigSchema.js";
|
|
10
11
|
export type { ParsedDocument, RawSlide } from "./slideParser.js";
|
|
11
12
|
export { parseSlideDocument } from "./slideParser.js";
|
|
12
|
-
export
|
|
13
|
-
export {
|
|
13
|
+
export { templateKeys } from "./textTemplate.js";
|
|
14
|
+
export type { CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, LoadedTheme, } from "./types.js";
|
|
15
|
+
export { AcceptType, RESERVED_KEY } from "./types.js";
|
package/dist/markdown/index.js
CHANGED
|
@@ -9,6 +9,8 @@ export { parseSlotContent } from "./blocks/registry.js";
|
|
|
9
9
|
export { compileDeck } from "./deckCompiler.js";
|
|
10
10
|
export { parseInlineRuns } from "./inline.js";
|
|
11
11
|
export { parseRegion } from "./mdast.js";
|
|
12
|
+
export { strict } from "./schema/strict.js";
|
|
12
13
|
export { loadThemeConfig, parseThemeConfig, ThemeConfigSchema } from "./schema/themeConfigSchema.js";
|
|
13
14
|
export { parseSlideDocument } from "./slideParser.js";
|
|
14
|
-
export {
|
|
15
|
+
export { templateKeys } from "./textTemplate.js";
|
|
16
|
+
export { AcceptType, RESERVED_KEY } from "./types.js";
|
|
@@ -22,5 +22,7 @@ export interface InlineState {
|
|
|
22
22
|
underline?: boolean;
|
|
23
23
|
link?: string;
|
|
24
24
|
breakAsNewline?: boolean;
|
|
25
|
+
/** Where the text sits (the slot region), to name in an error. */
|
|
26
|
+
region?: string;
|
|
25
27
|
}
|
|
26
28
|
export declare function walkPhrasingChildren(children: PhrasingContent[], state: InlineState): TextRun[];
|
package/dist/markdown/inline.js
CHANGED
|
@@ -71,12 +71,13 @@ function walkPhrasing(node, state) {
|
|
|
71
71
|
// path it collapses to a space.
|
|
72
72
|
return [makeRun(state.breakAsNewline ? "\n" : " ", state)];
|
|
73
73
|
default:
|
|
74
|
-
// Any other phrasing node with a literal `value` (
|
|
75
|
-
//
|
|
74
|
+
// Any other phrasing node with a literal `value` (inline HTML) is emitted as
|
|
75
|
+
// that text. One without (an image inside a sentence) has nothing to show,
|
|
76
|
+
// so it fails rather than vanish from the slide.
|
|
76
77
|
if ("value" in node && typeof node.value === "string") {
|
|
77
78
|
return [makeRun(node.value, state)];
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
throw new Error(`${state.region ? `${state.region}: ` : ""}"${node.type}" inside text isn't supported.`);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
export function walkPhrasingChildren(children, state) {
|
|
@@ -6,8 +6,8 @@ export declare const ThemeConfigSchema: z.ZodObject<{
|
|
|
6
6
|
slideNumber: z.ZodNumber;
|
|
7
7
|
description: z.ZodOptional<z.ZodString>;
|
|
8
8
|
variant: z.ZodOptional<z.ZodEnum<{
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
readonly Light: "light";
|
|
10
|
+
readonly Dark: "dark";
|
|
11
11
|
}>>;
|
|
12
12
|
parameters: z.ZodArray<z.ZodObject<{
|
|
13
13
|
shapeName: z.ZodString;
|
|
@@ -40,15 +40,6 @@ export declare const ThemeConfigSchema: z.ZodObject<{
|
|
|
40
40
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
41
41
|
}, z.core.$strict>>;
|
|
42
42
|
}, z.core.$strict>>;
|
|
43
|
-
assets: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
|
-
path: z.ZodString;
|
|
45
|
-
type: z.ZodEnum<{
|
|
46
|
-
icon: "icon";
|
|
47
|
-
image: "image";
|
|
48
|
-
background: "background";
|
|
49
|
-
}>;
|
|
50
|
-
description: z.ZodString;
|
|
51
|
-
}, z.core.$strict>>>;
|
|
52
43
|
template: z.ZodString;
|
|
53
44
|
mermaid: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
54
45
|
primary: z.ZodString;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname } from "node:path";
|
|
3
3
|
import * as z from "zod";
|
|
4
|
-
import { AcceptType,
|
|
4
|
+
import { AcceptType, Variant } from "../types.js";
|
|
5
5
|
import { strict } from "./strict.js";
|
|
6
6
|
/**
|
|
7
7
|
* Fail-fast runtime validation for a `theme.json`. The hand-written types in
|
|
@@ -16,13 +16,12 @@ import { strict } from "./strict.js";
|
|
|
16
16
|
*
|
|
17
17
|
* Every object is a `strictObject` so an unknown key throws instead of being
|
|
18
18
|
* silently dropped — strictness does NOT propagate, so each nested object is
|
|
19
|
-
* independently strict. The
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* independently strict. The exception is the `mermaid` record, whose keys are
|
|
20
|
+
* user-defined variant names (a `z.record`, open by design); strictness lands on
|
|
21
|
+
* its entries.
|
|
22
22
|
*/
|
|
23
|
-
// Reuse the const-object
|
|
24
|
-
const
|
|
25
|
-
const variantSchema = z.enum(Object.values(Variant));
|
|
23
|
+
// Reuse the const-object enum as runtime values — no third copy of the literals.
|
|
24
|
+
const variantSchema = z.enum(Variant);
|
|
26
25
|
// A table specimen's repeatable row range: `[start, end]`, 0-based inclusive,
|
|
27
26
|
// non-negative integers. The range is validated against the specimen's actual row
|
|
28
27
|
// count at fill time, where the row count is known.
|
|
@@ -35,14 +34,6 @@ const FrameSchema = strict({
|
|
|
35
34
|
cx: z.number(),
|
|
36
35
|
cy: z.number(),
|
|
37
36
|
});
|
|
38
|
-
const AssetEntrySchema = strict({
|
|
39
|
-
path: z.string(),
|
|
40
|
-
type: assetTypeSchema,
|
|
41
|
-
description: z.string(),
|
|
42
|
-
});
|
|
43
|
-
// AssetCatalog: `{ category: { name: AssetEntry } }`. The two record levels are
|
|
44
|
-
// OPEN (names are user-defined); only the leaf entry is strict.
|
|
45
|
-
const AssetCatalogSchema = z.record(z.string(), z.record(z.string(), AssetEntrySchema));
|
|
46
37
|
const MermaidVariantSchema = strict({
|
|
47
38
|
primary: z.string(),
|
|
48
39
|
primaryContrast: z.string(),
|
|
@@ -106,7 +97,6 @@ const LayoutSchema = strict({
|
|
|
106
97
|
});
|
|
107
98
|
export const ThemeConfigSchema = strict({
|
|
108
99
|
layouts: z.array(LayoutSchema),
|
|
109
|
-
assets: AssetCatalogSchema,
|
|
110
100
|
template: z.string(),
|
|
111
101
|
mermaid: MermaidConfigSchema.optional(),
|
|
112
102
|
fonts: z.array(ThemeFontSchema).optional(),
|
|
@@ -8,6 +8,8 @@ export interface ParsedDocument {
|
|
|
8
8
|
slides: RawSlide[];
|
|
9
9
|
}
|
|
10
10
|
export declare function parseSlideDocument(source: string): ParsedDocument;
|
|
11
|
+
/** True for a parsed YAML document that is a mapping (`key: value` pairs). */
|
|
12
|
+
export declare function isYamlMapping(parsed: unknown): parsed is Record<string, unknown>;
|
|
11
13
|
export declare class FrontmatterParseError extends Error {
|
|
12
14
|
constructor(slideIndex: number, yamlSource: string, cause: unknown);
|
|
13
15
|
}
|
|
@@ -15,12 +15,16 @@ export function parseSlideDocument(source) {
|
|
|
15
15
|
// GLOBAL FRONTMATTER
|
|
16
16
|
// ============================================
|
|
17
17
|
const GLOBAL_FM_RE = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
|
|
18
|
+
/** True for a parsed YAML document that is a mapping (`key: value` pairs). */
|
|
19
|
+
export function isYamlMapping(parsed) {
|
|
20
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
|
|
21
|
+
}
|
|
18
22
|
function extractGlobalFrontmatter(source) {
|
|
19
23
|
const match = source.match(GLOBAL_FM_RE);
|
|
20
24
|
if (!match)
|
|
21
25
|
return { global: {}, rest: source };
|
|
22
26
|
const parsed = parseYaml(match[1]);
|
|
23
|
-
if (
|
|
27
|
+
if (isYamlMapping(parsed)) {
|
|
24
28
|
return {
|
|
25
29
|
global: parsed,
|
|
26
30
|
rest: source.slice(match[0].length),
|
|
@@ -142,9 +146,8 @@ function parseFrontmatter(yaml, slideIndex) {
|
|
|
142
146
|
return {};
|
|
143
147
|
try {
|
|
144
148
|
const result = parseYaml(yaml);
|
|
145
|
-
if (
|
|
149
|
+
if (isYamlMapping(result))
|
|
146
150
|
return result;
|
|
147
|
-
}
|
|
148
151
|
}
|
|
149
152
|
catch (err) {
|
|
150
153
|
throw new FrontmatterParseError(slideIndex, yaml, err);
|
package/dist/markdown/types.d.ts
CHANGED
|
@@ -2,31 +2,6 @@ import type { RootContent } from "mdast";
|
|
|
2
2
|
import { type BodyRows, type Frame, type ImageFill, type TableFill, type TemplateFill, type TextFill } from "../engine/index.js";
|
|
3
3
|
import type { MermaidConfig } from "./blocks/mermaidTheme.js";
|
|
4
4
|
export type { BodyRows };
|
|
5
|
-
/**
|
|
6
|
-
* An asset's scaling/cropping tolerance, declared in the theme catalog. The
|
|
7
|
-
* compiler maps it to the engine's object-fit `fit`. `icon`: never enlarge,
|
|
8
|
-
* never crop. `image`: never crop, may scale. `background`: crop and scale freely.
|
|
9
|
-
*/
|
|
10
|
-
export declare const AssetType: {
|
|
11
|
-
readonly Icon: "icon";
|
|
12
|
-
readonly Image: "image";
|
|
13
|
-
readonly Background: "background";
|
|
14
|
-
};
|
|
15
|
-
export type AssetType = (typeof AssetType)[keyof typeof AssetType];
|
|
16
|
-
/**
|
|
17
|
-
* A theme's declaration of a reusable image asset. Purely compiler-facing —
|
|
18
|
-
* the engine never sees this type; the compiler resolves an entry's `path`
|
|
19
|
-
* to a fully-qualified filesystem path and wraps it as an ImageFill before
|
|
20
|
-
* handing the deck to the engine.
|
|
21
|
-
*/
|
|
22
|
-
export type AssetEntry = {
|
|
23
|
-
path: string;
|
|
24
|
-
/** Required — a missing type is a fail-fast error. */
|
|
25
|
-
type: AssetType;
|
|
26
|
-
description: string;
|
|
27
|
-
};
|
|
28
|
-
/** Two-level catalog: `{ category: { name: AssetEntry } }`. */
|
|
29
|
-
export type AssetCatalog = Record<string, Record<string, AssetEntry>>;
|
|
30
5
|
/**
|
|
31
6
|
* The engine content types a slot may `accept`. A subset of the engine's
|
|
32
7
|
* `SlotType`: `Template` is parameter-only, never a body block. There is no
|
|
@@ -68,7 +43,7 @@ export type EngineFill = TextFill | TableFill | ImageFill | TemplateFill;
|
|
|
68
43
|
export type BlockFill = TextFill | TableFill | ImageFill;
|
|
69
44
|
/**
|
|
70
45
|
* Everything a block handler needs to compile a node into its engine fill: the
|
|
71
|
-
*
|
|
46
|
+
* diagnostic context to name the offending layout/slide/slot
|
|
72
47
|
* when a region's markdown shape is illegal (a stray standalone block mixed into
|
|
73
48
|
* prose), and the theme-level `config` — from which code/mermaid compile read
|
|
74
49
|
* their one-per-theme style (`codeTheme`, `mermaid`, `mermaidVariant`), not the
|
|
@@ -79,10 +54,11 @@ export type BlockFill = TextFill | TableFill | ImageFill;
|
|
|
79
54
|
* imports the per-kind handlers, so the reverse would cycle.
|
|
80
55
|
*/
|
|
81
56
|
export type BlockContext = {
|
|
82
|
-
resolveAssetRef: (ref: string) => ImageFill;
|
|
83
57
|
layoutName: string;
|
|
84
58
|
slideNo: number;
|
|
85
59
|
source: string;
|
|
60
|
+
/** Where the region sits, for the author: the prefix block handlers put on their errors. */
|
|
61
|
+
region: string;
|
|
86
62
|
config: CompilerConfig;
|
|
87
63
|
/**
|
|
88
64
|
* The current layout's tonal surface, threaded from its `variant`. The code
|
|
@@ -251,7 +227,6 @@ export type ThemeFont = {
|
|
|
251
227
|
*/
|
|
252
228
|
export type CompilerThemeConfig = {
|
|
253
229
|
layouts: CompilerLayout[];
|
|
254
|
-
assets: AssetCatalog;
|
|
255
230
|
template: string;
|
|
256
231
|
mermaid?: MermaidConfig;
|
|
257
232
|
/**
|
|
@@ -276,7 +251,7 @@ export type CompilerThemeConfig = {
|
|
|
276
251
|
};
|
|
277
252
|
/**
|
|
278
253
|
* A theme as loaded from disk: its validated config plus the directory it
|
|
279
|
-
* lives in, which
|
|
254
|
+
* lives in, which its relative paths (template, fonts) resolve against.
|
|
280
255
|
*/
|
|
281
256
|
export type LoadedTheme = CompilerThemeConfig & {
|
|
282
257
|
rootDir: string;
|
package/dist/markdown/types.js
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
import { SlotType, } from "../engine/index.js";
|
|
2
|
-
// ── Asset catalog (compiler / theme-metadata only) ───────────────────────────
|
|
3
|
-
/**
|
|
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.
|
|
7
|
-
*/
|
|
8
|
-
export const AssetType = {
|
|
9
|
-
Icon: "icon",
|
|
10
|
-
Image: "image",
|
|
11
|
-
Background: "background",
|
|
12
|
-
};
|
|
13
2
|
// ── AcceptType discriminator (what a slot accepts) ────────────────────────────
|
|
14
3
|
/**
|
|
15
4
|
* The engine content types a slot may `accept`. A subset of the engine's
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# Markdown Syntax Reference
|
|
2
2
|
|
|
3
|
-
This document covers the detailed syntax for writing slide content in tycoslide deck files.
|
|
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 `
|
|
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.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## File structure
|
|
10
|
+
|
|
11
|
+
A deck file has three parts:
|
|
12
|
+
|
|
13
|
+
1. **Global frontmatter** (required) -- the first `---`-delimited block.
|
|
14
|
+
2. **Slides** -- each begins with a `---` separator. A slide's frontmatter sits between `---` delimiters. Body content follows the closing `---`.
|
|
15
|
+
3. **Slide separators** -- a `---` on its own line separates slides.
|
|
16
|
+
|
|
9
17
|
## Global frontmatter
|
|
10
18
|
|
|
11
19
|
Every deck file starts with a global frontmatter block declaring the theme:
|
|
@@ -20,6 +28,23 @@ theme: ./theme.json
|
|
|
20
28
|
|
|
21
29
|
The output `.pptx` is written next to the deck file, named after it (`deck.md` → `deck.pptx`).
|
|
22
30
|
|
|
31
|
+
## Slide frontmatter
|
|
32
|
+
|
|
33
|
+
Every slide must have a `layout:` key. All other frontmatter keys map 1:1 to the layout's **parameters**. A value containing a colon-then-space must be quoted -- `title: "The change: compute on the difference"` -- or YAML reads it as a second key and the build fails.
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
---
|
|
37
|
+
layout: Body
|
|
38
|
+
title: Key Achievements
|
|
39
|
+
subtitle: This Quarter
|
|
40
|
+
---
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `layout` is required and consumed by the compiler (not forwarded as content).
|
|
44
|
+
- All other frontmatter keys fill parameters: `title` fills the `title` parameter, `subtitle` fills the `subtitle` parameter, etc.
|
|
45
|
+
- Slots (accepting `text`, `table`, `image`) are filled by body regions, not frontmatter -- see below.
|
|
46
|
+
- A slide may also carry a `notes:` block in frontmatter -- plain-text speaker notes for the slide's notes page (see [Speaker notes](#speaker-notes)). It is slide-level metadata, not a parameter or slot.
|
|
47
|
+
|
|
23
48
|
---
|
|
24
49
|
|
|
25
50
|
## Body content
|
|
@@ -28,7 +53,7 @@ A slide's body is split into regions with `::key::` markers. Each region fills t
|
|
|
28
53
|
|
|
29
54
|
```markdown
|
|
30
55
|
---
|
|
31
|
-
layout: Body # ← placeholder; use a real layout from your
|
|
56
|
+
layout: Body # ← placeholder; use a real layout from your theme
|
|
32
57
|
title: Key Achievements
|
|
33
58
|
---
|
|
34
59
|
|
|
@@ -85,30 +110,15 @@ Content after a marker goes to the slot matching that name. The marker names mus
|
|
|
85
110
|
|
|
86
111
|
---
|
|
87
112
|
|
|
88
|
-
## Parameters and slots
|
|
89
|
-
|
|
90
|
-
A layout advertises two kinds of author-facing input, split by one rule: **a parameter is one value on a frontmatter line; a slot is a multi-line region in the body** (a `::key::` region). In `manifest.json` each layout carries two lists, `parameters` and `slots`:
|
|
113
|
+
## Parameters and slots
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
{
|
|
94
|
-
"name": "Feature with code",
|
|
95
|
-
"parameters": [
|
|
96
|
-
{ "key": "title", "type": "template" },
|
|
97
|
-
{ "key": "subtitle", "type": "template" },
|
|
98
|
-
{ "key": "logo", "type": "image", "required": true }
|
|
99
|
-
],
|
|
100
|
-
"slots": [
|
|
101
|
-
{ "key": "body", "accepts": ["text"] },
|
|
102
|
-
{ "key": "diagram", "accepts": ["image"] }
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
```
|
|
115
|
+
A layout advertises two kinds of author-facing input, split by one rule: **a parameter is one value on a frontmatter line; a slot is a multi-line region in the body** (a `::key::` region).
|
|
106
116
|
|
|
107
117
|
### Parameter types (frontmatter lines)
|
|
108
118
|
|
|
109
119
|
Fill a parameter by putting a value under its key in the slide's frontmatter.
|
|
110
120
|
|
|
111
|
-
- **`template`** -- a shape whose existing runs are walked and replaced in place, preserving each run's style. Behind the scenes each text shape carries one `template` string with `{key}` placeholders (e.g. `{title}`, or `{name}\n{jobTitle}` for a two-line credits shape), but you never see the template: the
|
|
121
|
+
- **`template`** -- a shape whose existing runs are walked and replaced in place, preserving each run's style. Behind the scenes each text shape carries one `template` string with `{key}` placeholders (e.g. `{title}`, or `{name}\n{jobTitle}` for a two-line credits shape), but you never see the template: the layout advertises **one key per placeholder**, and you fill each key as a plain scalar in frontmatter. A single-placeholder title shape gives you one key:
|
|
112
122
|
```yaml
|
|
113
123
|
title: Q3 Results
|
|
114
124
|
```
|
|
@@ -120,7 +130,7 @@ Fill a parameter by putting a value under its key in the slide's frontmatter.
|
|
|
120
130
|
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.
|
|
121
131
|
### Body content shapes
|
|
122
132
|
|
|
123
|
-
Fill a slot by writing a `::key::` region in the body; the marker maps to the slot with that `key`. A slot
|
|
133
|
+
Fill a slot by writing a `::key::` region in the body; the marker maps to the slot with that `key`. A slot lists which content types it `accepts` (`text`, `table`, `image`) -- write content whose shape matches one of them:
|
|
124
134
|
|
|
125
135
|
- **text** (slots that accept `text`) -- markdown paragraphs and bullets, rebuilt from the template's specimen paragraph styles. A fenced code block also routes here:
|
|
126
136
|
````markdown
|
|
@@ -134,13 +144,13 @@ Fill a slot by writing a `::key::` region in the body; the marker maps to the sl
|
|
|
134
144
|
````
|
|
135
145
|
The language tag (e.g. `sql`, `python`, `typescript`) is required -- it drives syntax highlighting, using the theme's `codeTheme` (set once in `theme.json`, not per slot). Colors are applied as native text runs in the output, not images.
|
|
136
146
|
- **table** (slots that accept `table`) -- a GFM table. Write it in the slot region between `|`-delimited headers and rows; cells inherit inline formatting (bold, italic, links).
|
|
137
|
-
- **image** (slots that accept `image`) --
|
|
147
|
+
- **image** (slots that accept `image`) -- an image, written as `` with the path relative to the deck:
|
|
138
148
|
```markdown
|
|
139
149
|
::logo::
|
|
140
150
|
|
|
141
|
-

|
|
142
152
|
```
|
|
143
|
-
The
|
|
153
|
+
The alt text becomes the image's alt text in PowerPoint; leave it empty for an image that is only decoration. The title is optional and holds image options as YAML. The only option is `fit`, one of `contain` (the default, which shows the whole image and never crops), `cover` (fills the frame and center-crops) or `scale-down` (shows the whole image and never enlarges it). Anything else in the title fails the build. A fenced `mermaid` block also fills an image slot, rendering to a themed PNG (see below).
|
|
144
154
|
|
|
145
155
|
---
|
|
146
156
|
|
|
@@ -205,9 +215,8 @@ The theme owns all styling. These directives are rejected at build time:
|
|
|
205
215
|
|
|
206
216
|
Each parameter or slot in the layout definition may declare:
|
|
207
217
|
- **`accepts`** (slots, required) -- an array of `text`, `table`, `image`.
|
|
208
|
-
- **`required: true`** -- the slide has no usable default and the build fails if the parameter/slot has no value
|
|
218
|
+
- **`required: true`** -- the slide has no usable default and the build fails if the parameter/slot has no value.
|
|
209
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.
|
|
210
|
-
- **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).
|
|
211
220
|
|
|
212
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).
|
|
213
222
|
|
|
@@ -247,6 +256,29 @@ subtitle: Advanced Topics
|
|
|
247
256
|
|
|
248
257
|
---
|
|
249
258
|
|
|
259
|
+
## Build
|
|
260
|
+
|
|
261
|
+
Build the deck (replace `<deck.md>` with your deck file):
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npx tycoslide build <deck.md>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Common errors and fixes:
|
|
268
|
+
|
|
269
|
+
| Error | Fix |
|
|
270
|
+
|-------|-----|
|
|
271
|
+
| `unknown layout "xyz"` | Check layout names in `theme.json` |
|
|
272
|
+
| A parameter or slot didn't fill | Use the key names the layout declares -- parameters in frontmatter, slots as body regions |
|
|
273
|
+
| YAML parse error | Fix the YAML syntax in the slide's frontmatter |
|
|
274
|
+
| `image "…" not found at …` | Check the path; it is relative to the deck file |
|
|
275
|
+
| `is not a set of options` | The image title holds options like `"fit: contain"`; put a description in the alt text |
|
|
276
|
+
| `Skipped setting relation target` | The asset image couldn't be placed; check the path and file |
|
|
277
|
+
| `forbidden style directive` | Remove `style`, `classDef`, `linkStyle`, or `%%{init}` from your mermaid block -- use `class` for grouping instead |
|
|
278
|
+
| `no "mermaid" block` | The theme has no mermaid color config -- add a `mermaid` section to theme.json |
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
250
282
|
## Full Example
|
|
251
283
|
|
|
252
284
|
```markdown
|
|
@@ -303,5 +335,5 @@ title: Office Map
|
|
|
303
335
|
|
|
304
336
|
::hero::
|
|
305
337
|
|
|
306
|
-

|
|
307
339
|
```
|