@taloswork/ose-shared 0.1.9 → 0.1.10

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.
Files changed (56) hide show
  1. package/mcp/gen/mcp.json +2 -2
  2. package/package.json +3 -3
  3. package/skills/cozy-static-svg/SKILL.md +1 -1
  4. package/skills/diagram-design/SKILL.md +1 -1
  5. package/skills/document/SKILL.md +63 -75
  6. package/skills/document/bin/deck-build.cjs +51 -1
  7. package/skills/document/bin/deck-qa.cjs +18745 -279
  8. package/skills/document/bin/doc-convert.cjs +9280 -76489
  9. package/skills/document/bin/doc-fill.cjs +0 -0
  10. package/skills/document/bin/doc-gen.cjs +3043 -46825
  11. package/skills/document/bin/doc-run.cjs +65095 -1967
  12. package/skills/document/bin/doc-scan.cjs +0 -0
  13. package/skills/document/bin/doc-text.cjs +0 -0
  14. package/skills/document/bin/doc-validate.cjs +18067 -226
  15. package/skills/document/bin/doc-xml.cjs +1 -1
  16. package/skills/document/bin/docx-annotate.cjs +0 -0
  17. package/skills/document/bin/html2pptx.cjs +32498 -145280
  18. package/skills/document/bin/ocr-worker.cjs +0 -0
  19. package/skills/document/bin/ocr.cjs +0 -0
  20. package/skills/document/bin/pdf-form.cjs +0 -0
  21. package/skills/document/bin/pdf-images.cjs +0 -0
  22. package/skills/document/bin/pdf-ops.cjs +0 -0
  23. package/skills/document/bin/pdf-render.cjs +11 -9
  24. package/skills/document/bin/pdf-tables.cjs +0 -0
  25. package/skills/document/bin/pptx-clone.cjs +21 -25
  26. package/skills/document/bin/pptx-embed-fonts.cjs +8 -6
  27. package/skills/document/bin/pptx-set-image.cjs +0 -0
  28. package/skills/document/bin/pptx-slide.cjs +22 -32
  29. package/skills/document/bin/pptx-theme.cjs +1 -20
  30. package/skills/document/bin/pptx-tokenize.cjs +1 -1
  31. package/skills/document/bin/xlsx-query.cjs +0 -0
  32. package/skills/document/bin/xlsx-recalc.cjs +0 -0
  33. package/skills/document/bin-reference.md +35 -0
  34. package/skills/document/content-pipeline.md +19 -18
  35. package/skills/document/deck-design.md +110 -211
  36. package/skills/document/layouts/README.md +4 -33
  37. package/skills/document/motion/README.md +4 -2
  38. package/skills/document/office.md +5 -1
  39. package/skills/document/rewrite.md +43 -99
  40. package/skills/document/styles/README.md +0 -7
  41. package/skills/document/theme-system.md +1 -0
  42. package/skills/document/themes/README.md +5 -5
  43. package/skills/document/themes/anthropic-clay.json +1 -7
  44. package/skills/document/themes/apple-keynote.json +1 -7
  45. package/skills/document/themes/black-gold.json +1 -2
  46. package/skills/document/themes/cinereel.json +1 -2
  47. package/skills/document/themes/cool-tech.json +1 -2
  48. package/skills/document/themes/hand-drawn.json +1 -2
  49. package/skills/document/themes/ivory-gold.json +1 -2
  50. package/skills/document/themes/magazine-ink.json +1 -2
  51. package/skills/document/themes/mckinsey-consulting.json +1 -7
  52. package/skills/document/themes/mono.json +1 -2
  53. package/skills/document/themes/swiss-grid.json +1 -2
  54. package/skills/document/themes/warm-editorial.json +1 -2
  55. package/skills/image-layout/SKILL.md +1 -1
  56. package/skills/image-layout/bin/html2img.cjs +0 -0
@@ -1,16 +1,16 @@
1
1
  # Deck design — html2pptx (hybrid) is THE deck engine
2
2
 
3
3
  **Every deck is built ONE way:** author slide HTML and render with `html2pptx` in
4
- its default **HYBRID** mode — `bin/html2pptx.cjs deck.html out.pptx --theme <t>`
4
+ its default **HYBRID** mode — `bin/html2pptx.cjs deck.html out.pptx --style <id>`
5
5
  (no `--editable`/`--rasterize`). Chromium gives you the full power of HTML/CSS
6
6
  (flexbox/grid, layering, per-element SVG) and converts to NATIVE editable pptx
7
7
  text boxes/shapes, with the decorative visual layer baked into one pixel-perfect
8
8
  backdrop — designed look **AND** editable words + editable `data-chart`. This is
9
9
  the path for every deck: pitch / keynote / report / design-y / plain alike.
10
10
 
11
- `doc-gen pptx` archetypes and `doc-run` PptxGenJS are **NOT deck-building paths** —
12
- do not use them to build a deck. (They remain for NON-deck jobs only: structured
13
- data tables, batch/deterministic document generation, shape-level one-offs.)
11
+ `doc-gen` makes no decks, and `doc-run` PptxGenJS is **NOT a deck-building path** —
12
+ do not use it to build a deck. (It remains for NON-deck jobs only: structured
13
+ data tables, batch/deterministic generation, shape-level one-offs.)
14
14
 
15
15
  html2pptx needs a browser — the app's Chromium service or the bundled Playwright.
16
16
  If neither is available, **say so and stop; never silently downgrade** to a lesser
@@ -33,11 +33,11 @@ every slide from zero is the #1 "AI-generated" tell; compose from the catalog fi
33
33
  ## html2pptx — HTML/CSS is the layout engine
34
34
 
35
35
  ```
36
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --theme <theme.json> # DEFAULT = hybrid (visuals + editable text)
37
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <name> # structure/skin split: paint layouts/ with styles/<name>/
38
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --theme <t> --editable # plain native (effects flatten)
39
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --theme <t> --rasterize # pixel-perfect, NOT editable
40
- node <skill>/bin/html2pptx.cjs snapshot.layout.json out.pptx # headless-safe input (always plain native)
36
+ node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> [--theme-v2] # DEFAULT = hybrid: paint layouts/ with the catalog entry
37
+ node <skill>/bin/html2pptx.cjs deck.html --preview-dir <dir> --style <id> # authoring preview: PNG per slide, no pptx
38
+ node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> --editable # plain native (effects flatten)
39
+ node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> --rasterize # pixel-perfect, NOT editable
40
+ node <skill>/bin/html2pptx.cjs snapshot.layout.json out.pptx # headless-safe input (always plain native)
41
41
  ```
42
42
 
43
43
  ### ⭐ PREFERRED workflow — structure/skin split (`--style`)
@@ -128,12 +128,15 @@ needs) so it becomes reusable: bespoke first, then graduate the winners into the
128
128
  Contract (violations fail loudly, nothing degrades silently):
129
129
 
130
130
  - One `<section class="slide">` per slide, laid out on a **1280×720 px** canvas
131
- (= 13.33×7.5 in). The bin injects a reset + the theme's CSS variables — style
132
- with `var(--primary|--accent|--secondary|--text|--muted|--card-bg|--border|
133
- --slide-bg|--title-color|--body-color|--font-heading|--font-body)`, never
134
- hardcode brand colors. html2pptx **warns** (`N hardcoded brand color(s) bypass
135
- theme vars`) when a slide writes a hex instead of a `var(--…)` — that deck won't
136
- re-theme, so fix it before shipping. (SVG diagram semantic colors are exempt.)
131
+ (= 13.33×7.5 in). Color comes from the skin (`--style`), whose tokens every skin
132
+ defines: `var(--accent|--title|--body|--muted|--hair|--slide-bg|--title-color|
133
+ --body-color|--font-heading|--font-body)` plus the ramp `var(--c1..--c4)` — never
134
+ hardcode brand colors, and never use a name outside this set (`--primary`,
135
+ `--text`, `--card-bg`, `--border` are NOT defined by skins: they only exist under
136
+ the legacy `--theme` path, so on a `--style` deck they silently stick on their
137
+ fallback). html2pptx **warns** (`N hardcoded brand color(s) bypass theme vars`)
138
+ when a slide writes a hex instead of a `var(--…)` — that deck won't re-skin, so
139
+ fix it before shipping. (SVG diagram semantic colors are exempt.)
137
140
  - **Tailwind utility classes are available** (vendored offline — no CDN needed):
138
141
  author like a modern web deck — `grid grid-cols-2 gap-16`, `flex items-center
139
142
  justify-center`, `p-16`, `text-6xl font-bold`, `rounded-3xl`, `bg-blue-600`,
@@ -176,8 +179,8 @@ Contract (violations fail loudly, nothing degrades silently):
176
179
  - Fonts are measured by Chromium but re-laid-out by PowerPoint — keep ~10%
177
180
  slack in text boxes; don't butt text against box edges.
178
181
 
179
- After generating: `doc-validate --strict --design-lint`, then render-and-look
180
- (doc-convert → PDF → view) like any deck.
182
+ After generating: the build reports `valid` and `qa` itself; sign off with
183
+ `deck-qa --final` like any deck ("Verify before delivering").
181
184
 
182
185
  ## HTML slide authoring — the design-first route
183
186
 
@@ -281,12 +284,17 @@ The shipped `layouts/*.tpl` are this recipe applied as structure-only slides —
281
284
  paste the nearest one and paint it with a skin (`--style`) rather than rebuilding
282
285
  the stack by hand; each already carries the 8 layers correctly wired.
283
286
 
284
- **动画优先由族的 motion 预设派发**(`motion/<id>.json`,`catalog.json` 里该 entry 的
285
- `motion` 字段指到它;`deck-build` 填槽时按每页 layout 的 role 自动注入
286
- `data-anim*`/`data-transition*`)。手写 `data-anim`/`data-transition` 仍然优先 ——
287
- `deck-build` 只在元素还没有该属性时才注入,用于特例覆盖派发结果。没有 motion 预设
288
- 的族(当前 13 个 skin 在 P0 都没有)保持这一段以下描述的默认行为:不写
289
- `data-anim` 就没有任何动画。
287
+ **Every catalog entry ships a motion preset; the scenario decides whether
288
+ `deck-build` applies it.** (`motion/<id>.json`, named by the entry's `motion` field.)
289
+ Put the confirmed scenario in the spec as `"scenario"`: a **presented** deck (pitch /
290
+ 路演 / 发布会 / keynote / 大会 / 讲座 / 销售) gets the preset — `data-anim*` /
291
+ `data-transition*` injected per slide by the layout's role; a **read** deck (内审 /
292
+ 评审 / 报告 / 汇报 / 讲义 / 培训 / 述职) gets none; no or an unknown scenario keeps the
293
+ preset. `"motion": true|false` overrides the scenario. The build's JSON says which
294
+ applied and why (`motion: {on, reason}`). Hand-written `data-anim` /
295
+ `data-transition` always wins — `deck-build` only adds an attribute an element does
296
+ not already have, and a deck authored straight into `deck.html` animates only
297
+ where you write `data-anim`.
290
298
 
291
299
  ## Motion — entrance animation + slide transitions
292
300
 
@@ -297,10 +305,10 @@ build sequence — not a video, not a GIF). Mark an element with `data-anim` and
297
305
  becomes one build step; the deck then plays like a keynote instead of appearing
298
306
  all at once.
299
307
 
300
- **Off unless you ask for it.** No `data-anim` anywhere → no `<p:timing>` → byte-
301
- identical output to before. Reach for it when the scenario is a **stage** —
302
- 发布会 / 路演 / pitch / 主题演讲 — and skip it for 内审 / 报告 / 讲义 decks that
303
- people read rather than watch.
308
+ **The attribute itself is opt-in.** No `data-anim` on the page → no `<p:timing>`.
309
+ Write your own when the scenario is a **stage** — 发布会 / 路演 / pitch / 主题演讲;
310
+ for 内审 / 报告 / 讲义 decks that people read rather than watch, add none beyond
311
+ what the preset already injects.
304
312
 
305
313
  #### The attribute contract
306
314
 
@@ -472,56 +480,11 @@ A face that is bundled but neither baked nor embedded is *still worth bundling*
472
480
  backdrop and the picker previews come out right), just don't assume the native text
473
481
  followed. `hand-drawn`'s Gochi Hand heading is exactly that case today.
474
482
 
475
- ## Running a script (PptxGenJS via doc-run — NOT for decks)
476
-
477
- > **This section is reference for NON-deck freeform PptxGenJS only** (bespoke
478
- > one-off shapes, non-deck pptx). **Do not build a deck this way** — every deck
479
- > goes through html2pptx hybrid (see the top of this file). Kept here so the
480
- > capability is documented, not as a deck path.
481
-
482
- Write `deck.js`, run it with **`node <skill>/bin/doc-run.cjs deck.js`** —
483
- `pptxgenjs` (and `jszip`, `pdf-lib`, `docx`, `xlsx`, `hyperformula`) are bundled
484
- inside doc-run, so `require("pptxgenjs")` works on any machine with no install
485
- step (packaged app included; `doc-run --list` prints the registry). Bare
486
- `node deck.js` only resolves those libs on a dev checkout — always use doc-run.
487
-
488
- ## Canvas & grid
489
-
490
- - Layout: 16:9 wide = **13.33 × 7.5 inches**, origin top-left, units in inches.
491
- - Safe margins: 0.6 left/right, 0.3 top, 0.45 bottom (footer/page number zone).
492
- - Use a 12-column mental grid: column width ≈ 1.0", gutters 0.1".
493
- - Vertical rhythm: title zone y 0.3–1.05; content y 1.25–7.0.
494
-
495
- ```js
496
- const pptxgen = require("pptxgenjs");
497
- const p = new pptxgen();
498
- p.defineLayout({ name: "WIDE", width: 13.33, height: 7.5 });
499
- p.layout = "WIDE";
500
- ```
501
-
502
- ## Theme first — never hardcode colors
503
-
504
- Load the active theme (the user's template skill ships one, or use the document
505
- skill's `themes/mckinsey-consulting.json`). PptxGenJS colors are bare hex (no `#`).
506
-
507
- ```js
508
- const { readFileSync } = require("node:fs");
509
- const t = JSON.parse(readFileSync(process.env.TALOS_DOC_THEME ?? "<skill>/themes/mckinsey-consulting.json", "utf8"));
510
- const C = {
511
- primary: (t.colors?.primary ?? "#1a3c6e").replace("#", ""),
512
- text: (t.colors?.text ?? "#1f2328").replace("#", ""),
513
- muted: (t.colors?.muted ?? "#5b6570").replace("#", ""),
514
- cardBg: (t.colors?.tableHeaderBg ?? "#eef2f8").replace("#", ""),
515
- border: (t.colors?.tableBorder ?? "#c6d0dd").replace("#", ""),
516
- };
517
- const FONT = t.fonts?.body || undefined; // undefined → platform default (CJK-safe)
518
- ```
519
-
520
483
  ## Design principles (the constraint EVERY deck honors)
521
484
 
522
- This is the "design ideas" bar for a freeform deck. The archetype path already
523
- bakes most of it into the engine (dominant accent, contrast-picked text, motif,
524
- no under-title lines); when you hand-build slides YOU are responsible for it.
485
+ This is the "design ideas" bar for a freeform deck. The catalog layouts
486
+ (`layouts/*.tpl`) already bake most of it in (dominant accent, contrast-picked
487
+ text, motif, no under-title lines); when you hand-build slides YOU are responsible for it.
525
488
  Not a menu — treat it as a checklist the deck must pass.
526
489
 
527
490
  **Color — commit, don't sprinkle.**
@@ -537,7 +500,7 @@ Not a menu — treat it as a checklist the deck must pass.
537
500
  - No slide is a wall of text. Each one gets a structural element: a colored band,
538
501
  a big number, an icon row, a 2×2, a chart, or a supporting image. A title +
539
502
  bullets on white is the classic AI tell — restructure it (this is exactly what
540
- `--design-lint` flags as `text-only-slide`).
503
+ the build's `qa` flags as `text-only-slide`).
541
504
  - Reach for the strong composition patterns: two-column split, icon+label rows,
542
505
  a 2×2 grid, a half-bleed image with text on the other half, one hero figure.
543
506
  - Vary the layout slide-to-slide. Ten identical bullet slides is the single
@@ -563,7 +526,7 @@ Not a menu — treat it as a checklist the deck must pass.
563
526
  **Contrast — readability is non-negotiable.**
564
527
  - Text on fills must clear ~4.5:1 (large/bold ~3:1). Dark fills → light text
565
528
  (`F1FAEE`/`FFFFFF`); light fills → the `text` color. When unsure compute
566
- luminance. `--design-lint` flags the worst offenders as `low-contrast-text`.
529
+ luminance. The build's `qa` flags the worst offenders as `low-contrast-text`.
567
530
 
568
531
  **The AI tells to avoid** (these scream "template"):
569
532
  - A thin accent LINE under the title. Never. It's the #1 giveaway; use space,
@@ -612,17 +575,17 @@ per page; add them as the deck needs. Catalogs in `assets/snippets/`:
612
575
 
613
576
  All snippets are hybrid-native: chips, rules, frames, gradients, SVG **bake into the
614
577
  backdrop**; labels, page numbers, chart data stay **native/editable**. Route every
615
- color through theme vars (`var(--accent, …)`) so one `--theme` reskins them — the same
578
+ color through the skin tokens (`var(--accent, …)`) so one skin reskins them — the same
616
579
  `footer-pageno` fragment becomes tech-cyan or 国风-朱红 by swapping `--accent`.
617
580
 
618
581
  **Pairing a snippet with the theme** — two independent dimensions:
619
- - **Color = automatic.** Snippets read only the **12 injected vars** (`--primary
620
- --accent --secondary --text --muted --card-bg --border --slide-bg --title-color
621
- --body-color --font-heading --font-body`). Set the deck `--theme` and every snippet
622
- recolors for free; the value after the comma is just the no-theme fallback. Any
623
- OTHER var name (e.g. `--accent2`) is never injected → it silently sticks on its
624
- fallback, so **only use those 12 names**. Need one snippet in an off-theme color?
625
- Override on its `<section>`: `style="--accent:#b8292e;--muted:#9a8b76"`.
582
+ - **Color = automatic.** Snippets read only the skin tokens listed in the slide
583
+ contract above (`--accent --title --body --muted --hair --slide-bg --title-color
584
+ --body-color --font-heading --font-body --c1..--c4`). Pick the skin and every
585
+ snippet recolors for free; the value after the comma is just the fallback. Any
586
+ OTHER var name (e.g. `--accent2`, `--primary`) is not defined → it silently sticks
587
+ on its fallback, so **only use those names**. Need one snippet in an off-skin
588
+ color? Override on its `<section>`: `style="--accent:#b8292e;--muted:#9a8b76"`.
626
589
  - **Form = your choice.** The theme supplies the palette, NOT the shape. A `neon-dot`
627
590
  on a 侘寂 light theme reads wrong even recolored — pick the motif/functional family
628
591
  that matches the style's aesthetic (seal→chinese, gold-flourish→black-gold,
@@ -632,139 +595,75 @@ color through theme vars (`var(--accent, …)`) so one `--theme` reskins them
632
595
  are intentionally hardcoded; the theme reskins their title/bg/accent but the ramp
633
596
  stays so the hierarchy reads.
634
597
 
635
- ## Shape vocabulary (the pieces everything is built from)
636
-
637
- ```js
638
- // Card
639
- s.addShape("roundRect", { x, y, w, h, rectRadius: 0.07, fill: { color: C.cardBg }, line: { color: C.border, width: 1 } });
640
- // Accent bar (left edge of a card, or under a heading)
641
- s.addShape("rect", { x, y, w: 0.07, h, fill: { color: C.primary } });
642
- // Divider / timeline spine
643
- s.addShape("line", { x, y, w, h: 0, line: { color: C.primary, width: 2 } });
644
- // Node / dot
645
- s.addShape("ellipse", { x, y, w: 0.18, h: 0.18, fill: { color: C.primary } });
646
- // Chevron / arrow for flows
647
- s.addShape("chevron", { x, y, w: 1.2, h: 0.5, fill: { color: C.primary } });
648
- // Layered stack (architecture): repeat roundRects with y += h + 0.15, widths shrinking
649
- ```
650
-
651
- Text goes ON shapes as separate `addText` (easier to position than shape text):
652
-
653
- ```js
654
- s.addText("标题", { x, y, w, h, fontSize: 14, bold: true, color: C.text, fontFace: FONT, align: "left", valign: "top" });
655
- ```
656
-
657
- ## Complete skeleton (title + custom quadrant slide)
658
-
659
- ```js
660
- const pptxgen = require("pptxgenjs"); const { readFileSync } = require("node:fs");
661
- const t = JSON.parse(readFileSync("<theme.json path>", "utf8"));
662
- const hex = (c, d) => (c ?? d).replace("#", "");
663
- const C = { primary: hex(t.colors?.primary, "#1a3c6e"), text: hex(t.colors?.text, "#1f2328"),
664
- muted: hex(t.colors?.muted, "#5b6570"), cardBg: hex(t.colors?.tableHeaderBg, "#eef2f8") };
665
- const p = new pptxgen();
666
- p.defineLayout({ name: "W", width: 13.33, height: 7.5 }); p.layout = "W";
667
-
668
- // ── cover
669
- let s = p.addSlide();
670
- s.background = { color: C.primary };
671
- s.addShape("rect", { x: 0.6, y: 3.0, w: 1.1, h: 0.06, fill: { color: "FFFFFF" } });
672
- s.addText("WorkBuddy 产品路演", { x: 0.6, y: 3.15, w: 12, h: 1, fontSize: 38, bold: true, color: "FFFFFF" });
673
- s.addText("AI Agent 数字员工 · 2026 Q3", { x: 0.6, y: 4.2, w: 12, h: 0.5, fontSize: 15, color: "E8EEF6" });
674
-
675
- // ── custom: 2×2 quadrant (no archetype for this — the freeform case)
676
- s = p.addSlide();
677
- s.addText("市场象限", { x: 0.6, y: 0.3, w: 12, h: 0.7, fontSize: 26, bold: true, color: C.primary });
678
- const qx = 0.9, qy = 1.4, qw = 11.5, qh = 5.4;
679
- s.addShape("line", { x: qx + qw / 2, y: qy, w: 0, h: qh, line: { color: C.muted, width: 1 } });
680
- s.addShape("line", { x: qx, y: qy + qh / 2, w: qw, h: 0, line: { color: C.muted, width: 1 } });
681
- const cell = (cx, cy, title, body, hot) => {
682
- s.addShape("roundRect", { x: cx, y: cy, w: qw / 2 - 0.3, h: qh / 2 - 0.3, rectRadius: 0.08,
683
- fill: { color: hot ? C.primary : C.cardBg }, line: { color: C.muted, width: 1 } });
684
- s.addText(title, { x: cx + 0.2, y: cy + 0.15, w: qw / 2 - 0.7, h: 0.4, fontSize: 14, bold: true, color: hot ? "FFFFFF" : C.text });
685
- s.addText(body, { x: cx + 0.2, y: cy + 0.6, w: qw / 2 - 0.7, h: qh / 2 - 1.0, fontSize: 11.5, color: hot ? "E8EEF6" : C.text, valign: "top" });
686
- };
687
- cell(qx + 0.15, qy + 0.15, "高频 × 高价值", "跨境电商对账 —— 切入点", true);
688
- cell(qx + qw / 2 + 0.15, qy + 0.15, "低频 × 高价值", "合同合规勾稽", false);
689
- cell(qx + 0.15, qy + qh / 2 + 0.15, "高频 × 低价值", "日报汇总", false);
690
- cell(qx + qw / 2 + 0.15, qy + qh / 2 + 0.15, "低频 × 低价值", "暂不投入", false);
691
-
692
- p.writeFile({ fileName: "deck.pptx" }).then(() => console.log("ok"));
693
- ```
694
-
695
598
  ## Verify before delivering (MANDATORY automated visual-QA loop)
696
599
 
697
600
  **Assume there are problems — your job is to find them.** Treat QA as a bug hunt,
698
- not a confirmation step. Use the cheapest faithful surface for each stage: direct
699
- HTML screenshots while authoring, then PPTX checks and one Office-rendered final pass.
700
- Do not launch LibreOffice after every CSS/content edit.
701
-
702
- **Gate 1 — structural + schema** (blocking): `doc-validate.cjs deck.pptx
703
- --strict` — exit 1 = fix the reported part, never deliver (`--strict` = full
704
- ECMA-376 XSD).
705
-
706
- **Gate 2 — mechanical QA** (cheap, token-free, run EVERY round):
707
- `deck-qa.cjs deck.pptx --json` → `{ ok, findings:[{slide,code,message}] }`.
708
- It deterministically catches **off-canvas** (content clipped past the edge),
709
- **tiny-font** (<10pt), **shape-overlap** (colliding text), **text-only-slide**,
710
- **low-contrast-text**, **placeholder-text** (a layout pasted but never filled —
711
- 替换为…/占位/在这里写/Lorem still in a slide), and **font-mismatch** (CJK text set
712
- in a Latin-only font → 豆腐; expected language defaults to the system locale,
713
- override with `TALOS_DECK_LANG`). **Fix every finding first** — these
714
- are objective bugs and free to detect, so they must never reach the vision pass or
715
- delivery. (This is the same lint as `doc-validate --design-lint`, JSON-shaped for
716
- the loop.)
601
+ not a confirmation step. Use the cheapest faithful surface for each stage: HTML
602
+ screenshots while authoring, the build's own checks on every build, and one
603
+ Office-rendered sign-off at the end. Do not launch LibreOffice after every edit.
604
+
605
+ **Machine checks come with the build.** Every `html2pptx … out.pptx` run prints,
606
+ next to the build result, `valid` (does the file open — `false` exits 1: never
607
+ deliver) and `qa: { ok, findings:[{slide,code,message}] }` — deck-qa's lint:
608
+ **off-canvas**, **tiny-font** (<10pt), **shape-overlap**, **text-only-slide**,
609
+ **low-contrast-text**, **placeholder-text** (替换为…/占位/在这里写/Lorem left in a
610
+ slide) and **font-mismatch** (CJK in a Latin-only font → 豆腐; language defaults to
611
+ the system locale, override with `TALOS_DECK_LANG`). **Fix every finding before
612
+ anything else** — they are objective and free, so they must never reach the
613
+ vision pass or delivery. No separate `doc-validate` / `deck-qa` call is needed
614
+ after a build.
717
615
 
718
616
  **The loop** (≤2 delivery rounds, then report residuals — never silently ship):
719
617
 
720
- 1. **Fast authoring preview:** before generating the delivery PPTX, run
721
- `html2pptx.cjs deck.html --preview-dir /tmp/deck-preview --style <id> ...`.
722
- The first pass renders every slide; after a fix add `--preview-pages 3,7-8` to
723
- capture only changed slides. Inspect and iterate on these HTML-faithful images.
724
- 2. **Build + machine gates:** generate `deck.pptx`, then run Gate 1 and Gate 2 above.
725
- Fix every objective finding before the expensive final render.
726
- 3. **Final Office render:** once HTML preview and machine gates are clean, run
727
- `doc-convert.cjs deck.pptx /tmp/deck.pdf --cache-dir /tmp/talos-office-cache` then
728
- `pdf-render.cjs /tmp/deck.pdf all 1` → one PNG per slide. This happens once in
729
- the normal path and verifies PPTX/Office-only drift; rerun it only after fixing
730
- drift found in this final pass.
731
- **Reading the previews is the single most token-expensive step in the whole
732
- deck flow** (an image costs ≈ w×h∕750 tokens; a full-res 1280×720 slide ≈ 1.2k
733
- each, ~15k for a 12-page deck — and you re-read every round). The trailing `1`
734
- renders at ≈72dpi (¼ the pixels of the default `2`) — **≈¼ the vision tokens**,
735
- and slide composition/color/hierarchy read fine at that size. Two ways to spend
736
- even less: (a) Gates 1–2 are token-FREE — never send a slide to vision that a
737
- machine already flagged; (b) use `--preview-pages` during HTML iteration and
738
- read only changed slides. `pdf-render 3-3` saves PNG work but does **not** avoid
739
- the whole-deck Office conversion, so reserve it for a genuine final-PPTX rerun.
740
- Bump scale back to `2` only when fine print genuinely needs it.
741
- 4. **Vision critic — the part only a model can do.** LOOK at every slide image
742
- (not "glance"); **if sub-agent dispatch is available, critique slides in
743
- parallel — one fresh-eyes agent per slide** (they catch what you, having
744
- authored it, will not). Pass each critic the slide's `deck-qa` findings so it
745
- does NOT re-report mechanical issues — it judges only what the machine can't:
746
- visual balance / whitespace, hierarchy & focal point, a real visual anchor
747
- (not a text wall), palette/motif consistency with the theme, image quality,
748
- crowding/uneven gaps, misaligned sibling columns, decorative lines sized for
749
- one-line text when the title wrapped, and any leftover placeholders Gate 2
750
- can't phrase-match — unfilled `{{tokens}}`, "undefined", stray "xxx" (the
751
- 替换为/占位/在这里写/Lorem class is already caught mechanically in Gate 2, so
752
- don't re-report those). Each critic returns **{slide, verdict: pass|fail,
618
+ 1. **Author against the HTML preview.** `html2pptx.cjs deck.html --preview-dir
619
+ <dir> --style <id> …` renders every slide the first time; after a fix,
620
+ `--preview-pages 3,7-8` renders only the changed ones. Read these images and
621
+ iterate until the composition is clean.
622
+ 2. **Build and fix the build's findings** (`html2pptx … deck.pptx`, see above).
623
+ 3. **Sign off once: `deck-qa.cjs deck.pptx --final`.** One call runs strict XSD
624
+ validation (a failure exits 1), the lint, an Office render (LibreOffice via
625
+ `doc-convert`, cached), and the render check: `render-overflow` (text Office
626
+ re-wrapped onto more lines than its box holds, or past its side) and
627
+ `render-collision` (text that grew out of its box onto another). It returns
628
+ `review` — the slides with such a finding or a native chart (Office draws
629
+ charts; the preview never showed them) — and `previews`, a ≈72dpi image of
630
+ each. **Read only `previews`.** Every other slide already passed your eyes in
631
+ the HTML preview, and in the default hybrid mode its visuals are baked from
632
+ that same render; Office only re-lays the native text, which was just
633
+ measured. Re-wrapping at a different word, spacing CJK and Latin apart or a
634
+ substituted Latin face is normal, not a defect.
635
+ - **Read every slide instead** (`pdf-render.cjs <deck>.office.pdf all 1`) when
636
+ `html2pptx` reported a `layout (fallback)` engine (no baked visuals, so the
637
+ preview no longer matches) or you changed the PPTX after building it
638
+ (`doc-xml`, `pptx-slide`, a script).
639
+ - `office.faithful: false` (no LibreOffice, only a Chromium reconstruction) or
640
+ `office.error`: there is no Office layout to check. The HTML preview stands;
641
+ say so when delivering.
642
+ 4. **Vision critic — the part only a model can do.** It judges the HTML preview
643
+ images (step 1) and the `previews` of step 3 — never a second full pass over
644
+ slides already judged. LOOK at every image you critique (not "glance").
645
+ **Either you critique a slide or a sub-agent does, not both:** with sub-agent
646
+ dispatch, hand slides to fresh-eyes critics in parallel and read their verdicts
647
+ instead of opening the images yourself. Give each critic the slide's findings
648
+ so it does NOT re-report them — it judges only what a machine can't: balance
649
+ and whitespace, hierarchy and focal point, a real visual anchor (not a text
650
+ wall), palette/motif consistency, image quality, crowding and uneven gaps,
651
+ misaligned sibling columns, decorative lines sized for one-line text when the
652
+ title wrapped, and leftovers the lint can't phrase-match (`{{tokens}}`,
653
+ "undefined", stray "xxx"). Each critic returns **{slide, verdict: pass|fail,
753
654
  defects:[…]}**.
754
- 3. **Regenerate ONLY the failing slides** (targeted, not the whole deck):
755
- - **doc-gen route** — edit that slide's spec object (swap archetype/variant,
756
- move detail to notes, split overloaded content), re-run `doc-gen`.
757
- - **html2pptx route** — re-author just that `<section>`'s HTML (author decks
758
- one section per file so a slide regenerates in isolation), re-run
759
- `html2pptx`; reassemble.
760
- - **structural** — `pptx-slide` (dup/remove/reorder) or `pptx-clone` for
761
- template-driven slides.
762
- 4. **Re-run Gate 2 + render + critic on the regenerated slides only** — one fix
763
- often creates another. Repeat until a full pass finds nothing new **or** 2
764
- rounds elapse. If defects remain after round 2, **deliver with an explicit
765
- note listing the residual issues** — do not present a flawed deck as finished.
766
-
767
- **Do not declare done before at least one complete gate→critic→fix cycle.**
655
+ 5. **Regenerate only the failing slides.** Re-author that `<section>` (one section
656
+ per file lets a slide rebuild in isolation) and rebuild, or use `pptx-slide`
657
+ (dup/remove/reorder) / `pptx-clone` for template-driven slides. Then preview
658
+ the changed slides, rebuild and run `--final` again — one fix often creates
659
+ another. Stop when a pass finds nothing new **or** after 2 rounds; if defects
660
+ remain, **deliver with an explicit note listing them**.
661
+
662
+ **Reading images is the most token-expensive step of the deck flow** (≈ w×h∕750
663
+ tokens each; a 1280×720 slide ≈ 1.2k). Preview and `--final` images are ≈72dpi,
664
+ ¼ of the default render; `pdf-render … 2` only when fine print must be legible.
665
+
666
+ **Do not declare done before at least one complete build→sign-off→critic cycle.**
768
667
 
769
668
  ## CJK notes
770
669
 
@@ -66,8 +66,10 @@ after touching a `.tpl`.
66
66
  让高度由内容决定:网格用 `grid-auto-rows:min-content`(下限必须显式写 `min-content` ——
67
67
  `1fr`/`auto` 的自动下限对 `overflow` 非 visible 的网格项一律归零),余量交给外层
68
68
  `justify-content:space-between`。这样内容超量时会把后续元素顶出页面,被 `deck-qa` 的
69
- `off-canvas`/`overlap` 抓到,而不是变成一排缺了下半截的字。`deck-qa` 的第三条硬失败
70
- `clipped` 专门守这件事,并且对**整册版式库**逐个版式扫,不只是 `sweep-spec.json` 里那几个。
69
+ `off-canvas`/`overlap` 抓到,而不是变成一排缺了下半截的字。被祖先 `overflow:hidden`
70
+ 裁掉的文字不会出现在 pptx 的几何里,运行时的 `deck-qa` 看不到——它只在版式生产线的
71
+ `scripts/deck/deck-qa-sweep.mjs`(`clipped` 硬失败,逐个版式扫整册库)里被拦截;写 bespoke
72
+ 页时请直接避免定高容器。
71
73
 
72
74
  ## Catalog
73
75
 
@@ -186,34 +188,3 @@ styles change their visual grammar without changing layout HTML.
186
188
 
187
189
  Adding a **Theme binding** = one entry in `theme-system.json`; adding a **layout** = one
188
190
  `layouts/<id>.tpl` plus a candidate role in `profiles.json`. Regenerate `slots.json`.
189
-
190
- ## 反向通道 —— 包内版式如何进入共享库
191
-
192
- 一个 `packs/<id>/` 可以带自己的专属版式(`packs/<id>/layouts/<id>.tpl`,与共享同名
193
- 即遮蔽)。当包里做出来的某一页被证明**不是**包专属的装饰、而是别的族/包也会用得上
194
- 的结构时,把它提升进这个共享库,而不是让每个包各自重复发明。
195
-
196
- **准入判据**(spec §7.3):**剥掉所有颜色与 `font-family` 之后,它仍表达一个共享库
197
- 没有的结构。** 是 → 提升;否(结构其实通用,专属感全靠色彩/字体/图形语汇撑)→ 留在
198
- 包内,不要提升。
199
-
200
- 跑 `node scripts/ppt-tpl/promote-layout.mjs <packId> <layoutId>`。它只机械校验"可
201
- 提升"的必要条件(零硬编码色、不带私有 `font-family`、只用契约内 class、规范的
202
- 1280×720 单 `section` + `data-layout`、至少一个可填槽位、`layouts/README.md` 表头
203
- 格式没变)——**是不是结构性创新仍需人来判**,脚本不代劳。任一条不满足就拒绝写入
204
- (不移动文件),并逐条报出缺什么。
205
-
206
- 校验通过后脚本会:把 `.tpl` 移到 `layouts/`、重跑 `scripts/deck/gen-slots.mjs` 重生成
207
- `slots.json`、在本文件的 Catalog 表格里插入一行占位(`role`/`structure`/`density`/
208
- `has-image`/`pick when` 均为 `TODO`,附 `promoted from packs/<packId>`)。写入中途
209
- 任何一步失败都会整体回滚(删掉刚写的 `.tpl`,`slots.json`/本文件复原),不会留下
210
- 半成品。
211
-
212
- 提升不是终点,脚本打印的四步收尾都要做:补全本文件里那一行的全部 `TODO`、从
213
- `packs/<packId>/layouts/` 删掉原文件(否则同名遮蔽会让包永远看不到共享版本、也
214
- 永远吃不到后续对共享版式的修复)、`node scripts/deck/deck-skin-samples.mjs && node
215
- scripts/deck/deck-fidelity.mjs` 确认它在所有皮肤下都成立、再跑
216
- `TALOS_TEST_SCOPE=unit npx vitest run tests/unit/deck-layouts.test.ts`。
217
-
218
- 新版式若用到契约外的 class,先扩 `styles/_contract-defaults.css`(本脚本不代劳)——
219
- 但只有 **≥3 个族/包各自独立地需要同一个新原语**时才扩,避免契约被单个包的口味撑肥。
@@ -4,8 +4,10 @@
4
4
  `deck-build` 填槽时按 role 注入 `data-anim*` / `data-transition*`;deck.html 里已有的
5
5
  同名属性优先,不被覆盖。
6
6
 
7
- **motion 是可选的** —— 没有预设文件的族不注入任何动画,与 `data-anim` 默认全关一致。
8
- 23 个 entry 现在都配了(P0 有意留空以保证行为零变化,P2 补齐)。
7
+ **`catalog.json` 的每个 entry 都配了预设**(33/33)。用不用由 spec 的 `scenario` 决定
8
+ (`packages/core/src/document/deck-motion.ts::motionForDeck`):演示型场景(pitch/路演/发布会/
9
+ 大会/讲座/销售)注入,阅读型场景(内审/评审/报告/汇报/讲义/培训/述职)不注入,未写或认不出
10
+ 的场景保持注入;`"motion": true|false` 显式覆盖。没有预设的族不注入任何动画。
9
11
 
10
12
  效果集必须与 `packages/core/src/document/pptx-anim.ts` 一致:
11
13
  fade · fade-up · float · appear · fly-up · fly-down · fly-left · fly-right
@@ -10,7 +10,11 @@ prompt carries a `delivery="file"` pointer at that sidecar. So:
10
10
 
11
11
  ## What this skill IS for (Office)
12
12
 
13
- - **Generate / edit** .docx / .pptx → read **rewrite.md**, use `doc-gen`.
13
+ - **Generate** a .docx → `doc-gen` (read **rewrite.md**); a deck → the deck path
14
+ (SKILL.md → deck-design.md; `doc-gen` makes no decks).
15
+ - **Edit** an existing .docx / .pptx → **rewrite.md**: `doc-xml replace` for text,
16
+ `doc-fill` for `{{tokens}}`, `pptx-slide` / `pptx-clone` for slides,
17
+ `docx-annotate` for comments and tracked changes.
14
18
  - Everything visual (a diagram/chart that matters) → if you can see images, look
15
19
  at the original (see vision.md).
16
20