@codename/deck 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +96 -2
- package/dist/deck.bundle.mjs +114 -0
- package/dist/deck.bundle.mjs.map +7 -0
- package/dist/deck.css +164 -0
- package/dist/deck.js +275 -0
- package/dist/deck.js.map +7 -0
- package/dist/deck.mjs +50 -0
- package/dist/deck.mjs.map +7 -0
- package/dist/types/blocks.d.ts +25 -0
- package/dist/types/deck.d.ts +42 -0
- package/dist/types/file.d.ts +38 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/kit.d.ts +73 -0
- package/dist/types/look.d.ts +55 -0
- package/dist/types/parse.d.ts +20 -0
- package/dist/types/pictures.d.ts +19 -0
- package/dist/types/pptx.d.ts +18 -0
- package/dist/types/render.d.ts +34 -0
- package/dist/types/repair.d.ts +5 -0
- package/dist/types/strings.d.ts +2 -0
- package/dist/types/version.d.ts +1 -0
- package/dist/types/yaml.d.ts +2 -0
- package/docs/findings.md +57 -0
- package/docs/format.md +70 -0
- package/docs/host.md +50 -0
- package/docs/kits.md +76 -0
- package/package.json +60 -4
- package/src/blocks.js +99 -0
- package/src/css.d.ts +5 -0
- package/src/deck.css +164 -0
- package/src/deck.js +359 -0
- package/src/file.js +74 -0
- package/src/index.js +12 -0
- package/src/kit.js +185 -0
- package/src/look.js +124 -0
- package/src/parse.js +180 -0
- package/src/pictures.js +47 -0
- package/src/pptx.js +310 -0
- package/src/render.js +241 -0
- package/src/repair.js +51 -0
- package/src/standalone.js +89 -0
- package/src/strings.js +10 -0
- package/src/version.js +2 -0
- package/src/yaml.js +137 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { createDeck, markdownOf } from './deck.js';
|
|
2
|
+
export { parseDeck } from './parse.js';
|
|
3
|
+
export { renderSlide } from './render.js';
|
|
4
|
+
export { STANDARD, mergeKits, layoutOf, inferLayout, labelOf, tokensFrom } from './kit.js';
|
|
5
|
+
export { tokensOf, schemeOf, LOOKS, readable, contrast } from './look.js';
|
|
6
|
+
export { readDeckFile, writeDeckFile, rendererUrl, escape, unescape } from './file.js';
|
|
7
|
+
export { toPptx } from './pptx.js';
|
|
8
|
+
export { defaultBlocks, libraryAt } from './blocks.js';
|
|
9
|
+
export { inlinePictures } from './pictures.js';
|
|
10
|
+
export { deckIn } from './repair.js';
|
|
11
|
+
export { VERSION } from './version.js';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export type Budget = {
|
|
2
|
+
required?: boolean;
|
|
3
|
+
max_words?: number;
|
|
4
|
+
max_chars?: number;
|
|
5
|
+
items?: [number, number];
|
|
6
|
+
item_max_words?: number;
|
|
7
|
+
heading_max_words?: number;
|
|
8
|
+
kind?: string;
|
|
9
|
+
};
|
|
10
|
+
export type Layout = {
|
|
11
|
+
label?: Record<string, string> | string;
|
|
12
|
+
intent?: string;
|
|
13
|
+
use_when?: string;
|
|
14
|
+
avoid_when?: string;
|
|
15
|
+
slots: string[];
|
|
16
|
+
budgets: Record<string, Budget>;
|
|
17
|
+
areas: string[][];
|
|
18
|
+
columns: string[];
|
|
19
|
+
rows: string[];
|
|
20
|
+
align: 'start' | 'center' | 'end';
|
|
21
|
+
justify: 'start' | 'center' | 'end';
|
|
22
|
+
paper: 'paper' | 'fill' | 'ink' | 'soft';
|
|
23
|
+
title: 'normal' | 'large' | 'huge';
|
|
24
|
+
pptx: string;
|
|
25
|
+
numbered: boolean;
|
|
26
|
+
};
|
|
27
|
+
export type Kit = {
|
|
28
|
+
name?: string;
|
|
29
|
+
tokens: Record<string, any>;
|
|
30
|
+
layouts: Record<string, Layout>;
|
|
31
|
+
};
|
|
32
|
+
/** The names brains give the usual layouts, for the ones deck has. */
|
|
33
|
+
export declare const ALIASES: Record<string, string>;
|
|
34
|
+
/** deck's own kit: the usual layouts, the ones Slidev names and the ones PowerPoint has, as data. */
|
|
35
|
+
export declare const STANDARD: Kit;
|
|
36
|
+
/**
|
|
37
|
+
* A layout as given, checked: its slots, areas and tracks in the grammar, its words' place, paper
|
|
38
|
+
* and size among their names. @param {any} given @returns {{ layout: Layout | null, problems: string[] }}
|
|
39
|
+
*/
|
|
40
|
+
export declare function layoutOf(given: any): {
|
|
41
|
+
layout: Layout | null;
|
|
42
|
+
problems: string[];
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Design tokens as the W3C's Design Tokens format (DTCG 2025.10) writes them, read into a kit's:
|
|
46
|
+
* `color.light` and `color.dark` groups (bg, surface, fg, muted, accent) or flat colours, `font.sans`
|
|
47
|
+
* and the like; each `$value` a hex string or a colour object with its `hex`. Tokens already flat
|
|
48
|
+
* pass through. @param {any} given @returns {Record<string, any>}
|
|
49
|
+
*/
|
|
50
|
+
export declare function tokensFrom(given: any): Record<string, any>;
|
|
51
|
+
/**
|
|
52
|
+
* Kits merged, the later over the earlier: their tokens, and their layouts by name, each checked.
|
|
53
|
+
* @param {...any} kits @returns {{ kit: Kit, problems: string[] }}
|
|
54
|
+
*/
|
|
55
|
+
export declare function mergeKits(...kits: any[]): {
|
|
56
|
+
kit: Kit;
|
|
57
|
+
problems: string[];
|
|
58
|
+
};
|
|
59
|
+
/** A layout's name in a language, else its own name. @param {Layout} layout @param {string} name @param {string} lang */
|
|
60
|
+
export declare function labelOf(layout: Layout, name: string, lang: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* The layout a slide that names none is given, from what it holds, as Pandoc's PowerPoint writer
|
|
63
|
+
* chooses: the first slide is the cover; a heading alone, a section; slots beyond the first, the
|
|
64
|
+
* layout of two (or three) columns that has them; a number alone as its heading, a fact; a quote
|
|
65
|
+
* alone, a quote; else the default. @param {{ index: number, slots: { name: string, text: string }[] }} slide
|
|
66
|
+
*/
|
|
67
|
+
export declare function inferLayout(slide: {
|
|
68
|
+
index: number;
|
|
69
|
+
slots: {
|
|
70
|
+
name: string;
|
|
71
|
+
text: string;
|
|
72
|
+
}[];
|
|
73
|
+
}): "cover" | "default" | "fact" | "quote" | "section" | "three-cols" | "two-cols" | "two-cols-header";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type Tokens = {
|
|
2
|
+
paper: string;
|
|
3
|
+
ink: string;
|
|
4
|
+
primary: string;
|
|
5
|
+
voice: string;
|
|
6
|
+
radius: number;
|
|
7
|
+
muted?: string;
|
|
8
|
+
soft?: string;
|
|
9
|
+
dark?: Record<string, string>;
|
|
10
|
+
fonts?: {
|
|
11
|
+
heading: string[];
|
|
12
|
+
body: string[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type Scheme = {
|
|
16
|
+
paper: string;
|
|
17
|
+
ink: string;
|
|
18
|
+
muted: string;
|
|
19
|
+
line: string;
|
|
20
|
+
primary: string;
|
|
21
|
+
onPrimary: string;
|
|
22
|
+
soft: string;
|
|
23
|
+
series: string[];
|
|
24
|
+
};
|
|
25
|
+
/** @typedef {{ paper: string, ink: string, primary: string, voice: string, radius: number, muted?: string, soft?: string, dark?: Record<string, string>, fonts?: { heading: string[], body: string[] } }} Tokens */
|
|
26
|
+
/** @typedef {{ paper: string, ink: string, muted: string, line: string, primary: string, onPrimary: string, soft: string, series: string[] }} Scheme */
|
|
27
|
+
/** Looks a deck can name (`theme:`), each a handful of tokens. */
|
|
28
|
+
export declare const LOOKS: Record<string, Tokens>;
|
|
29
|
+
/** Type for each voice: the device's faces, so a deck needs nothing to download to look right. */
|
|
30
|
+
export declare const VOICES: Record<string, {
|
|
31
|
+
heading: string;
|
|
32
|
+
body: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* The tokens a deck's settings give, over the project's look and a named one.
|
|
36
|
+
* @param {Record<string, any>} settings @param {Partial<Tokens>} [project]
|
|
37
|
+
* @returns {Tokens}
|
|
38
|
+
*/
|
|
39
|
+
export declare function tokensOf(settings: Record<string, any>, project?: Partial<Tokens>): Tokens;
|
|
40
|
+
/**
|
|
41
|
+
* Light or dark, worked out from the tokens: dark swaps paper and ink and lifts the primary.
|
|
42
|
+
* @param {Tokens} tokens @param {'light' | 'dark'} scheme @returns {Scheme}
|
|
43
|
+
*/
|
|
44
|
+
export declare function schemeOf(tokens: Tokens, scheme: 'light' | 'dark'): Scheme;
|
|
45
|
+
/** @param {string} value @returns {[number, number, number] | null} */
|
|
46
|
+
export declare function hex(value: string): [number, number, number] | null;
|
|
47
|
+
/** @param {number[]} rgb */
|
|
48
|
+
export declare const css: (rgb: number[]) => string;
|
|
49
|
+
/** WCAG's contrast ratio. @param {number[]} a @param {number[]} b */
|
|
50
|
+
export declare function contrast(a: number[], b: number[]): number;
|
|
51
|
+
/**
|
|
52
|
+
* The colour, its hue kept, deepened or lightened toward black or white until it reads at the
|
|
53
|
+
* ratio on the paper. @param {number[]} colour @param {number[]} paper @param {number} ratio
|
|
54
|
+
*/
|
|
55
|
+
export declare function readable(colour: number[], paper: number[], ratio: number): [number, number, number];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Slot = {
|
|
2
|
+
name: string;
|
|
3
|
+
text: string;
|
|
4
|
+
start: number;
|
|
5
|
+
};
|
|
6
|
+
export type Slide = {
|
|
7
|
+
index: number;
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
settings: Record<string, any>;
|
|
11
|
+
slots: Slot[];
|
|
12
|
+
notes: string;
|
|
13
|
+
title: string;
|
|
14
|
+
};
|
|
15
|
+
export type Deck = {
|
|
16
|
+
settings: Record<string, any>;
|
|
17
|
+
slides: Slide[];
|
|
18
|
+
};
|
|
19
|
+
/** @param {string} source @returns {Deck} */
|
|
20
|
+
export declare function parseDeck(source: string): Deck;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deck with its pictures inside it.
|
|
3
|
+
* @param {string} source
|
|
4
|
+
* @param {(place: string) => Promise<{ type: string, bytes: Uint8Array } | null>} read
|
|
5
|
+
* @param {{ web?: boolean, largest?: number }} [options] `web`: pictures on the web too; `largest`: bytes past which a picture stays a link
|
|
6
|
+
* @returns {Promise<{ source: string, inlined: string[], missing: string[], kept: string[] }>}
|
|
7
|
+
*/
|
|
8
|
+
export declare function inlinePictures(source: string, read: (place: string) => Promise<{
|
|
9
|
+
type: string;
|
|
10
|
+
bytes: Uint8Array;
|
|
11
|
+
} | null>, { web: fromWeb, largest }?: {
|
|
12
|
+
web?: boolean;
|
|
13
|
+
largest?: number;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
source: string;
|
|
16
|
+
inlined: string[];
|
|
17
|
+
missing: string[];
|
|
18
|
+
kept: string[];
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('./deck.js').DeckView} view
|
|
3
|
+
* @param {{ PptxGenJS: any, JSZip: any }} libraries
|
|
4
|
+
* @returns {Promise<{ blob: Blob, report: { slides: number, charts: number, pictures: number, tables: number, placeholders: number } }>}
|
|
5
|
+
*/
|
|
6
|
+
export declare function toPptx(view: import('./deck.js').DeckView, { PptxGenJS, JSZip }: {
|
|
7
|
+
PptxGenJS: any;
|
|
8
|
+
JSZip: any;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
blob: Blob;
|
|
11
|
+
report: {
|
|
12
|
+
slides: number;
|
|
13
|
+
charts: number;
|
|
14
|
+
pictures: number;
|
|
15
|
+
tables: number;
|
|
16
|
+
placeholders: number;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type Slide = import('./parse.js').Slide;
|
|
2
|
+
export type Slot = import('./parse.js').Slot;
|
|
3
|
+
export type Drawer = (element: HTMLElement, source: string) => void | Promise<void>;
|
|
4
|
+
export type Context = {
|
|
5
|
+
blocks: Record<string, Drawer>;
|
|
6
|
+
resolve: (src: string) => string | null;
|
|
7
|
+
headings: 'real' | 'aria';
|
|
8
|
+
number: (n: number) => string;
|
|
9
|
+
total: number;
|
|
10
|
+
kit: import('./kit.js').Kit;
|
|
11
|
+
lex: (markdown: string) => any[];
|
|
12
|
+
slide: string;
|
|
13
|
+
};
|
|
14
|
+
/** @typedef {import('./parse.js').Slide} Slide */
|
|
15
|
+
/** @typedef {import('./parse.js').Slot} Slot */
|
|
16
|
+
/** @typedef {(element: HTMLElement, source: string) => void | Promise<void>} Drawer */
|
|
17
|
+
/** @typedef {{ blocks: Record<string, Drawer>, resolve: (src: string) => string | null, headings: 'real' | 'aria', number: (n: number) => string, total: number, kit: import('./kit.js').Kit, lex: (markdown: string) => any[], slide: string }} Context */
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} tag @param {Record<string, string | number | boolean | undefined>} [attributes]
|
|
20
|
+
* @param {(Node | string | null | undefined | false)[]} [children]
|
|
21
|
+
*/
|
|
22
|
+
export declare function h(tag: string, attributes?: Record<string, string | number | boolean | undefined>, children?: (Node | string | null | undefined | false)[]): HTMLElement;
|
|
23
|
+
/** An address a slide may show: the web's, a picture held in the file, or one beside it. @param {string} href */
|
|
24
|
+
export declare function safeUrl(href: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* A slide, laid out by its layout, with its slots, steps and number.
|
|
27
|
+
* @param {Slide} slide @param {Context} context @returns {{ element: HTMLElement, steps: number }}
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderSlide(slide: Slide, context: Context): {
|
|
30
|
+
element: HTMLElement;
|
|
31
|
+
steps: number;
|
|
32
|
+
};
|
|
33
|
+
/** @param {any[]} tokens @param {Context} context @returns {Node[]} */
|
|
34
|
+
export declare function inline(tokens: any[] | undefined, context: Context): Node[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.1";
|
package/docs/findings.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# What phase 0 measured
|
|
2
|
+
|
|
3
|
+
Measured on 25 October 2026 on a Mac (M3 Max), in Playwright's Chromium and WebKit, Microsoft PowerPoint 16.114 and Keynote 14, with two local brains through LM Studio: Qwen 3.6 35B A3B Splash and Qwen 3.5 4B.
|
|
4
|
+
|
|
5
|
+
## Size
|
|
6
|
+
|
|
7
|
+
| File | gzipped |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `dist/deck.js`, the whole file renderer, `marked` inside | 37.8 KB (113 KB as held in a file) |
|
|
10
|
+
| `dist/deck.mjs`, for a host with its own `marked` | 21.1 KB |
|
|
11
|
+
| `dist/deck.css` | 3.8 KB |
|
|
12
|
+
| A linked 11-slide `.deck.html` | 2.7 KB, not gzipped |
|
|
13
|
+
|
|
14
|
+
## Browsers
|
|
15
|
+
|
|
16
|
+
From disk (`file://`) in Chromium and WebKit: the first slide in 150 to 460 ms; no slide over its box; no error. Steps, the Morph between two slides sharing a heading, a change on a slide going back into the Markdown, a change in the Markdown drawn on the slide, the presenter's view, all slides at once, light and dark, Arabic right to left and Japanese. With scripts off, the Markdown as text. In a host page under `style-src 'self'` and `trusted-types 'none'`: no violation.
|
|
17
|
+
|
|
18
|
+
## PowerPoint
|
|
19
|
+
|
|
20
|
+
Made in the browser in 50 to 140 ms. PowerPoint opens every file without a repair: English light and dark, Arabic (bullets, the chart's axis and the table's columns mirrored), Japanese, and a deck with its own kit, whose layouts PowerPoint lists by their labels. Fixed during phase 0: text boxes as wide as their slot, titles anchored at their foot, centred layouts centred, Arabic upright.
|
|
21
|
+
|
|
22
|
+
Left: a list beside a picture touches the slide's edge; CSS decorations (a quote's mark, a section's rule) are not exported; faces a kit names should be ones Office has.
|
|
23
|
+
|
|
24
|
+
**Keynote is not a target.** It opens the files, says the format is invalid and drops the native charts; text, tables, layouts, diagrams and notes survive.
|
|
25
|
+
|
|
26
|
+
## Brains, with a draft skill
|
|
27
|
+
|
|
28
|
+
`eval/skill.md`, four requests (a support team's numbers, a children's park for a town council, training new staff, a school trip) in English, French, Arabic and Japanese.
|
|
29
|
+
|
|
30
|
+
- Neither brain used the asked-for ` ````deck ` fence (0 of 8). With `deckIn`'s repairs, 8 of 8 decks, 55 slides, none over its box, every chart parsed and drawn, no stray HTML, the language right every time.
|
|
31
|
+
- The 35B brain makes real decks (sections, a big number, two columns, a chart, a timeline). The 4B makes usable, weaker ones: charts nobody needed, squeezed under bullets.
|
|
32
|
+
|
|
33
|
+
## Brains, with no help
|
|
34
|
+
|
|
35
|
+
The same requests, asking for a title slide, a section, two columns, steps and notes on every slide, naming only the dialect, scored in that dialect's own syntax (`eval/dialects/`).
|
|
36
|
+
|
|
37
|
+
| Asked in | 35B, of 5 | 4B, of 5 | HTML and CSS invented (35B / 4B) |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| Slidev | 2.3 | 0.8 (one reply empty) | 34 / 9 |
|
|
40
|
+
| Marp | 1.8 | 1.8 (7 of 8 decks without `marp: true`) | 22 / 14 |
|
|
41
|
+
| Quarto (Pandoc) | 3.0 | 2.0 | 2 / 88 |
|
|
42
|
+
|
|
43
|
+
Brains blend dialects: Slidev decks with Pandoc's `::: notes`, Quarto decks with remark's `???`, Marp decks with reveal.js's fragments. Read liberally, all 23 replies that held anything became decks: 144 slides, no error.
|
|
44
|
+
|
|
45
|
+
## Diagrams
|
|
46
|
+
|
|
47
|
+
Fourteen samples (`test/diagrams/`), from a flowchart to a quadrant chart, two in Japanese and Arabic.
|
|
48
|
+
|
|
49
|
+
| Renderer | gzipped | Samples drawn |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| Mermaid 12.2 | 1,567 KB | 14 |
|
|
52
|
+
| @mermaid-js/tiny 12.2 | 744 KB | not run (no mind maps) |
|
|
53
|
+
| beautiful-mermaid 1.1 | 458 KB | not run (6 kinds) |
|
|
54
|
+
| **Pele 1.10** | 254 KB all, 130 KB twelve kinds, 17–52 KB a kind | 14, with no DOM, 0.14–1.3 ms each |
|
|
55
|
+
| @crafter/mermaid 0.0.3 | 31 KB | 9, and no licence in its packages |
|
|
56
|
+
|
|
57
|
+
Pele's drawings match Mermaid's or are clearer. It was first released on 17 October 2026: deck pins its version and draws the samples in its tests.
|
package/docs/format.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# The deck file
|
|
2
|
+
|
|
3
|
+
## The page
|
|
4
|
+
|
|
5
|
+
A deck is one HTML file, named `<name>.deck.html`. Any browser opens it; the renderer draws and presents it. A host reads its Markdown as text and never runs the page.
|
|
6
|
+
|
|
7
|
+
````html
|
|
8
|
+
<!doctype html>
|
|
9
|
+
<html lang="en" dir="ltr">
|
|
10
|
+
<meta charset="utf-8">
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12
|
+
<meta name="generator" content="deck 0.1.0">
|
|
13
|
+
<title>A second oven</title>
|
|
14
|
+
<style>#deck{display:block;white-space:pre-wrap;overflow-wrap:anywhere;font:1rem/1.6 ui-monospace,Menlo,Consolas,monospace;max-width:72ch;margin:2rem auto;padding:0 1rem}</style>
|
|
15
|
+
<body>
|
|
16
|
+
<script type="text/markdown" id="deck">
|
|
17
|
+
…the Markdown…
|
|
18
|
+
</script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/@codename/deck@0.1.0/dist/deck.js"></script>
|
|
20
|
+
</html>
|
|
21
|
+
````
|
|
22
|
+
|
|
23
|
+
- **The Markdown** sits whole in one element, `<script type="text/markdown" id="deck">`, which no browser runs. Nothing inside a script element is decoded, so `R&D` stays `R&D`.
|
|
24
|
+
- **One escape.** `<script` and `</script`, in any case, gain a backslash after their `<`, and one more for each already there; reading takes one away. The HTML parser would otherwise end the element at `</script`, and a `<script` after `<!--` would keep it from ending. `readDeckFile(writeDeckFile(md))` gives back `md` exactly.
|
|
25
|
+
- **In the body.** Without the explicit `<body>`, the parser would put the element in the head, which is never shown.
|
|
26
|
+
- **With scripts off,** the style above shows the Markdown as plain text in the body.
|
|
27
|
+
- **The renderer** is linked by default, to this version of the package on jsDelivr (`rendererUrl()`). A host may link another address, or hold the renderer whole (`{ code }`, about 113 KB), its `<!--`, `<script` and `</script` written with `\x3C`. A `data-base` attribute on the renderer's script says where the libraries a slide needs come from: a CDN's npm mirror (the default) or a `node_modules/` folder.
|
|
28
|
+
- **Pictures** the deck names by a host's own address are held inside the file as `data:` addresses when it leaves the host (`inlinePictures`). Pictures on the web stay links unless asked for. A page opened from disk can't read back a picture beside it, so a file meant to travel holds its pictures.
|
|
29
|
+
- **Title, language and direction** are in the head for whatever reads the page before the renderer does.
|
|
30
|
+
|
|
31
|
+
## The Markdown
|
|
32
|
+
|
|
33
|
+
Slidev's form, as `@slidev/parser` splits it, and the parts a deck for knowledge workers needs. Every deck written this way opens in Slidev too; ECharts blocks and kits need an addon there.
|
|
34
|
+
|
|
35
|
+
### Written and read
|
|
36
|
+
|
|
37
|
+
- **Slides** between lines holding only `---`. A `---` followed by a `key:` line opens that slide's settings, YAML up to the next `---` line; a ` ```yaml ` block at a slide's top does the same.
|
|
38
|
+
- **The first slide's settings** are the deck's too (its headmatter): `title`, `lang`, `dir`, `colorSchema` (`auto`, `light`, `dark`), `transition`, `theme`, `themeConfig`, `kit`.
|
|
39
|
+
- **A slide's settings:** `layout`, `transition` (`slide-left`, `slide-right`, `slide-up`, `fade`, `view-transition`; `a | b` for forward and back), `class` (`invert`, `text-center`), `image`, `background`, `alt`, `title`, `paginate`.
|
|
40
|
+
- **Slots:** `::name::` alone on a line starts the slot of that name; what comes before is `default`.
|
|
41
|
+
- **Notes:** the last HTML comment at a slide's end.
|
|
42
|
+
- **Steps:** `<v-clicks>` and `</v-clicks>` around a list, each on its own line with empty lines around; `<v-click>` around a block.
|
|
43
|
+
- **Code** in fences, with lines marked as Slidev marks them: ` ```ts {2,4-5} `.
|
|
44
|
+
- **Charts:** ` ```echarts ` holding an ECharts option as JSON, data only.
|
|
45
|
+
- **Diagrams:** ` ```mermaid ` holding Mermaid's language.
|
|
46
|
+
- **Markdown** as CommonMark and GFM have it: headings, lists, task lists, tables, quotes, emphasis, links, pictures.
|
|
47
|
+
|
|
48
|
+
### Added by deck
|
|
49
|
+
|
|
50
|
+
- ` ```echarts `, `lang` and `dir`.
|
|
51
|
+
- `kit`: tokens and layouts as data ([kits.md](kits.md)).
|
|
52
|
+
- A layout chosen from what a slide holds when it names none.
|
|
53
|
+
- A Morph between two slides that share a heading, a picture or a list item, with `transition: view-transition`.
|
|
54
|
+
|
|
55
|
+
### Also read, as brains write it
|
|
56
|
+
|
|
57
|
+
Brains blend dialects. These are read, never rewritten:
|
|
58
|
+
|
|
59
|
+
- Pandoc's `::: notes`, `:::: columns` holding `::: column`s (slots `left`, `right`, or `left`, `middle`, `right`; what follows them `bottom`), `::: incremental`.
|
|
60
|
+
- remark's `???` and reveal.js's `Note:` (what follows is the notes), reveal.js's `<aside class="notes">`.
|
|
61
|
+
- Slides at headings of the first two levels when a deck has no `---` at all, as Pandoc splits a document.
|
|
62
|
+
- Steps written as `<li v-click>`.
|
|
63
|
+
- A `---` followed by words, which Slidev would take for settings, as a new slide.
|
|
64
|
+
- Names brains give layouts: `title`, `two-columns`, `comparison`, `divider`, `thanks`…
|
|
65
|
+
|
|
66
|
+
`deckIn(answer)` finds a deck in a brain's answer and mends what brains get wrong most: a deck fenced as ` ```markdown ` or with nothing, a chart's fence inside three backticks, settings after an empty line, steps without their empty lines, a chart fenced as ` ```json `, words after a slot's marker.
|
|
67
|
+
|
|
68
|
+
### Left out
|
|
69
|
+
|
|
70
|
+
Vue components and expressions, UnoCSS classes, `<style>`, imported slides, Monaco, drawing. Other HTML gives up its words alone.
|
package/docs/host.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Drawing decks in a host
|
|
2
|
+
|
|
3
|
+
A host is a page that draws decks itself: Jean, or any other. deck builds every element with the DOM's own calls, sets custom properties through the CSSOM rather than style attributes, and needs no Trusted Types policy, so it runs under a policy with `style-src 'self'` and `trusted-types 'none'`. Load `@codename/deck` (with your own `marked` through an import map) or `@codename/deck/bundle`, and `@codename/deck/css` as a stylesheet.
|
|
4
|
+
|
|
5
|
+
## createDeck(element, options)
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import { createDeck } from '@codename/deck'
|
|
9
|
+
|
|
10
|
+
const view = createDeck(document.querySelector('#deck'), {
|
|
11
|
+
source, // the Markdown
|
|
12
|
+
lang: 'fr', dir: 'ltr', // else the deck's own, else the page's
|
|
13
|
+
kit, // the host's kit: an organisation's, a person's, a project's, merged
|
|
14
|
+
scheme: 'auto', // 'light', 'dark' or 'auto', over the deck's colorSchema
|
|
15
|
+
blocks: { echarts, mermaid }, // drawers for fenced blocks: (box, source) => void | Promise<void>
|
|
16
|
+
resolve: (src) => url, // a picture's address as the page can load it, or null
|
|
17
|
+
strings, // the bar's words, and `slide` and `carousel` for screen readers, else deck's own (en, fr, ar, ja)
|
|
18
|
+
lex: (markdown) => tokens, // how a slide's Markdown is read: the host's own marked, its extensions too; else marked's GFM
|
|
19
|
+
headings: 'aria', // 'real' (h1–h6) or 'aria' (role=heading), for a deck inside a page
|
|
20
|
+
editable: true,
|
|
21
|
+
onChange: (source) => {}, // after each change, by the Markdown or on a slide
|
|
22
|
+
onSave: (source) => {}, // ⌘S or Ctrl+S
|
|
23
|
+
onExport: (view) => {}, // the bar's Save as PowerPoint
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The view: `source`, `deck` (parsed), `at`, `scheme`, `kit`, `kitProblems`, `lang`, `dir`, `slides()`, `setSource(text)`, `go(index, step)`, `toggle('is-editing' | 'is-presenting' | 'is-overview')`, `present()`, `setScheme(scheme)`, `fitAll()`, `destroy()`.
|
|
28
|
+
|
|
29
|
+
Events on the element: `deck-scheme` (`detail: { scheme, colours, tokens }`) when the look is set, `deck-move` (`detail: { at, step }`) on each move.
|
|
30
|
+
|
|
31
|
+
Every block that can be changed by hand carries `data-edit="from-to"`, the offsets of its words in the source, so a host can point at a part of a slide and change it in the Markdown.
|
|
32
|
+
|
|
33
|
+
## Drawers
|
|
34
|
+
|
|
35
|
+
`defaultBlocks({ base, root, versions })` gives the drawers a file uses alone: ECharts as SVG in the deck's colours and type, its category axis mirrored right to left; Pele for Mermaid's language, its SVG shown as a picture coloured by Pele's variables, its words the picture's text. Each library loads the first time a slide needs it, from `base` (jsDelivr's npm mirror, or a `node_modules/` folder, `libraryAt()`). A host with a strict policy gives its own: Pele's SVG holds style attributes, so it belongs in an image or a worker's output, not in the page's DOM.
|
|
36
|
+
|
|
37
|
+
## PowerPoint
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const { blob, report } = await toPptx(view, { PptxGenJS, JSZip })
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
PptxGenJS 4 (its bundle carries JSZip) is the host's to load, on demand. The file is made from the deck as drawn: a slide layout per deck layout, named by its label in the deck's language, with a placeholder for its title and each slot; lists as lists with their levels; tables; bars, lines, areas, pies, rings, points and radars as native charts with their numbers, other charts and diagrams as pictures; notes; transitions; the kit's colours as the theme. `report` counts slides, charts, tables, pictures and placeholders.
|
|
44
|
+
|
|
45
|
+
## Files
|
|
46
|
+
|
|
47
|
+
- `readDeckFile(html)`: the Markdown, or `null`; never parses the page.
|
|
48
|
+
- `writeDeckFile(markdown, { title, lang, dir, renderer, base })`: the page. `renderer` is `{ src }` (default: `rendererUrl()`) or `{ code }`.
|
|
49
|
+
- `inlinePictures(source, read, { web, largest })`: the deck with its pictures inside it. `read(place)` gives `{ type, bytes }` for a picture the host holds. Hosts call it on a deck leaving them (a download, a share).
|
|
50
|
+
- `deckIn(answer)`: a brain's deck from its answer, mended, with what was mended.
|
package/docs/kits.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Kits
|
|
2
|
+
|
|
3
|
+
A kit is a design system and the layouts that go with it, as data. deck checks every layout against the grammar below and drops what fails, with a reason, so no kit can do more than lay out a slide: no CSS, no addresses, no code. A person, an organisation or a brain can make one safely.
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
kit:
|
|
7
|
+
name: Martin's bakery
|
|
8
|
+
tokens: { primary: '#c2410c', voice: elegant, radius: 4 }
|
|
9
|
+
layouts:
|
|
10
|
+
three-numbers:
|
|
11
|
+
label: { en: Three numbers, fr: Trois chiffres }
|
|
12
|
+
intent: Prove the title with three numbers.
|
|
13
|
+
slots: [default, one, two, three]
|
|
14
|
+
areas: default default default | one two three
|
|
15
|
+
rows: auto 1fr
|
|
16
|
+
align: center
|
|
17
|
+
pptx: Comparison
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Where kits come from
|
|
21
|
+
|
|
22
|
+
Kits stack, the later over the earlier: deck's own (`STANDARD`), then what the host gives (`options.kit`: an organisation's, a person's, a project's, merged by the host), then the file's own `kit` in its headmatter. A host writes the kit a deck was drawn with into its file, so it looks the same wherever it goes.
|
|
23
|
+
|
|
24
|
+
## Tokens
|
|
25
|
+
|
|
26
|
+
Flat:
|
|
27
|
+
|
|
28
|
+
| Token | What it is |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `paper`, `ink`, `primary` | Colours, as `#rgb` or `#rrggbb` |
|
|
31
|
+
| `muted`, `soft` | Optional: secondary words, a pale fill |
|
|
32
|
+
| `voice` | `calm`, `elegant`, `bold`, `friendly`: the device's own faces for headings and text |
|
|
33
|
+
| `fonts` | Optional: `{ heading: [...], body: [...] }`, face stacks, over the voice |
|
|
34
|
+
| `radius` | Corners, in pixels |
|
|
35
|
+
| `dark` | Optional: `{ paper, ink, primary, muted, soft }` for the dark scheme, else worked out |
|
|
36
|
+
|
|
37
|
+
Or in the W3C's Design Tokens format (DTCG 2025.10): `color.light` and `color.dark` groups of `bg`, `surface`, `fg`, `muted`, `accent` (or flat colours), `font.sans`, `font.heading`, `font.body`; each `$value` a hex string or a colour object with its `hex`. `tokensFrom()` reads them.
|
|
38
|
+
|
|
39
|
+
Whatever the tokens, light and dark are each checked: words at 4.5:1 on their paper, a chart's marks at 3:1, each colour deepened or lightened at its hue until it reads.
|
|
40
|
+
|
|
41
|
+
## Layouts
|
|
42
|
+
|
|
43
|
+
| Field | Grammar | Default |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `slots` | Names (`a-z`, `0-9`, `-`), or a map of names to budgets; `title` there means the heading the default slot opens with | `[default]` |
|
|
46
|
+
| `areas` | Rows of slot names or `.`, split by `\|` or new lines, or a list of rows; every slot a rectangle | the slots in a row |
|
|
47
|
+
| `columns`, `rows` | One track a column or row: `auto`, `min-content`, `max-content`, `1fr`…`99fr`, `10%`… | `1fr` each |
|
|
48
|
+
| `align` | `start`, `center`, `end`: across | `start` |
|
|
49
|
+
| `justify` | `start`, `center`, `end`: down | `start` |
|
|
50
|
+
| `paper` | `paper`, `fill` (the primary), `ink`, `soft` | `paper` |
|
|
51
|
+
| `title` | `normal`, `large`, `huge` | `normal` |
|
|
52
|
+
| `pptx` | Title Slide, Title and Content, Section Header, Two Content, Comparison, Title Only, Blank, Content with Caption, Picture with Caption | by its slots |
|
|
53
|
+
| `numbered` | `false` for no slide number | `true` |
|
|
54
|
+
| `label` | Words, or words by language: `{ en: …, fr: … }`; PowerPoint's layout is named so | the name |
|
|
55
|
+
| `intent`, `use_when`, `avoid_when` | Words for the brain choosing a layout | none |
|
|
56
|
+
| budgets | `required`, `max_words`, `max_chars`, `items: [min, max]`, `item_max_words`, `heading_max_words`, `kind` | none |
|
|
57
|
+
|
|
58
|
+
A catalog's `types` are read as `layouts`. Budgets are kept with each layout for a linter and for the brain; deck 0.1 does not check them yet.
|
|
59
|
+
|
|
60
|
+
## deck's own layouts
|
|
61
|
+
|
|
62
|
+
| Layout | Slots | In PowerPoint |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `cover`, `intro`, `end` | one | Title Slide |
|
|
65
|
+
| `default` | one | Title and Content |
|
|
66
|
+
| `section` | one | Section Header |
|
|
67
|
+
| `two-cols` | `default`, `right` | Two Content |
|
|
68
|
+
| `two-cols-header` | `default` across, `left`, `right`, `bottom` | Comparison |
|
|
69
|
+
| `three-cols` | `default` across, `left`, `middle`, `right` | Comparison |
|
|
70
|
+
| `center`, `statement`, `fact`, `quote` | one | Title Only |
|
|
71
|
+
| `image-left`, `image-right` | one, beside the slide's `image` | Picture with Caption |
|
|
72
|
+
| `image`, `full`, `none` | one | Blank |
|
|
73
|
+
|
|
74
|
+
## A slide that names none
|
|
75
|
+
|
|
76
|
+
As Pandoc's PowerPoint writer chooses: a slide with `middle` is `three-cols`, with `left` `two-cols-header`, with `right` `two-cols`; the first slide is `cover`; a heading alone is `section`; a number alone as its heading, a line or two under it, is `fact`; a quote alone, its author under it, is `quote`; else `default`. A layout the kit lacks falls back the same way.
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codename/deck",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Slide decks as one web page: Markdown in Slidev's form inside a .deck.html file any browser presents, a renderer that runs anywhere, kits of layouts and looks, and an editable PowerPoint out",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/types/index.d.ts",
|
|
10
|
+
"default": "./dist/deck.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./bundle": {
|
|
13
|
+
"types": "./dist/types/index.d.ts",
|
|
14
|
+
"default": "./dist/deck.bundle.mjs"
|
|
15
|
+
},
|
|
16
|
+
"./file": "./dist/deck.js",
|
|
17
|
+
"./css": "./dist/deck.css",
|
|
18
|
+
"./src/*": "./src/*"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src",
|
|
23
|
+
"docs",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"slides",
|
|
29
|
+
"presentation",
|
|
30
|
+
"markdown",
|
|
31
|
+
"slidev",
|
|
32
|
+
"powerpoint",
|
|
33
|
+
"pptx"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "node build.mjs && tsc -p tsconfig.types.json",
|
|
37
|
+
"examples": "npm run build && node examples/make.mjs",
|
|
38
|
+
"test": "node --test test/*.test.js",
|
|
39
|
+
"test:browsers": "npm run examples && node test/browser.mjs && node test/kit-browser.mjs",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"check": "npm run typecheck && npm test && npm run test:browsers",
|
|
42
|
+
"eval": "node eval/run.mjs",
|
|
43
|
+
"eval:check": "node eval/check.mjs && node eval/dialects/check.mjs && node eval/dialects/draw.mjs",
|
|
44
|
+
"prepare": "git config core.hooksPath .githooks || true",
|
|
45
|
+
"prepublishOnly": "npm run build && npm test"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"marked": "^18.0.14"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"echarts": "6.1.0",
|
|
52
|
+
"esbuild": "0.28.2",
|
|
53
|
+
"marked": "^18.1.1",
|
|
54
|
+
"pele": "1.10.0",
|
|
55
|
+
"playwright": "1.64.1",
|
|
56
|
+
"pptxgenjs": "^4.0.0",
|
|
57
|
+
"typescript": "^7.0.2"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=22"
|
|
61
|
+
}
|
|
62
|
+
}
|