orz-slides 0.1.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.
@@ -0,0 +1,386 @@
1
+ ---
2
+ name: orz-slides
3
+ description: Author and edit self-contained .slides.html presentation decks (orz-slides). Use when a user wants to turn notes or markdown into a browser-based slide deck — one portable HTML file that presents in any browser (reveal.js), is written in orz-markdown with a layout syntax, and can be edited in-browser. Covers the deck source format, slide/layout/region markers, structure templates, and per-slide capacity budgets so slides do not overflow.
4
+ ---
5
+
6
+ # orz-slides — author a `.slides.html` deck
7
+
8
+ `orz-slides` turns a **deck source** into **one `.slides.html` file** that:
9
+
10
+ - presents in any modern browser (reveal.js: keyboard/touch nav, slide overview),
11
+ - is authored entirely in **orz-markdown** (math, mermaid, smiles, qr, charts,
12
+ tabs, containers) with a small **layout syntax**,
13
+ - can be **edited in the browser** (per-slide pop-out CodeMirror editor + live
14
+ preview) and **saves itself** back into the file.
15
+
16
+ The deck source is the single source of truth, embedded in the file:
17
+
18
+ ```html
19
+ <script type="text/orz-slides" id="orz-deck">
20
+ ...deck config + slides (this is what you write)...
21
+ </script>
22
+ ```
23
+
24
+ You write only the **deck source**. Never hand-write the surrounding HTML
25
+ (reveal scaffold, runtime, CDN links) — saving regenerates it. To change a deck,
26
+ edit the deck source (in a `.md`-ish file fed to the CLI, or in-browser) and let
27
+ the tool re-serialize.
28
+
29
+ > Status: orz-slides is **functional but not yet published to npm**. The
30
+ > authoring syntax below is implemented; generate decks with the CLI
31
+ > (`orz-slides deck.md`). Speaker view (**S**), step-reveal fragments, an on-deck
32
+ > timer (**T**), and slide numbers are wired; **PDF export** is the remaining
33
+ > planned presenter feature.
34
+
35
+ ## When to use it
36
+
37
+ - A user wants a **single shareable file** that presents like a slide deck with
38
+ no install — email it, host it, open it offline-of-tooling in a browser.
39
+ - The content is **markdown-native** (bullets, math, diagrams, chemistry, code)
40
+ and the author wants slides without a pixel-design tool.
41
+ - Prefer `.md.html` (orz-mdhtml) when the output is a *document* to read/annotate;
42
+ prefer `.slides.html` (orz-slides) when it is a *deck* to present.
43
+
44
+ ---
45
+
46
+ ## The deck source format
47
+
48
+ A deck source is plain text: an optional leading `<!-- deck … -->` config block,
49
+ then a sequence of slides. **Every slide begins with a `<!-- slide … -->`
50
+ marker** — that marker is also the slide separator. There is no bare `---`.
51
+
52
+ ```
53
+ <!-- deck
54
+ title: Controlled Polymerization
55
+ theme: executive
56
+ ratio: 16:9
57
+ author: Dr. Yu Wang
58
+ footer: Internal · v3 · 2026
59
+ -->
60
+
61
+ <!-- slide template=title -->
62
+ # Controlled Polymerization
63
+ ## RAFT vs ATRP
64
+ **Dr. Yu Wang** · Louisiana · 2026
65
+
66
+ <!-- slide 2col 3/2 -->
67
+ ## Results
68
+ <!-- @left -->
69
+ - Accuracy **92%**
70
+ <!-- @right -->
71
+ {{smiles C(=S)(SC)SC}}
72
+ ```
73
+
74
+ ### Deck config (`<!-- deck … -->`, optional)
75
+
76
+ YAML-ish `key: value` lines. `:` is for config values; it never appears in a
77
+ layout. Keys: `title`, `theme`, `ratio` (`16:9` default, or `4:3`), `author`,
78
+ `footer` (deck-wide footer shown on normal slides), `transition` (default reveal
79
+ transition).
80
+
81
+ Theme ids: `paper`, `architect`, `executive`, `sage`, `poppy`, `neon`, `chalk`
82
+ (plus a base). Readers can switch live in the editor.
83
+
84
+ ### Slide marker (`<!-- slide … -->`, mandatory)
85
+
86
+ The marker carries the layout (a preset alias or a raw split) plus per-slide
87
+ options:
88
+
89
+ ```
90
+ <!-- slide --> single region (plain markdown slide)
91
+ <!-- slide 2col 3/2 --> a preset with a track ratio
92
+ <!-- slide col 3/2 { main; side } --> a raw split (same grammar as presets)
93
+ <!-- slide template=title --> a structure-page template
94
+ <!-- slide 2col bg=#0b3 t=fade --> with options
95
+ ```
96
+
97
+ Per-slide options (all optional): `bg=` (color or image), `t=` (transition),
98
+ `fit=` (`fit` | `scroll` | `off`, default `fit`), `class=`, `id=`, and the bare
99
+ flag `step` (step-reveal — see below). Template variants use `v=` (e.g.
100
+ `template=title v=2`).
101
+
102
+ ### Step-reveal fragments
103
+
104
+ Reveal a slide's content one piece at a time:
105
+
106
+ - **Whole slide** — add the bare `step` flag: `<!-- slide step -->` (or combined
107
+ with a layout, `<!-- slide step 2col -->`). Lists reveal **per item**; other
108
+ top-level blocks (paragraphs, images, tables) reveal **one at a time**, in
109
+ document order, across all regions.
110
+ - **Individual block** — tag it with `{{attrs[.fragment]}}` (orz-markdown attrs),
111
+ e.g. `A revealed paragraph.{{attrs[.fragment]}}`. Note: attrs **cannot** tag a
112
+ list item — use the `step` flag for per-bullet reveal.
113
+
114
+ ### Presenting
115
+
116
+ reveal.js keyboard nav, plus: **S** opens a self-contained **speaker view**
117
+ (current + next slide, the slide's `@notes`, a wall clock and a start/pause/reset
118
+ timer; arrows there drive the deck). **T** toggles an on-deck clock/timer
119
+ overlay. Slide numbers (`c/t`) and a progress bar show during the presentation.
120
+
121
+ > Track ratios always use `/` (CSS-aligned: `3/2`, `auto/1fr`, `30%/1fr`).
122
+ > `:` is reserved for `key: value` config. One symbol, one meaning.
123
+
124
+ ---
125
+
126
+ ## The slide frame & the heading rule
127
+
128
+ Every **normal slide** has the same three-band vertical frame:
129
+
130
+ ```
131
+ ┌─────────────────────┐
132
+ │ title band │ ← the slide's leading h2 (auto-lifted)
133
+ ├─────────────────────┤
134
+ │ content area │ ← divided by the layout grammar
135
+ ├─────────────────────┤
136
+ │ footer band │ ← optional (deck footer and/or @footer)
137
+ └─────────────────────┘
138
+ ```
139
+
140
+ Headings are **tightly scoped** — one rule per level, so a heading's role is
141
+ never ambiguous:
142
+
143
+ | Level | Role |
144
+ |---|---|
145
+ | **h1** (`#`) | **Title pages only** (`template=title`) — the presentation title. **Not allowed on a normal slide.** |
146
+ | **h2** (`##`) | The **slide title** of a normal slide. **Exactly one**, and it is the slide's first content. A second h2 (or any h1) on a normal slide is a **lint error**. |
147
+ | **h3–h6** | In-slide sub-headings (ordinary markdown). |
148
+
149
+ The leading h2 is **auto-lifted** into the title band — never put it inside a
150
+ region. A titled two-column slide is simply:
151
+
152
+ ```
153
+ <!-- slide 2col -->
154
+ ## Results ← becomes the title band (spans the slide)
155
+ <!-- @left --> …
156
+ <!-- @right --> …
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Regions (`<!-- @name -->`)
162
+
163
+ The content area is split into **regions**, one per layout leaf. A region's body
164
+ runs from its `<!-- @name -->` marker to the next marker and is rendered as
165
+ orz-markdown. Content **before** the first region marker goes to the layout's
166
+ **primary region** (the first leaf), so a single-region slide needs no markers
167
+ at all.
168
+
169
+ Region names are **flat and unique per slide**, regardless of nesting depth —
170
+ the nesting lives in the layout, not the names. A marker is always just
171
+ `<!-- @side -->`.
172
+
173
+ ### The three reserved regions
174
+
175
+ These names have fixed meaning in **every** layout:
176
+
177
+ | Marker | Meaning |
178
+ |---|---|
179
+ | `<!-- @notes -->` | Speaker notes → reveal's `<aside class="notes">`. **Never shown** on the slide; stored in the deck, round-tripped on save, and shown in the **speaker view** (press **S** while presenting). |
180
+ | `<!-- @footer -->` | This slide's footer band — shows on that slide alone and overrides the deck-wide footer. The deck-wide footer (`<!-- deck footer: … -->`) appears on every slide **except** the opening `title` page; add an `@footer` to a title slide to force one. |
181
+ | `<!-- @float … -->` | A **free-positioned overlay**, outside the grid (see below). |
182
+
183
+ ### Floats (`<!-- @float … -->`)
184
+
185
+ A float is an overlay box layered on top of the layout at a fixed position and
186
+ size — the one escape from the grid. Use it sparingly (a badge, callout,
187
+ watermark, logo, pull-out figure): **typically zero per slide**, occasionally
188
+ one or two.
189
+
190
+ ```
191
+ <!-- @float left=58% top=10% w=36% h=44% -->
192
+ > Key takeaway: **narrow PDI** across all methods.
193
+ ```
194
+
195
+ Geometry attributes (percent of the slide, or `px`): any of `left` `right`
196
+ `top` `bottom` `w` `h`; optional `z=` (default = declaration order, so a later
197
+ float sits on top). The body is orz-markdown rendered inside the box. Each
198
+ `<!-- @float … -->` is its own overlay (repeatable).
199
+
200
+ ---
201
+
202
+ ## The layout grammar (recursive splits)
203
+
204
+ The content area is divided by **one rule applied recursively**: split a box into
205
+ **rows** or **columns**; each cell is a **named region** or **another split**.
206
+
207
+ ```
208
+ split := ("row" | "col") tracks "{" item (";" item)* "}"
209
+ item := region-name | split
210
+ tracks := token ("/" token)* // 2/1 · auto/1fr/auto · 30%/1fr · 200px/1fr
211
+ ```
212
+
213
+ - `col 2/1 { left; right }` — two columns, 2:1.
214
+ - `row auto/1 { head; col 1/1 { a; b } }` — a header row above two columns.
215
+ - Leaves are **region names**, filled by `<!-- @name -->`.
216
+
217
+ Because the rule is recursive, **headers, footers, sidebars, quadrants, and
218
+ arbitrary grids are all just splits** — nothing is special-cased:
219
+
220
+ ```
221
+ <!-- slide row auto/1/auto { banner; col 3/2 { main; row 1/1 { fig; note } }; bar } -->
222
+ ```
223
+
224
+ ### Presets (named layouts — aliases)
225
+
226
+ Presets are **aliases** that expand to the grammar, so the everyday case stays
227
+ terse. They are sugar, not a separate system — use a preset, a raw split, or mix
228
+ freely. An optional `[a/b]` sets the track ratio.
229
+
230
+ | Preset | Expands to | Regions |
231
+ |---|---|---|
232
+ | *(none)* | single region | `body` |
233
+ | `2col [a/b]` | `col a/b { left; right }` | left, right |
234
+ | `3col` | `col 1/1/1 { left; mid; right }` | left, mid, right |
235
+ | `2row [a/b]` | `row a/b { top; bottom }` | top, bottom |
236
+ | `main-side [a/b]` | `col a/b { main; side }` (default `2/1`) | main, side |
237
+ | `quad` | `row 1/1 { col 1/1 { tl; tr }; col 1/1 { bl; br } }` | tl, tr, bl, br |
238
+
239
+ ---
240
+
241
+ ## Structure-page templates (`template=`)
242
+
243
+ Templates are structure pages. A `template=` slide reads structured fields from
244
+ its markdown — `# title`, `## subtitle`, then the rest as a meta/byline block —
245
+ and lays them out with dedicated styling instead of the region grid. Pick a
246
+ layout with `v=` (e.g. `template=title v=2`):
247
+
248
+ | Template | Write | Layouts (`v=`) |
249
+ |---|---|---|
250
+ | `title` | `#` title, `##` subtitle, author/date line | **v1** centered + accent rule (default) · **v2** left accent bar · **v3** uppercase kicker · **v4** split (bold accent band beside the title) · **v5** full-color cover (title bottom-left) · **v6** oversized editorial (huge type) |
251
+ | `section` | `#` section title (rest = meta) | **v1** underline rule (default) · **v2** centered accent · **v3** surface band + left bar · **v4** outlined box |
252
+ | `outline` | an agenda list | **v1** plain (default) · **v2** cards · **v3** two columns |
253
+ | `closing` | thanks / contact / a `{{qr}}` | **v1** centered (default) · **v2** full-color cover · **v3** oversized |
254
+
255
+ All template styling adapts to the active theme (via `--accent` / `--ink` /
256
+ `--muted`), light or dark.
257
+
258
+ ---
259
+
260
+ ## Capacity budgets — author within the slide, don't overflow
261
+
262
+ Scale-to-fit is a **safety net, not a licence to overflow.** These are
263
+ per-container budgets for a **16:9 slide at the default theme font**. They
264
+ **scale with a region's area**: a half-width column gets ~half a full body's
265
+ budget; a quad cell ~a quarter.
266
+
267
+ | Container | Budget (16:9, default font) |
268
+ |---|---|
269
+ | Slide title (h2) | ≤ ~10 words, 1 line |
270
+ | Title-page title (h1) | ≤ ~8 words |
271
+ | Full body (single region) | ≤ ~6 bullets **or** ~55 words **or** ~14 code lines |
272
+ | One column of `2col` | ≤ ~5 bullets / ~35 words |
273
+ | One cell of `3col` / `quad` | ≤ ~3–4 bullets / ~20 words |
274
+ | `side` (in `main-side`) | ≤ ~4 short bullets, or one small figure |
275
+ | Bullet line | ≤ ~10 words, 1 line; avoid nesting beyond 1 level |
276
+ | Table | ≤ ~6 rows × 4 cols (full width); fewer in a column |
277
+ | Code block | ≤ ~12 lines full / ~8 in a column |
278
+ | Figure / diagram / chart | one primary visual per region |
279
+
280
+ **Core rule: prefer splitting into another slide over crowding one.** Budgets
281
+ shrink proportionally for `4:3` and for smaller regions; estimate any nested
282
+ split with `budget ≈ baseline × region-area-fraction`.
283
+
284
+ ---
285
+
286
+ ## Do / don't
287
+
288
+ **Do**
289
+ - Start every slide with a `<!-- slide … -->` marker.
290
+ - Put exactly one `## h2` as the first line of each normal slide (the title).
291
+ - Reach for a **preset** first; drop to a raw split only when you need nesting.
292
+ - Keep region content under the budgets above; split a crowded slide in two.
293
+ - Use `<!-- @notes -->` for speaker notes — they never clutter the slide.
294
+ - Use a deck-wide footer (`<!-- deck footer: … -->`) and override per-slide with
295
+ `<!-- @footer -->` only when needed.
296
+
297
+ **Don't**
298
+ - Don't use `#` (h1) on a normal slide, or a second `##` (h2) — both are lint
299
+ errors.
300
+ - Don't hand-edit the generated `.slides.html`; edit the deck source.
301
+ - Don't use `:` in a layout, or `/` in a config value.
302
+ - Don't reuse a region name within a slide (names are unique per slide).
303
+ - Don't lean on floats for layout — that is what splits are for. Floats are
304
+ occasional overlays, not the grid.
305
+
306
+ ---
307
+
308
+ ## Example slides
309
+
310
+ ### A titled two-column slide
311
+
312
+ ```
313
+ <!-- slide 2col 3/2 -->
314
+ ## Results at a glance
315
+ <!-- @left -->
316
+ - Accuracy **92%** across all runs
317
+ - Latency under **40 ms**
318
+ - Zero regressions in CI
319
+ <!-- @right -->
320
+ {{chart
321
+ type: bar
322
+ labels: A, B, C
323
+ series: Score = 92, 81, 76
324
+ }}
325
+ <!-- @notes -->
326
+ Lead with accuracy; the latency number is the surprise — pause here.
327
+ ```
328
+
329
+ ### A nested-split slide (header row over a main + stacked-figures column)
330
+
331
+ ```
332
+ <!-- slide row auto/1 { head; col 3/2 { main; row 1/1 { figA; figB } } } -->
333
+ ## Pipeline architecture
334
+ <!-- @head -->
335
+ Ingest → transform → serve, with back-pressure between stages.
336
+ <!-- @main -->
337
+ ```mermaid
338
+ graph LR; A[Ingest]-->B[Transform]-->C[Serve]
339
+ ```
340
+ <!-- @figA -->
341
+ {{qrcode https://example.org/spec}}
342
+ <!-- @figB -->
343
+ *Spec v3 — scan to read.*
344
+ ```
345
+
346
+ ### A title page
347
+
348
+ ```
349
+ <!-- slide template=title -->
350
+ # Controlled Polymerization
351
+ ## RAFT vs ATRP — a practical comparison
352
+ **Dr. Yu Wang** · University of Louisiana · 2026
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Special content & enhancers
358
+
359
+ Region bodies are full orz-markdown. The deck runs client enhancers, drawn per
360
+ visible slide (and re-run on edit): **KaTeX** (math/mhchem, pre-rendered),
361
+ **Mermaid**, **SmilesDrawer** (chemistry), and **`{{chart}}`** (simple
362
+ bar/line/pie/doughnut via Chart.js — the line-based `type:` / `labels:` /
363
+ `series:` config shown above). For the full orz-markdown syntax (containers
364
+ `::: name`, `{{name body}}` plugins, `{{attrs[#id .class]}}`, tabs), read the
365
+ orz-markdown skill at `node_modules/orz-markdown/orz-markdown-skills/SKILL.md`.
366
+
367
+ ## What the generated file needs at view time
368
+
369
+ "Self-contained" means *one file*. By default the CLI **inlines** the engine,
370
+ reveal's core CSS, and **all seven themes**, so a text deck **presents and
371
+ switches themes offline**. The libraries that load from CDN (cached after first
372
+ load) are only: KaTeX (math), Mermaid, SmilesDrawer, Chart.js, and CodeMirror
373
+ (the editor) — so a deck that uses math/diagrams/charts, or in-browser editing,
374
+ needs internet. `--cdn` instead references the engine + theme from jsDelivr
375
+ (smaller files, needs network). Editing/Save in place needs a Chromium browser
376
+ (File System Access API); presenting works in all modern browsers.
377
+
378
+ ## Security
379
+
380
+ `.slides.html` files are **self-contained executable HTML** — opening one runs its
381
+ embedded JavaScript. Treat them like programs: only open or edit files from a
382
+ trusted source, and advise users the same. The one-click framework update is
383
+ opt-in (edit view only, explicit confirm) and always fetches from the hardcoded
384
+ canonical jsDelivr packages — a tampered file can't redirect it. A file's
385
+ integrity can't be verified from inside it; for authenticity, verify out-of-band.
386
+ See the README "Security" section.
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "orz-slides",
3
+ "version": "0.1.0",
4
+ "description": "Self-contained, editable HTML slide decks (.slides.html) authored in orz-markdown with a layout syntax. Sibling of orz-mdhtml.",
5
+ "type": "module",
6
+ "bin": {
7
+ "orz-slides": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "assets",
12
+ "orz-slides-skills"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && npm run bundle",
16
+ "bundle": "tsx build/bundle.ts",
17
+ "gen": "tsx src/cli.ts",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "typecheck": "tsc --noEmit",
21
+ "clean": "rm -rf dist"
22
+ },
23
+ "keywords": [
24
+ "slides",
25
+ "reveal.js",
26
+ "orz-markdown",
27
+ "markdown",
28
+ "presentation",
29
+ "self-contained",
30
+ "single-file"
31
+ ],
32
+ "author": "Yu Wang <yuwang@orz.how>",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/wangyu16/orz-slides"
37
+ },
38
+ "dependencies": {
39
+ "orz-markdown": "^1.3.0",
40
+ "reveal.js": "^5.0.4"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.11.0",
44
+ "esbuild": "^0.21.5",
45
+ "path-browserify": "^1.0.1",
46
+ "tsx": "^4.7.0",
47
+ "typescript": "^5.4.0",
48
+ "vitest": "^2.0.0"
49
+ }
50
+ }