@liustack/pptwise 0.22.0 → 0.24.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.
Files changed (42) hide show
  1. package/README.md +72 -41
  2. package/README.zh-CN.md +72 -41
  3. package/dist/{chunk-M35M4QUC.js → chunk-2CA3JRFR.js} +460 -499
  4. package/dist/chunk-2CA3JRFR.js.map +1 -0
  5. package/dist/{chunk-WL5KWYKS.js → chunk-G7WQ7KSP.js} +34800 -30625
  6. package/dist/chunk-G7WQ7KSP.js.map +1 -0
  7. package/dist/{chunk-VUOLBHD7.js → chunk-RQUKZYSH.js} +1 -1
  8. package/dist/{chunk-VUOLBHD7.js.map → chunk-RQUKZYSH.js.map} +1 -1
  9. package/dist/{chunk-3ZUKISTY.js → chunk-VEZGVYJS.js} +2 -2
  10. package/dist/cli.js +911 -462
  11. package/dist/cli.js.map +1 -1
  12. package/dist/index.d.ts +5320 -1534
  13. package/dist/index.js +15 -13
  14. package/dist/node.d.ts +1 -1
  15. package/dist/node.js +2 -2
  16. package/dist/{pixel-audit-H5K6JK3X.js → pixel-audit-M5Q5WCST.js} +4 -4
  17. package/dist/pixel-audit-M5Q5WCST.js.map +1 -0
  18. package/dist/{registry-C0GJH7ZT.d.ts → registry-D8bkC8ff.d.ts} +1 -1
  19. package/dsh/preview-tool.js +20 -17
  20. package/package.json +2 -1
  21. package/skills/pptwise/SKILL.md +133 -70
  22. package/skills/pptwise/SKILL.zh-CN.md +136 -73
  23. package/skills/pptwise/references/branding.md +34 -8
  24. package/skills/pptwise/references/branding.zh-CN.md +36 -8
  25. package/skills/pptwise/references/components.md +63 -33
  26. package/skills/pptwise/references/components.zh-CN.md +63 -33
  27. package/skills/pptwise/references/density.md +23 -9
  28. package/skills/pptwise/references/density.zh-CN.md +23 -9
  29. package/skills/pptwise/references/images.md +26 -13
  30. package/skills/pptwise/references/images.zh-CN.md +26 -13
  31. package/skills/pptwise/references/layouts.md +30 -26
  32. package/skills/pptwise/references/layouts.zh-CN.md +30 -26
  33. package/skills/pptwise/references/spec.md +143 -69
  34. package/skills/pptwise/references/spec.zh-CN.md +143 -69
  35. package/skills/pptwise/references/validate.md +33 -45
  36. package/skills/pptwise/references/validate.zh-CN.md +33 -45
  37. package/skills/pptwise/scripts/run.ps1 +1 -1
  38. package/skills/pptwise/scripts/run.sh +1 -1
  39. package/dist/chunk-M35M4QUC.js.map +0 -1
  40. package/dist/chunk-WL5KWYKS.js.map +0 -1
  41. package/dist/pixel-audit-H5K6JK3X.js.map +0 -1
  42. /package/dist/{chunk-3ZUKISTY.js.map → chunk-VEZGVYJS.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts","../src/themes/presets.ts","../src/themes/extract/brand-extract.ts","../src/themes/brand-theme-file.ts","../src/cli/theme-fork.ts","../src/spec/index.ts","../src/spec/assemble.ts","../src/render/asset-brief.ts"],"sourcesContent":["export const VERSION = \"0.24.0\"\n","/**\n * The factory preset library.\n *\n * The 24 built-ins are no longer a closed set of \"the themes pptwise has\".\n * They are the shelf a new workspace theme is copied off (charter: 创建 =\n * 拷贝, `.issues/2026-08-30-theme-first-principles/charter.md`). This module\n * is the read source for that copy: it names what is on the shelf, what\n * occasion each preset answers, and it hands out an independent copy that\n * the copier owns outright. Nothing here keeps a link back to the preset —\n * a copy never inherits later, which is the whole point of copying.\n *\n * Decoration is pushed down on the way out. A built-in declares one\n * theme-wide `motif` anchor, but the public menu contract carries decoration\n * per entry, so {@link copyThemePreset} writes the anchor into every entry\n * that has not already declared its own decoration and whose face does not\n * structurally suppress the motif. The copy therefore needs no theme-level\n * anchor to look like its source.\n */\nimport { THEME_ID_CONSTRAINT, THEME_ID_PATTERN } from \"@/ir\"\nimport { PptwiseError } from \"../errors\"\nimport { BUILTIN_THEME_FILES, CANONICAL_THEME_IDS, type CanonicalThemeId } from \"./index\"\nimport { THEME_OCCASIONS, type IdentityStrength, type Occasion } from \"./occasions\"\nimport { getLayout } from \"../layouts/registry\"\nimport type { BuiltinThemeDeclaration, Menu, MenuEntry } from \"./schema\"\n\n/** One shelf entry: what a picker needs to choose a starting point. */\nexport interface ThemePresetSummary {\n readonly id: CanonicalThemeId\n readonly label: string\n readonly occasions: readonly Occasion[]\n readonly identity: IdentityStrength\n}\n\n/** Every preset on the shelf, in canonical order. */\nexport const THEME_PRESETS: readonly ThemePresetSummary[] = CANONICAL_THEME_IDS.map((id) => ({\n id,\n label: BUILTIN_THEME_FILES[id].label,\n occasions: THEME_OCCASIONS[id].occasions,\n identity: THEME_OCCASIONS[id].identity,\n}))\n\n/** Whether `id` names a factory preset. */\nexport function isThemePresetId(id: string): id is CanonicalThemeId {\n return (CANONICAL_THEME_IDS as readonly string[]).includes(id)\n}\n\n/** One shelf entry by id. Unknown ids are an error, never a silent fallback. */\nexport function getThemePreset(id: string): ThemePresetSummary {\n const summary = THEME_PRESETS.find((preset) => preset.id === id)\n if (!summary) {\n throw new PptwiseError(\n `unknown theme preset \"${id}\". Installed presets: ${CANONICAL_THEME_IDS.join(\", \")}`,\n )\n }\n return summary\n}\n\nfunction withDecor(entry: MenuEntry, motif: BuiltinThemeDeclaration[\"motif\"]): MenuEntry {\n if (entry.decor !== undefined || motif === undefined) return structuredClone(entry)\n // A face that paints its own identity never receives the theme motif, so\n // writing the anchor onto it would only mislead a reader of the copy.\n if (getLayout(entry.face)?.suppressMotif === true) return structuredClone(entry)\n return {\n ...structuredClone(entry),\n decor: motif.params ? { kind: \"motif\", id: motif.id, params: { ...motif.params } } : { kind: \"motif\", id: motif.id },\n }\n}\n\nfunction copyMenu(menu: Menu, motif: BuiltinThemeDeclaration[\"motif\"]): Menu {\n const content: Menu[\"content\"] = {}\n for (const [kind, entry] of Object.entries(menu.content) as [keyof Menu[\"content\"], MenuEntry][]) {\n content[kind] = withDecor(entry, motif)\n }\n return {\n cover: withDecor(menu.cover, motif),\n chapter: withDecor(menu.chapter, motif),\n content,\n ending: withDecor(menu.ending, motif),\n }\n}\n\n/**\n * Copy one preset under a new id. The result shares nothing with the\n * preset: style tokens, brand, and menu are all fresh objects, and the\n * theme-wide motif anchor has been written into the menu entries.\n *\n * `targetId` may equal a preset id. That is how a freeze copy keeps the\n * bound name while the file shadows the factory shelf.\n */\nexport function copyThemePreset(presetId: string, targetId: string): BuiltinThemeDeclaration {\n getThemePreset(presetId)\n if (!THEME_ID_PATTERN.test(targetId)) {\n throw new PptwiseError(`invalid theme id \"${targetId}\". ${THEME_ID_CONSTRAINT}`)\n }\n const preset: BuiltinThemeDeclaration = BUILTIN_THEME_FILES[presetId as CanonicalThemeId]\n const record = THEME_OCCASIONS[preset.id]\n return {\n version: 2,\n id: targetId as BuiltinThemeDeclaration[\"id\"],\n label: preset.label,\n style: { ...structuredClone(preset.style), id: targetId },\n ...(preset.brand ? { brand: structuredClone(preset.brand) } : {}),\n occasions: [...record.occasions],\n identity: record.identity,\n menu: copyMenu(preset.menu, preset.motif),\n }\n}\n","/**\n * Local brand-color/font extraction from a user's own `.thmx`/`.potx`/`.pptx`\n * OOXML theme part (brand-extract wave, roadmap §2.0.1: the free\n * adoption-friction remover — \"the output doesn't look like our company\" is\n * pptwise's #1 enterprise-adoption blocker, and every user who hits it\n * already has a company template on disk). Everything in this module runs\n * against zip bytes only (`jszip`, already a browser-safe dependency) —\n * `src/index.ts`'s dependency closure stays free of Node-only deps\n * (`AGENTS.md`'s layout rule), so this can run in a browser as readily as\n * the CLI's `pptwise brand extract` wraps it for.\n *\n * Rewritten from `.issues/notes/brand-extraction-probe.py` (the 39/39\n * feasibility reference: every macOS Office `.thmx` extracted cleanly) —\n * read that file for the reference regex shapes, not embedded here as\n * Python. This is a TS reimplementation, not a port with a Python shim.\n *\n * ── Slot → token mapping (roadmap §2.0.1's table, 裁定 2) ──────────────\n *\n * dk1/lt1 (by measured lightness) → text / bg — see below, not a fixed\n * dk1→text/lt1→bg assignment\n * lt2 → surface (falls back to bg when absent)\n * accent1 (or dk2) → primary/accent\n * accent1-6 → chartPalette (OOXML's 6 accent slots map 1:1 onto\n * pptwise's chart palette — no reshaping needed)\n * — → muted, derived (see {@link deriveMuted})\n *\n * ── bg/text assignment: by measured lightness, not slot name ────────────\n *\n * OOXML's `dk1`/`lt1` are slot *names*, not a guarantee about which one is\n * actually darker. Empirically verified against all 39 real `.thmx` files\n * shipped with a local macOS Office install (`.issues/notes/brand-extraction-probe.py`'s\n * own corpus) before writing this: every single one — including\n * \"Dark Gradient.thmx\", the one visually dark-background theme in that\n * set — declares the conventional `dk1=#000000`/`lt1=#FFFFFF` unchanged;\n * six declare a near-black-but-not-pure `dk1` (e.g. `#131313`), never a\n * *light* `dk1`. A theme's actual dark/light background comes from the\n * slide master's own background fill (`<p:bg>`, often referencing `dk2` or a\n * gradient) — a part this extractor deliberately never reads (theme part\n * only, 裁定 1) — not from which literal color sits in `dk1` vs `lt1`. So\n * this extractor makes no claim about detecting \"is this a dark theme\" at\n * all; it only guards against the (real, if rare outside this local corpus)\n * case of a producer that swapped which raw color occupies which *named*\n * slot: {@link resolveBgText} assigns `text` to whichever of `dk1`/`lt1`\n * measures darker and `bg` to whichever measures lighter, regardless of\n * which slot name it came from — always a legible dark-ink-on-light-bg (or,\n * for a genuinely inverted producer, the reverse) pairing, never a\n * name-literal assignment that could land dark-on-dark. `isDark` (below)\n * measures \"darker\" with nothing but the already-exported `contrastRatio`\n * (`../../render/ink`) against pure black/white — no new luminance formula, per\n * this wave's \"reuse existing ratio/mix utilities\" discipline.\n */\nimport JSZip from \"jszip\"\nimport { PptwiseError } from \"../../errors\"\nimport { contrastRatio } from \"../../render/ink\"\nimport { mixHex } from \"../../components/color-mix\"\nimport type { Menu, ThemeFile } from \"../schema\"\nimport type { StyleTokens } from \"../tokens\"\n\n/** The complete v2 theme-file shape written by `pptwise brand extract`. */\nexport type BrandThemeFile = ThemeFile\n\nexport interface ExtractBrandThemeOptions {\n /** Theme id — defaults to a slug of {@link ExtractBrandThemeOptions.label},\n * or (when that's also absent) a slug of the source theme part's own\n * `<a:clrScheme name=\"…\">`. The CLI (`pptwise brand extract`) always\n * passes one explicitly (`--id`, or a slug of the `-o` filename — 裁定\n * 4) — this default only ever fires for a bare SDK caller. */\n id?: string\n /** Human-readable label — defaults to the source theme part's own\n * `<a:clrScheme name=\"…\">`. */\n label?: string\n}\n\n/** Materialized consulting-style menu used by shallow brand extraction. */\nconst EXTRACTED_THEME_MENU = {\n cover: { face: \"poster-center\" },\n chapter: { face: \"masthead-chapter\" },\n content: {\n points: { face: \"two-column\" },\n list: { face: \"bento-panel\" },\n comparison: { face: \"two-column\" },\n process: { face: \"rail-numbered\" },\n data: { face: \"bento-panel\" },\n photo: { face: \"asymmetric-triptych\" },\n statement: { face: \"statement\" },\n quote: { face: \"pull-quote\" },\n fact: { face: \"stat-hero\" },\n evidence: { face: \"one-evidence\" },\n hierarchy: { face: \"two-column\" },\n },\n ending: { face: \"poster-ending\" },\n} satisfies Menu\n\n/** Every OOXML color-scheme slot this parser reads. `hlink`/`folHlink` are\n * parsed (mirroring probe.py) but never mapped onto a pptwise token — no\n * hyperlink-color concept exists in `StyleColors` today. */\ntype ClrSlot = \"dk1\" | \"lt1\" | \"dk2\" | \"lt2\" | \"accent1\" | \"accent2\" | \"accent3\" | \"accent4\" | \"accent5\" | \"accent6\" | \"hlink\" | \"folHlink\"\ntype ClrSlots = Partial<Record<ClrSlot, string>>\n\nconst ACCENT_SLOTS: readonly ClrSlot[] = [\"accent1\", \"accent2\", \"accent3\", \"accent4\", \"accent5\", \"accent6\"]\n\n// Theme part location (probe.py's own comment): `.thmx` puts it at\n// `theme/theme/theme1.xml`, `.pptx`/`.potx` at `ppt/theme/theme1.xml` — this\n// regex matches either, and any numbered variant (`theme2.xml`, …). A\n// multi-variant `.thmx` also carries per-variant copies under\n// `theme/themeVariants/variant<N>/theme/theme1.xml`; those are excluded by\n// path, and among the survivors the *shortest* path wins (probe.py's own\n// `sorted(cands, key=len)[0]`) — the top-level part is always the shortest.\nconst THEME_PART_RE = /theme\\d*\\.xml$/\nconst THEME_VARIANTS_SEGMENT = \"themeVariants\"\n\nconst CLR_SCHEME_RE = /<a:clrScheme name=\"([^\"]*)\">([\\s\\S]*?)<\\/a:clrScheme>/\nconst CLR_SLOT_RE =\n /<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)>\\s*<a:(?:srgbClr val=\"([0-9A-Fa-f]{6})\"|sysClr[^>]*lastClr=\"([0-9A-Fa-f]{6})\")/g\nconst FONT_SCHEME_RE =\n /<a:fontScheme name=\"([^\"]*)\">[\\s\\S]*?<a:majorFont>\\s*<a:latin typeface=\"([^\"]*)\"[\\s\\S]*?<a:minorFont>\\s*<a:latin typeface=\"([^\"]*)\"/\n\ninterface ThemePart {\n path: string\n xml: string\n}\n\nasync function findThemePart(zip: JSZip): Promise<ThemePart | undefined> {\n const candidates = Object.keys(zip.files)\n .filter((name) => !zip.files[name]!.dir && THEME_PART_RE.test(name) && !name.includes(THEME_VARIANTS_SEGMENT))\n .sort((a, b) => a.length - b.length)\n const path = candidates[0]\n if (path === undefined) return undefined\n const xml = await zip.files[path]!.async(\"string\")\n return { path, xml }\n}\n\nfunction parseColors(xml: string): { schemeName: string | undefined; slots: ClrSlots } {\n const match = CLR_SCHEME_RE.exec(xml)\n if (!match) return { schemeName: undefined, slots: {} }\n const slots: ClrSlots = {}\n for (const m of match[2]!.matchAll(CLR_SLOT_RE)) {\n const slot = m[1] as ClrSlot\n const hex = m[2] ?? m[3]\n if (hex) slots[slot] = `#${hex.toUpperCase()}`\n }\n return { schemeName: match[1]?.trim() || undefined, slots }\n}\n\nfunction parseFonts(xml: string): { major: string | undefined; minor: string | undefined } {\n const m = FONT_SCHEME_RE.exec(xml)\n if (!m) return { major: undefined, minor: undefined }\n return { major: m[2]?.trim() || undefined, minor: m[3]?.trim() || undefined }\n}\n\n/** `hex` reads as closer to black than to white — see this file's own header\n * comment (\"bg/text assignment: by measured lightness, not slot name\"). No\n * new luminance math: two `contrastRatio` (`../../render/ink`) calls against pure\n * black/white, compared against each other, rather than a fresh relative-\n * luminance computation of its own. */\nfunction isDark(hex: string): boolean {\n return contrastRatio(hex, \"#FFFFFF\") > contrastRatio(hex, \"#000000\")\n}\n\n/** `text` = whichever of `dk1`/`lt1` measures darker, `bg` = whichever\n * measures lighter — see this file's own header comment for why this is\n * a measured-lightness assignment, not a `dk1`-always-means-text one. The\n * conventional case (`dk1` dark, `lt1` light — every real-world sample this\n * wave checked) and the by-name assignment agree; this only diverges for a\n * producer that put an unconventional raw color in either named slot. */\nfunction resolveBgText(dk1: string, lt1: string): { bg: string; text: string } {\n return isDark(dk1) === isDark(lt1)\n ? { bg: lt1, text: dk1 } // ambiguous (both/neither read dark) — keep the conventional dk1→text/lt1→bg mapping\n : isDark(dk1)\n ? { bg: lt1, text: dk1 }\n : { bg: dk1, text: lt1 }\n}\n\n/** How many discrete steps {@link deriveMuted} walks from the most-muted\n * (blended fully toward `bg`) candidate back toward `text` itself — same\n * 20-step (5% increment) granularity `metaInk` (`../../render/ink.ts`) already\n * uses for its own stepped ink walk, reused here rather than picked fresh. */\nconst MUTED_STEPS = 20\n/** The body-text WCAG floor (`CONTRAST_RATIO_BODY` in `../../render/ink.ts`,\n * inlined rather than imported since that constant isn't exported) — a\n * derived `muted` token must clear this against *both* `bg` and `surface`,\n * not just registerTheme's lower 3.0:1 registration floor, so it reads\n * correctly as body text on either background a theme might paint. */\nconst MUTED_TARGET_RATIO = 4.5\n\n/**\n * Derive a `muted` token from `text` by blending it toward `bg` in\n * {@link MUTED_STEPS} discrete steps (`mixHex`, `../../components/color-mix.ts`\n * — the same solid-hex blend `swot.tsx`/`bmc.tsx`/`waterfall.tsx` already\n * share) and measuring each candidate with the already-exported\n * `contrastRatio` (`../../render/ink.ts`) — no new color math, per this wave's own\n * discipline. Walks from the most-muted end (`t=1`, blended fully toward\n * `bg`) down to `t=0` (`text` itself, the highest-contrast endpoint) and\n * returns the *first* candidate, meaning the most-muted one, that clears\n * {@link MUTED_TARGET_RATIO} against **both** `bg` and `surface` (the two\n * backgrounds `colors.muted` can actually render against). Throws when no\n * step clears the floor, because returning `text` would hide an unusable\n * anchor combination and could still fail on `surface`.\n */\nfunction findMutedCandidate(text: string, bg: string, surface: string): string | undefined {\n for (let step = MUTED_STEPS; step >= 0; step--) {\n const t = step / MUTED_STEPS\n const candidate = mixHex(text, bg, t)\n if (contrastRatio(candidate, bg) >= MUTED_TARGET_RATIO && contrastRatio(candidate, surface) >= MUTED_TARGET_RATIO) {\n return candidate.toUpperCase()\n }\n }\n return undefined\n}\n\nexport function deriveMuted(text: string, bg: string, surface: string): string {\n const candidate = findMutedCandidate(text, bg, surface)\n if (candidate !== undefined) return candidate\n throw new PptwiseError(\n `cannot derive colors.muted with a contrast ratio of ${MUTED_TARGET_RATIO.toFixed(1)}:1 against both colors.bg (${bg}) and colors.surface (${surface}) from colors.text (${text}). Choose compatible background and text anchors`,\n )\n}\n\n/** Keywords (lower-cased, substring match) that mark a font name as\n * belonging to the serif family — used only to pick which of two\n * `SAFE_FONTS` fallback chains {@link buildFontStack} appends after the\n * extracted face, mirroring consulting's own `[\"Bower\", \"Georgia\", \"Source\n * Han Serif SC\", \"serif\"]` precedent (`../builtin/consulting.ts`) for a serif brand\n * font, or a sans equivalent otherwise. This is a display-quality heuristic,\n * not a safety one: `resolveFontFace` (`../../render/fonts.ts`) already falls\n * back to a Windows-safe CJK default (`Microsoft YaHei`) when nothing in\n * the stack matches `SAFE_FONTS` at all, so a missed classification here\n * degrades to \"the fallback face doesn't visually match the brand font's\n * register,\" never a broken/unmeasured export. */\nconst SERIF_HINTS = [\n \"times\",\n \"georgia\",\n \"cambria\",\n \"garamond\",\n \"palatino\",\n \"constantia\",\n \"book antiqua\",\n \"minion\",\n \"serif\",\n \"cochin\",\n \"didot\",\n \"baskerville\",\n \"sitka\",\n \"century\",\n \"goudy\",\n \"bodoni\",\n \"caslon\",\n \"perpetua\",\n \"rockwell\",\n \"playfair\",\n]\n\nfunction isSerifName(name: string): boolean {\n const n = name.toLowerCase()\n return SERIF_HINTS.some((hint) => n.includes(hint))\n}\n\n/** Extracted face at the head of the stack (when present), followed by a\n * Windows-safe fallback chain matching its apparent register — see\n * {@link SERIF_HINTS}'s own doc comment. `undefined` (no `<a:latin\n * typeface>` found at all) skips straight to the sans-serif fallback chain\n * alone — the common Office default (Calibri/Calibri Light) is itself\n * sans, so this is the safer bare default. */\nfunction buildFontStack(extracted: string | undefined): string[] {\n if (extracted === undefined) return [\"Calibri\", \"Microsoft YaHei\", \"sans-serif\"]\n return isSerifName(extracted)\n ? [extracted, \"Georgia\", \"Source Han Serif SC\", \"serif\"]\n : [extracted, \"Calibri\", \"Microsoft YaHei\", \"sans-serif\"]\n}\n\n/** Lower-cases, replaces every run of non-alphanumeric characters with a\n * single hyphen, and trims leading/trailing hyphens — a plain, dependency-free\n * slug (no new package for one string transform). Empty input (or input\n * that's entirely non-alphanumeric — a CJK-only deck name reduces to nothing\n * here) falls back to `fallback` rather than producing an empty/invalid\n * identifier. The fallback is a parameter because this function now serves\n * two callers whose empty-input answer differs: a brand theme id (`\"brand\"`,\n * the default) and a workspace deck directory name (`\"deck\"`,\n * `../../cli/workspace.ts`). */\nexport function slugify(input: string, fallback = \"brand\"): string {\n const slug = input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n return slug || fallback\n}\n\n/**\n * Extract a {@link BrandThemeFile} from raw OOXML package bytes (a\n * `.thmx`/`.potx`/`.pptx` file's own bytes — this function only ever reads\n * the theme part inside, nothing else in the package). Deterministic: the\n * same bytes always produce the same output (no randomness, no wall-clock\n * read) — required for this wave's fixture tests' double-run-equality\n * assertions.\n *\n * Throws {@link PptwiseError} for two *structural* failures — data this\n * function has no reasonable default for:\n * - no theme part found in the package at all\n * - a theme part with no `dk1`/`lt1` (nothing to derive `bg`/`text` from) or\n * no accent color at all (nothing to derive `primary`/`chartPalette` from)\n *\n * Everything else degrades gracefully rather than throwing — a missing\n * `lt2` falls back to `bg` (no distinct surface tone available), a missing\n * `accent1` falls back to `dk2` then to the first available chart color, a\n * missing font falls back to a Windows-safe generic stack — so a real-world\n * theme missing a slot or two (this wave's own fixture matrix covers\n * several) still produces a usable, if plainer, theme. An unreadable palette\n * is rejected while deriving `muted`.\n */\nexport async function extractBrandTheme(\n bytes: Uint8Array | ArrayBuffer,\n opts: ExtractBrandThemeOptions = {},\n): Promise<BrandThemeFile> {\n const zip = await JSZip.loadAsync(bytes)\n const part = await findThemePart(zip)\n if (!part) {\n throw new PptwiseError(\n \"no theme part found in this file — expected a .thmx/.potx/.pptx OOXML package with a ppt/theme/theme1.xml (or theme/theme/theme1.xml) part\",\n )\n }\n const { schemeName, slots } = parseColors(part.xml)\n if (!slots.dk1 || !slots.lt1) {\n throw new PptwiseError(`theme part ${part.path} is missing dk1/lt1 colors — cannot derive bg/text tokens`)\n }\n const chartPalette = ACCENT_SLOTS.map((slot) => slots[slot]).filter((c): c is string => c !== undefined)\n if (chartPalette.length === 0) {\n throw new PptwiseError(`theme part ${part.path} has no accent colors — cannot derive a chart palette or primary color`)\n }\n\n const { bg, text } = resolveBgText(slots.dk1, slots.lt1)\n const surface = slots.lt2 ?? bg\n const primary = slots.accent1 ?? slots.dk2 ?? chartPalette[0]!\n const accent = slots.accent2 ?? primary\n const muted = deriveMuted(text, bg, surface)\n\n const fonts = parseFonts(part.xml)\n const heading = buildFontStack(fonts.major)\n const body = buildFontStack(fonts.minor ?? fonts.major)\n\n const label = opts.label ?? schemeName ?? \"brand\"\n const id = opts.id ?? slugify(opts.label ?? schemeName ?? \"brand\")\n\n const style: StyleTokens = {\n id,\n colors: { bg, surface, primary, accent, text, muted, chartPalette },\n fonts: { heading, body },\n defaultBackgrounds: {\n cover: { kind: \"color\", value: bg },\n chapter: { kind: \"color\", value: bg },\n content: { kind: \"color\", value: bg },\n ending: { kind: \"color\", value: bg },\n },\n }\n\n return { version: 2, id, label, style, brand: {}, menu: EXTRACTED_THEME_MENU }\n}\n","/**\n * Parse and register user-authored v2 self-contained theme files. File I/O\n * stays in the CLI. File loading uses `installThemeFile` so a freeze copy\n * can shadow a builtin and a failed reload cannot drop a previous definition.\n */\nimport { PptwiseError } from \"../errors\"\nimport { installThemeFile } from \"./definitions\"\nimport { ThemeFileSchema, type ThemeFile } from \"./schema\"\n\n/** Kept as the existing exported symbol, now pointing at the unified v2 contract. */\nexport const BrandThemeFileSchema = ThemeFileSchema\nexport { ThemeFileSchema }\n\n/** Parse already decoded JSON as a public v2 theme file. */\nexport function parseBrandThemeFile(raw: unknown, source: string): ThemeFile {\n const result = ThemeFileSchema.safeParse(raw)\n if (!result.success) {\n const detail = result.error.issues\n .map((issue) => `${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"\\n\")\n throw new PptwiseError(\n `invalid theme file ${source}: current theme format is version 2 and every file is self-contained.\\n${detail}`,\n )\n }\n return result.data as ThemeFile\n}\n\n/**\n * Register one parsed v2 file through the replace channel. A builtin id is\n * a freeze/shadow. Re-reading a custom id replaces the previous definition\n * only after every gate passes.\n */\nexport function registerBrandThemeFile(file: ThemeFile): string {\n installThemeFile(file)\n return file.id\n}\n","import type { BackgroundSpec } from \"../ir\"\nimport { PptwiseError } from \"../errors\"\nimport { contrastRatio } from \"../render/ink\"\nimport { assertContrastFloor } from \"../themes/definitions\"\nimport { deriveMuted } from \"../themes/extract/brand-extract\"\nimport { HexTokenSchema, ThemeFileSchema, type ThemeFile } from \"../themes/schema\"\n\nexport interface ForkThemeAnchors {\n primary: string\n accent?: string\n bg?: string\n text?: string\n surface?: string\n chartPalette?: string[]\n}\n\nexport interface ForkThemeIdentity {\n id: string\n label?: string\n fonts?: ThemeFile[\"style\"][\"fonts\"]\n}\n\ninterface Hsl {\n h: number\n s: number\n l: number\n}\n\nfunction clamp(value: number, min = 0, max = 1): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const canonical = HexTokenSchema.parse(hex)\n const n = Number.parseInt(canonical.slice(1), 16)\n return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255]\n}\n\nfunction hexToHsl(hex: string): Hsl {\n const [r, g, b] = hexToRgb(hex)\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const delta = max - min\n const l = (max + min) / 2\n if (delta === 0) return { h: 0, s: 0, l }\n const s = delta / (1 - Math.abs(2 * l - 1))\n let h = 0\n if (max === r) h = 60 * (((g - b) / delta) % 6)\n else if (max === g) h = 60 * ((b - r) / delta + 2)\n else h = 60 * ((r - g) / delta + 4)\n return { h: (h + 360) % 360, s, l }\n}\n\nfunction hslToHex({ h, s, l }: Hsl): string {\n const hue = ((h % 360) + 360) % 360\n const chroma = (1 - Math.abs(2 * l - 1)) * s\n const segment = hue / 60\n const x = chroma * (1 - Math.abs((segment % 2) - 1))\n const [r1, g1, b1] =\n segment < 1 ? [chroma, x, 0]\n : segment < 2 ? [x, chroma, 0]\n : segment < 3 ? [0, chroma, x]\n : segment < 4 ? [0, x, chroma]\n : segment < 5 ? [x, 0, chroma]\n : [chroma, 0, x]\n const m = l - chroma / 2\n const channel = (value: number) => Math.round((value + m) * 255).toString(16).padStart(2, \"0\")\n return `#${channel(r1)}${channel(g1)}${channel(b1)}`.toUpperCase()\n}\n\nfunction hueDelta(from: number, to: number): number {\n return ((to - from + 540) % 360) - 180\n}\n\nfunction rebaseRelativeColor(\n color: string,\n sourcePlane: string,\n targetPlane: string,\n sourceText: string,\n targetText: string,\n): string {\n const value = hexToHsl(color)\n const source = hexToHsl(sourcePlane)\n const target = hexToHsl(targetPlane)\n const oldText = hexToHsl(sourceText)\n const newText = hexToHsl(targetText)\n const sourceRange = oldText.l - source.l\n const progress = Math.abs(sourceRange) < 1e-6\n ? 0\n : (value.l - source.l) / sourceRange\n const lightness = clamp(target.l + progress * (newText.l - target.l))\n const offset = source.s > 0.02 && value.s > 0.02\n ? hueDelta(source.h, value.h)\n : 0\n const hue = target.s > 0.02 ? target.h + offset : value.h\n const saturation = clamp(target.s + value.s - source.s)\n return hslToHex({ h: hue, s: saturation, l: lightness })\n}\n\ntype AnchorRole = \"bg\" | \"surface\" | \"text\" | \"primary\" | \"accent\"\ntype SlideBackgroundRole = \"cover\" | \"chapter\" | \"content\" | \"ending\"\ntype FiveAnchors = Record<AnchorRole, string>\n\nconst ORDINARY_BACKGROUND_ANCHORS: readonly AnchorRole[] = [\"bg\", \"surface\", \"primary\", \"accent\", \"text\"]\nconst CHAPTER_BACKGROUND_ANCHORS: readonly AnchorRole[] = [\"primary\", \"bg\", \"surface\", \"accent\", \"text\"]\n\nfunction rgbDistance(a: string, b: string): number {\n const [ar, ag, ab] = hexToRgb(a)\n const [br, bg, bb] = hexToRgb(b)\n return Math.hypot(ar - br, ag - bg, ab - bb)\n}\n\n/** Resolve a background to its nearest semantic anchor. A donor can carry\n * identical primary and accent anchors. Equal distances keep the first role\n * in the ordered list, so that ambiguous tie resolves to primary\n * deterministically. */\nfunction resolveBackgroundAnchor(\n color: string,\n slideRole: SlideBackgroundRole,\n source: FiveAnchors,\n): AnchorRole {\n const roles = slideRole === \"chapter\" ? CHAPTER_BACKGROUND_ANCHORS : ORDINARY_BACKGROUND_ANCHORS\n let best = roles[0]!\n let bestDistance = rgbDistance(color, source[best])\n for (const role of roles.slice(1)) {\n const distance = rgbDistance(color, source[role])\n if (distance < bestDistance) {\n best = role\n bestDistance = distance\n }\n }\n return best\n}\n\nfunction deriveBackgroundColor(\n color: string,\n slideRole: SlideBackgroundRole,\n source: FiveAnchors,\n target: FiveAnchors,\n): string {\n const role = resolveBackgroundAnchor(color, slideRole, source)\n return rebaseRelativeColor(color, source[role], target[role], source.text, target.text)\n}\n\nfunction deriveBackground(\n spec: BackgroundSpec,\n slideRole: SlideBackgroundRole,\n source: FiveAnchors,\n target: FiveAnchors,\n): BackgroundSpec {\n if (spec.kind === \"color\") {\n return { ...spec, value: deriveBackgroundColor(spec.value, slideRole, source, target) }\n }\n if (spec.kind === \"gradient\") {\n return {\n ...spec,\n from: deriveBackgroundColor(spec.from, slideRole, source, target),\n to: deriveBackgroundColor(spec.to, slideRole, source, target),\n }\n }\n if (spec.overlay !== undefined) {\n return {\n ...spec,\n overlay: {\n ...spec.overlay,\n color: deriveBackgroundColor(spec.overlay.color, slideRole, source, target),\n },\n }\n }\n return spec\n}\n\nfunction rotatePaletteColor(color: string, sourceAnchor: string, targetAnchor: string): string {\n const value = hexToHsl(color)\n const source = hexToHsl(sourceAnchor)\n const target = hexToHsl(targetAnchor)\n if (value.s <= 0.02 || source.s <= 0.02 || target.s <= 0.02) {\n return hslToHex(value)\n }\n return hslToHex({ ...value, h: value.h + hueDelta(source.h, target.h) })\n}\n\nfunction derivePaletteSequence(\n sequence: readonly string[],\n sourceAnchor: string,\n targetAnchor: string,\n): string[] {\n return [\n HexTokenSchema.parse(targetAnchor),\n ...sequence.slice(1).map((color) => rotatePaletteColor(color, sourceAnchor, targetAnchor)),\n ]\n}\n\nconst STATUS_CONTRAST_RATIO = 4.5\nconst STATUS_LIGHTNESS_STEPS = 1000\n\nfunction calibrateStatusColor(\n role: \"danger\" | \"warning\" | \"success\",\n color: string,\n bg: string,\n surface: string,\n): string {\n const source = hexToHsl(color)\n const original = hslToHex(source)\n const clearsFloor = (candidate: string) =>\n contrastRatio(candidate, bg) >= STATUS_CONTRAST_RATIO\n && contrastRatio(candidate, surface) >= STATUS_CONTRAST_RATIO\n if (clearsFloor(original)) return original\n\n let best: { color: string; distance: number } | undefined\n for (let step = 0; step <= STATUS_LIGHTNESS_STEPS; step++) {\n const lightness = step / STATUS_LIGHTNESS_STEPS\n const candidate = hslToHex({ ...source, l: lightness })\n if (!clearsFloor(candidate)) continue\n const distance = Math.abs(lightness - source.l)\n if (best === undefined || distance < best.distance) best = { color: candidate, distance }\n }\n if (best !== undefined) return best.color\n throw new PptwiseError(\n `cannot calibrate colors.${role} to a ${STATUS_CONTRAST_RATIO.toFixed(1)}:1 contrast ratio against both colors.bg (${bg}) and colors.surface (${surface}) while preserving its hue`,\n )\n}\n\nfunction buildForkedTheme(\n source: ThemeFile,\n anchors: ForkThemeAnchors,\n identity: ForkThemeIdentity,\n): ThemeFile {\n const bg = HexTokenSchema.parse(anchors.bg ?? source.style.colors.bg)\n const primary = HexTokenSchema.parse(anchors.primary)\n const accent = HexTokenSchema.parse(anchors.accent ?? source.style.colors.accent)\n const text = HexTokenSchema.parse(anchors.text ?? source.style.colors.text)\n const surface = HexTokenSchema.parse(anchors.surface ?? source.style.colors.surface)\n const muted = deriveMuted(text, bg, surface)\n\n const sourceColors = source.style.colors\n const sourceAnchors: FiveAnchors = {\n bg: sourceColors.bg,\n surface: sourceColors.surface,\n text: sourceColors.text,\n primary: sourceColors.primary,\n accent: sourceColors.accent,\n }\n const targetAnchors: FiveAnchors = { bg, surface, text, primary, accent }\n const chartPalette = anchors.chartPalette ?? sourceColors.chartPalette\n const chartAnchor = anchors.chartPalette === undefined ? sourceColors.primary : chartPalette[0]!\n const colors: ThemeFile[\"style\"][\"colors\"] = {\n ...sourceColors,\n bg,\n primary,\n accent,\n text,\n surface,\n muted,\n chartPalette: derivePaletteSequence(chartPalette, chartAnchor, primary),\n }\n if (sourceColors.accentPool !== undefined) {\n colors.accentPool = derivePaletteSequence(sourceColors.accentPool, sourceColors.accent, accent)\n }\n if (sourceColors.panel !== undefined) {\n colors.panel = rebaseRelativeColor(sourceColors.panel, sourceColors.bg, bg, sourceColors.text, text)\n }\n if (sourceColors.border !== undefined) {\n colors.border = rebaseRelativeColor(sourceColors.border, sourceColors.surface, surface, sourceColors.text, text)\n }\n if (sourceColors.cardStroke !== undefined) {\n colors.cardStroke = rebaseRelativeColor(sourceColors.cardStroke, sourceColors.surface, surface, sourceColors.text, text)\n }\n if (sourceColors.danger !== undefined) {\n colors.danger = calibrateStatusColor(\"danger\", sourceColors.danger, bg, surface)\n }\n if (sourceColors.warning !== undefined) {\n colors.warning = calibrateStatusColor(\"warning\", sourceColors.warning, bg, surface)\n }\n if (sourceColors.success !== undefined) {\n colors.success = calibrateStatusColor(\"success\", sourceColors.success, bg, surface)\n }\n\n const defaultBackgrounds = {\n cover: deriveBackground(source.style.defaultBackgrounds.cover, \"cover\", sourceAnchors, targetAnchors),\n chapter: deriveBackground(source.style.defaultBackgrounds.chapter, \"chapter\", sourceAnchors, targetAnchors),\n content: deriveBackground(source.style.defaultBackgrounds.content, \"content\", sourceAnchors, targetAnchors),\n ending: deriveBackground(source.style.defaultBackgrounds.ending, \"ending\", sourceAnchors, targetAnchors),\n }\n\n const file = {\n version: 2 as const,\n id: identity.id,\n label: identity.label ?? source.label,\n style: {\n ...source.style,\n id: identity.id,\n colors,\n fonts: identity.fonts ?? source.style.fonts,\n defaultBackgrounds,\n },\n brand: source.brand,\n occasions: source.occasions,\n identity: source.identity,\n menu: structuredClone(source.menu),\n }\n return ThemeFileSchema.parse(file) as ThemeFile\n}\n\nexport function forkTheme(\n source: ThemeFile,\n anchors: ForkThemeAnchors,\n identity: ForkThemeIdentity,\n): ThemeFile {\n const file = buildForkedTheme(source, anchors, identity)\n assertContrastFloor(file.id, file.style)\n return file\n}\n","/**\n * Deck spec schema and validation. A spec binds one theme, then names the\n * semantic kind of each content page. It never stores render selection state.\n * This module stays pure and Node-free so it remains inside `src/index.ts`'s\n * browser-safe dependency closure.\n */\nimport { z } from \"zod\"\nimport { PptwiseError } from \"../errors\"\nimport {\n BrandSchema,\n COMPONENT_TYPES,\n DeckBrandingSchema,\n KIND_VALUES,\n MetaSchema,\n NarrativeProfileInputSchema,\n THEME_ID_CONSTRAINT,\n THEME_ID_PATTERN,\n THEME_REQUIRED_MESSAGE,\n themeIssueMessage,\n} from \"../ir\"\nimport { normalizeDeckRootAliases } from \"../ir/field-aliases\"\nimport {\n normalizeNarrativeShape,\n resolveNarrative,\n type NarrativeProfile,\n type Pacing,\n} from \"../narrative\"\nimport { CAPACITY } from \"../audit/capacity\"\nimport { type SlideType } from \"../layouts/registry\"\nimport { offeredContentKinds, resolveLayoutId } from \"../render/layout-selection\"\nimport { getInstalledThemeIds, getThemeDefinition } from \"../themes/definitions\"\n\n// ── schema ───────────────────────────────────────────────────────────────\n\nexport type PageSpecType = SlideType\nexport type PageKind = (typeof KIND_VALUES)[number]\n\n/**\n * Every page locks its id, type, and heading. Content pages additionally lock\n * a semantic `kind`, which must be offered by the bound theme menu. `focus`\n * and `summary` remain optional authoring hints.\n */\nconst CommonPageSpecFields = {\n id: z.string(),\n heading: z.string(),\n /** Optional authoring hint pointing fill at a preferred content kind or component type, not a face. */\n focus: z.string().optional(),\n /** Free-text content anchor read by the fill step. */\n summary: z.string().optional(),\n}\n\nexport const PageSpecSchema = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"cover\"), ...CommonPageSpecFields }).strict(),\n z.object({ type: z.literal(\"chapter\"), ...CommonPageSpecFields }).strict(),\n z\n .object({\n type: z.literal(\"content\"),\n kind: z.enum(KIND_VALUES),\n ...CommonPageSpecFields,\n })\n .strict(),\n z.object({ type: z.literal(\"ending\"), ...CommonPageSpecFields }).strict(),\n])\n\nexport type PageSpec = z.infer<typeof PageSpecSchema>\n\n/**\n * Top-level deck spec shape. Narrative defaults are resolved by validation\n * and assembly without being written back into the parsed spec. Theme is\n * required. The spec version is independent from the IR version.\n */\nexport const DeckSpecSchema = z\n .object({\n version: z.literal(\"1\").default(\"1\"),\n // Same open-schema/closed-semantic split as PptxIRSchema's `narrative`\n // field — see `NarrativeProfileInputSchema`'s doc comment in `ir/index.ts`\n // for the full rationale (reused verbatim here, not redefined, so the\n // two can't drift apart). Field renamed from `scenario` to `narrative`\n // this task (spec §8.1's `DeckPlan`→`DeckSpec` rename, task 2) — its\n // *value* was already in the new strategy/pacing vocabulary as of task 1\n // (vocabulary-v4 rename) — `resolveNarrative` below is what actually\n // enforces that.\n narrative: z.union([z.string(), NarrativeProfileInputSchema]).optional(),\n theme: z\n .string({\n error: (iss) => (iss.input === undefined ? THEME_REQUIRED_MESSAGE : undefined),\n })\n .regex(THEME_ID_PATTERN, THEME_ID_CONSTRAINT),\n filename: z.string().optional(),\n meta: MetaSchema.default({}),\n /** Deck logo placement — reused verbatim from the IR's own `brand` field\n * (`BrandSchema`, `../ir`) so the deck spec and IR can't drift apart on\n * shape, same pattern as `meta` just above. Unlike `meta`, no\n * `.default({})`: IR's own `brand` field is a bare `.optional()` with no\n * default either (`undefined` means \"no brand\", not \"an empty brand\n * object\") — consumed by `Branding` (`src/render/branding.tsx`) for\n * the deck's logo image and corner position. */\n brand: BrandSchema.optional(),\n /**\n * Where the brand footer and logo appear — reused verbatim from the IR's\n * own `branding` field (`DeckBrandingSchema`, `../ir`) so the spec and IR\n * cannot drift. Optional, no default: omitted stays unset and assemble\n * does not write `\"cover-only\"` into the IR. The renderer treats that\n * as `\"cover-only\"`. Omitted by default. Write `\"full\"` only when every\n * content page needs the brand footer. `\"full\"` also paints confidentiality\n * and date on cover and ending meta rows. A silent menu entry can suppress\n * branding for its own page.\n */\n branding: DeckBrandingSchema.optional(),\n pages: z.array(PageSpecSchema),\n })\n .strict()\n\nexport type DeckSpec = z.infer<typeof DeckSpecSchema>\n\n/** JSON Schema for the deck spec — feed this to a model before it writes one (see `pptwise schema --spec`). */\nexport function specJsonSchema(): Record<string, unknown> {\n return z.toJSONSchema(DeckSpecSchema) as Record<string, unknown>\n}\n\n// ── result / issue types ────────────────────────────────────────────────\n\nexport interface SpecValidationIssue {\n path: string\n message: string\n /** The offending page's `id`, when the issue is scoped to one specific\n * page and that page's `id` could be determined — absent for deck-level\n * issues (e.g. \"pages non-empty\") and for structural issues on a page\n * whose own `id` itself failed to parse. */\n pageId?: string\n}\n\nexport interface SpecValidateResult {\n ok: boolean\n spec?: DeckSpec\n errors: SpecValidationIssue[]\n /** Editorial advisories never change `ok`. */\n warnings?: SpecValidationIssue[]\n /**\n * Same shape and channel as `ValidateResult.normalized` (`../validate-core.ts`)\n * — human-readable `path: alias → canonical`-style rewrite entries for every\n * deterministic pre-parse rewrite `validateSpec` applied before parsing.\n * Sources: `normalizeDeckRootAliases` (identity, no current root aliases) and\n * `normalizeNarrativeShape` (`../narrative`, T0b fix 2): a top-level\n * `narrative: {id: \"<preset>\"}` shape rewritten to the bare preset string.\n * Present only when at least one rewrite happened; informational, never\n * gates `ok` on its own.\n * `cli/commands.ts`'s `runSpecValidate` prints this through the same\n * `normalizedNote` helper `runValidate`/`runRender` already use for the\n * bare-IR path's own component field-alias notes.\n */\n normalized?: string[]\n}\n\nexport function formatSpecIssues(errors: SpecValidationIssue[]): string {\n return errors.map((e) => (e.pageId ? `page \"${e.pageId}\" — ${e.path}: ${e.message}` : `${e.path}: ${e.message}`)).join(\"\\n\")\n}\n\n/**\n * `\"invalid spec (N issue[s]):\\n<formatted issues>\"` — the exact\n * {@link PptwiseError} message both `runSpecValidate` (`src/cli/commands.ts`)\n * and {@link assembleDeck}'s (`./assemble.ts`) step 1 throw on a failed\n * {@link validateSpec} call. Extracted here instead of duplicated verbatim at\n * each call site so the two can't drift on wording — reuses\n * {@link formatSpecIssues} for the per-issue body.\n */\nexport function formatInvalidSpecError(errors: SpecValidationIssue[]): string {\n return `invalid spec (${errors.length} issue${errors.length === 1 ? \"\" : \"s\"}):\\n${formatSpecIssues(errors)}`\n}\n\n/**\n * Bound theme id on a validated spec. Theme is required. There is no\n * consulting fallback.\n */\nexport function resolveSpecThemeId(spec: DeckSpec): string {\n return spec.theme\n}\n\n// ── hard gate: pages non-empty ──────────────────────────────────────────\n\nfunction checkPagesNonEmpty(spec: DeckSpec): SpecValidationIssue[] {\n if (spec.pages.length > 0) return []\n return [{ path: \"pages\", message: \"spec has no pages — a spec needs at least a cover page and an ending page\" }]\n}\n\n// ── hard gate: boundary types ───────────────────────────────────────────\n\n/**\n * Structural boundary gate (spec §5): the deck must open on a cover page and\n * close on an ending page, and no interior page may claim either type —\n * cover/ending are reserved for the two boundary positions. `content` and\n * `chapter` are both legal interior types. Called only when `spec.pages` is non-empty\n * (see {@link validateSpec}) — on a single-page spec `first`/`last` are the\n * same page and both checks run against it independently, so a lone page\n * that is neither cover nor ending reports both violations.\n */\nfunction checkBoundaryTypes(spec: DeckSpec): SpecValidationIssue[] {\n const { pages } = spec\n const errors: SpecValidationIssue[] = []\n const first = pages[0]!\n const last = pages[pages.length - 1]!\n if (first.type !== \"cover\") {\n errors.push({\n path: \"pages.0.type\",\n pageId: first.id,\n message: `first page must be type \"cover\" (got \"${first.type}\") — a spec must open with a cover page`,\n })\n }\n if (last.type !== \"ending\") {\n errors.push({\n path: `pages.${pages.length - 1}.type`,\n pageId: last.id,\n message: `last page must be type \"ending\" (got \"${last.type}\") — a spec must close with an ending page`,\n })\n }\n for (let i = 1; i < pages.length - 1; i++) {\n const page = pages[i]!\n if (page.type === \"cover\" || page.type === \"ending\") {\n errors.push({\n path: `pages.${i}.type`,\n pageId: page.id,\n message: `page \"${page.id}\" is type \"${page.type}\", only allowed as the first (cover) or last (ending) page — use \"content\" or \"chapter\" for interior pages`,\n })\n }\n }\n return errors\n}\n\n// ── hard gate: page id required + unique ────────────────────────────────\n\n/**\n * Path-traversal-safety check (CWE-22 defense-in-depth, W5 whole-branch\n * review finding 1) on a spec-authored page id — this id becomes `slide.id`\n * at assemble time ({@link buildSlide} in `./assemble.ts`, step 5) and, from\n * there, a `pages/<id>.json` / `assets/<id><ext>` file name if the resulting\n * IR is ever disassembled again (`runDisassemble`'s page write and\n * `writeOneAsset`, both via `assertSafeFileSegment` in `../cli/deck-dir.ts`\n * — the actual write-time gates, and the only checks that matter for\n * *every* id regardless of provenance, since a hand-authored bare IR skips\n * this module entirely). Rejecting an unsafe id here too, at spec-validation\n * time, is pure defense-in-depth: it means a spec-authored id is already\n * safe by the time it could ever reach either sink.\n *\n * This module stays Node-free (`AGENTS.md`'s layout rule, this file's own\n * top comment), so the check is duplicated as plain string logic instead of\n * importing `assertSafeFileSegment` itself (which needs `node:path`'s\n * `resolve`/`relative`) — same \"duplicate a few lines rather than pull a\n * Node-touching module into this closure\" call {@link specHeadingLength}'s\n * own doc comment makes just above. A single trailing path segment can only\n * ever escape whatever directory it is joined under if it is itself\n * absolute, contains a `/`/`\\` separator, or is exactly `\"..\"` —\n * `assertSafeFileSegment`'s own doc comment walks through why those lexical\n * checks alone are already sufficient (it additionally cross-checks via\n * `resolve`/`relative` as belt-and-suspenders, not because the lexical\n * checks alone fall short). Keep in sync with `assertSafeFileSegment` if\n * either check's rules ever change.\n */\nfunction isUnsafePageId(id: string): boolean {\n return id.includes(\"/\") || id.includes(\"\\\\\") || id === \"..\"\n}\n\n/**\n * `id` is required at the schema level (`PageSpecSchema.id: z.string()`, no\n * `.optional()`) so a missing key is already a structural error by the time\n * this runs — what remains to check here is (a) an empty/whitespace-only\n * string, which the schema's plain `z.string()` lets through, (b) a value\n * unsafe to use as a page/asset file name ({@link isUnsafePageId}, W5\n * whole-branch review finding 1), and (c) cross-page uniqueness, which no\n * per-page schema can express. Kebab-case is suggested by spec §5 but never\n * enforced (\"kebab-case suggested, not required\") — neither (a) nor (b)\n * narrows that: spaces, underscores, and uppercase all stay legal, only path\n * separators and a bare `\"..\"` are not.\n */\nfunction checkPageIds(spec: DeckSpec): SpecValidationIssue[] {\n const errors: SpecValidationIssue[] = []\n const seen = new Map<string, number[]>()\n spec.pages.forEach((page, i) => {\n if (page.id.trim() === \"\") {\n errors.push({ path: `pages.${i}.id`, message: `page ${i + 1} has an empty id — every page needs a non-empty, unique id` })\n return\n }\n if (isUnsafePageId(page.id)) {\n errors.push({\n path: `pages.${i}.id`,\n pageId: page.id,\n message: `page id \"${page.id}\" is not a safe file name — ids used as page/asset file names must not contain path separators or \"..\"`,\n })\n return\n }\n const indices = seen.get(page.id)\n if (indices) indices.push(i)\n else seen.set(page.id, [i])\n })\n for (const [id, indices] of seen) {\n if (indices.length < 2) continue\n errors.push({\n path: \"pages\",\n pageId: id,\n message: `duplicate page id \"${id}\" used by ${indices.length} pages (positions ${indices.map((i) => i + 1).join(\", \")}) — page ids must be unique within a spec`,\n })\n }\n return errors\n}\n\n// ── hard gate: heading required + length ────────────────────────────────\n\n/**\n * `CAPACITY.headingMaxChars` (`svg/audit/capacity.ts`) — the exact same 48\n * numeric source `ir-quality.ts`'s long-heading warning reads (see that\n * constant's own derivation comment there). Not re-derived here.\n */\nconst HEADING_MAX_CHARS = CAPACITY.headingMaxChars\n\n/**\n * Character count for the heading-length gate — deliberately plain\n * `.length` (CJK characters count as 1 each), matching `ir-quality.ts`'s own\n * `charLen` helper's semantics exactly (that function's doc comment: \"Count\n * characters. CJK characters count as 1 each (same as .length)\") — not\n * `measureTextUnits`'s visual-width weighting, a different unit system used\n * elsewhere in this codebase for a different purpose (bullets budgets).\n * Duplicated rather than imported: `charLen` is a one-line function and\n * importing it would pull `ir-quality.ts`'s whole module graph\n * (`layout-selection.ts`, `svg-text-layout.ts`, ...) into this Node-free\n * package for a single line of logic. Keep in sync with `ir-quality.ts`'s\n * `charLen` if that one ever changes.\n */\nfunction specHeadingLength(heading: string): number {\n return heading.length\n}\n\nfunction checkHeadings(spec: DeckSpec): SpecValidationIssue[] {\n const errors: SpecValidationIssue[] = []\n spec.pages.forEach((page, i) => {\n if (page.heading.trim() === \"\") {\n errors.push({ path: `pages.${i}.heading`, pageId: page.id, message: `page \"${page.id}\" is missing a required heading` })\n return\n }\n const length = specHeadingLength(page.heading)\n if (length > HEADING_MAX_CHARS) {\n errors.push({\n path: `pages.${i}.heading`,\n pageId: page.id,\n message: `page \"${page.id}\" heading is ${length} characters, exceeds the ${HEADING_MAX_CHARS}-character limit — tighten it into a short, assertive phrase`,\n })\n }\n })\n return errors\n}\n\n// ── hard gate: theme resolution ─────────────────────────────────────────\n\n/**\n * Installed-theme check, same shape as `validateIr`'s own (`api.ts`) —\n * `theme` stays an open string at the schema layer (like IR's `theme.id`),\n * this hard gate is where an unknown id is actually rejected.\n */\nfunction checkTheme(spec: DeckSpec): SpecValidationIssue[] {\n const themeId = resolveSpecThemeId(spec)\n const installed = getInstalledThemeIds()\n if (installed.includes(themeId)) return []\n const message =\n themeId === \"bloom\"\n ? 'theme id \"bloom\" was removed — current format uses an installed theme id (see `pptwise themes`)'\n : `unknown theme \"${themeId}\" — available: ${installed.join(\", \")} (see \\`pptwise themes\\`)`\n return [{ path: \"theme\", message }]\n}\n\n// ── hard gate: focus vocabulary ─────────────────────────────────────────\n\n/**\n * Focus vocabulary gate: `focus` is optional authoring guidance pointing a\n * later fill step at a preferred content kind or component type. Face ids\n * and layout ids are not authoring vocabulary. When present it must be one\n * of {@link KIND_VALUES} or {@link COMPONENT_TYPES}.\n */\nfunction checkFocusVocabulary(spec: DeckSpec): SpecValidationIssue[] {\n const kinds: readonly string[] = KIND_VALUES\n const errors: SpecValidationIssue[] = []\n spec.pages.forEach((page, i) => {\n if (page.focus === undefined) return\n if (page.focus === \"logo_wall\") {\n errors.push({\n path: `pages.${i}.focus`,\n pageId: page.id,\n message: 'component type \"logo_wall\" is not in the current vocabulary — use \"image_grid\"',\n })\n return\n }\n if (page.focus === \"banner-heading\") {\n errors.push({\n path: `pages.${i}.focus`,\n pageId: page.id,\n message:\n '\"banner-heading\" is a face id, not authoring vocabulary — focus accepts a content kind or a component type',\n })\n return\n }\n if (kinds.includes(page.focus) || COMPONENT_TYPES.includes(page.focus)) {\n return\n }\n errors.push({\n path: `pages.${i}.focus`,\n pageId: page.id,\n message:\n `unknown focus \"${page.focus}\" — expected a content kind (${KIND_VALUES.join(\", \")}) ` +\n `or a component type (${COMPONENT_TYPES.join(\", \")})`,\n })\n })\n return errors\n}\n\n// ── theme-menu hard gate and kind-distribution advisory ─────────────────\n\nfunction checkThemeMenuKinds(spec: DeckSpec): SpecValidationIssue[] {\n const menu = getThemeDefinition(resolveSpecThemeId(spec)).menu\n if (menu === undefined) {\n return [\n {\n path: \"theme\",\n message: `theme \"${resolveSpecThemeId(spec)}\" has no menu`,\n },\n ]\n }\n\n const offered = offeredContentKinds(menu)\n return spec.pages.flatMap((page, index) => {\n if (page.type !== \"content\" || resolveLayoutId(\"content\", page.kind, menu) !== null) return []\n return [\n {\n path: `pages.${index}.kind`,\n pageId: page.id,\n message: `kind \"${page.kind}\" is not offered by theme \"${resolveSpecThemeId(\n spec,\n )}\". Available content kinds: ${offered.join(\", \")}`,\n },\n ]\n })\n}\n\n/** Report each maximal run of three or more identical content-page kinds. */\nfunction checkKindDistribution(spec: DeckSpec): SpecValidationIssue[] {\n const contentPages = spec.pages\n .map((page, index) => ({ page, index }))\n .filter(\n (\n entry,\n ): entry is {\n page: Extract<PageSpec, { type: \"content\" }>\n index: number\n } => entry.page.type === \"content\",\n )\n const warnings: SpecValidationIssue[] = []\n let start = 0\n while (start < contentPages.length) {\n let end = start + 1\n while (end < contentPages.length && contentPages[end]!.page.kind === contentPages[start]!.page.kind) end++\n if (end - start >= 3) {\n const run = contentPages.slice(start, end)\n warnings.push({\n path: \"pages\",\n pageId: run[0]!.page.id,\n message: `${run.length} consecutive content pages use kind \"${run[0]!.page.kind}\" (${run\n .map((entry) => entry.page.id)\n .join(\", \")}). Vary the explanation pattern where the argument allows it`,\n })\n }\n start = end\n }\n return warnings\n}\n\n// ── hard gate: page count vs pacing ─────────────────────────────────────\n\n/**\n * Deck-level page-count range per pacing (spec §5's pacing table,\n * initial values — \"dense 8-30 / balanced 6-24 / spacious 4-16\", not yet\n * tuned against real usage). Independent of `PACING_BUDGETS`\n * (`narrative/index.ts`, per-slide component-count/bullets editorial\n * budget) — this is a separate, deck-wide page-count concern the spec calls\n * out as its own hard gate (\"page count vs. pacing recommended range\").\n * Message wording renamed from \"delivery\" to \"pacing\" (vocabulary-v4\n * residual, routed from the task 1 review) — the axis itself was already\n * `Pacing` at the type level, this closes the last stale word in the\n * error text.\n */\nexport const SPEC_PAGE_COUNT_RANGE: Record<Pacing, { min: number; max: number }> = {\n dense: { min: 8, max: 30 },\n balanced: { min: 6, max: 24 },\n spacious: { min: 4, max: 16 },\n}\n\nfunction checkPageCount(spec: DeckSpec, pacing: Pacing): SpecValidationIssue[] {\n const { min, max } = SPEC_PAGE_COUNT_RANGE[pacing]\n const n = spec.pages.length\n if (n >= min && n <= max) return []\n return [\n {\n path: \"pages\",\n message: `spec has ${n} pages — \"${pacing}\" pacing expects ${min}-${max} pages, change pacing or add/remove pages`,\n },\n ]\n}\n\n// ── entry point ──────────────────────────────────────────────────────────\n\n/** Best-effort page id lookup straight off the *raw* (pre-parse) input, used\n * only for structural (zod) issues — the page that failed to parse may\n * still have a readable `id` sitting right next to whatever field failed. */\nfunction pageIdFromRawInput(input: unknown, index: number): string | undefined {\n if (typeof input !== \"object\" || input === null) return undefined\n const pages = (input as Record<string, unknown>).pages\n if (!Array.isArray(pages)) return undefined\n const page = pages[index] as unknown\n if (typeof page !== \"object\" || page === null) return undefined\n const id = (page as Record<string, unknown>).id\n return typeof id === \"string\" ? id : undefined\n}\n\n/**\n * Parse raw JSON, then run isolated hard-gate categories in order. A hard\n * failure short-circuits later categories. Kind distribution runs last as an\n * editorial advisory and never changes `ok`.\n *\n * Before the schema parse, {@link normalizeNarrativeShape} (`../narrative`,\n * T0b fix 2 scope extension) runs on the raw input, exactly mirroring\n * `validateIr`'s own pre-parse pass (`../validate-core.ts`) — a top-level\n * `narrative: {id: \"<preset>\"}` shape is rewritten to the bare preset\n * string before `DeckSpecSchema.safeParse` ever sees it, so the correction\n * lands in the returned `spec` itself (read again by\n * `checkFocusVocabulary`/`checkPageCount` below, and by `runSpecValidate`'s\n * own OK-summary line, `../cli/commands.ts`), not just this function's own\n * local `resolveNarrative` call below. Every return path is wrapped in\n * `withNormalized` so the rewrite note (`SpecValidateResult.normalized`)\n * surfaces on success *or* failure, success or failure alike — same\n * \"informational, never gates `ok`\" contract `ValidateResult.normalized`\n * has.\n */\nexport function validateSpec(input: unknown): SpecValidateResult {\n const rootAliasPass = normalizeDeckRootAliases(input)\n const narrativeShapePass = normalizeNarrativeShape(rootAliasPass.value)\n const normalizedInput = narrativeShapePass.value\n const normalized = [...rootAliasPass.normalized, ...narrativeShapePass.normalized]\n const withNormalized = (result: SpecValidateResult): SpecValidateResult =>\n normalized.length > 0 ? { ...result, normalized } : result\n\n const r = DeckSpecSchema.safeParse(normalizedInput)\n if (!r.success) {\n const errors = r.error.issues.map((issue) => {\n const path = issue.path.join(\".\")\n const m = /^pages\\.(\\d+)/.exec(path)\n return {\n path,\n message: themeIssueMessage(path, issue.message, issue.input, issue.code),\n pageId: m ? pageIdFromRawInput(normalizedInput, Number(m[1])) : undefined,\n }\n })\n return withNormalized({ ok: false, errors })\n }\n const spec = r.data\n\n const emptyErrors = checkPagesNonEmpty(spec)\n if (emptyErrors.length > 0) return withNormalized({ ok: false, errors: emptyErrors })\n\n const boundaryErrors = checkBoundaryTypes(spec)\n if (boundaryErrors.length > 0) return withNormalized({ ok: false, errors: boundaryErrors })\n\n const idErrors = checkPageIds(spec)\n if (idErrors.length > 0) return withNormalized({ ok: false, errors: idErrors })\n\n const headingErrors = checkHeadings(spec)\n if (headingErrors.length > 0) return withNormalized({ ok: false, errors: headingErrors })\n\n const themeErrors = checkTheme(spec)\n if (themeErrors.length > 0) return withNormalized({ ok: false, errors: themeErrors })\n\n const menuErrors = checkThemeMenuKinds(spec)\n if (menuErrors.length > 0) return withNormalized({ ok: false, errors: menuErrors })\n\n // Narrative resolution (spec §5's defaults chain), same open-schema/\n // closed-semantic split as validateIr's own (api.ts) — see that\n // function's comment for the full rationale. `spec.narrative`'s inferred\n // type is wider than `resolveNarrative`'s parameter — safe to narrow here\n // because `resolveNarrative` validates every key/value itself at runtime.\n // When the input was an `{id}` shape, `spec.narrative` here already reads\n // as the rescued bare string (`normalizeNarrativeShape` rewrote it before\n // the schema parse above) — this call just resolves that string like any\n // other preset id. `resolveNarrative`'s own entry additionally tolerates\n // the unrescued `{id}` shape directly too (see its doc comment), so this\n // line stays correct even if some future caller of `resolveNarrative`\n // reaches it without going through a normalizeNarrativeShape pass first.\n let resolvedAxes: NarrativeProfile\n try {\n resolvedAxes = resolveNarrative(spec.narrative as string | Partial<NarrativeProfile> | undefined)\n } catch (err) {\n if (!(err instanceof PptwiseError)) throw err\n return withNormalized({ ok: false, errors: [{ path: \"narrative\", message: err.message }] })\n }\n\n const focusErrors = checkFocusVocabulary(spec)\n if (focusErrors.length > 0) return withNormalized({ ok: false, errors: focusErrors })\n\n const pageCountErrors = checkPageCount(spec, resolvedAxes.pacing)\n if (pageCountErrors.length > 0) return withNormalized({ ok: false, errors: pageCountErrors })\n\n const warnings = checkKindDistribution(spec)\n return withNormalized({\n ok: true,\n spec,\n errors: [],\n ...(warnings.length > 0 ? { warnings } : {}),\n })\n}\n","/** Pure deck-project assembly and disassembly. Selection stays in render. */\nimport { PptwiseError } from \"../errors\"\nimport { PptxIRSchema, type BackgroundSpec, type Component, type PptxIR, type Slide } from \"../ir\"\nimport { formatInvalidSpecError, validateSpec, type DeckSpec, type PageSpec } from \"./index\"\n\n/** Fillable fields stored in one `pages/<id>.json` record. */\nexport interface PageContent {\n components?: Component[]\n background?: BackgroundSpec\n image_side?: \"left\" | \"right\"\n footnote?: string\n notes?: string\n}\n\nexport interface AssembleResult {\n ir: PptxIR\n}\n\nconst LOCKED_KEYS = [\"type\", \"kind\", \"heading\"] as const\n\nfunction buildSlide(page: PageSpec, content: PageContent | undefined): Record<string, unknown> {\n const locked = {\n id: page.id,\n type: page.type,\n heading: page.heading,\n ...(page.type === \"content\" ? { kind: page.kind } : {}),\n }\n if (content === undefined) {\n return {\n ...locked,\n placeholder: true,\n ...(page.summary !== undefined ? { subheading: page.summary } : {}),\n }\n }\n return {\n ...locked,\n ...(page.type !== \"content\" && page.summary !== undefined ? { subheading: page.summary } : {}),\n ...(content.components !== undefined ? { components: content.components } : {}),\n ...(content.background !== undefined ? { background: content.background } : {}),\n ...(content.image_side !== undefined ? { image_side: content.image_side } : {}),\n ...(content.footnote !== undefined ? { footnote: content.footnote } : {}),\n ...(content.notes !== undefined ? { notes: content.notes } : {}),\n }\n}\n\n/**\n * Assemble spec-owned semantics with page content. No layout, seed, beat, or\n * other selection result is derived or written into the IR.\n */\nexport function assembleDeck(spec: unknown, pages: Record<string, PageContent>): AssembleResult {\n const validated = validateSpec(spec)\n if (!validated.ok) throw new PptwiseError(formatInvalidSpecError(validated.errors))\n const deckSpec = validated.spec!\n\n for (const page of deckSpec.pages) {\n const raw = pages[page.id]\n if (raw === undefined) continue\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new PptwiseError(`page \"${page.id}\": page content must be an object`)\n }\n for (const key of LOCKED_KEYS) {\n if (Object.hasOwn(raw, key)) {\n throw new PptwiseError(`page \"${page.id}\": \"${key}\" is locked by the spec. Remove it from the page file`)\n }\n }\n }\n\n const specIds = new Set(deckSpec.pages.map((page) => page.id))\n const orphanIds = Object.keys(pages).filter((id) => !specIds.has(id))\n if (orphanIds.length > 0) {\n throw new PptwiseError(\n `orphan page id${orphanIds.length === 1 ? \"\" : \"s\"} ${orphanIds\n .map((id) => `\"${id}\"`)\n .join(\", \")}. Delete the page file or add the page to the spec`,\n )\n }\n\n const rawIr = {\n version: \"5\" as const,\n ...(deckSpec.narrative !== undefined ? { narrative: deckSpec.narrative } : {}),\n theme: { id: deckSpec.theme },\n ...(deckSpec.filename !== undefined ? { filename: deckSpec.filename } : {}),\n ...(deckSpec.brand !== undefined ? { brand: deckSpec.brand } : {}),\n ...(deckSpec.branding !== undefined ? { branding: deckSpec.branding } : {}),\n meta: deckSpec.meta,\n slides: deckSpec.pages.map((page) => buildSlide(page, pages[page.id])),\n }\n const parsed = PptxIRSchema.safeParse(rawIr)\n if (!parsed.success) {\n const detail = parsed.error.issues.map((issue) => `${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`).join(\"\\n\")\n throw new PptwiseError(`assembled deck did not produce valid IR:\\n${detail}`)\n }\n return { ir: parsed.data }\n}\n\nconst UNTITLED_HEADING = \"Untitled\"\n\n/** Reconstruct the editable spec and page records representable by an IR. */\nexport function disassembleDeck(ir: PptxIR): {\n spec: DeckSpec\n pages: Record<string, PageContent>\n} {\n const pages: Record<string, PageContent> = {}\n const pageSpecs: PageSpec[] = ir.slides.map((slide, index) => {\n const id = slide.id ?? `p-${index + 1}-${slide.type}`\n const heading = slide.heading !== undefined && slide.heading.trim() !== \"\" ? slide.heading : UNTITLED_HEADING\n const pageSpec = {\n id,\n type: slide.type,\n heading,\n ...(slide.type === \"content\" ? { kind: slide.kind } : {}),\n ...((slide.placeholder === true || slide.type !== \"content\") && slide.subheading !== undefined\n ? { summary: slide.subheading }\n : {}),\n } as PageSpec\n if (slide.placeholder !== true) pages[id] = extractPageContent(slide)\n return pageSpec\n })\n\n return {\n spec: {\n version: \"1\",\n ...(ir.narrative !== undefined ? { narrative: ir.narrative } : {}),\n theme: ir.theme.id,\n filename: ir.filename,\n ...(ir.brand !== undefined ? { brand: ir.brand } : {}),\n ...(ir.branding !== undefined ? { branding: ir.branding } : {}),\n meta: ir.meta,\n pages: pageSpecs,\n },\n pages,\n }\n}\n\nfunction extractPageContent(slide: Slide): PageContent {\n const content: PageContent = {}\n if (slide.components.length > 0) content.components = slide.components\n if (slide.background !== undefined) content.background = slide.background\n if (slide.image_side !== undefined) content.image_side = slide.image_side\n if (slide.footnote !== undefined) content.footnote = slide.footnote\n if (slide.notes !== undefined) content.notes = slide.notes\n return content\n}\n","import type { Component, PptxIR, Slide } from \"@/ir\"\nimport { renderSlideSvg } from \"../api\"\nimport { getPlatform } from \"../platform/registry\"\nimport { resolveStyle } from \"../themes\"\nimport { CANONICAL_THEME_IDS, THEME_LABELS, type CanonicalThemeId } from \"../themes/index\"\nimport { getThemeDefinition, type ThemeDefinition } from \"../themes/definitions\"\nimport type { StyleColors } from \"../themes/tokens\"\nimport { parseTransform } from \"../audit/svg-audit\"\n\ntype ImageComponent = Extract<Component, { type: \"image\" }>\n\n/**\n * Real rendered frame for one image slot, in canvas px (0-1280 × 0-720 —\n * `../constants`'s `CANVAS_W_PX`/`CANVAS_H_PX`). This is the whole point of\n * this module (asset-brief plan, 裁定 1): it comes from parsing an actual\n * render pass's SVG output, never from hand-copying `image.tsx`'s\n * `w * 0.5`/`MAX_IMAGE_H` constants — those change independently of this\n * file, and a shadow copy would silently start lying the day they do.\n */\nexport interface AssetBriefFrame {\n x: number\n y: number\n w: number\n h: number\n /** e.g. \"2:1\" when w:h reduces to a small clean ratio, else \"1.63:1\". */\n aspect: string\n}\n\nexport interface AssetBriefFit {\n mode: \"cover\" | \"contain\"\n /** One sentence: crop behavior + safe-zone guidance for a generator. */\n note: string\n}\n\nexport interface AssetBriefPalette {\n /** Deduplicated theme hex values, in `StyleColors`' own field order. */\n hexes: string[]\n primary: string\n accent: string\n}\n\nexport interface AssetBriefMood {\n /** `ThemeDefinition.tags` passed through as-is (empty for every one of\n * the 13 builtins today — none has adopted this field yet — but a\n * registered custom theme may set it). */\n tags: readonly string[]\n /** One sentence assembled from the theme's own label/motif — never a\n * hand-written per-theme paragraph (asset-brief plan 裁定: \"不新写 13 段\n * 文案\"). */\n description: string\n}\n\nexport interface AssetBriefPage {\n index: number\n id?: string\n type: Slide[\"type\"]\n heading?: string\n}\n\nexport interface AssetBriefItem {\n page: AssetBriefPage\n asset_id: string\n /** `ir.assets.images[asset_id].alt` passed straight through (A11Y-01 alt\n * chain wave, task 1) — this is already a per-asset structure, so a\n * generator agent filling in `missing`/`suggested_prompt` items sees\n * right alongside them which ids already have an accessibility\n * description written and which don't. Omitted (not an empty string)\n * when the asset has no `alt` — same \"don't fabricate a value that\n * isn't there\" discipline the rest of this file already follows for\n * `frame`/`suggested_pixels`. */\n alt?: string\n /** Discriminant reserved for v2 (asset-brief plan 裁定 4): only `\"image\"`\n * is produced today (one `AssetBriefItem` per `image` component, or per\n * shared frame — see `shared` below). `\"background\"` is the documented\n * extension slot for background asset specs, deliberately not built in\n * v1 (no `background` geometry extraction exists yet) — this field lets\n * a future v2 add it without a breaking shape change for consumers that\n * already switch on `kind`. */\n kind: \"image\"\n /** No usable `src` in `ir.assets.images` for this `asset_id` — this is a\n * generation to-do item, not a defect (asset-brief plan 裁定 2). */\n missing: boolean\n /** `false` when the selected layout dropped this image component\n * entirely (e.g. an overflow guard) — still listed rather than silently\n * dropped (task brief's explicit requirement). `frame`/`suggested_pixels`\n * are omitted in that case; there is no real geometry to report. */\n rendered: boolean\n frame?: AssetBriefFrame\n /** 2× the frame's own pixel dimensions — omitted alongside `frame` when `rendered` is false. */\n suggested_pixels?: { w: number; h: number }\n fit: AssetBriefFit\n palette: AssetBriefPalette\n mood: AssetBriefMood\n /** One English paragraph, paste-ready for an image-generation tool. */\n suggested_prompt: string\n /** True when this item's `frame` cannot be attributed to one specific\n * `image` component: `occurrenceCount` (>=2) `image` components on this\n * same page share this `asset_id`. Because a shared `asset_id` resolves\n * to the exact same dummy href in the render-only pass, every occurrence\n * produces an *identical* `<image>` `href` in the rendered SVG — there is\n * no signal left to tell which `<image>` element came from which\n * component, in principle, not just in this implementation. Rather than\n * guess (see `buildAssetBrief`'s own doc comment for the bug this\n * replaced), every real rendered frame for the shared `asset_id` gets its\n * own item, all flagged `shared: true`, none claiming a specific\n * component. Omitted (not `false`) for the single-occurrence case — the\n * overwhelmingly common one — so that path's output shape is byte-for-\n * byte what it was before this field existed. */\n shared?: true\n /** Present iff `shared` is true: how many `image` components on this page\n * reference this same `asset_id`. */\n occurrenceCount?: number\n}\n\nexport interface AssetBrief {\n theme: string\n items: AssetBriefItem[]\n}\n\n/**\n * A 1×1 transparent PNG, reused as the dummy asset every referenced\n * `asset_id` gets overridden to for the render-only pass below (asset-brief\n * plan 裁定 2). Its own pixel content is never inspected — only the `<image>`\n * geometry the renderer places it into matters — so one shared constant\n * asset is enough regardless of how many image slots a deck has.\n */\nconst DUMMY_PNG_DATA_URI =\n \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=\"\n\ninterface RawImageFrame {\n x: number\n y: number\n w: number\n h: number\n preserveAspectRatio: string\n}\n\n/**\n * Extract every `<image>` element's *absolute* canvas geometry out of one\n * rendered slide's SVG markup, keyed by the `#<asset_id>` fragment the\n * render-only override below appends to every dummy `href` (so this never\n * needs to guess which `<image>` belongs to which component — see\n * `buildAssetBrief`'s own comment on the override for why the fragment\n * always survives verbatim into the output).\n *\n * Parses via `DOMParser` behind the platform seam (`getPlatform().domParser\n * ?? globalThis.DOMParser`) — the exact same seam and the exact same\n * `parseTransform` accumulation `../audit/svg-audit.ts`'s `auditSvgMarkup`\n * already uses to walk a rendered slide's real transform stack. Iron rule 3\n * (`src/index.ts`'s dependency closure must stay Node-free) is why this\n * isn't a `linkedom` import: the platform seam is what keeps this file\n * usable in a browser (native `DOMParser`) and in Node (`installNodePlatform()`\n * registers linkedom's) alike, without this module ever importing either\n * directly. A hand-rolled regex tokenizer could also track `<g transform>`\n * nesting well enough for this renderer's own output shapes, but would be a\n * second, bespoke implementation of exactly what `parseTransform` already\n * does correctly for every existing consumer — reusing it is less code and\n * cannot drift from the audit path's own geometry truth.\n */\nfunction extractImageFrames(markup: string): Map<string, RawImageFrame[]> {\n const Parser = getPlatform().domParser ?? globalThis.DOMParser\n if (!Parser) {\n throw new Error(\n 'DOMParser unavailable — in Node, call installNodePlatform() from \"@liustack/pptwise/node\" first (the pptwise CLI does this automatically)',\n )\n }\n const doc = new Parser().parseFromString(markup, \"image/svg+xml\")\n const byAssetId = new Map<string, RawImageFrame[]>()\n\n const visit = (el: Element, ox: number, oy: number, os: number) => {\n const { dx, dy, scale } = parseTransform(el)\n const ax = ox + os * dx\n const ay = oy + os * dy\n const as = os * scale\n if (el.tagName.toLowerCase() === \"image\") {\n const href = el.getAttribute(\"href\") ?? \"\"\n const hashAt = href.indexOf(\"#\")\n if (hashAt !== -1) {\n const assetId = href.slice(hashAt + 1)\n const frame: RawImageFrame = {\n x: ax + as * Number(el.getAttribute(\"x\") ?? 0),\n y: ay + as * Number(el.getAttribute(\"y\") ?? 0),\n w: as * Number(el.getAttribute(\"width\") ?? 0),\n h: as * Number(el.getAttribute(\"height\") ?? 0),\n preserveAspectRatio: el.getAttribute(\"preserveAspectRatio\") ?? \"xMidYMid meet\",\n }\n const list = byAssetId.get(assetId)\n if (list) list.push(frame)\n else byAssetId.set(assetId, [frame])\n }\n }\n for (const child of Array.from(el.children)) visit(child, ax, ay, as)\n }\n visit(doc.documentElement, 0, 0, 1)\n return byAssetId\n}\n\nfunction gcd(a: number, b: number): number {\n let x = Math.abs(Math.round(a))\n let y = Math.abs(Math.round(b))\n while (y !== 0) [x, y] = [y, x % y]\n return x || 1\n}\n\n/**\n * \"2:1\" when `w/h` lands within 1% of a small `n:d` ratio (`d` up to 12 —\n * covers every whole/half/thirds ratio this renderer's fixed slot math\n * produces, e.g. 613×307's 1.9967 ≈ 2/1), else a 2-decimal \"1.63:1\" fallback.\n * `w`/`h` are already-rounded canvas px, so false near-misses from float\n * noise (not real ratio ambiguity) are the only thing the tolerance guards\n * against.\n */\nfunction formatAspect(w: number, h: number): string {\n if (h <= 0 || w <= 0) return `${w}:${h}`\n const ratio = w / h\n const TOL = 0.01\n for (let d = 1; d <= 12; d++) {\n const n = Math.round(ratio * d)\n if (n >= 1 && Math.abs(ratio - n / d) <= TOL) {\n const g = gcd(n, d)\n return `${n / g}:${d / g}`\n }\n }\n return `${ratio.toFixed(2)}:1`\n}\n\nfunction toFrame(raw: RawImageFrame): AssetBriefFrame {\n const w = Math.round(raw.w)\n const h = Math.round(raw.h)\n return { x: Math.round(raw.x), y: Math.round(raw.y), w, h, aspect: formatAspect(w, h) }\n}\n\nfunction buildFit(mode: ImageComponent[\"fit\"], aspect: string | undefined): AssetBriefFit {\n if (mode === \"cover\") {\n return {\n mode,\n note: aspect\n ? `Cover crop (xMidYMid slice): generate close to ${aspect} to avoid any crop — otherwise the renderer center-crops to fill the frame, so keep essential subject matter within the center safe zone.`\n : `Cover crop (xMidYMid slice): the renderer center-crops to fill the frame — keep essential subject matter centered.`,\n }\n }\n return {\n mode,\n note: aspect\n ? `Contain fit (xMidYMid meet): the renderer letterboxes to show the whole image inside the frame with no crop — match ${aspect} to avoid empty margins.`\n : `Contain fit (xMidYMid meet): the renderer letterboxes to show the whole image inside the frame with no crop.`,\n }\n}\n\n/**\n * Dedupe theme identity colors into a flat hex list, in `StyleColors`' own\n * field order — deliberately excludes `chartPalette`/`accentPool` (data-viz\n * and multi-accent-cycling pools respectively, not the theme's core\n * identity a photo brief should pull from).\n */\nfunction buildPalette(colors: StyleColors): AssetBriefPalette {\n const candidates = [colors.bg, colors.surface, colors.panel, colors.primary, colors.accent, colors.text, colors.muted, colors.border]\n const hexes = [...new Set(candidates.filter((c): c is string => Boolean(c)))]\n return { hexes, primary: colors.primary, accent: colors.accent }\n}\n\nfunction themeLabel(id: string): string {\n return (CANONICAL_THEME_IDS as readonly string[]).includes(id) ? THEME_LABELS[id as CanonicalThemeId] : id\n}\n\nfunction humanizeMotif(motif: string): string {\n return motif.replace(/-motif$/, \"\").replace(/-/g, \" \")\n}\n\n/**\n * One assembled sentence from the theme's own already-declared label/tags/\n * motif — never a 13-theme hand-written paragraph set (asset-brief plan's\n * own explicit constraint). Every one of the 13 builtins has an empty\n * `tags` array today (none has adopted the field yet), so `tagPhrase` is a\n * no-op for all of them right now and only starts contributing once a theme\n * (builtin or registered) actually sets one.\n */\nfunction buildMood(themeId: string, themeDef: ThemeDefinition): AssetBriefMood {\n const label = themeLabel(themeId)\n const tagPhrase = themeDef.tags.length > 0 ? ` (${themeDef.tags.join(\", \")})` : \"\"\n const motifPhrase = themeDef.motif ? ` with a ${humanizeMotif(themeDef.motif)} decorative motif` : \"\"\n return { tags: themeDef.tags, description: `${label} theme${tagPhrase}${motifPhrase}.` }\n}\n\nfunction buildPrompt(mood: AssetBriefMood, palette: AssetBriefPalette, frame: AssetBriefFrame | undefined, fit: AssetBriefFit): string {\n const supporting = palette.hexes.filter((h) => h !== palette.primary && h !== palette.accent)\n const paletteText = `Color palette: primary ${palette.primary}, accent ${palette.accent}${supporting.length > 0 ? `, supporting tones ${supporting.join(\", \")}` : \"\"}.`\n const compositionText = frame\n ? `Compose for a ${frame.aspect} frame. ${fit.note}`\n : `Frame geometry unavailable — this image slot was not rendered under the deck's currently selected layout; generate at a versatile aspect ratio and verify placement once a layout renders it.`\n return `${mood.description} ${paletteText} ${compositionText}`\n}\n\n/**\n * Build an image-generation brief for every `image` component in a deck: the\n * real rendered frame (from an actual render pass — see `extractImageFrames`),\n * fit/crop mode with a safe-zone note, suggested generation pixels (2× the\n * frame), the resolved theme's palette/mood, and a paste-ready English\n * `suggested_prompt`. Pure function of `ir` (same input → same output, proven\n * by `asset-brief.test.ts`'s double-call check) — like `auditDeck`, it never\n * mutates its argument and never touches the filesystem or network.\n *\n * v1 scope (asset-brief plan 裁定 4): only `image`-typed components — not\n * `image_grid`/`image_compare` (separate component types with their own\n * asset_id arrays), and not `background` asset specs. Both are natural v2\n * extensions of this same shape and are deliberately left off `AssetBrief`\n * rather than bolted on half-done.\n *\n * Missing-asset handling (裁定 2): every `image` component's `asset_id`,\n * present or not, gets overridden to the same dummy 1×1 PNG (tagged with a\n * `#<asset_id>` fragment) in an in-memory copy of `ir` used *only* for this\n * function's own extraction render pass — `ir` itself, and the real render/\n * export path, never see this override. `missing` below is decided from the\n * real `ir.assets.images` (before the override), so it still accurately\n * reports \"nothing usable was ever supplied here\" even though the\n * extraction render always has *something* to draw. Overriding every\n * asset_id uniformly (not just the missing ones) is what makes the fragment\n * a reliable identifier regardless of whether the real asset was already\n * resolved — the alternative (leaving resolved assets' real `src` alone)\n * would have no way to tell two different real images' hrefs apart when a\n * page has more than one.\n *\n * Known limitation: an `asset_id` shared between an `image` component and a\n * `background` asset spec on the same page would get the same dummy\n * fragment on both, which could ambiguate which rendered `<image>` this\n * function attributes to the component — rare in practice (v1 doesn't cover\n * background geometry at all, see above) and left as a documented gap\n * rather than engineered around.\n *\n * Shared-`asset_id` handling (task reviewer finding, fixed after v1's first\n * pass): the IR schema places no uniqueness constraint on `asset_id`\n * (`src/ir/index.ts`) — two different `image` components on the same page\n * legally reference the same one. Because the override above (deliberately)\n * maps a shared `asset_id` to one shared dummy href, both occurrences render\n * an *identical* `<image href>` — the rendered SVG carries no signal left to\n * tell them apart. (v1's first pass tried anyway: a FIFO queue keyed by\n * `asset_id`, drained in `slide.components` order under the assumption that\n * extraction order would match — it doesn't in general, e.g.\n * `content-image-lead-split.tsx` renders its narrow-column body *before*\n * its visual-lead column in the JSX it returns, so the queue was backwards\n * and every frame ended up attributed to the wrong occurrence. Not a fixable\n * ordering bug: no ordering convention could be relied on across every\n * layout's own JSX emission order, present and future.) The honest fix:\n * per page, group `image` components by `asset_id` first. A group of size 1\n * (the overwhelmingly common case) keeps the exact single-item shape this\n * function always produced. A group of size >1 abandons per-component\n * attribution entirely — it emits one item per *real rendered frame* for\n * that `asset_id` (not per component), each flagged `shared: true` with the\n * group's `occurrenceCount`, so every real frame is still reported (nothing\n * silently dropped) without asserting a specific component<->frame pairing\n * that cannot be known. If fewer frames render than there are occurrences\n * (e.g. the layout dropped one), the shortfall is padded with `rendered:\n * false` shared items so the count of components sharing the id is still\n * fully visible to a reader of the brief.\n */\nexport function buildAssetBrief(ir: PptxIR): AssetBrief {\n const themeDef = getThemeDefinition(ir.theme.id)\n const tokens = resolveStyle(ir.theme.id)\n const palette = buildPalette(tokens.colors)\n const mood = buildMood(ir.theme.id, themeDef)\n\n const occurrences: { slideIndex: number; component: ImageComponent }[] = []\n ir.slides.forEach((slide, slideIndex) => {\n for (const component of slide.components) {\n if (component.type === \"image\") occurrences.push({ slideIndex, component })\n }\n })\n\n const overrides: PptxIR[\"assets\"][\"images\"] = {}\n for (const { component } of occurrences) {\n overrides[component.asset_id] = { src: `${DUMMY_PNG_DATA_URI}#${component.asset_id}` }\n }\n const renderIr: PptxIR = { ...ir, assets: { images: { ...ir.assets.images, ...overrides } } }\n\n const framesBySlide = new Map<number, Map<string, RawImageFrame[]>>()\n for (const slideIndex of new Set(occurrences.map((o) => o.slideIndex))) {\n framesBySlide.set(slideIndex, extractImageFrames(renderSlideSvg(renderIr, slideIndex)))\n }\n\n const items: AssetBriefItem[] = []\n ir.slides.forEach((slide, slideIndex) => {\n // Group this page's `image` components by `asset_id`, preserving each\n // id's first-encounter order — same overall item ordering the old flat\n // `occurrences.map` produced for the (overwhelmingly common)\n // one-component-per-asset_id case.\n const groups = new Map<string, ImageComponent[]>()\n for (const component of slide.components) {\n if (component.type !== \"image\") continue\n const list = groups.get(component.asset_id)\n if (list) list.push(component)\n else groups.set(component.asset_id, [component])\n }\n if (groups.size === 0) return\n\n const page: AssetBriefPage = { index: slideIndex, id: slide.id, type: slide.type, heading: slide.heading }\n const frameMap = framesBySlide.get(slideIndex)\n const isMissing = (assetId: string) => !ir.assets.images[assetId]?.src\n const altOf = (assetId: string) => ir.assets.images[assetId]?.alt\n\n for (const [assetId, group] of groups) {\n const frames = frameMap?.get(assetId) ?? []\n\n if (group.length === 1) {\n // Unchanged single-occurrence path — exact same output as before\n // this fix (task requirement).\n const raw = frames[0]\n const frame = raw ? toFrame(raw) : undefined\n const fit = buildFit(group[0]!.fit, frame?.aspect)\n items.push({\n page,\n asset_id: assetId,\n alt: altOf(assetId),\n kind: \"image\",\n missing: isMissing(assetId),\n rendered: frame !== undefined,\n frame,\n suggested_pixels: frame ? { w: frame.w * 2, h: frame.h * 2 } : undefined,\n fit,\n palette,\n mood,\n suggested_prompt: buildPrompt(mood, palette, frame, fit),\n })\n continue\n }\n\n // Shared asset_id, >1 occurrence on this page — see this function's\n // own doc comment (\"Shared-asset_id handling\") for why per-component\n // attribution is skipped rather than guessed. `fit` mode is read off\n // the group's first component; if occurrences disagree on `fit` that\n // choice is a documented simplification, not a claim about which\n // occurrence any one frame belongs to.\n const occurrenceCount = group.length\n const sharedFit = group[0]!.fit\n const missing = isMissing(assetId)\n for (const raw of frames) {\n const frame = toFrame(raw)\n const fit = buildFit(sharedFit, frame.aspect)\n items.push({\n page,\n asset_id: assetId,\n alt: altOf(assetId),\n kind: \"image\",\n missing,\n rendered: true,\n frame,\n suggested_pixels: { w: frame.w * 2, h: frame.h * 2 },\n fit,\n palette,\n mood,\n shared: true,\n occurrenceCount,\n suggested_prompt: buildPrompt(mood, palette, frame, fit),\n })\n }\n // Fewer real frames than occurrences: pad with `rendered: false`\n // shared items so the full occurrenceCount stays visible rather than\n // silently under-reporting how many components reference this id.\n for (let i = frames.length; i < occurrenceCount; i++) {\n const fit = buildFit(sharedFit, undefined)\n items.push({\n page,\n asset_id: assetId,\n alt: altOf(assetId),\n kind: \"image\",\n missing,\n rendered: false,\n fit,\n palette,\n mood,\n shared: true,\n occurrenceCount,\n suggested_prompt: buildPrompt(mood, palette, undefined, fit),\n })\n }\n }\n })\n\n return { theme: ir.theme.id, items }\n}\n"],"mappings":"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","names":[]}