@tycoworks/tycoslide 0.14.0 → 0.15.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 -47
- package/dist/cli.js +1 -1
- package/dist/engine/dom.js +2 -5
- package/dist/engine/fillers/image.d.ts +2 -2
- package/dist/engine/fillers/image.js +4 -2
- package/dist/engine/types.d.ts +1 -1
- package/dist/engine/types.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/manifest.d.ts +3 -3
- package/dist/markdown/blocks/mermaid.js +2 -2
- package/dist/markdown/deckCompiler.js +15 -6
- package/dist/markdown/index.d.ts +1 -1
- package/dist/markdown/schema/themeConfigSchema.d.ts +4 -4
- package/dist/markdown/schema/themeConfigSchema.js +1 -1
- package/dist/markdown/types.d.ts +18 -1
- package/package.json +1 -1
- package/theme-package/syntax.md +2 -2
package/README.md
CHANGED
|
@@ -2,67 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
Create editable, on-brand PowerPoint slides from markdown, using your existing .pptx templates.
|
|
4
4
|
|
|
5
|
-
> **Early release.** tycoslide is under active development.
|
|
6
|
-
|
|
7
5
|
## Getting started
|
|
8
6
|
|
|
9
7
|
1. **Create a tycoslide theme.** Install the [create-theme](skills/create-theme) skill (`npx skills add tycoworks/tycoslide`) and give an agent such as Claude Code or Codex your `.pptx`. You'll get back an npm package of mapped layouts, colors, and visual assets, like the [tycoworks-theme](https://github.com/tycoworks/tycoworks-theme).
|
|
10
|
-
2. **Write slides in markdown.**
|
|
8
|
+
2. **Write slides in markdown.** GitHub-flavored markdown is supported; see [Markdown support](#markdown-support) below. tycoslide themes also include an agent skill, so an agent can write the slides for you.
|
|
11
9
|
3. **Build.** `npx tycoslide build deck.md` compiles the markdown into an editable PowerPoint file.
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## See it working
|
|
14
12
|
|
|
15
|
-
[
|
|
13
|
+
[how-it-works.md](https://github.com/tycoworks/tycoworks-theme/blob/main/how-it-works.md) is a deck about tycoslide, written against the [tycoworks-theme](https://github.com/tycoworks/tycoworks-theme). Clone the theme and build it:
|
|
16
14
|
|
|
17
15
|
```bash
|
|
18
16
|
git clone https://github.com/tycoworks/tycoworks-theme && cd tycoworks-theme
|
|
19
17
|
npm install
|
|
20
|
-
npx tycoslide build
|
|
18
|
+
npx tycoslide build how-it-works.md
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
````markdown
|
|
26
|
-
---
|
|
27
|
-
theme: ./theme.json
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
layout: Title
|
|
32
|
-
title: Quarterly Review
|
|
33
|
-
name: Jane Doe
|
|
34
|
-
jobTitle: Engineering
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
layout: Image right
|
|
39
|
-
title: How requests flow
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
::body::
|
|
43
|
-
|
|
44
|
-
- Every request is checked before it reaches the model
|
|
45
|
-
- Rejected requests never leave the gateway
|
|
21
|
+
## Markdown support
|
|
46
22
|
|
|
47
|
-
|
|
23
|
+
- Paragraphs, bullets and numbered lists, with bold, italic and links
|
|
24
|
+
- Tables
|
|
25
|
+
- Code, with [Shiki](https://shiki.style) syntax highlighting
|
|
26
|
+
- [Mermaid](https://mermaid.js.org) diagrams
|
|
27
|
+
- Images, from the theme's asset catalog or a file path
|
|
28
|
+
- Speaker notes
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
flowchart TD
|
|
51
|
-
A[Client] --> B[Gateway] --> C[Model]
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
layout: Code
|
|
56
|
-
title: Calling the API
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
::code::
|
|
60
|
-
|
|
61
|
-
```python
|
|
62
|
-
client = Client(api_key)
|
|
63
|
-
deck = client.build("deck.md")
|
|
64
|
-
```
|
|
65
|
-
````
|
|
30
|
+
Full syntax in [syntax.md](theme-package/syntax.md).
|
|
66
31
|
|
|
67
32
|
## Requirements
|
|
68
33
|
|
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ program
|
|
|
38
38
|
if (!absConfigPath) {
|
|
39
39
|
throw new Error(`${basename(deckPath)}: missing required "${RESERVED_KEY.THEME}" in global frontmatter`);
|
|
40
40
|
}
|
|
41
|
-
const config = { ...loadThemeConfig(absConfigPath), browserPath: opts.browserPath };
|
|
41
|
+
const config = { ...loadThemeConfig(absConfigPath), browserPath: opts.browserPath, deckDir: dirname(absDeckPath) };
|
|
42
42
|
const deck = await compileDeck(doc, config);
|
|
43
43
|
// Always write the .pptx next to the input deck, named after it.
|
|
44
44
|
const outName = basename(deckPath).replace(/\.md$/, ".pptx");
|
package/dist/engine/dom.js
CHANGED
|
@@ -346,14 +346,11 @@ export function rebuildParagraphs(shape, paragraphs, startIndex, relation, shape
|
|
|
346
346
|
continue;
|
|
347
347
|
const isBullet = para.bullet !== undefined;
|
|
348
348
|
const inLevel = para.bullet?.level ?? 0;
|
|
349
|
-
// Skip fully-empty text (matches previous fillText behavior).
|
|
350
|
-
if (para.runs.length === 1 && !para.runs[0].text)
|
|
351
|
-
continue;
|
|
352
349
|
// Bulleted content needs a bulleted specimen to model on. A plain-only shape
|
|
353
350
|
// (e.g. a designer "description" line, buNone) accepts plain text only, so
|
|
354
351
|
// bulleting it is an authoring error — fail fast rather than emit a run with
|
|
355
|
-
// no modelled style (which renders in the app default
|
|
356
|
-
// a dark slide whose real text
|
|
352
|
+
// no modelled style (which renders in the app default color, e.g. black on
|
|
353
|
+
// a dark slide whose real text color lives only in the specimen's rPr).
|
|
357
354
|
if (isBullet && bullets.size === 0) {
|
|
358
355
|
throw new Error(`Shape "${shapeName}": received bulleted content, but its specimen has no bulleted paragraph to model — this slot accepts plain text only.`);
|
|
359
356
|
}
|
|
@@ -17,7 +17,7 @@ declare const Placement: {
|
|
|
17
17
|
readonly Crop: "crop";
|
|
18
18
|
readonly Fit: "fit";
|
|
19
19
|
};
|
|
20
|
-
/** Either a symmetric `<a:srcRect>` crop (fill/cover) or a resized+re-
|
|
20
|
+
/** Either a symmetric `<a:srcRect>` crop (fill/cover) or a resized+re-centerd frame (fit/contain). */
|
|
21
21
|
type FitGeometry = {
|
|
22
22
|
placement: typeof Placement.Crop;
|
|
23
23
|
left: number;
|
|
@@ -36,7 +36,7 @@ export type GeometryResult = {
|
|
|
36
36
|
/**
|
|
37
37
|
* Size a picture shape from its `ImageFit` (via `computeGeometry`): either write
|
|
38
38
|
* `<a:srcRect>` insets to fill-and-crop, or shrink the frame to the image's
|
|
39
|
-
* aspect ratio and re-
|
|
39
|
+
* aspect ratio and re-center (fit/letterbox). Advisory warnings from the
|
|
40
40
|
* geometry pass go to `console.warn`.
|
|
41
41
|
*
|
|
42
42
|
* `image.path` is assumed absolute — the compiler / caller resolves it before the
|
|
@@ -24,7 +24,7 @@ const MIN_SCALE = 0.2;
|
|
|
24
24
|
/**
|
|
25
25
|
* Size a picture shape from its `ImageFit` (via `computeGeometry`): either write
|
|
26
26
|
* `<a:srcRect>` insets to fill-and-crop, or shrink the frame to the image's
|
|
27
|
-
* aspect ratio and re-
|
|
27
|
+
* aspect ratio and re-center (fit/letterbox). Advisory warnings from the
|
|
28
28
|
* geometry pass go to `console.warn`.
|
|
29
29
|
*
|
|
30
30
|
* `image.path` is assumed absolute — the compiler / caller resolves it before the
|
|
@@ -83,7 +83,9 @@ export function computeGeometry(frame, imgW, imgH, fit) {
|
|
|
83
83
|
if (scaleRatio > 1) {
|
|
84
84
|
warnings.push(`enlarged to ${Math.round(scaleRatio * 100)}% of native — will look soft; supply a larger image`);
|
|
85
85
|
}
|
|
86
|
-
else if (scaleRatio < MIN_SCALE) {
|
|
86
|
+
else if (scaleRatio < MIN_SCALE && fit !== ImageFit.ScaleDown) {
|
|
87
|
+
// An icon's contract is "never enlarge"; landing small in an icon well is
|
|
88
|
+
// its normal use, so only image and background warn about shrinking.
|
|
87
89
|
warnings.push(`shrunk to ${Math.round(scaleRatio * 100)}% of native — the slot is far smaller than the image`);
|
|
88
90
|
}
|
|
89
91
|
const shownW = imgW * scale;
|
package/dist/engine/types.d.ts
CHANGED
|
@@ -89,7 +89,7 @@ export type TableFill = {
|
|
|
89
89
|
/**
|
|
90
90
|
* How a picture is scaled into its frame — the engine's image-sizing directive,
|
|
91
91
|
* mirroring CSS `object-fit`. `contain`: fit the whole image, scale both ways,
|
|
92
|
-
* letterbox. `cover`: fill the frame,
|
|
92
|
+
* letterbox. `cover`: fill the frame, center-crop the overflow. `scale-down`:
|
|
93
93
|
* like contain but never enlarge past native (a small image sits at native size).
|
|
94
94
|
*/
|
|
95
95
|
export declare const ImageFit: {
|
package/dist/engine/types.js
CHANGED
|
@@ -22,7 +22,7 @@ export const SlotType = {
|
|
|
22
22
|
/**
|
|
23
23
|
* How a picture is scaled into its frame — the engine's image-sizing directive,
|
|
24
24
|
* mirroring CSS `object-fit`. `contain`: fit the whole image, scale both ways,
|
|
25
|
-
* letterbox. `cover`: fill the frame,
|
|
25
|
+
* letterbox. `cover`: fill the frame, center-crop the overflow. `scale-down`:
|
|
26
26
|
* like contain but never enlarge past native (a small image sits at native size).
|
|
27
27
|
*/
|
|
28
28
|
export const ImageFit = {
|
package/dist/index.d.ts
CHANGED
|
@@ -20,9 +20,9 @@ export declare function toEngineConfig(config: CompilerConfig): Config;
|
|
|
20
20
|
* and hands the deck to the engine's primitives-only `generate()`. The deck is
|
|
21
21
|
* structurally equivalent to the engine's `Deck` once `output` is present, so no
|
|
22
22
|
* cast is required. `buildDeck` does not itself validate `config` — a
|
|
23
|
-
* programmatic caller assembling a `CompilerConfig` by hand should load
|
|
24
|
-
* through `loadThemeConfig` (or `parseThemeConfig`) first to get the same
|
|
25
|
-
* fail-fast structural checks the CLI gets
|
|
23
|
+
* programmatic caller assembling a `CompilerConfig` by hand should load the
|
|
24
|
+
* theme through `loadThemeConfig` (or `parseThemeConfig`) first to get the same
|
|
25
|
+
* fail-fast structural checks the CLI gets, then add the deck's `deckDir`.
|
|
26
26
|
*
|
|
27
27
|
* Fails fast if `deck.output` is missing: `generate()` requires it, and the CLI
|
|
28
28
|
* populates it before calling `buildDeck`; a programmatic caller that forgot to
|
|
@@ -36,6 +36,6 @@ export type { Config, Deck, DeckStep, GenerateOptions, ImageFill, Layout, Slot,
|
|
|
36
36
|
export { fillImage, fillTable, fillTemplate, fillText, generate, SlotType } from "./engine/index.js";
|
|
37
37
|
export { ASSETS_ARCHIVE, ASSETS_FILE } from "./files.js";
|
|
38
38
|
export { generateAssetCatalog, generateManifest } from "./manifest.js";
|
|
39
|
-
export type { AssetCatalog, AssetEntry, CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, MermaidConfig, MermaidVariant, ParsedDocument, RawSlide, } from "./markdown/index.js";
|
|
39
|
+
export type { AssetCatalog, AssetEntry, CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, LoadedTheme, MermaidConfig, MermaidVariant, ParsedDocument, RawSlide, } from "./markdown/index.js";
|
|
40
40
|
export { AcceptType, compileMarkdownDeck, loadThemeConfig, parseThemeConfig } from "./markdown/index.js";
|
|
41
41
|
export { expandAssets } from "./skillZip.js";
|
package/dist/index.js
CHANGED
|
@@ -98,9 +98,9 @@ export function toEngineConfig(config) {
|
|
|
98
98
|
* and hands the deck to the engine's primitives-only `generate()`. The deck is
|
|
99
99
|
* structurally equivalent to the engine's `Deck` once `output` is present, so no
|
|
100
100
|
* cast is required. `buildDeck` does not itself validate `config` — a
|
|
101
|
-
* programmatic caller assembling a `CompilerConfig` by hand should load
|
|
102
|
-
* through `loadThemeConfig` (or `parseThemeConfig`) first to get the same
|
|
103
|
-
* fail-fast structural checks the CLI gets
|
|
101
|
+
* programmatic caller assembling a `CompilerConfig` by hand should load the
|
|
102
|
+
* theme through `loadThemeConfig` (or `parseThemeConfig`) first to get the same
|
|
103
|
+
* fail-fast structural checks the CLI gets, then add the deck's `deckDir`.
|
|
104
104
|
*
|
|
105
105
|
* Fails fast if `deck.output` is missing: `generate()` requires it, and the CLI
|
|
106
106
|
* populates it before calling `buildDeck`; a programmatic caller that forgot to
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CompilerThemeConfig } from "./markdown/types.js";
|
|
2
2
|
/** The layouts document: read whole, so it carries no open-ended list. */
|
|
3
|
-
export declare function generateManifest(config:
|
|
3
|
+
export declare function generateManifest(config: CompilerThemeConfig): string;
|
|
4
4
|
/** The catalog document: searched by name, never read whole. */
|
|
5
|
-
export declare function generateAssetCatalog(config:
|
|
5
|
+
export declare function generateAssetCatalog(config: CompilerThemeConfig): string;
|
|
@@ -143,7 +143,7 @@ function fontFaceCss(fonts) {
|
|
|
143
143
|
.join("\n");
|
|
144
144
|
}
|
|
145
145
|
const LAUNCH_ARGS = { headless: true, args: ["--no-sandbox"] };
|
|
146
|
-
/** Device pixels per CSS pixel when
|
|
146
|
+
/** Device pixels per CSS pixel when rasterizing a diagram. Sized for print density. */
|
|
147
147
|
const MERMAID_RASTER_SCALE = 6;
|
|
148
148
|
/**
|
|
149
149
|
* Find a browser rather than ship one. tycoslide never downloads Chromium: the
|
|
@@ -247,7 +247,7 @@ ${fontFaceCss(fonts)}
|
|
|
247
247
|
// Written inside the try so the finally always cleans it up, even if launch throws.
|
|
248
248
|
writeFileSync(htmlPath, html);
|
|
249
249
|
browser = await launchChromium(chromium, browserPath);
|
|
250
|
-
// Diagrams land in slide-sized frames, so they are
|
|
250
|
+
// Diagrams land in slide-sized frames, so they are rasterized well above CSS
|
|
251
251
|
// scale: a typical flowchart is ~270 CSS px wide against a slot wanting
|
|
252
252
|
// ~1400 px at print density. 2x left them visibly pixelated.
|
|
253
253
|
const page = await browser.newPage({
|
|
@@ -245,11 +245,12 @@ export async function compileDeck(doc, config) {
|
|
|
245
245
|
assertUniqueSlideNumbers(layouts);
|
|
246
246
|
for (const layout of layouts)
|
|
247
247
|
validateLayout(layout);
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
|
|
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) => {
|
|
253
254
|
const match = ASSET_REF_RE.exec(ref);
|
|
254
255
|
if (!match) {
|
|
255
256
|
throw new Error(`Asset reference "${ref}" must be in the form $category.name (e.g. $logos.primary).`);
|
|
@@ -271,7 +272,15 @@ export async function compileDeck(doc, config) {
|
|
|
271
272
|
.join(", ");
|
|
272
273
|
throw new Error(`Unknown asset reference "${ref}". Available: ${available}`);
|
|
273
274
|
}
|
|
274
|
-
return
|
|
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);
|
|
275
284
|
};
|
|
276
285
|
// Slides compile in order: a slide's structural errors (unknown layout/key,
|
|
277
286
|
// bad asset ref, accept-type mismatch) fire before its own content is rendered.
|
package/dist/markdown/index.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export { parseRegion } from "./mdast.js";
|
|
|
9
9
|
export { loadThemeConfig, parseThemeConfig, ThemeConfigSchema } from "./schema/themeConfigSchema.js";
|
|
10
10
|
export type { ParsedDocument, RawSlide } from "./slideParser.js";
|
|
11
11
|
export { parseSlideDocument } from "./slideParser.js";
|
|
12
|
-
export type { AssetCatalog, AssetEntry, CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, } from "./types.js";
|
|
12
|
+
export type { AssetCatalog, AssetEntry, CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, LoadedTheme, } from "./types.js";
|
|
13
13
|
export { AcceptType, AssetType, RESERVED_KEY } from "./types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import { type
|
|
2
|
+
import { type CompilerThemeConfig, type LoadedTheme } from "../types.js";
|
|
3
3
|
export declare const ThemeConfigSchema: z.ZodObject<{
|
|
4
4
|
layouts: z.ZodArray<z.ZodObject<{
|
|
5
5
|
name: z.ZodString;
|
|
@@ -43,8 +43,8 @@ export declare const ThemeConfigSchema: z.ZodObject<{
|
|
|
43
43
|
assets: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
44
|
path: z.ZodString;
|
|
45
45
|
type: z.ZodEnum<{
|
|
46
|
-
image: "image";
|
|
47
46
|
icon: "icon";
|
|
47
|
+
image: "image";
|
|
48
48
|
background: "background";
|
|
49
49
|
}>;
|
|
50
50
|
description: z.ZodString;
|
|
@@ -82,10 +82,10 @@ export declare const ThemeConfigSchema: z.ZodObject<{
|
|
|
82
82
|
*/
|
|
83
83
|
export declare function parseThemeConfig(raw: unknown, sourcePath: string): CompilerThemeConfig;
|
|
84
84
|
/**
|
|
85
|
-
* Read, validate, and root a `theme.json` at `absPath` into a `
|
|
85
|
+
* Read, validate, and root a `theme.json` at `absPath` into a `LoadedTheme`.
|
|
86
86
|
* The single loader the CLI and programmatic callers share: file read/JSON
|
|
87
87
|
* failures fail fast with the path; structural failures go through
|
|
88
88
|
* `parseThemeConfig`. `rootDir` (the config's directory) is attached AFTER
|
|
89
89
|
* validation — it is not a JSON field.
|
|
90
90
|
*/
|
|
91
|
-
export declare function loadThemeConfig(absPath: string):
|
|
91
|
+
export declare function loadThemeConfig(absPath: string): LoadedTheme;
|
|
@@ -129,7 +129,7 @@ export function parseThemeConfig(raw, sourcePath) {
|
|
|
129
129
|
return r.data;
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
|
-
* Read, validate, and root a `theme.json` at `absPath` into a `
|
|
132
|
+
* Read, validate, and root a `theme.json` at `absPath` into a `LoadedTheme`.
|
|
133
133
|
* The single loader the CLI and programmatic callers share: file read/JSON
|
|
134
134
|
* failures fail fast with the path; structural failures go through
|
|
135
135
|
* `parseThemeConfig`. `rootDir` (the config's directory) is attached AFTER
|
package/dist/markdown/types.d.ts
CHANGED
|
@@ -274,8 +274,25 @@ export type CompilerThemeConfig = {
|
|
|
274
274
|
};
|
|
275
275
|
mermaidVariant?: string;
|
|
276
276
|
};
|
|
277
|
-
|
|
277
|
+
/**
|
|
278
|
+
* A theme as loaded from disk: its validated config plus the directory it
|
|
279
|
+
* lives in, which the catalog's relative asset paths resolve against.
|
|
280
|
+
*/
|
|
281
|
+
export type LoadedTheme = CompilerThemeConfig & {
|
|
278
282
|
rootDir: string;
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Everything one build needs: the loaded theme plus what this particular deck
|
|
286
|
+
* brings. Neither addition is a theme.json field; both are attached per
|
|
287
|
+
* invocation, the way `rootDir` is attached after load.
|
|
288
|
+
*/
|
|
289
|
+
export type CompilerConfig = LoadedTheme & {
|
|
290
|
+
/**
|
|
291
|
+
* The deck file's directory, which a body image written as a file path
|
|
292
|
+
* resolves against. The CLI takes it from the deck's location; a
|
|
293
|
+
* programmatic caller that compiles from a string supplies it.
|
|
294
|
+
*/
|
|
295
|
+
deckDir: string;
|
|
279
296
|
/**
|
|
280
297
|
* Browser executable to render mermaid with, from `--browser-path`. Added
|
|
281
298
|
* after load like `rootDir`: it is a per-invocation choice, not a theme's.
|
package/package.json
CHANGED
package/theme-package/syntax.md
CHANGED
|
@@ -134,13 +134,13 @@ Fill a slot by writing a `::key::` region in the body; the marker maps to the sl
|
|
|
134
134
|
````
|
|
135
135
|
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
136
|
- **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`) -- a picture from the theme's asset catalog,
|
|
137
|
+
- **image** (slots that accept `image`) -- a picture, written as `` for one from the theme's asset catalog, or `` for a file relative to the deck:
|
|
138
138
|
```markdown
|
|
139
139
|
::logo::
|
|
140
140
|
|
|
141
141
|

|
|
142
142
|
```
|
|
143
|
-
The categories and names are
|
|
143
|
+
The categories and names are cataloged in `assets.json`. How the picture is scaled and cropped comes from 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. A file path has no catalog entry, so it is always fitted as `image`; put a picture in the catalog when it needs another fit or when agents should be able to find it. A fenced `mermaid` block also fills an image slot, rendering to a themed PNG (see below).
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|