stunning-md 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stunning-md/parse.ts","../src/stunning-md/analyze/text.ts","../src/stunning-md/analyze/tables.ts","../src/stunning-md/analyze/plan.ts","../src/stunning-md/theme/themes.ts","../src/stunning-md/classifier.ts"],"sourcesContent":["import type { Image, Paragraph, PhrasingContent, Root, RootContent } from \"mdast\"\nimport remarkFrontmatter from \"remark-frontmatter\"\nimport remarkGfm from \"remark-gfm\"\nimport remarkMath from \"remark-math\"\nimport remarkParse from \"remark-parse\"\nimport { unified } from \"unified\"\nimport { parse as parseYaml } from \"yaml\"\n\nexport type Frontmatter = Record<string, unknown>\n\nexport type ParsedMarkdown = {\n root: Root\n frontmatter: Frontmatter\n}\n\nconst processor = unified().use(remarkParse).use(remarkGfm).use(remarkMath).use(remarkFrontmatter, [\"yaml\"])\n\nconst attr = (tag: string, name: string) =>\n new RegExp(`\\\\b${name}\\\\s*=\\\\s*(\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`, \"i\").exec(tag)?.slice(2).find((v) => v != null)\n\nconst decodeEntities = (text: string) =>\n text\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n\n/**\n * Raw HTML is never injected. The useful parts are recovered as markdown nodes —\n * images, line breaks and text — and everything else is dropped.\n */\nfunction htmlToPhrasing(html: string): PhrasingContent[] {\n const out: PhrasingContent[] = []\n const withoutComments = html.replace(/<!--[\\s\\S]*?-->/g, \"\").replace(/<(script|style)[\\s\\S]*?<\\/\\1>/gi, \"\")\n const tokens = withoutComments.split(/(<[^>]+>)/g)\n let href: string | undefined\n for (const token of tokens) {\n if (!token) continue\n if (token.startsWith(\"<\")) {\n if (/^<img\\b/i.test(token)) {\n const src = attr(token, \"src\")\n if (!src) continue\n const image: Image = { type: \"image\", url: src, alt: attr(token, \"alt\") ?? \"\", title: attr(token, \"title\") ?? null }\n out.push(href ? { type: \"link\", url: href, children: [image] } : image)\n } else if (/^<br\\b/i.test(token)) {\n out.push({ type: \"break\" })\n } else if (/^<a\\b/i.test(token)) {\n href = attr(token, \"href\")\n } else if (/^<\\/a/i.test(token)) {\n href = undefined\n }\n continue\n }\n const text = decodeEntities(token).replace(/\\s+/g, \" \")\n if (!text.trim()) continue\n out.push(href ? { type: \"link\", url: href, children: [{ type: \"text\", value: text }] } : { type: \"text\", value: text })\n }\n return out\n}\n\ntype Definitions = Map<string, { url: string; title?: string | null }>\n\nfunction collectDefinitions(nodes: RootContent[], into: Definitions = new Map()): Definitions {\n for (const node of nodes) {\n if (node.type === \"definition\") into.set(node.identifier, { url: node.url, title: node.title })\n else if (\"children\" in node) collectDefinitions(node.children as RootContent[], into)\n }\n return into\n}\n\nfunction cleanPhrasing(children: PhrasingContent[], defs: Definitions): PhrasingContent[] {\n return children.flatMap((child, index): PhrasingContent[] => {\n // \"$60M and $64M\" is money, not maths: as in Pandoc, the dollars must hug the\n // formula and the closing one must not run into a digit.\n if (child.type === \"inlineMath\") {\n const next = children[index + 1]\n const digitAfter = next?.type === \"text\" && /^\\d/.test(next.value)\n if (/^\\s|\\s$/.test(child.value) || digitAfter) return [{ type: \"text\", value: `$${child.value}$` }]\n }\n if (child.type === \"html\") return htmlToPhrasing(child.value)\n // Reference-style links and images are resolved so renderers only meet the direct forms.\n if (child.type === \"imageReference\") {\n const def = defs.get(child.identifier)\n return def ? [{ type: \"image\", url: def.url, alt: child.alt ?? \"\", title: def.title }] : []\n }\n if (child.type === \"linkReference\") {\n const def = defs.get(child.identifier)\n const inner = cleanPhrasing(child.children, defs)\n return def ? [{ type: \"link\", url: def.url, title: def.title, children: inner } as PhrasingContent] : inner\n }\n if (\"children\" in child) {\n return [{ ...child, children: cleanPhrasing(child.children as PhrasingContent[], defs) } as PhrasingContent]\n }\n return [child]\n })\n}\n\nfunction cleanBlocks(nodes: RootContent[], defs: Definitions): RootContent[] {\n return nodes.flatMap((node): RootContent[] => {\n if (node.type === \"html\") {\n // An HTML heading (`<h1 align=\"center\">`) keeps its role.\n const heading = /^\\s*<h([1-6])\\b[^>]*>([\\s\\S]*?)<\\/h\\1>\\s*$/i.exec(node.value)\n if (heading) {\n const children = htmlToPhrasing(heading[2])\n return children.length ? [{ type: \"heading\", depth: Number(heading[1]) as 1, children }] : []\n }\n const children = htmlToPhrasing(node.value)\n return children.length ? [{ type: \"paragraph\", children } satisfies Paragraph] : []\n }\n if (node.type === \"yaml\" || node.type === \"definition\") return []\n if (node.type === \"paragraph\" || node.type === \"heading\") {\n const children = cleanPhrasing(node.children, defs)\n return children.length ? [{ ...node, children }] : []\n }\n if (node.type === \"tableCell\") return [{ ...node, children: cleanPhrasing(node.children, defs) }]\n if (\"children\" in node) {\n return [{ ...node, children: cleanBlocks(node.children as RootContent[], defs) } as RootContent]\n }\n return [node]\n })\n}\n\nexport function parseMarkdown(markdown: string): ParsedMarkdown {\n const tree = processor.parse(markdown) as Root\n let frontmatter: Frontmatter = {}\n const first = tree.children[0]\n if (first?.type === \"yaml\") {\n try {\n const data = parseYaml(first.value)\n if (data && typeof data === \"object\" && !Array.isArray(data)) frontmatter = data as Frontmatter\n } catch {\n // Malformed frontmatter is ignored rather than failing the whole document.\n }\n }\n return { root: { ...tree, children: cleanBlocks(tree.children, collectDefinitions(tree.children)) }, frontmatter }\n}\n","import type { Image, Nodes, PhrasingContent } from \"mdast\"\n\n/** Plain text of any markdown node. Image alt text is not counted as prose. */\nexport function toText(node: Nodes | Nodes[] | undefined): string {\n if (!node) return \"\"\n if (Array.isArray(node)) return node.map(toText).join(\"\")\n if (node.type === \"text\" || node.type === \"inlineCode\" || node.type === \"code\") return node.value\n if (node.type === \"break\") return \" \"\n if (node.type === \"image\") return \"\"\n if (\"children\" in node) {\n const separator = node.type === \"paragraph\" || node.type === \"heading\" || node.type === \"listItem\" ? \" \" : \"\"\n return (node.children as Nodes[]).map(toText).join(separator)\n }\n return \"\"\n}\n\n/** Running prose only — no headings, code, tables or maths. This is what a reader would call \"the text\". */\nexport function proseOf(node: Nodes): string {\n if (node.type === \"heading\" || node.type === \"code\" || node.type === \"table\" || node.type === \"math\" || node.type === \"yaml\") return \"\"\n if (node.type === \"paragraph\") return toText(node)\n if (\"children\" in node) return (node.children as Nodes[]).map(proseOf).filter(Boolean).join(\" \")\n return \"\"\n}\n\nexport function countWords(text: string): number {\n const trimmed = text.trim()\n return trimmed ? trimmed.split(/\\s+/).length : 0\n}\n\nexport function wordsIn(node: Nodes | Nodes[] | undefined): number {\n if (!node) return 0\n if (Array.isArray(node)) return node.reduce((sum, child) => sum + wordsIn(child), 0)\n if (node.type === \"code\") return 0\n if (\"children\" in node && node.type !== \"paragraph\" && node.type !== \"heading\") {\n return (node.children as Nodes[]).reduce((sum, child) => sum + wordsIn(child), 0)\n }\n return countWords(toText(node))\n}\n\nexport function slugify(text: string, taken: Set<string>): string {\n const base =\n text\n .toLowerCase()\n .normalize(\"NFKD\")\n .replace(/[̀-ͯ]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"section\"\n let slug = base\n for (let n = 2; taken.has(slug); n++) slug = `${base}-${n}`\n taken.add(slug)\n return slug\n}\n\n/** The images a paragraph consists of, if it holds nothing but images (optionally linked). */\nexport function imagesOnly(children: PhrasingContent[]): { image: Image; href?: string }[] | null {\n const found: { image: Image; href?: string }[] = []\n for (const child of children) {\n if (child.type === \"image\") found.push({ image: child })\n else if (child.type === \"link\" && child.children.length === 1 && child.children[0].type === \"image\") {\n found.push({ image: child.children[0], href: child.url })\n } else if (child.type === \"break\" || (child.type === \"text\" && !child.value.trim())) continue\n else return null\n }\n return found.length ? found : null\n}\n","import type { PhrasingContent, Table } from \"mdast\"\nimport type { ChartSpec, ColumnType, TableColumn, TableModel, TableRow, VizKind, VizPlan } from \"../types\"\nimport { toText } from \"./text\"\n\ntype Unit = { prefix: string; suffix: string }\ntype ParsedNumber = { value: number } & Unit\n\nconst MISSING = /^(|-|–|—|n\\/?a|na|null|none|tbd|\\?)$/i\nconst CURRENCY = \"[$€£¥₹₩₽]|[A-Z]{1,3}\\\\$|(?:USD|EUR|GBP|SGD|JPY|CHF|AUD|CAD|INR|CNY)\\\\b\"\nconst NUMBER = new RegExp(\n `^([(]?)\\\\s*([-−–+]?)\\\\s*(${CURRENCY})?\\\\s*([-−–+]?)\\\\s*(\\\\d[\\\\d,' ]*(?:\\\\.\\\\d+)?|\\\\.\\\\d+)\\\\s*` +\n `(k|K|M|B|T|bn|mn|m|thousand|million|billion|trillion)?\\\\s*(%|‰|[a-zA-Z°µ²³/]{1,8})?\\\\s*([)]?)$`,\n)\nconst MAGNITUDE: Record<string, number> = {\n k: 1e3, K: 1e3, thousand: 1e3,\n M: 1e6, m: 1e6, mn: 1e6, million: 1e6,\n B: 1e9, bn: 1e9, billion: 1e9,\n T: 1e12, trillion: 1e12,\n}\n\nexport function parseNumber(raw: string): ParsedNumber | null {\n const text = raw.trim()\n const match = NUMBER.exec(text)\n if (!match) return null\n const [, open, signA, currency = \"\", signB, digits, magnitude, suffixRaw = \"\", close] = match\n let suffix = suffixRaw\n let scale = 1\n if (magnitude) {\n // A bare lowercase \"m\" is metres unless money is involved (\"$5m\").\n if (magnitude === \"m\" && !currency) suffix = `m${suffix}`\n else scale = MAGNITUDE[magnitude]\n }\n const value = Number(digits.replace(/[,' ]/g, \"\")) * scale\n if (!Number.isFinite(value)) return null\n const negative = (open && close) || /[-−–]/.test(signA + signB)\n return { value: negative ? -value : value, prefix: currency.trim(), suffix }\n}\n\nconst MONTHS = [\"jan\", \"feb\", \"mar\", \"apr\", \"may\", \"jun\", \"jul\", \"aug\", \"sep\", \"oct\", \"nov\", \"dec\"]\nconst monthIndex = (name: string) => MONTHS.indexOf(name.slice(0, 3).toLowerCase())\nconst MONTH_NAME = \"(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\\\\.?\"\nconst fullYear = (y: string) => (y.length === 2 ? 2000 + Number(y) : Number(y))\nconst validYear = (y: number) => y >= 1000 && y <= 2200\n\n/** Parses the date shapes that show up in tables. Returns UTC epoch milliseconds. */\nexport function parseDate(raw: string): number | null {\n const text = raw.trim().replace(/\\s+/g, \" \")\n let m: RegExpExecArray | null\n if ((m = /^(\\d{4})$/.exec(text))) return validYear(+m[1]) ? Date.UTC(+m[1], 0, 1) : null\n if ((m = /^(\\d{4})[-/.](\\d{1,2})(?:[-/.](\\d{1,2}))?(?:[T ][\\d:.]+Z?)?$/.exec(text))) {\n return +m[2] >= 1 && +m[2] <= 12 ? Date.UTC(+m[1], +m[2] - 1, +(m[3] ?? 1)) : null\n }\n if ((m = /^(?:FY|CY) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[1]), 0, 1)\n if ((m = /^Q([1-4]) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[2]), (+m[1] - 1) * 3, 1)\n if ((m = /^'?(\\d{2}|\\d{4}) ?Q([1-4])$/i.exec(text))) return Date.UTC(fullYear(m[1]), (+m[2] - 1) * 3, 1)\n if ((m = /^H([12]) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[2]), (+m[1] - 1) * 6, 1)\n if ((m = new RegExp(`^${MONTH_NAME}$`, \"i\").exec(text))) return Date.UTC(2000, monthIndex(m[1]), 1)\n if ((m = new RegExp(`^${MONTH_NAME} '?(\\\\d{2}|\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(fullYear(m[2]), monthIndex(m[1]), 1)\n }\n if ((m = new RegExp(`^${MONTH_NAME} (\\\\d{1,2})(?:st|nd|rd|th)?,? (\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(+m[3], monthIndex(m[1]), +m[2])\n }\n if ((m = new RegExp(`^(\\\\d{1,2})(?:st|nd|rd|th)? ${MONTH_NAME},? (\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(+m[3], monthIndex(m[2]), +m[1])\n }\n if ((m = /^(\\d{1,2})[/.](\\d{1,2})[/.](\\d{2}|\\d{4})$/.exec(text))) {\n const [a, b] = [+m[1], +m[2]]\n const [month, day] = a > 12 ? [b, a] : [a, b]\n return month >= 1 && month <= 12 && day <= 31 ? Date.UTC(fullYear(m[3]), month - 1, day) : null\n }\n return null\n}\n\nconst ID_HEADER = /^(#|no\\.?|nr\\.?|id|rank|pos(ition)?|s\\/n|index|ref)$/i\nconst TIME_HEADER = /\\b(year|date|when|period|month|quarter|fy|time|day|week)\\b/i\nconst SHARE_HEADER = /\\b(share|allocation|mix|breakdown|distribution|portion|weight|split|composition)\\b/i\nconst SUMMARY_LABEL = /^(grand )?(total|sum|overall|subtotal|average|avg|mean|median|all)\\b/i\n\n/** Reads \"Revenue ($M)\" → { prefix: \"$\", suffix: \"M\" }. */\nfunction unitFromHeader(label: string): Unit | undefined {\n const match = /[([]\\s*([^)\\]]{1,10})\\s*[)\\]]\\s*$/.exec(label)\n if (!match) return label.trim().endsWith(\"%\") ? { prefix: \"\", suffix: \"%\" } : undefined\n const hint = match[1].trim()\n const currency = new RegExp(`^(${CURRENCY})`).exec(hint)\n if (currency) return { prefix: currency[1], suffix: hint.slice(currency[1].length).trim() }\n if (/^in /i.test(hint)) return { prefix: \"\", suffix: ` ${hint.slice(3)}` }\n return { prefix: \"\", suffix: hint === \"%\" ? \"%\" : ` ${hint}` }\n}\n\nconst mostCommon = (values: string[]) => {\n const counts = new Map<string, number>()\n for (const v of values) counts.set(v, (counts.get(v) ?? 0) + 1)\n return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? \"\"\n}\n\nexport function buildTableModel(node: Table): TableModel {\n const [head, ...body] = node.children\n const headers = (head?.children ?? []).map((cell) => toText(cell).trim())\n const width = Math.max(headers.length, ...body.map((row) => row.children.length))\n const cellText = body.map((row) => Array.from({ length: width }, (_, c) => toText(row.children[c]).trim()))\n\n const columns: TableColumn[] = []\n const parsed: (number | string | null)[][] = body.map(() => [])\n\n for (let c = 0; c < width; c++) {\n const label = headers[c] || \"\"\n const cells = cellText.map((row) => row[c])\n const present = cells.filter((text) => !MISSING.test(text))\n const numbers = cells.map((text) => (MISSING.test(text) ? null : parseNumber(text)))\n const dates = cells.map((text) => (MISSING.test(text) ? null : parseDate(text)))\n const numericCount = numbers.filter(Boolean).length\n const dateCount = dates.filter((d) => d != null).length\n const enough = (count: number) => present.length > 0 && count >= Math.max(1, Math.ceil(present.length * 0.9))\n\n const allYears = enough(dateCount) && present.every((text) => /^\\d{4}$/.test(text))\n let type: ColumnType = \"text\"\n // Bare four-digit numbers are years only where a time axis is expected.\n if (enough(dateCount) && (!allYears || c === 0 || TIME_HEADER.test(label))) type = \"date\"\n else if (enough(numericCount) && !ID_HEADER.test(label)) type = \"number\"\n\n let unit: Unit | undefined\n if (type === \"number\") {\n const found = numbers.filter((n): n is ParsedNumber => n != null)\n const prefix = mostCommon(found.map((n) => n.prefix))\n const suffix = mostCommon(found.map((n) => n.suffix))\n unit = prefix || suffix ? { prefix, suffix } : unitFromHeader(label)\n }\n\n const align = node.align?.[c] ?? (type === \"number\" ? \"right\" : \"left\")\n columns.push({ key: `c${c}`, label: label || `Column ${c + 1}`, type, unit, align })\n cellText.forEach((row, r) => {\n parsed[r][c] = type === \"number\" ? (numbers[r]?.value ?? null) : type === \"date\" ? dates[r] : row[c]\n })\n }\n\n const rows: TableRow[] = body.map((row, r) => ({\n text: Object.fromEntries(columns.map((col, c) => [col.key, cellText[r][c]])),\n value: Object.fromEntries(columns.map((col, c) => [col.key, parsed[r][c]])),\n cells: Array.from({ length: width }, (_, c) => (row.children[c]?.children ?? []) as PhrasingContent[]),\n }))\n\n return { node, columns, rows }\n}\n\nconst unitKey = (unit?: Unit) => `${unit?.prefix ?? \"\"}|${unit?.suffix ?? \"\"}`\n\n/**\n * Series are only plotted together when they share a unit and a comparable scale.\n * Anything else gets its own chart — two scales on one plot mislead.\n */\nfunction groupSeries(model: TableModel, numeric: TableColumn[], rowIndices: number[]): ChartSpec[] {\n const byUnit = new Map<string, TableColumn[]>()\n for (const column of numeric) {\n const key = unitKey(column.unit)\n byUnit.set(key, [...(byUnit.get(key) ?? []), column])\n }\n const charts: ChartSpec[] = []\n for (const group of byUnit.values()) {\n const peak = (column: TableColumn) =>\n Math.max(0, ...rowIndices.map((r) => Math.abs((model.rows[r].value[column.key] as number | null) ?? 0)))\n const sorted = [...group].sort((a, b) => peak(b) - peak(a))\n let cluster: TableColumn[] = []\n for (const column of sorted) {\n if (cluster.length && peak(cluster[0]) > peak(column) * 30) {\n charts.push({ seriesKeys: cluster.map((c) => c.key), unit: cluster[0].unit })\n cluster = []\n }\n cluster.push(column)\n }\n if (cluster.length) charts.push({ seriesKeys: cluster.map((c) => c.key), unit: cluster[0].unit })\n }\n // Keep the document's column order within and across charts.\n const order = (key: string) => model.columns.findIndex((c) => c.key === key)\n for (const chart of charts) chart.seriesKeys.sort((a, b) => order(a) - order(b))\n return charts.sort((a, b) => order(a.seriesKeys[0]) - order(b.seriesKeys[0]))\n}\n\n/** Chooses how a table is best shown, from its shape and the types of its columns. */\nexport function planViz(model: TableModel): VizPlan {\n const { columns, rows } = model\n const all = rows.map((_, i) => i)\n const table = (reason: string, candidates: VizKind[] = []): VizPlan => ({\n kind: \"table\",\n charts: [],\n rowIndices: all,\n candidates: [\"table\", ...candidates],\n reason,\n })\n\n if (rows.length === 0 || columns.length < 2) return table(\"too small to visualise\")\n\n let numeric = columns.filter((c) => c.type === \"number\")\n const dateColumn = columns.find((c) => c.type === \"date\")\n const texts = columns.filter((c) => c.type === \"text\")\n let label: TableColumn | undefined = dateColumn ?? texts[0]\n // A leading numeric column that only ever rises (1, 4, 8, 16…) is an ordered axis, not a measure.\n let ordinal = false\n if (!label && rows.length >= 3 && numeric.length >= 2 && numeric[0] === columns[0]) {\n const steps = rows.map((row) => row.value[columns[0].key] as number | null)\n if (steps.every((v, i) => v != null && (i === 0 || v > steps[i - 1]!))) {\n label = columns[0]\n numeric = numeric.slice(1)\n ordinal = true\n }\n }\n const otherTexts = texts.filter((c) => c !== label)\n const avgLength = (column: TableColumn) =>\n rows.reduce((sum, row) => sum + row.text[column.key].length, 0) / rows.length\n\n // A single row of numbers is a row of headline figures.\n if (rows.length === 1 && numeric.length >= 2 && numeric.length <= 6) {\n return {\n kind: \"stats\",\n labelKey: label?.key,\n charts: [{ seriesKeys: numeric.map((c) => c.key) }],\n rowIndices: all,\n candidates: [\"stats\", \"table\"],\n reason: \"one row of figures\",\n }\n }\n if (rows.length < 2) return table(\"single row\")\n\n if (numeric.length === 0) {\n if (dateColumn && otherTexts.length + (texts.includes(label!) ? 0 : texts.length) >= 1 && rows.length >= 3) {\n return {\n kind: \"timeline\",\n labelKey: dateColumn.key,\n charts: [],\n rowIndices: all,\n candidates: [\"timeline\", \"table\"],\n reason: \"dated events without figures\",\n }\n }\n if (columns.length === 2 && rows.length <= 12 && avgLength(columns[0]) <= 28) {\n return {\n kind: \"facts\",\n labelKey: columns[0].key,\n charts: [],\n rowIndices: all,\n candidates: [\"facts\", \"table\"],\n reason: \"short key–value pairs\",\n }\n }\n return table(\"no numeric columns\")\n }\n\n if (!label) {\n if (numeric.length >= 2 && rows.length >= 5) {\n return {\n kind: \"scatter\",\n labelKey: numeric[0].key,\n charts: [{ seriesKeys: [numeric[1].key], unit: numeric[1].unit }],\n rowIndices: all,\n candidates: [\"scatter\", \"table\"],\n reason: \"two measures and no category\",\n }\n }\n return table(\"no label column\")\n }\n\n // Records with as much description as measurement read better as a table.\n if (otherTexts.length >= numeric.length || otherTexts.some((c) => avgLength(c) > 40)) {\n // …unless they are dated, in which case they are a sequence of events.\n if (dateColumn && rows.length >= 3 && numeric.length <= 1) {\n return {\n kind: \"timeline\",\n labelKey: dateColumn.key,\n charts: [],\n rowIndices: all,\n candidates: [\"timeline\", \"table\"],\n reason: \"dated events with descriptions\",\n }\n }\n return table(\"descriptive columns outweigh figures\", dateColumn ? [\"timeline\"] : [])\n }\n\n const labels = rows.map((row) => row.text[label.key])\n let rowIndices = all.filter((r) => !(rows.length > 2 && SUMMARY_LABEL.test(labels[r])))\n if (new Set(rowIndices.map((r) => labels[r])).size < rowIndices.length) {\n return table(\"repeated labels — these are records, not categories\")\n }\n if (rowIndices.length < 2) return table(\"too few rows\")\n\n const charts = groupSeries(model, numeric, rowIndices)\n if (charts.length > 3 || charts.some((c) => c.seriesKeys.length > 8)) {\n return table(\"too many unrelated measures\", [\"bar\"])\n }\n const single = charts.length === 1 && charts[0].seriesKeys.length === 1\n const count = rowIndices.length\n\n if (label.type === \"date\" || ordinal) {\n const axis = label.key\n const times = rowIndices.map((r) => rows[r].value[axis] as number)\n if (times[0] > times[times.length - 1]) rowIndices = [...rowIndices].reverse()\n // One measure per plot: a trend once there are enough periods, bars to compare a few.\n const alone = charts.every((chart) => chart.seriesKeys.length === 1)\n const kind: VizKind = alone ? (count >= 8 ? \"area\" : \"bar\") : count >= 4 ? \"line\" : \"bar\"\n return {\n kind,\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [...new Set<VizKind>([kind, \"line\", \"bar\", \"area\", \"table\"])],\n reason: `${count} ${ordinal ? \"steps\" : \"periods\"} × ${numeric.length} measure(s)`,\n }\n }\n\n if (count > 30) return table(\"too many categories for a readable chart\", [\"bar\"])\n\n if (single) {\n const column = columns.find((c) => c.key === charts[0].seriesKeys[0])!\n const values = rowIndices.map((r) => (rows[r].value[column.key] as number | null) ?? 0)\n if (count <= 4 && otherTexts.length === 0) {\n return {\n kind: \"stats\",\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [\"stats\", \"bar\", \"table\"],\n reason: \"a handful of headline figures\",\n }\n }\n const sum = values.reduce((a, b) => a + b, 0)\n const wholeOf100 = column.unit?.suffix === \"%\" && sum > 98 && sum < 102\n if (count >= 3 && count <= 6 && values.every((v) => v >= 0) && (wholeOf100 || SHARE_HEADER.test(column.label))) {\n return {\n kind: \"donut\",\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [\"donut\", \"bar\", \"table\"],\n reason: \"parts of a whole\",\n }\n }\n }\n\n const cells = count * numeric.length\n if (!single && cells > 48) return table(\"too dense to chart\", [\"bar\"])\n const longest = Math.max(...rowIndices.map((r) => labels[r].length))\n return {\n kind: \"bar\",\n labelKey: label.key,\n charts,\n horizontal: single && (count > 7 || longest > 14),\n rowIndices,\n candidates: single && count <= 6 ? [\"bar\", \"donut\", \"stats\", \"table\"] : [\"bar\", \"table\"],\n reason: `${count} categories × ${numeric.length} measure(s)`,\n }\n}\n\n/** Re-plans a table as a specific form, when that form is one of its candidates. */\nexport function withVizKind(plan: VizPlan, model: TableModel, kind: VizKind): VizPlan {\n if (kind === plan.kind || !plan.candidates.includes(kind)) return plan\n const next: VizPlan = { ...plan, kind, reason: `${plan.reason} → overridden to ${kind}` }\n if (kind === \"timeline\") {\n next.labelKey = model.columns.find((c) => c.type === \"date\")?.key ?? plan.labelKey\n next.rowIndices = model.rows.map((_, i) => i)\n }\n if (kind === \"bar\" && plan.charts.length === 0) {\n const numeric = model.columns.filter((c) => c.type === \"number\")\n const label = model.columns.find((c) => c.type !== \"number\")\n if (!numeric.length || !label) return plan\n next.labelKey = label.key\n next.charts = groupSeries(model, numeric, plan.rowIndices)\n }\n if (kind === \"bar\") {\n const longest = Math.max(...next.rowIndices.map((r) => model.rows[r].text[next.labelKey ?? \"\"]?.length ?? 0))\n const single = next.charts.length === 1 && next.charts[0].seriesKeys.length === 1\n next.horizontal = single && (next.rowIndices.length > 7 || longest > 14)\n }\n return next\n}\n\n/** Compact, unit-aware number formatting for axes, tooltips and stat tiles. */\nexport function formatValue(value: number, unit?: Unit): string {\n const abs = Math.abs(value)\n const body =\n abs >= 10000\n ? new Intl.NumberFormat(\"en\", { notation: \"compact\", maximumFractionDigits: 1 }).format(value)\n : new Intl.NumberFormat(\"en\", { maximumFractionDigits: abs < 10 ? 2 : abs < 100 ? 1 : 0 }).format(value)\n const sign = body.startsWith(\"-\") ? \"−\" : \"\"\n return `${sign}${unit?.prefix ?? \"\"}${body.replace(/^-/, \"\")}${unit?.suffix ?? \"\"}`\n}\n","import type { Blockquote, Heading, List, Paragraph, PhrasingContent, Root, RootContent } from \"mdast\"\nimport type { Frontmatter } from \"../parse\"\nimport type {\n Block,\n DocumentPlan,\n Hero,\n ImageMeta,\n ImageRef,\n Judgements,\n ListVariant,\n Section,\n SectionLayout,\n TocEntry,\n} from \"../types\"\nimport { buildTableModel, planViz, withVizKind } from \"./tables\"\nimport { countWords, imagesOnly, slugify, toText, wordsIn } from \"./text\"\n\nexport type ImageMetaMap = Record<string, ImageMeta | null | undefined>\n\nexport type PlanInput = {\n root: Root\n frontmatter?: Frontmatter\n /** Natural image sizes keyed by the URL written in the markdown. */\n images?: ImageMetaMap\n judgements?: Judgements\n}\n\nconst BADGE_URL = /shields\\.io|badgen\\.net|badge\\.fury\\.io|\\/badge(s)?[./]|badge\\.svg|travis-ci|circleci\\.com|codecov\\.io|coveralls\\.io|img\\.shields/i\nconst LOGO_NAME = /logo|icon|brand|wordmark|avatar|emblem/i\nconst BANNER_NAME = /banner|cover|hero|header|splash|masthead/i\n\n/** Every image URL in the document, for size probing. */\nexport function collectImageUrls(root: Root): string[] {\n const urls = new Set<string>()\n const visit = (node: unknown) => {\n const n = node as { type?: string; url?: string; children?: unknown[] }\n if (n.type === \"image\" && n.url) urls.add(n.url)\n n.children?.forEach(visit)\n }\n visit(root)\n return [...urls]\n}\n\nfunction isBadge(image: ImageRef): boolean {\n if (BADGE_URL.test(image.src)) return true\n return !!image.meta && image.meta.height <= 40 && image.meta.width <= 320\n}\n\nfunction classifyHeroImage(image: ImageRef): Hero[\"variant\"] | null {\n const meta = image.meta\n // An image that could not be measured cannot be trusted to carry the page.\n if (!meta || meta.width < 48) return null\n const aspect = meta.width / meta.height\n const name = `${image.src.split(/[?#]/)[0]} ${image.alt}`\n const vector = /\\.svg$/i.test(image.src.split(/[?#]/)[0])\n if (LOGO_NAME.test(name) && !(meta.width >= 1600 && aspect >= 1.8)) return \"logo\"\n if (meta.width >= 1200 && aspect >= 1.5 && !vector) return \"banner\"\n if (BANNER_NAME.test(name) && meta.width >= 900 && aspect >= 1.5) return \"banner\"\n if (vector || Math.max(meta.width, meta.height) <= 640) return \"logo\"\n return \"figure\"\n}\n\ntype Context = {\n images: ImageMetaMap\n judgements: Judgements\n slugs: Set<string>\n sectionDepth: number\n splitCount: number\n dataCount: number\n}\n\nfunction imageRef(found: { image: { url: string; alt?: string | null; title?: string | null }; href?: string }, ctx: Context): ImageRef {\n return {\n src: found.image.url,\n alt: found.image.alt ?? \"\",\n title: found.image.title ?? undefined,\n href: found.href,\n meta: ctx.images[found.image.url] ?? undefined,\n }\n}\n\nfunction planList(node: List): { variant: ListVariant; columns: 1 | 2 } {\n const items = node.children\n if (items.some((item) => item.checked != null)) return { variant: \"checklist\", columns: 1 }\n const nested = items.some((item) => item.children.some((child) => child.type === \"list\" || child.type === \"code\"))\n const words = items.map((item) => wordsIn(item))\n const total = words.reduce((a, b) => a + b, 0)\n const longest = Math.max(0, ...words)\n // Short lists are a moment of emphasis; long ones are body copy that happens to be bulleted.\n if (!nested && items.length >= 2 && items.length <= 8 && longest <= 28 && total <= 130) {\n return { variant: \"feature\", columns: items.length >= 4 && longest <= 12 ? 2 : 1 }\n }\n return { variant: \"body\", columns: 1 }\n}\n\nconst ATTRIBUTION = /^\\s*(?:[—–]|--|~)\\s*(.+)$/\n\nfunction planQuote(node: Blockquote): Extract<Block, { kind: \"quote\" }> {\n let children = node.children as RootContent[]\n const first = children[0]\n\n // GitHub-style alerts: > [!NOTE]\n if (first?.type === \"paragraph\" && first.children[0]?.type === \"text\") {\n const match = /^\\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]\\s*/i.exec(first.children[0].value)\n if (match) {\n const rest: PhrasingContent[] = [\n { type: \"text\", value: first.children[0].value.slice(match[0].length) },\n ...first.children.slice(1),\n ]\n const body = rest.some((child) => toText(child).trim()) ? [{ ...first, children: rest }] : []\n return { kind: \"quote\", variant: \"callout\", calloutType: match[1].toLowerCase(), children: [...body, ...children.slice(1)] }\n }\n }\n\n let attribution: string | undefined\n const last = children[children.length - 1]\n if (last?.type === \"paragraph\") {\n const whole = ATTRIBUTION.exec(toText(last))\n if (whole && children.length > 1) {\n attribution = whole[1].trim()\n children = children.slice(0, -1)\n } else {\n // Attribution on the final line of the same paragraph.\n const breakAt = last.children.findLastIndex((child) => child.type === \"break\")\n const tail = breakAt >= 0 ? last.children.slice(breakAt + 1) : []\n const lineMatch = tail.length ? ATTRIBUTION.exec(toText(tail)) : /\\n\\s*(?:[—–]|--|~)\\s*([^\\n]+)$/.exec(toText(last))\n if (lineMatch && breakAt >= 0) {\n attribution = lineMatch[1].trim()\n children = [...children.slice(0, -1), { ...last, children: last.children.slice(0, breakAt) }]\n } else if (lineMatch && last.children.length === 1 && last.children[0].type === \"text\") {\n attribution = lineMatch[1].trim()\n const value = last.children[0].value.replace(/\\n\\s*(?:[—–]|--|~)\\s*[^\\n]+$/, \"\")\n children = [...children.slice(0, -1), { ...last, children: [{ type: \"text\", value }] }]\n }\n }\n }\n\n const words = wordsIn(children)\n const simple = children.every((child) => child.type === \"paragraph\")\n return { kind: \"quote\", variant: simple && words <= 45 ? \"pull\" : \"aside\", children, attribution }\n}\n\nfunction buildBlocks(nodes: RootContent[], ctx: Context, sectionId: string): Block[] {\n const blocks: Block[] = []\n for (const node of nodes) {\n switch (node.type) {\n case \"heading\":\n blocks.push({\n kind: \"heading\",\n depth: Math.max(1, node.depth - ctx.sectionDepth),\n id: slugify(toText(node), ctx.slugs),\n children: node.children,\n })\n break\n case \"paragraph\": {\n const found = imagesOnly(node.children)\n const refs = found?.map((f) => imageRef(f, ctx))\n if (refs && !refs.every(isBadge)) {\n const previous = blocks[blocks.length - 1]\n const images = previous?.kind === \"media\" ? [...previous.images, ...refs] : refs\n const media: Block = {\n kind: \"media\",\n images,\n variant: images.length >= 3 ? \"slideshow\" : images.length === 2 ? \"pair\" : \"single\",\n }\n if (previous?.kind === \"media\") blocks[blocks.length - 1] = media\n else blocks.push(media)\n } else blocks.push({ kind: \"content\", node })\n break\n }\n case \"list\":\n blocks.push({ kind: \"list\", node, ...planList(node) })\n break\n case \"blockquote\":\n blocks.push(planQuote(node))\n break\n case \"code\":\n blocks.push({ kind: \"code\", lang: node.lang ?? undefined, value: node.value })\n break\n case \"table\": {\n const id = `${sectionId}-data-${++ctx.dataCount}`\n const table = buildTableModel(node)\n const viz = planViz(table)\n const wanted = ctx.judgements.viz?.[id]\n blocks.push({ kind: \"data\", id, table, viz: wanted ? withVizKind(viz, table, wanted) : viz })\n break\n }\n case \"thematicBreak\":\n blocks.push({ kind: \"rule\" })\n break\n default:\n blocks.push({ kind: \"content\", node })\n }\n }\n return blocks\n}\n\ntype RawSection = { heading?: Heading; nodes: RootContent[] }\n\ntype Shape = {\n words: number\n images: ImageRef[]\n heavy: boolean\n quoteOnly: boolean\n cards: { heading: Heading; nodes: RootContent[] }[] | null\n cardIntro: RootContent[]\n}\n\nfunction measure(raw: RawSection, ctx: Context): Shape {\n const { nodes } = raw\n const images = nodes.flatMap((node) =>\n node.type === \"paragraph\" ? (imagesOnly(node.children) ?? []).map((f) => imageRef(f, ctx)).filter((i) => !isBadge(i)) : [],\n )\n const prose = nodes.filter((node) => node.type !== \"table\" && node.type !== \"code\")\n const heavy = nodes.some((node) => node.type === \"table\" || node.type === \"code\" || node.type === \"math\")\n const quotes = nodes.filter((node) => node.type === \"blockquote\")\n const quoteOnly =\n quotes.length === 1 &&\n !heavy &&\n images.length === 0 &&\n wordsIn(quotes[0]) <= 70 &&\n wordsIn(nodes.filter((node) => node !== quotes[0])) <= 25 &&\n !/^\\[!/.test(toText(quotes[0]).trim())\n\n // Sub-sections that are each a short blurb read as a set of cards.\n const subDepth = ctx.sectionDepth + 1\n const firstSub = nodes.findIndex((node) => node.type === \"heading\" && node.depth === subDepth)\n let cards: Shape[\"cards\"] = null\n if (firstSub >= 0 && !nodes.some((node) => node.type === \"heading\" && node.depth > subDepth)) {\n const groups: { heading: Heading; nodes: RootContent[] }[] = []\n for (const node of nodes.slice(firstSub)) {\n if (node.type === \"heading\") groups.push({ heading: node, nodes: [] })\n else groups[groups.length - 1].nodes.push(node)\n }\n const blurb = (group: { nodes: RootContent[] }) =>\n group.nodes.length > 0 &&\n wordsIn(group.nodes) <= 70 &&\n group.nodes.every(\n (node) => node.type === \"paragraph\" || (node.type === \"list\" && node.children.length <= 5 && wordsIn(node) <= 50),\n ) &&\n group.nodes.filter((node) => node.type === \"paragraph\" && imagesOnly(node.children)).length <= 1\n if (groups.length >= 2 && groups.length <= 12 && groups.every(blurb)) cards = groups\n }\n\n return {\n words: wordsIn(prose),\n images,\n heavy,\n quoteOnly,\n cards,\n cardIntro: firstSub >= 0 ? nodes.slice(0, firstSub) : nodes,\n }\n}\n\n/** Layouts the content can fill, best fit first. `prose` always works. */\nfunction rankLayouts(raw: RawSection, shape: Shape): { layouts: SectionLayout[]; reason: string } {\n const { words, images, heavy } = shape\n const layouts: SectionLayout[] = []\n let reason = \"default reading layout\"\n const pick = (layout: SectionLayout, why: string) => {\n if (!layouts.length) reason = why\n if (!layouts.includes(layout)) layouts.push(layout)\n }\n const lead = images[0]?.meta\n const aspect = lead ? lead.width / lead.height : 0\n const simple = !heavy && !shape.cards\n\n if (shape.quoteOnly) pick(\"quote\", \"a single short quotation\")\n if (simple && images.length === 1 && lead && lead.width >= 1400 && aspect >= 1.3 && words <= 90) {\n pick(\"fullbleed\", `large ${lead.width}×${lead.height} image with ${words} words`)\n }\n if (simple && images.length === 1 && lead && lead.width >= 320 && words >= 20 && words <= 260) {\n pick(\"split\", `one image beside ${words} words`)\n }\n // A long run of prose before the sub-sections is an article with asides, not a card deck.\n const cardsLead = !!shape.cards && (wordsIn(shape.cardIntro) <= 90 || shape.cards.length >= 4)\n if (cardsLead) pick(\"cards\", `${shape.cards!.length} short sub-sections`)\n if (!heavy && images.length >= 2 && words <= 80) pick(\"showcase\", `${images.length} images, little text`)\n // Only a titled section is promoted to a statement: untitled opening text that\n // was not made a hero should not be dressed up as one.\n if (simple && raw.heading && images.length === 0 && words <= 45 && raw.nodes.length <= 3) {\n pick(\"statement\", `only ${words} words — give it room`)\n }\n pick(\"prose\", reason)\n\n // Looser fits: not the first choice, but still valid when picked by hand.\n if (shape.cards) pick(\"cards\", \"\")\n if (simple && images.length >= 1 && lead && lead.width >= 1000 && aspect >= 1.2 && words <= 160) pick(\"fullbleed\", \"\")\n if (simple && images.length >= 1 && lead && words >= 8 && words <= 400) pick(\"split\", \"\")\n if (!heavy && images.length >= 1 && words <= 160) pick(\"showcase\", \"\")\n if (simple && images.length === 0 && words > 0 && words <= 110 && raw.nodes.length <= 5) pick(\"statement\", \"\")\n return { layouts, reason }\n}\n\nfunction buildSection(raw: RawSection, ctx: Context, index: number): Section {\n const titleText = raw.heading ? toText(raw.heading).trim() : \"\"\n const id = raw.heading ? slugify(titleText, ctx.slugs) : slugify(\"introduction\", ctx.slugs)\n const shape = measure(raw, ctx)\n const { layouts, reason } = rankLayouts(raw, shape)\n const wanted = ctx.judgements.layouts?.[id]\n const layout = wanted && layouts.includes(wanted) ? wanted : layouts[0]\n\n let blocks: Block[]\n let feature: ImageRef | undefined\n let flip: boolean | undefined\n\n if (layout === \"cards\" && shape.cards) {\n blocks = [\n ...buildBlocks(shape.cardIntro, ctx, id),\n {\n kind: \"cards\",\n items: shape.cards.map((card) => ({\n id: slugify(toText(card.heading), ctx.slugs),\n title: card.heading.children,\n blocks: buildBlocks(card.nodes, ctx, id),\n })),\n },\n ]\n } else if (layout === \"split\" || layout === \"fullbleed\") {\n feature = shape.images[0]\n // The feature image leaves the flow; any further images stay where they were.\n let taken = false\n const rest = raw.nodes.filter((node) => {\n if (taken || node.type !== \"paragraph\") return true\n const found = imagesOnly(node.children)\n if (found?.length === 1 && found[0].image.url === feature!.src) return !(taken = true)\n return true\n })\n blocks = buildBlocks(rest, ctx, id)\n if (layout === \"split\") flip = ctx.splitCount++ % 2 === 1\n } else {\n blocks = buildBlocks(raw.nodes, ctx, id)\n }\n\n const tone = layout === \"quote\" ? \"invert\" : layout === \"statement\" ? \"tint\" : \"plain\"\n return {\n id,\n title: raw.heading?.children ?? [],\n titleText,\n layout,\n tone,\n blocks,\n feature,\n flip,\n words: shape.words,\n reason: wanted && layout === wanted ? `chosen: ${wanted}` : `${reason}${index === 0 && !raw.heading ? \" (introduction)\" : \"\"}`,\n alternatives: layouts,\n }\n}\n\nconst asString = (value: unknown) => (typeof value === \"string\" && value.trim() ? value.trim() : undefined)\n\n/**\n * Turns a parsed document into a layout plan. Pure and deterministic: the same\n * tree, image sizes and judgements always yield the same plan.\n */\nexport function planDocument(input: PlanInput): DocumentPlan {\n const { root } = input\n const frontmatter = input.frontmatter ?? {}\n const nodes = root.children\n const headings = nodes.filter((node): node is Heading => node.type === \"heading\")\n\n // The title is a leading H1 — one that comes before any other heading.\n const titleNode = headings[0]?.depth === 1 ? headings[0] : undefined\n const sectionHeadings = headings.filter((h) => h !== titleNode)\n const sectionDepth = sectionHeadings.length ? Math.min(...sectionHeadings.map((h) => h.depth)) : 2\n\n const ctx: Context = {\n images: input.images ?? {},\n judgements: input.judgements ?? {},\n slugs: new Set([\"top\"]),\n sectionDepth,\n splitCount: 0,\n dataCount: 0,\n }\n\n const firstSection = nodes.findIndex((node) => node.type === \"heading\" && node !== titleNode && node.depth === sectionDepth)\n const preamble = (firstSection >= 0 ? nodes.slice(0, firstSection) : nodes).filter((node) => node !== titleNode)\n const body = firstSection >= 0 ? nodes.slice(firstSection) : []\n\n // --- Hero -----------------------------------------------------------------\n const badges: ImageRef[] = []\n const lead: PhrasingContent[][] = []\n let heroImage: ImageRef | undefined\n let heroVariant: Hero[\"variant\"] = \"plain\"\n const intro: RootContent[] = []\n let leadOpen = true\n let leadWords = 0\n\n const coverUrl = asString(frontmatter.image) ?? asString(frontmatter.cover) ?? asString(frontmatter.banner)\n if (coverUrl) {\n const candidate: ImageRef = { src: coverUrl, alt: \"\", meta: ctx.images[coverUrl] ?? undefined }\n const variant = classifyHeroImage(candidate)\n if (variant) [heroImage, heroVariant] = [candidate, variant]\n }\n\n for (const node of preamble) {\n if (node.type === \"paragraph\") {\n const found = imagesOnly(node.children)\n if (found) {\n const refs = found.map((f) => imageRef(f, ctx))\n if (refs.every(isBadge)) {\n badges.push(...refs)\n continue\n }\n if (!heroImage && refs.length === 1 && ctx.judgements.heroImageUsable !== false) {\n const variant = classifyHeroImage(refs[0])\n if (variant) {\n ;[heroImage, heroVariant] = [refs[0], variant]\n continue\n }\n }\n } else if (leadOpen && lead.length < 2) {\n const words = countWords(toText(node))\n if (leadWords + words <= 70) {\n lead.push((node as Paragraph).children)\n leadWords += words\n continue\n }\n }\n }\n // The lead is the opening run of short paragraphs; anything else ends it.\n if (!(node.type === \"paragraph\" && imagesOnly(node.children))) leadOpen = false\n intro.push(node)\n }\n\n // A hero is never forced: without a title or an image fit to lead the page,\n // the opening paragraphs are simply the start of the text.\n const frontTitle = asString(frontmatter.title)\n const hasHero = !!titleNode || !!frontTitle || !!heroImage\n if (!hasHero) {\n intro.splice(0, intro.length, ...preamble)\n lead.length = 0\n badges.length = 0\n leadWords = 0\n }\n\n const description = asString(frontmatter.description) ?? asString(frontmatter.subtitle) ?? asString(frontmatter.summary)\n if (hasHero && description && !lead.length) lead.push([{ type: \"text\", value: description }])\n\n const title: PhrasingContent[] = titleNode?.children ?? (frontTitle ? [{ type: \"text\", value: frontTitle }] : [])\n const eyebrowParts = [asString(frontmatter.category), asString(frontmatter.author)]\n const date = frontmatter.date\n if (date instanceof Date) eyebrowParts.push(date.toISOString().slice(0, 10))\n else if (asString(date)) eyebrowParts.push(asString(date))\n\n const hero: Hero = {\n variant: heroImage ? heroVariant : \"plain\",\n title,\n titleText: toText(title).trim(),\n lead,\n image: heroImage,\n badges,\n eyebrow: (hasHero && eyebrowParts.filter(Boolean).join(\" · \")) || undefined,\n }\n\n // --- Sections ---------------------------------------------------------------\n const raws: RawSection[] = []\n if (intro.length) raws.push({ nodes: intro })\n for (const node of body) {\n if (node.type === \"heading\" && node.depth === sectionDepth) raws.push({ heading: node, nodes: [] })\n else raws[raws.length - 1].nodes.push(node)\n }\n const sections = raws.map((raw, index) => buildSection(raw, ctx, index))\n\n // Two tinted bands in a row would merge into one; keep the rhythm.\n for (let i = 1; i < sections.length; i++) {\n if (sections[i].tone === \"tint\" && sections[i - 1].tone === \"tint\") sections[i].tone = \"plain\"\n }\n\n // --- Navigation ---------------------------------------------------------------\n const toc: TocEntry[] = []\n for (const section of sections) {\n if (!section.titleText) continue\n toc.push({ id: section.id, text: section.titleText, depth: 0 })\n const subs: { id: string; text: string }[] = []\n for (const block of section.blocks) {\n if (block.kind === \"heading\" && block.depth === 1) subs.push({ id: block.id, text: toText(block.children) })\n if (block.kind === \"cards\") for (const item of block.items) subs.push({ id: item.id, text: toText(item.title) })\n }\n for (const sub of subs) toc.push({ ...sub, depth: 1 })\n }\n const titled = sections.filter((s) => s.titleText).length\n const words = sections.reduce((sum, s) => sum + s.words, 0) + leadWords\n\n return {\n hero,\n sections,\n toc: toc.length > 40 ? toc.filter((entry) => entry.depth === 0) : toc,\n showToc: titled >= 3,\n words,\n readingTime: Math.max(1, Math.round(words / 220)),\n }\n}\n","import type { CSSProperties } from \"react\"\nimport type { Appearance, FontPairingId, PaletteId, ThemeChoice } from \"../types\"\n\nexport type PaletteTokens = {\n bg: string\n /** Cards, code blocks and tinted section bands. */\n surface: string\n fg: string\n /** Secondary text. */\n muted: string\n border: string\n accent: string\n accentFg: string\n}\n\n/**\n * Chart drawing styles: `linework` is monochrome print-style ink with hatching,\n * `instrument` has rounded, saturated marks on calm surfaces, `soft` adds\n * gradients and depth, and `flat` is plain solid colour.\n */\nexport type ChartStyle = \"linework\" | \"instrument\" | \"soft\" | \"flat\"\n\nexport type Theme = {\n id: PaletteId\n name: string\n /** The kinds of document this theme suits. */\n description: string\n /** Its key colours in a few plain words — read by the classifier alongside the description. */\n look: string\n /** Typography used unless the caller picks another. */\n fonts: FontPairingId\n /** 0 = soft and rounded … 1 = sharp and formal. Sets the corner radius. */\n formality: number\n /** How charts are drawn under this theme. */\n chart: ChartStyle\n /** Fallback matching when no classifier is configured. */\n keywords: RegExp\n light: PaletteTokens\n dark: PaletteTokens\n}\n\nconst tokens = (bg: string, surface: string, fg: string, muted: string, border: string, accent: string, accentFg: string): PaletteTokens => ({\n bg,\n surface,\n fg,\n muted,\n border,\n accent,\n accentFg,\n})\n\nexport const themes: Record<PaletteId, Theme> = {\n paper: {\n id: \"paper\",\n name: \"Paper\",\n description: \"essays, stories, opinion, personal writing\",\n look: \"cream paper, terracotta accent\",\n fonts: \"editorial\",\n formality: 0.5,\n chart: \"flat\",\n keywords: /\\b(essay|story|chapter|novel|memoir|poem|letter|journal|philosophy|opinion|notebook|writing)\\b/gi,\n light: tokens(\"#faf7f2\", \"#f1ebe1\", \"#1f1b16\", \"#6b6257\", \"#e0d7c9\", \"#a8481f\", \"#ffffff\"),\n dark: tokens(\"#171411\", \"#221d18\", \"#f1ebe2\", \"#a89f92\", \"#352e26\", \"#e59468\", \"#1a120c\"),\n },\n ink: {\n id: \"ink\",\n name: \"Ink\",\n description: \"plain notes, memos, minimal design writing\",\n look: \"white, black ink, no colour\",\n fonts: \"modern\",\n formality: 0.7,\n chart: \"linework\",\n keywords: /\\b(design|minimal|typography|studio|portfolio|notes|memo|manifesto)\\b/gi,\n light: tokens(\"#ffffff\", \"#f4f4f5\", \"#0a0a0a\", \"#5f5f68\", \"#e4e4e7\", \"#18181b\", \"#ffffff\"),\n dark: tokens(\"#0a0a0a\", \"#18181b\", \"#fafafa\", \"#a1a1aa\", \"#27272a\", \"#f4f4f5\", \"#0a0a0a\"),\n },\n ocean: {\n id: \"ocean\",\n name: \"Ocean\",\n description: \"business reports, finance, strategy, investors\",\n look: \"cool white, navy, corporate blue\",\n fonts: \"modern\",\n formality: 0.6,\n chart: \"soft\",\n keywords: /\\b(revenue|profit|quarter|q[1-4]|market|investor|financial|growth|customers?|sales|strategy|kpi|forecast|budget|margin|earnings|business)\\b/gi,\n light: tokens(\"#f7fafc\", \"#eaf1f7\", \"#0f1f2e\", \"#52667a\", \"#d5e0eb\", \"#0b5fa8\", \"#ffffff\"),\n dark: tokens(\"#0b1420\", \"#121f2e\", \"#e8f0f7\", \"#93a7ba\", \"#213347\", \"#62b0f0\", \"#06121d\"),\n },\n forest: {\n id: \"forest\",\n name: \"Forest\",\n description: \"nature, environment, outdoors, gardening\",\n look: \"sage background, deep green\",\n fonts: \"friendly\",\n formality: 0.35,\n chart: \"instrument\",\n keywords: /\\b(nature|climate|forest|garden|plants?|sustainab\\w+|environment\\w*|organic|wildlife|hiking|ecology|carbon|farm\\w*|valley|trail)\\b/gi,\n light: tokens(\"#f6f8f3\", \"#e9efe2\", \"#17231a\", \"#586a5b\", \"#d3ddcd\", \"#2b6a3c\", \"#ffffff\"),\n dark: tokens(\"#0e1510\", \"#17211a\", \"#e6efe6\", \"#97aa9a\", \"#28372c\", \"#7fc890\", \"#0b160e\"),\n },\n sunset: {\n id: \"sunset\",\n name: \"Sunset\",\n description: \"travel journals, itineraries, destinations\",\n look: \"warm peach, burnt orange\",\n fonts: \"elegant\",\n formality: 0.4,\n chart: \"soft\",\n keywords: /\\b(travel|trip|beach|hotel|festival|holiday|itinerary|adventure|temples?|shrine|guesthouse|city|walks?|journey|island)\\b/gi,\n light: tokens(\"#fff9f4\", \"#fdeee2\", \"#2a1710\", \"#78594b\", \"#f0d8c8\", \"#bb3a15\", \"#ffffff\"),\n dark: tokens(\"#1a100c\", \"#281912\", \"#fbeee6\", \"#c0a497\", \"#3d281e\", \"#ff8f63\", \"#200e06\"),\n },\n violet: {\n id: \"violet\",\n name: \"Violet\",\n description: \"tech products, startups, AI, launches\",\n look: \"lavender, vivid purple\",\n fonts: \"friendly\",\n formality: 0.25,\n chart: \"instrument\",\n keywords: /\\b(product|startup|launch|ai|model|platform|features?|app|saas|creative|innovation|roadmap|pricing|beta)\\b/gi,\n light: tokens(\"#faf8ff\", \"#f0ebfd\", \"#1a1530\", \"#625b7e\", \"#dfd8f5\", \"#6236e0\", \"#ffffff\"),\n dark: tokens(\"#100d1c\", \"#1a162c\", \"#efeaff\", \"#a69fc4\", \"#2c2647\", \"#a892ff\", \"#120b2a\"),\n },\n terminal: {\n id: \"terminal\",\n name: \"Terminal\",\n description: \"software docs, READMEs, API and developer guides\",\n look: \"cool grey, teal\",\n fonts: \"technical\",\n formality: 0.6,\n chart: \"flat\",\n keywords: /\\b(install|npm|api|cli|usage|config\\w*|repository|function|library|docker|git|sdk|endpoint|compile|typescript|python|server|deploy\\w*)\\b/gi,\n light: tokens(\"#f6f8f9\", \"#e9eff1\", \"#0d1b21\", \"#4f656e\", \"#d2dde1\", \"#0a716c\", \"#ffffff\"),\n dark: tokens(\"#0a0f12\", \"#121b20\", \"#e2ecef\", \"#8ba2ab\", \"#202f36\", \"#45dcc2\", \"#04201b\"),\n },\n chambers: {\n id: \"chambers\",\n name: \"Chambers\",\n description: \"legal documents, contracts, policy, regulation\",\n look: \"ivory, navy and gold\",\n fonts: \"classic\",\n formality: 0.9,\n chart: \"flat\",\n keywords: /\\b(agreement|contract|clause|shall|court|law|legal|policy|regulation|act|statute|party|parties|compliance|hereby|pursuant|terms)\\b/gi,\n light: tokens(\"#fbfaf7\", \"#f0eee6\", \"#151c2c\", \"#575e70\", \"#dbd8cd\", \"#1f3a6e\", \"#ffffff\"),\n dark: tokens(\"#0d111b\", \"#161c2b\", \"#ece9e0\", \"#a0a6b5\", \"#283046\", \"#cdae66\", \"#1a1405\"),\n },\n academia: {\n id: \"academia\",\n name: \"Academia\",\n description: \"scholarly papers, research, theses, lecture notes\",\n look: \"ivory, oxblood red\",\n fonts: \"scholarly\",\n formality: 0.9,\n chart: \"linework\",\n keywords: /\\b(research|study|abstract|hypothesis|methodology|findings|literature|thesis|theorem|proof|citation|journal|university|experiment)\\b/gi,\n light: tokens(\"#fbf9f3\", \"#f1eddf\", \"#1c1812\", \"#655c4c\", \"#ddd6c2\", \"#7b1e22\", \"#ffffff\"),\n dark: tokens(\"#13110c\", \"#1e1b13\", \"#efe9d8\", \"#aca38c\", \"#332e20\", \"#e58a86\", \"#240808\"),\n },\n blueprint: {\n id: \"blueprint\",\n name: \"Blueprint\",\n description: \"engineering specs, architecture, hardware, proposals\",\n look: \"drafting blue-white, cobalt\",\n fonts: \"geometric\",\n formality: 0.8,\n chart: \"flat\",\n keywords: /\\b(specification|architecture|system|design doc|requirements?|protocol|diagram|engineering|hardware|sensor|circuit|tolerance|proposal|rfc)\\b/gi,\n light: tokens(\"#f5f8fd\", \"#e6eefa\", \"#0c1a33\", \"#4c5f80\", \"#cfdcf0\", \"#1447c8\", \"#ffffff\"),\n dark: tokens(\"#08122a\", \"#0f1d3d\", \"#e4ecfb\", \"#93a6cc\", \"#1d3160\", \"#7aa5ff\", \"#05102a\"),\n },\n midnight: {\n id: \"midnight\",\n name: \"Midnight\",\n description: \"night-time, space, science fiction, games, nightlife\",\n look: \"deep indigo, electric cyan\",\n fonts: \"geometric\",\n formality: 0.4,\n chart: \"instrument\",\n keywords: /\\b(space|galaxy|stars?|orbit|planet|astronom\\w+|game|gaming|cyber\\w*|future|sci-?fi|night|dark|dusk|cinematic|neon|milky way)\\b/gi,\n light: tokens(\"#f6f6fc\", \"#eaeaf7\", \"#14132b\", \"#5a5980\", \"#d7d6ee\", \"#3d2fd0\", \"#ffffff\"),\n dark: tokens(\"#07071a\", \"#10102a\", \"#ecebff\", \"#9c9bc6\", \"#232350\", \"#5ee6ff\", \"#03141a\"),\n },\n rose: {\n id: \"rose\",\n name: \"Rose\",\n description: \"weddings, beauty, fashion, celebrations\",\n look: \"blush pink, deep rose\",\n fonts: \"luxe\",\n formality: 0.3,\n chart: \"soft\",\n keywords: /\\b(wedding|bride|beauty|fashion|skincare|romance|love|invitation|bouquet|engagement|anniversary|bridal)\\b/gi,\n light: tokens(\"#fff7f7\", \"#fbe9ea\", \"#2b1519\", \"#7a5560\", \"#f1d3d6\", \"#b3305a\", \"#ffffff\"),\n dark: tokens(\"#1a0e11\", \"#27161b\", \"#fbe9ec\", \"#c59ba5\", \"#3e242b\", \"#ff8fae\", \"#2a0a14\"),\n },\n sand: {\n id: \"sand\",\n name: \"Sand\",\n description: \"interiors, craft, slow living, quiet luxury\",\n look: \"stone beige, muted olive\",\n fonts: \"luxe\",\n formality: 0.7,\n chart: \"flat\",\n keywords: /\\b(interior|furniture|ceramics?|craft|atelier|linen|slow living|minimalis[mt]|gallery|curated|artisan)\\b/gi,\n light: tokens(\"#f8f5ef\", \"#ece6da\", \"#26221b\", \"#6a6253\", \"#dcd3c2\", \"#5d6b2f\", \"#ffffff\"),\n dark: tokens(\"#15130f\", \"#201d17\", \"#efe9dc\", \"#aba28f\", \"#353026\", \"#b9c77a\", \"#171a08\"),\n },\n citrus: {\n id: \"citrus\",\n name: \"Citrus\",\n description: \"children, schools, community, playful guides\",\n look: \"sunny yellow, bold orange\",\n fonts: \"rounded\",\n formality: 0.05,\n chart: \"instrument\",\n keywords: /\\b(kids?|children|school|classroom|play|fun|games?|party|club|camp|volunteers?|community|activities|learn\\w*)\\b/gi,\n light: tokens(\"#fffdf2\", \"#fbf3c9\", \"#1f1a05\", \"#6b6130\", \"#eee2a6\", \"#b84a00\", \"#ffffff\"),\n dark: tokens(\"#151302\", \"#211e08\", \"#fdf8d8\", \"#bdb37a\", \"#37320f\", \"#ffd23f\", \"#1c1600\"),\n },\n crimson: {\n id: \"crimson\",\n name: \"Crimson\",\n description: \"news, journalism, press releases, politics\",\n look: \"newsprint white, strong red\",\n fonts: \"gazette\",\n formality: 0.85,\n chart: \"flat\",\n keywords: /\\b(news|report(ed|er|ing)?|press release|journalis\\w+|election|government|minister|breaking|investigation|headline|spokesperson|announced)\\b/gi,\n light: tokens(\"#ffffff\", \"#f6f2f2\", \"#120a0a\", \"#5f5252\", \"#e6dcdc\", \"#c1121f\", \"#ffffff\"),\n dark: tokens(\"#0f0a0a\", \"#1b1313\", \"#f7efef\", \"#b5a3a3\", \"#2e2121\", \"#ff5a63\", \"#1f0406\"),\n },\n slate: {\n id: \"slate\",\n name: \"Slate\",\n description: \"operations manuals, industrial reports, safety procedures\",\n look: \"steel grey, safety orange\",\n fonts: \"slab\",\n formality: 0.75,\n chart: \"flat\",\n keywords: /\\b(manual|procedure|maintenance|safety|inspection|operations?|runbook|checklist|equipment|construction|logistics|warehouse|incident)\\b/gi,\n light: tokens(\"#f4f6f7\", \"#e5eaed\", \"#131c22\", \"#4d5c66\", \"#cfd8de\", \"#b4470b\", \"#ffffff\"),\n dark: tokens(\"#0d1215\", \"#161d22\", \"#e3eaee\", \"#93a2ab\", \"#26313a\", \"#ff9248\", \"#1e0d02\"),\n },\n lagoon: {\n id: \"lagoon\",\n name: \"Lagoon\",\n description: \"healthcare, medicine, patient information, wellbeing\",\n look: \"white and aqua, calm teal\",\n fonts: \"modern\",\n formality: 0.45,\n chart: \"instrument\",\n keywords: /\\b(health\\w*|medical|patients?|clinic\\w*|treatment|symptoms?|doctor|nurse|therapy|wellness|wellbeing|diagnos\\w+|care|vaccine)\\b/gi,\n light: tokens(\"#f5fbfb\", \"#e2f3f3\", \"#0b2226\", \"#46666b\", \"#c8e2e3\", \"#0a7480\", \"#ffffff\"),\n dark: tokens(\"#06161a\", \"#0d2126\", \"#e0f3f4\", \"#8db4b9\", \"#17363d\", \"#4fd4df\", \"#031a1d\"),\n },\n plum: {\n id: \"plum\",\n name: \"Plum\",\n description: \"arts, music, theatre, film, exhibitions\",\n look: \"soft mauve, deep plum\",\n fonts: \"elegant\",\n formality: 0.5,\n chart: \"soft\",\n keywords: /\\b(art|artist|music|concert|theatre|theater|film|exhibition|gallery|opera|dance|festival|programme|culture|orchestra)\\b/gi,\n light: tokens(\"#fbf7fa\", \"#f1e6ef\", \"#24121f\", \"#6c5066\", \"#e2d0de\", \"#7a1f6b\", \"#ffffff\"),\n dark: tokens(\"#150b13\", \"#21131e\", \"#f5e8f1\", \"#b99ab2\", \"#372232\", \"#f0a6d8\", \"#2a0a22\"),\n },\n poster: {\n id: \"poster\",\n name: \"Poster\",\n description: \"events, sports, bold announcements, manifestos\",\n look: \"black and white, electric blue\",\n fonts: \"poster\",\n formality: 1,\n chart: \"flat\",\n keywords: /\\b(event|tickets?|match|league|team|championship|tournament|race|drop|lineup|kick-?off|season|score|win)\\b/gi,\n light: tokens(\"#ffffff\", \"#f0f0f0\", \"#000000\", \"#555555\", \"#d9d9d9\", \"#1a3cff\", \"#ffffff\"),\n dark: tokens(\"#000000\", \"#141414\", \"#ffffff\", \"#a8a8a8\", \"#2b2b2b\", \"#e8ff3a\", \"#101400\"),\n },\n espresso: {\n id: \"espresso\",\n name: \"Espresso\",\n description: \"food, recipes, coffee, restaurants, small makers\",\n look: \"cream, coffee brown, caramel\",\n fonts: \"slab\",\n formality: 0.5,\n chart: \"flat\",\n keywords: /\\b(recipe|food|restaurant|cook\\w*|bak\\w+|coffee|cafe|menu|ingredients?|wine|taste|kitchen|dinner|chef|flavou?r)\\b/gi,\n light: tokens(\"#f8f1e9\", \"#eee0d0\", \"#2a1a10\", \"#6f5646\", \"#dfcbb7\", \"#8b4a12\", \"#ffffff\"),\n dark: tokens(\"#17100b\", \"#231912\", \"#f3e6d8\", \"#b79f8c\", \"#3a2a1f\", \"#e9a763\", \"#241204\"),\n },\n console: {\n id: \"console\",\n name: \"Console\",\n description: \"changelogs, release notes, security write-ups, logs\",\n look: \"black screen, phosphor green\",\n fonts: \"mono\",\n formality: 0.95,\n chart: \"linework\",\n keywords: /\\b(changelog|release notes|patch|exploit|vulnerabilit\\w+|cve|payload|shell|kernel|stack trace|commit|binary|ctf|debug\\w*)\\b/gi,\n light: tokens(\"#f7f9f4\", \"#e8eee0\", \"#101a0e\", \"#4c5f48\", \"#d0dcc6\", \"#1f7a1f\", \"#ffffff\"),\n dark: tokens(\"#050a05\", \"#0c140c\", \"#d7f5d0\", \"#86a880\", \"#1a2a1a\", \"#5cf25c\", \"#021002\"),\n },\n}\n\nexport const themeList = Object.values(themes)\n\nexport type FontPairing = {\n id: FontPairingId\n name: string\n /** The headline typeface and its character, in a few words — read by the classifier. */\n look: string\n heading: string\n body: string\n mono: string\n /** Google Fonts `family=` parameters. */\n google: string[]\n headingWeight: number\n /** Heading letter-spacing in em. */\n tracking: number\n}\n\nconst MONO = `\"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace`\nconst MONO_GOOGLE = \"JetBrains+Mono:wght@400;600\"\nconst SANS = \"system-ui, sans-serif\"\nconst SERIF = \"Georgia, serif\"\n\nexport const fontPairings: Record<FontPairingId, FontPairing> = {\n editorial: {\n id: \"editorial\",\n name: \"Editorial\",\n look: \"Fraunces, literary serif\",\n heading: `\"Fraunces\", ${SERIF}`,\n body: `\"Newsreader\", ${SERIF}`,\n mono: MONO,\n google: [\"Fraunces:opsz,wght@9..144,400..700\", \"Newsreader:ital,opsz,wght@0,6..72,400..600;1,6..72,400..600\", MONO_GOOGLE],\n headingWeight: 560,\n tracking: -0.02,\n },\n modern: {\n id: \"modern\",\n name: \"Modern\",\n look: \"Inter, neutral sans\",\n heading: `\"Inter Tight\", \"Inter\", ${SANS}`,\n body: `\"Inter\", ${SANS}`,\n mono: MONO,\n google: [\"Inter+Tight:wght@500..800\", \"Inter:wght@400..700\", MONO_GOOGLE],\n headingWeight: 680,\n tracking: -0.03,\n },\n technical: {\n id: \"technical\",\n name: \"Technical\",\n look: \"Space Grotesk, engineered sans\",\n heading: `\"Space Grotesk\", ${SANS}`,\n body: `\"IBM Plex Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Space+Grotesk:wght@400..700\", \"IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.03,\n },\n elegant: {\n id: \"elegant\",\n name: \"Elegant\",\n look: \"Playfair Display, display serif\",\n heading: `\"Playfair Display\", ${SERIF}`,\n body: `\"Source Sans 3\", ${SANS}`,\n mono: MONO,\n google: [\"Playfair+Display:ital,wght@0,400..800;1,400..800\", \"Source+Sans+3:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.015,\n },\n friendly: {\n id: \"friendly\",\n name: \"Friendly\",\n look: \"Bricolage Grotesque, warm sans\",\n heading: `\"Bricolage Grotesque\", ${SANS}`,\n body: `\"DM Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Bricolage+Grotesque:opsz,wght@12..96,400..800\", \"DM+Sans:ital,opsz,wght@0,9..40,400..700;1,9..40,400..700\", MONO_GOOGLE],\n headingWeight: 700,\n tracking: -0.03,\n },\n classic: {\n id: \"classic\",\n name: \"Classic\",\n look: \"Spectral, book serif\",\n heading: `\"Spectral\", ${SERIF}`,\n body: `\"Public Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Spectral:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500\", \"Public+Sans:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.012,\n },\n scholarly: {\n id: \"scholarly\",\n name: \"Scholarly\",\n look: \"EB Garamond, old-style serif\",\n heading: `\"EB Garamond\", ${SERIF}`,\n body: `\"EB Garamond\", ${SERIF}`,\n mono: MONO,\n google: [\"EB+Garamond:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.01,\n },\n geometric: {\n id: \"geometric\",\n name: \"Geometric\",\n look: \"Sora, geometric sans\",\n heading: `\"Sora\", ${SANS}`,\n body: `\"Manrope\", ${SANS}`,\n mono: MONO,\n google: [\"Sora:wght@400..700\", \"Manrope:wght@400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.035,\n },\n luxe: {\n id: \"luxe\",\n name: \"Luxe\",\n look: \"Cormorant Garamond, fashion serif\",\n heading: `\"Cormorant Garamond\", ${SERIF}`,\n body: `\"Jost\", ${SANS}`,\n mono: MONO,\n google: [\"Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500\", \"Jost:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 500,\n tracking: -0.01,\n },\n rounded: {\n id: \"rounded\",\n name: \"Rounded\",\n look: \"Nunito, rounded sans\",\n heading: `\"Nunito\", ${SANS}`,\n body: `\"Nunito Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Nunito:wght@400;600;700;800;900\", \"Nunito+Sans:ital,wght@0,400;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 800,\n tracking: -0.02,\n },\n gazette: {\n id: \"gazette\",\n name: \"Gazette\",\n look: \"DM Serif Display, newspaper serif\",\n heading: `\"DM Serif Display\", ${SERIF}`,\n body: `\"Libre Franklin\", ${SANS}`,\n mono: MONO,\n google: [\"DM+Serif+Display:ital@0;1\", \"Libre+Franklin:ital,wght@0,400;0,500;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 400,\n tracking: -0.01,\n },\n slab: {\n id: \"slab\",\n name: \"Slab\",\n look: \"Zilla Slab, sturdy slab serif\",\n heading: `\"Zilla Slab\", ${SERIF}`,\n body: `\"Work Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Zilla+Slab:ital,wght@0,400;0,500;0,600;0,700;1,400\", \"Work+Sans:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.015,\n },\n poster: {\n id: \"poster\",\n name: \"Poster\",\n look: \"Archivo Black, heavy headlines\",\n heading: `\"Archivo Black\", \"Archivo\", ${SANS}`,\n body: `\"Archivo\", ${SANS}`,\n mono: MONO,\n google: [\"Archivo+Black\", \"Archivo:ital,wght@0,400;0,500;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 400,\n tracking: -0.035,\n },\n mono: {\n id: \"mono\",\n name: \"Mono\",\n look: \"JetBrains Mono, monospace\",\n heading: MONO,\n body: `\"IBM Plex Sans\", ${SANS}`,\n mono: MONO,\n google: [\"JetBrains+Mono:wght@400;500;600;700\", \"IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400\"],\n headingWeight: 600,\n tracking: -0.04,\n },\n}\n\nexport const fontPairingList = Object.values(fontPairings)\n\nexport function googleFontsUrl(pairing: FontPairing): string {\n return `https://fonts.googleapis.com/css2?${pairing.google.map((family) => `family=${family}`).join(\"&\")}&display=swap`\n}\n\n/**\n * What the classifier reads when choosing a theme: the content it suits, its\n * key colours and its typeface, so the choice can weigh appearance as well as\n * subject. Kept terse on purpose — classifier latency grows with every\n * character here, across every theme.\n */\nexport function describeTheme(theme: Theme): string {\n return `${theme.description}; ${theme.look}; ${fontPairings[theme.fonts].look}`\n}\n\n/** The complete default choice a theme stands for. */\nexport function themeChoice(id: PaletteId): ThemeChoice {\n const theme = themes[id]\n return { palette: id, fonts: theme.fonts, formality: theme.formality }\n}\n\n/**\n * The theme whose vocabulary stands out in the text, or `null` when none does.\n * This is evidence from the words themselves, independent of any classifier.\n */\nexport function matchTheme(text: string, stats: { codeBlocks: number; tables: number }): PaletteId | null {\n const words = Math.max(200, text.split(/\\s+/).length)\n let best: PaletteId | null = null\n let bestScore = 0.6\n for (const theme of themeList) {\n let score = ((text.match(theme.keywords) ?? []).length / words) * 100\n // Code and tables hint at a genre, but only words can confirm it.\n if (theme.id === \"terminal\") score += Math.min(3, stats.codeBlocks * 0.75)\n if (theme.id === \"ocean\" && score > 0) score += Math.min(0.6, stats.tables * 0.15)\n if (score > bestScore) [best, bestScore] = [theme.id, score]\n }\n return best\n}\n\n/**\n * Picks a theme from the words in the document — the fallback when no classifier\n * is configured, and what the loader is drawn in while the classifier decides.\n */\nexport function guessTheme(text: string, stats: { codeBlocks: number; tables: number }): ThemeChoice {\n return themeChoice(matchTheme(text, stats) ?? \"paper\")\n}\n\n/**\n * CSS custom properties for a theme. They use shadcn's token names, so every\n * shadcn component inside the scope picks the theme up without changes.\n */\nexport function themeVars(choice: ThemeChoice, appearance: Appearance): CSSProperties {\n const tokens = themes[choice.palette][appearance]\n const fonts = fontPairings[choice.fonts]\n const radius = 0.25 + (1 - choice.formality) * 0.75\n const soft = `color-mix(in oklab, ${tokens.accent} 12%, ${tokens.bg})`\n const vars: Record<string, string> = {\n \"--background\": tokens.bg,\n \"--foreground\": tokens.fg,\n \"--card\": tokens.surface,\n \"--card-foreground\": tokens.fg,\n \"--popover\": appearance === \"dark\" ? tokens.surface : tokens.bg,\n \"--popover-foreground\": tokens.fg,\n \"--primary\": tokens.accent,\n \"--primary-foreground\": tokens.accentFg,\n \"--secondary\": tokens.surface,\n \"--secondary-foreground\": tokens.fg,\n \"--muted\": tokens.surface,\n \"--muted-foreground\": tokens.muted,\n \"--accent\": soft,\n \"--accent-foreground\": tokens.fg,\n \"--border\": tokens.border,\n \"--input\": tokens.border,\n \"--ring\": tokens.accent,\n \"--radius\": `${radius.toFixed(3)}rem`,\n \"--smd-soft\": soft,\n \"--smd-bg\": tokens.bg,\n \"--smd-fg\": tokens.fg,\n \"--smd-surface\": tokens.surface,\n \"--smd-accent\": tokens.accent,\n \"--smd-font-heading\": fonts.heading,\n \"--smd-font-body\": fonts.body,\n \"--smd-font-mono\": fonts.mono,\n \"--smd-heading-weight\": String(fonts.headingWeight),\n \"--smd-tracking\": `${fonts.tracking}em`,\n colorScheme: appearance,\n }\n return vars as CSSProperties\n}\n","import { toText } from \"./analyze/text\"\nimport { describeTheme, themeList, themes } from \"./theme/themes\"\nimport type { DocumentPlan, Judgements, PaletteId, VizKind } from \"./types\"\n\n/**\n * Question shapes of a \"jev\"-compatible classifier endpoint: the request carries\n * a `state` (the text to judge) and named questions, each answered independently.\n */\nexport type ClassifierQuestion =\n | { type: \"choice\"; criteria: Record<string, string> }\n | { type: \"noul\"; criteria: { true: string; false: string } }\n | { type: \"score\"; criteria: string[] }\n\nexport type ClassifierRequest = {\n state: string\n questions: Record<string, ClassifierQuestion>\n}\n\nexport type ClassifierAnswer =\n | { type: \"choice\"; choice: string; confidence: number; probabilities?: Record<string, number> }\n | { type: \"noul\"; noul: number }\n | { type: \"score\"; score: number; confidence?: number }\n\nexport type ClassifierAnswers = Record<string, ClassifierAnswer>\n\n/** Anything that can answer a classifier request — usually a call to your own proxy route. */\nexport type Classify = (request: ClassifierRequest, signal?: AbortSignal) => Promise<ClassifierAnswers>\n\nconst cache = new Map<string, Promise<ClassifierAnswers>>()\n\n/**\n * A classifier that posts to `endpoint`. Point it at a route on your own server\n * that adds the API key (see `createClassifierHandler` in `stunning-md/server`) —\n * a key placed in browser code is public.\n */\nexport function createClassifier(options: { endpoint: string; headers?: Record<string, string> }): Classify {\n return (request) => {\n // Identical questions share one request, so it is never aborted on behalf of a single caller.\n const key = `${options.endpoint}\\n${JSON.stringify(request)}`\n const hit = cache.get(key)\n if (hit) return hit\n const pending = fetch(options.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(request),\n }).then(async (response) => {\n if (!response.ok) throw new Error(`classifier responded ${response.status}`)\n const data = (await response.json()) as { answers?: ClassifierAnswers }\n if (!data.answers) throw new Error(\"classifier response has no answers\")\n return data.answers\n })\n cache.set(key, pending)\n pending.catch(() => cache.delete(key))\n return pending\n }\n}\n\n/** Viz forms that plot figures against each other, as opposed to listing them. */\nconst COMPARATIVE: VizKind[] = [\"bar\", \"line\", \"area\", \"donut\", \"scatter\", \"stats\"]\n\nconst COMPARE_QUESTION: ClassifierQuestion = {\n type: \"noul\",\n criteria: {\n true: \"the numbers are measurements meant to be compared with each other, ranked, or read as a trend\",\n false: \"the numbers are identifiers, codes, settings, prices or specifications that are looked up one at a time\",\n },\n}\n\nconst WHOLE_QUESTION: ClassifierQuestion = {\n type: \"noul\",\n criteria: {\n true: \"the rows are parts that together make up one whole, such as shares of a total or a budget breakdown\",\n false: \"the rows are independent items that do not add up to a meaningful whole\",\n },\n}\n\n/**\n * A compact summary of the document: enough for tone and topic, small enough to\n * be cheap. `prose` should be running text only — code and table cells mislead.\n */\nexport function documentDigest(plan: DocumentPlan, prose: string): string {\n const headings = plan.sections.map((s) => s.titleText).filter(Boolean).slice(0, 12)\n return [\n plan.hero.titleText && `Title: ${plan.hero.titleText}`,\n headings.length && `Sections: ${headings.join(\" | \")}`,\n `Excerpt: ${prose.replace(/\\s+/g, \" \").slice(0, 400)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\")\n}\n\n/** How much probability a keyword match is worth when weighing the classifier's theme options. */\nconst HINT_WEIGHT = 0.25\n\nexport type JudgeOptions = {\n /** Minimum score before the classifier's theme is accepted over the keyword guess. */\n confidence?: number\n /** The theme the document's own keywords point to, if any (see `matchTheme`). */\n themeHint?: PaletteId\n /** Requests in flight at once. Default 3. */\n concurrency?: number\n /** Upper bound on tables sent for a form judgement. */\n maxTables?: number\n signal?: AbortSignal\n /**\n * Called as each answer lands, with the judgements so far and what is still\n * outstanding: `\"theme\"`, `\"hero\"`, or the id of a data block.\n */\n onProgress?: (progress: JudgeProgress) => void\n}\n\nexport type JudgeProgress = { judgements: Judgements; pending: string[] }\n\n/**\n * Asks the classifier the questions structure cannot answer: which theme —\n * colours and typefaces together — suits the content, whether a table's numbers\n * are there to be compared or just looked up, and whether the leading image\n * deserves to be the hero.\n * Questions are asked top-down — theme, hero, then tables in document order —\n * so the top of the page settles first. A failed request simply leaves its\n * judgement unset.\n */\nexport async function judgeDocument(\n plan: DocumentPlan,\n prose: string,\n classify: Classify,\n options: JudgeOptions = {},\n): Promise<Judgements> {\n const threshold = options.confidence ?? 0.4\n const judgements: Judgements = {}\n // A few requests at a time, in order, so small classifier deployments are not\n // flooded by a document full of tables.\n const queue: (() => Promise<void>)[] = []\n const pending = new Set<string>()\n const report = () =>\n options.onProgress?.({ judgements: { ...judgements, viz: judgements.viz && { ...judgements.viz } }, pending: [...pending] })\n const attempt = (key: string, request: ClassifierRequest, apply: (answers: ClassifierAnswers) => void) => {\n pending.add(key)\n queue.push(() =>\n classify(request, options.signal)\n .then(apply, () => undefined)\n .then(() => {\n pending.delete(key)\n if (!options.signal?.aborted) report()\n }),\n )\n }\n\n // One question settles the whole look. Each option spells out the theme's\n // colours and typefaces as well as the content it suits.\n attempt(\n \"theme\",\n {\n state: documentDigest(plan, prose),\n questions: { theme: { type: \"choice\", criteria: Object.fromEntries(themeList.map((t) => [t.id, describeTheme(t)])) } },\n },\n ({ theme }) => {\n if (theme?.type !== \"choice\") return\n // The document's own vocabulary counts as evidence too: it settles close\n // calls, but cannot overturn a classifier that is sure of itself.\n const odds: Record<string, number> = theme.probabilities ?? { [theme.choice]: theme.confidence }\n let best = \"\"\n let bestScore = 0\n for (const id of Object.keys(odds)) {\n const score = odds[id] + (id === options.themeHint ? HINT_WEIGHT : 0)\n if (id in themes && score > bestScore) [best, bestScore] = [id, score]\n }\n if (!best || bestScore < threshold) return\n const chosen = themes[best as PaletteId]\n judgements.theme = { palette: chosen.id, fonts: chosen.fonts, formality: chosen.formality }\n },\n )\n\n const image = plan.hero.image\n if (image) {\n attempt(\n \"hero\",\n {\n state: [\n `Document: ${plan.hero.titleText}`,\n `First image — alt text: \"${image.alt}\", file: ${image.src.split(/[?#]/)[0].split(\"/\").pop()}`,\n image.meta && `size: ${image.meta.width}×${image.meta.height}px`,\n `Text right after it: ${toText(plan.hero.lead.flat()).slice(0, 200)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n questions: {\n hero: {\n type: \"noul\",\n criteria: {\n true: \"the image is a logo, banner, cover photo or key visual that represents the whole document\",\n false: \"the image is a badge, screenshot, diagram, chart or incidental illustration that belongs in the body\",\n },\n },\n },\n },\n ({ hero }) => {\n // Only a clear \"no\" demotes the image; the structural checks already vouched for it.\n if (hero?.type === \"noul\" && hero.noul < 0.2) judgements.heroImageUsable = false\n },\n )\n }\n\n const tables = plan.sections\n .flatMap((section) =>\n section.blocks\n .flatMap((block) => (block.kind === \"cards\" ? block.items.flatMap((item) => item.blocks) : [block]))\n .flatMap((block) => (block.kind === \"data\" ? [{ section, block }] : [])),\n )\n .filter(({ block }) => block.viz.candidates.length > 1)\n .slice(0, options.maxTables ?? 8)\n\n for (const { section, block } of tables) {\n const { columns, rows } = block.table\n const { viz } = block\n if (!COMPARATIVE.includes(viz.kind)) continue\n const lines = [\n columns.map((c) => c.label).join(\" | \"),\n ...rows.slice(0, 12).map((row) => columns.map((c) => row.text[c.key]).join(\" | \")),\n ]\n // Whether numbers are measurements or mere reference values, and whether rows\n // form a whole, depends on what they mean — which the column types cannot say.\n const partsPossible = viz.candidates.includes(\"donut\") && viz.rowIndices.length >= 3\n attempt(\n block.id,\n {\n state: `Section: ${section.titleText || plan.hero.titleText}\\n${lines.join(\"\\n\")}`,\n questions: { compare: COMPARE_QUESTION, ...(partsPossible ? { whole: WHOLE_QUESTION } : {}) },\n },\n ({ compare, whole }) => {\n const set = (kind: VizKind) => ((judgements.viz ??= {})[block.id] = kind)\n if (compare?.type === \"noul\" && compare.noul < 0.12) return set(\"table\")\n if (whole?.type !== \"noul\") return\n if (whole.noul >= 0.9 && viz.kind !== \"donut\") set(\"donut\")\n else if (whole.noul <= 0.12 && viz.kind === \"donut\") set(\"bar\")\n },\n )\n }\n\n const worker = async () => {\n for (let task = queue.shift(); task; task = queue.shift()) {\n if (options.signal?.aborted) return\n await task()\n }\n }\n report()\n // The theme question is by far the heaviest and everything visible depends on\n // it, so it runs alone; the lighter questions then share the connection.\n await queue.shift()?.()\n await Promise.all(Array.from({ length: options.concurrency ?? 3 }, worker))\n return judgements\n}\n"],"mappings":"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","names":["tokens","children","words"]}
1
+ {"version":3,"sources":["../src/stunning-md/parse.ts","../src/stunning-md/analyze/text.ts","../src/stunning-md/analyze/tables.ts","../src/stunning-md/analyze/plan.ts","../src/stunning-md/theme/themes.ts","../src/stunning-md/classifier.ts"],"sourcesContent":["import type { Image, Paragraph, PhrasingContent, Root, RootContent } from \"mdast\"\nimport remarkFrontmatter from \"remark-frontmatter\"\nimport remarkGfm from \"remark-gfm\"\nimport remarkMath from \"remark-math\"\nimport remarkParse from \"remark-parse\"\nimport { unified } from \"unified\"\nimport { parse as parseYaml } from \"yaml\"\n\nexport type Frontmatter = Record<string, unknown>\n\nexport type ParsedMarkdown = {\n root: Root\n frontmatter: Frontmatter\n}\n\nconst processor = unified().use(remarkParse).use(remarkGfm).use(remarkMath).use(remarkFrontmatter, [\"yaml\"])\n\nconst attr = (tag: string, name: string) =>\n new RegExp(`\\\\b${name}\\\\s*=\\\\s*(\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`, \"i\").exec(tag)?.slice(2).find((v) => v != null)\n\nconst decodeEntities = (text: string) =>\n text\n .replace(/&nbsp;/g, \" \")\n .replace(/&amp;/g, \"&\")\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n\n/**\n * Raw HTML is never injected. The useful parts are recovered as markdown nodes —\n * images, line breaks and text — and everything else is dropped.\n */\nfunction htmlToPhrasing(html: string): PhrasingContent[] {\n const out: PhrasingContent[] = []\n const withoutComments = html.replace(/<!--[\\s\\S]*?-->/g, \"\").replace(/<(script|style)[\\s\\S]*?<\\/\\1>/gi, \"\")\n const tokens = withoutComments.split(/(<[^>]+>)/g)\n let href: string | undefined\n for (const token of tokens) {\n if (!token) continue\n if (token.startsWith(\"<\")) {\n if (/^<img\\b/i.test(token)) {\n const src = attr(token, \"src\")\n if (!src) continue\n const image: Image = { type: \"image\", url: src, alt: attr(token, \"alt\") ?? \"\", title: attr(token, \"title\") ?? null }\n out.push(href ? { type: \"link\", url: href, children: [image] } : image)\n } else if (/^<br\\b/i.test(token)) {\n out.push({ type: \"break\" })\n } else if (/^<a\\b/i.test(token)) {\n href = attr(token, \"href\")\n } else if (/^<\\/a/i.test(token)) {\n href = undefined\n }\n continue\n }\n const text = decodeEntities(token).replace(/\\s+/g, \" \")\n if (!text.trim()) continue\n out.push(href ? { type: \"link\", url: href, children: [{ type: \"text\", value: text }] } : { type: \"text\", value: text })\n }\n return out\n}\n\ntype Definitions = Map<string, { url: string; title?: string | null }>\n\nfunction collectDefinitions(nodes: RootContent[], into: Definitions = new Map()): Definitions {\n for (const node of nodes) {\n if (node.type === \"definition\") into.set(node.identifier, { url: node.url, title: node.title })\n else if (\"children\" in node) collectDefinitions(node.children as RootContent[], into)\n }\n return into\n}\n\nfunction cleanPhrasing(children: PhrasingContent[], defs: Definitions): PhrasingContent[] {\n return children.flatMap((child, index): PhrasingContent[] => {\n // \"$60M and $64M\" is money, not maths: as in Pandoc, the dollars must hug the\n // formula and the closing one must not run into a digit.\n if (child.type === \"inlineMath\") {\n const next = children[index + 1]\n const digitAfter = next?.type === \"text\" && /^\\d/.test(next.value)\n if (/^\\s|\\s$/.test(child.value) || digitAfter) return [{ type: \"text\", value: `$${child.value}$` }]\n }\n if (child.type === \"html\") return htmlToPhrasing(child.value)\n // Reference-style links and images are resolved so renderers only meet the direct forms.\n if (child.type === \"imageReference\") {\n const def = defs.get(child.identifier)\n return def ? [{ type: \"image\", url: def.url, alt: child.alt ?? \"\", title: def.title }] : []\n }\n if (child.type === \"linkReference\") {\n const def = defs.get(child.identifier)\n const inner = cleanPhrasing(child.children, defs)\n return def ? [{ type: \"link\", url: def.url, title: def.title, children: inner } as PhrasingContent] : inner\n }\n if (\"children\" in child) {\n return [{ ...child, children: cleanPhrasing(child.children as PhrasingContent[], defs) } as PhrasingContent]\n }\n return [child]\n })\n}\n\nfunction cleanBlocks(nodes: RootContent[], defs: Definitions): RootContent[] {\n return nodes.flatMap((node): RootContent[] => {\n if (node.type === \"html\") {\n // An HTML heading (`<h1 align=\"center\">`) keeps its role.\n const heading = /^\\s*<h([1-6])\\b[^>]*>([\\s\\S]*?)<\\/h\\1>\\s*$/i.exec(node.value)\n if (heading) {\n const children = htmlToPhrasing(heading[2])\n return children.length ? [{ type: \"heading\", depth: Number(heading[1]) as 1, children }] : []\n }\n const children = htmlToPhrasing(node.value)\n return children.length ? [{ type: \"paragraph\", children } satisfies Paragraph] : []\n }\n if (node.type === \"yaml\" || node.type === \"definition\") return []\n if (node.type === \"paragraph\" || node.type === \"heading\") {\n const children = cleanPhrasing(node.children, defs)\n return children.length ? [{ ...node, children }] : []\n }\n if (node.type === \"tableCell\") return [{ ...node, children: cleanPhrasing(node.children, defs) }]\n if (\"children\" in node) {\n return [{ ...node, children: cleanBlocks(node.children as RootContent[], defs) } as RootContent]\n }\n return [node]\n })\n}\n\nexport function parseMarkdown(markdown: string): ParsedMarkdown {\n const tree = processor.parse(markdown) as Root\n let frontmatter: Frontmatter = {}\n const first = tree.children[0]\n if (first?.type === \"yaml\") {\n try {\n const data = parseYaml(first.value)\n if (data && typeof data === \"object\" && !Array.isArray(data)) frontmatter = data as Frontmatter\n } catch {\n // Malformed frontmatter is ignored rather than failing the whole document.\n }\n }\n return { root: { ...tree, children: cleanBlocks(tree.children, collectDefinitions(tree.children)) }, frontmatter }\n}\n","import type { Image, Nodes, PhrasingContent } from \"mdast\"\n\n/** Plain text of any markdown node. Image alt text is not counted as prose. */\nexport function toText(node: Nodes | Nodes[] | undefined): string {\n if (!node) return \"\"\n if (Array.isArray(node)) return node.map(toText).join(\"\")\n if (node.type === \"text\" || node.type === \"inlineCode\" || node.type === \"code\") return node.value\n if (node.type === \"break\") return \" \"\n if (node.type === \"image\") return \"\"\n if (\"children\" in node) {\n const separator = node.type === \"paragraph\" || node.type === \"heading\" || node.type === \"listItem\" ? \" \" : \"\"\n return (node.children as Nodes[]).map(toText).join(separator)\n }\n return \"\"\n}\n\n/** Running prose only — no headings, code, tables or maths. This is what a reader would call \"the text\". */\nexport function proseOf(node: Nodes): string {\n if (node.type === \"heading\" || node.type === \"code\" || node.type === \"table\" || node.type === \"math\" || node.type === \"yaml\") return \"\"\n if (node.type === \"paragraph\") return toText(node)\n if (\"children\" in node) return (node.children as Nodes[]).map(proseOf).filter(Boolean).join(\" \")\n return \"\"\n}\n\nexport function countWords(text: string): number {\n const trimmed = text.trim()\n return trimmed ? trimmed.split(/\\s+/).length : 0\n}\n\nexport function wordsIn(node: Nodes | Nodes[] | undefined): number {\n if (!node) return 0\n if (Array.isArray(node)) return node.reduce((sum, child) => sum + wordsIn(child), 0)\n if (node.type === \"code\") return 0\n if (\"children\" in node && node.type !== \"paragraph\" && node.type !== \"heading\") {\n return (node.children as Nodes[]).reduce((sum, child) => sum + wordsIn(child), 0)\n }\n return countWords(toText(node))\n}\n\nexport function slugify(text: string, taken: Set<string>): string {\n const base =\n text\n .toLowerCase()\n .normalize(\"NFKD\")\n .replace(/[̀-ͯ]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"section\"\n let slug = base\n for (let n = 2; taken.has(slug); n++) slug = `${base}-${n}`\n taken.add(slug)\n return slug\n}\n\n/** The images a paragraph consists of, if it holds nothing but images (optionally linked). */\nexport function imagesOnly(children: PhrasingContent[]): { image: Image; href?: string }[] | null {\n const found: { image: Image; href?: string }[] = []\n for (const child of children) {\n if (child.type === \"image\") found.push({ image: child })\n else if (child.type === \"link\" && child.children.length === 1 && child.children[0].type === \"image\") {\n found.push({ image: child.children[0], href: child.url })\n } else if (child.type === \"break\" || (child.type === \"text\" && !child.value.trim())) continue\n else return null\n }\n return found.length ? found : null\n}\n","import type { PhrasingContent, Table } from \"mdast\"\nimport type { ChartSpec, ColumnType, TableColumn, TableModel, TableRow, VizKind, VizPlan } from \"../types\"\nimport { toText } from \"./text\"\n\ntype Unit = { prefix: string; suffix: string }\ntype ParsedNumber = { value: number } & Unit\n\nconst MISSING = /^(|-|–|—|n\\/?a|na|null|none|tbd|\\?)$/i\nconst CURRENCY = \"[$€£¥₹₩₽]|[A-Z]{1,3}\\\\$|(?:USD|EUR|GBP|SGD|JPY|CHF|AUD|CAD|INR|CNY)\\\\b\"\nconst NUMBER = new RegExp(\n `^([(]?)\\\\s*([-−–+]?)\\\\s*(${CURRENCY})?\\\\s*([-−–+]?)\\\\s*(\\\\d[\\\\d,' ]*(?:\\\\.\\\\d+)?|\\\\.\\\\d+)\\\\s*` +\n `(k|K|M|B|T|bn|mn|m|thousand|million|billion|trillion)?\\\\s*(%|‰|[a-zA-Z°µ²³/]{1,8})?\\\\s*([)]?)$`,\n)\nconst MAGNITUDE: Record<string, number> = {\n k: 1e3, K: 1e3, thousand: 1e3,\n M: 1e6, m: 1e6, mn: 1e6, million: 1e6,\n B: 1e9, bn: 1e9, billion: 1e9,\n T: 1e12, trillion: 1e12,\n}\n\nexport function parseNumber(raw: string): ParsedNumber | null {\n const text = raw.trim()\n const match = NUMBER.exec(text)\n if (!match) return null\n const [, open, signA, currency = \"\", signB, digits, magnitude, suffixRaw = \"\", close] = match\n let suffix = suffixRaw\n let scale = 1\n if (magnitude) {\n // A bare lowercase \"m\" is metres unless money is involved (\"$5m\").\n if (magnitude === \"m\" && !currency) suffix = `m${suffix}`\n else scale = MAGNITUDE[magnitude]\n }\n const value = Number(digits.replace(/[,' ]/g, \"\")) * scale\n if (!Number.isFinite(value)) return null\n const negative = (open && close) || /[-−–]/.test(signA + signB)\n return { value: negative ? -value : value, prefix: currency.trim(), suffix }\n}\n\nconst MONTHS = [\"jan\", \"feb\", \"mar\", \"apr\", \"may\", \"jun\", \"jul\", \"aug\", \"sep\", \"oct\", \"nov\", \"dec\"]\nconst monthIndex = (name: string) => MONTHS.indexOf(name.slice(0, 3).toLowerCase())\nconst MONTH_NAME = \"(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\\\\.?\"\nconst fullYear = (y: string) => (y.length === 2 ? 2000 + Number(y) : Number(y))\nconst validYear = (y: number) => y >= 1000 && y <= 2200\n\n/** Parses the date shapes that show up in tables. Returns UTC epoch milliseconds. */\nexport function parseDate(raw: string): number | null {\n const text = raw.trim().replace(/\\s+/g, \" \")\n let m: RegExpExecArray | null\n if ((m = /^(\\d{4})$/.exec(text))) return validYear(+m[1]) ? Date.UTC(+m[1], 0, 1) : null\n if ((m = /^(\\d{4})[-/.](\\d{1,2})(?:[-/.](\\d{1,2}))?(?:[T ][\\d:.]+Z?)?$/.exec(text))) {\n return +m[2] >= 1 && +m[2] <= 12 ? Date.UTC(+m[1], +m[2] - 1, +(m[3] ?? 1)) : null\n }\n if ((m = /^(?:FY|CY) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[1]), 0, 1)\n if ((m = /^Q([1-4]) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[2]), (+m[1] - 1) * 3, 1)\n if ((m = /^'?(\\d{2}|\\d{4}) ?Q([1-4])$/i.exec(text))) return Date.UTC(fullYear(m[1]), (+m[2] - 1) * 3, 1)\n if ((m = /^H([12]) ?'?(\\d{2}|\\d{4})$/i.exec(text))) return Date.UTC(fullYear(m[2]), (+m[1] - 1) * 6, 1)\n if ((m = new RegExp(`^${MONTH_NAME}$`, \"i\").exec(text))) return Date.UTC(2000, monthIndex(m[1]), 1)\n if ((m = new RegExp(`^${MONTH_NAME} '?(\\\\d{2}|\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(fullYear(m[2]), monthIndex(m[1]), 1)\n }\n if ((m = new RegExp(`^${MONTH_NAME} (\\\\d{1,2})(?:st|nd|rd|th)?,? (\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(+m[3], monthIndex(m[1]), +m[2])\n }\n if ((m = new RegExp(`^(\\\\d{1,2})(?:st|nd|rd|th)? ${MONTH_NAME},? (\\\\d{4})$`, \"i\").exec(text))) {\n return Date.UTC(+m[3], monthIndex(m[2]), +m[1])\n }\n if ((m = /^(\\d{1,2})[/.](\\d{1,2})[/.](\\d{2}|\\d{4})$/.exec(text))) {\n const [a, b] = [+m[1], +m[2]]\n const [month, day] = a > 12 ? [b, a] : [a, b]\n return month >= 1 && month <= 12 && day <= 31 ? Date.UTC(fullYear(m[3]), month - 1, day) : null\n }\n return null\n}\n\nconst ID_HEADER = /^(#|no\\.?|nr\\.?|id|rank|pos(ition)?|s\\/n|index|ref)$/i\nconst TIME_HEADER = /\\b(years?|dates?|when|periods?|months?|quarters?|fy|time|days?|weeks?)\\b/i\nconst SHARE_HEADER = /\\b(share|allocation|mix|breakdown|distribution|portion|weight|split|composition)\\b/i\nconst SUMMARY_LABEL = /^(grand )?(total|sum|overall|subtotal|average|avg|mean|median|all)\\b/i\n\n/** Reads \"Revenue ($M)\" → { prefix: \"$\", suffix: \"M\" }. */\nfunction unitFromHeader(label: string): Unit | undefined {\n const match = /[([]\\s*([^)\\]]{1,10})\\s*[)\\]]\\s*$/.exec(label)\n if (!match) return label.trim().endsWith(\"%\") ? { prefix: \"\", suffix: \"%\" } : undefined\n const hint = match[1].trim()\n const currency = new RegExp(`^(${CURRENCY})`).exec(hint)\n if (currency) return { prefix: currency[1], suffix: hint.slice(currency[1].length).trim() }\n if (/^in /i.test(hint)) return { prefix: \"\", suffix: ` ${hint.slice(3)}` }\n return { prefix: \"\", suffix: hint === \"%\" ? \"%\" : ` ${hint}` }\n}\n\nconst mostCommon = (values: string[]) => {\n const counts = new Map<string, number>()\n for (const v of values) counts.set(v, (counts.get(v) ?? 0) + 1)\n return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? \"\"\n}\n\nexport function buildTableModel(node: Table): TableModel {\n const [head, ...body] = node.children\n const headers = (head?.children ?? []).map((cell) => toText(cell).trim())\n const width = Math.max(headers.length, ...body.map((row) => row.children.length))\n const cellText = body.map((row) => Array.from({ length: width }, (_, c) => toText(row.children[c]).trim()))\n\n const columns: TableColumn[] = []\n const parsed: (number | string | null)[][] = body.map(() => [])\n\n for (let c = 0; c < width; c++) {\n const label = headers[c] || \"\"\n const cells = cellText.map((row) => row[c])\n const present = cells.filter((text) => !MISSING.test(text))\n const numbers = cells.map((text) => (MISSING.test(text) ? null : parseNumber(text)))\n const dates = cells.map((text) => (MISSING.test(text) ? null : parseDate(text)))\n const numericCount = numbers.filter(Boolean).length\n const dateCount = dates.filter((d) => d != null).length\n const enough = (count: number) => present.length > 0 && count >= Math.max(1, Math.ceil(present.length * 0.9))\n\n const allYears = enough(dateCount) && present.every((text) => /^\\d{4}$/.test(text))\n let type: ColumnType = \"text\"\n // Bare four-digit numbers are years only where a time axis is expected.\n if (enough(dateCount) && (!allYears || c === 0 || TIME_HEADER.test(label))) type = \"date\"\n else if (enough(numericCount) && !ID_HEADER.test(label)) type = \"number\"\n\n let unit: Unit | undefined\n if (type === \"number\") {\n const found = numbers.filter((n): n is ParsedNumber => n != null)\n const prefix = mostCommon(found.map((n) => n.prefix))\n const suffix = mostCommon(found.map((n) => n.suffix))\n unit = prefix || suffix ? { prefix, suffix } : unitFromHeader(label)\n }\n\n const align = node.align?.[c] ?? (type === \"number\" ? \"right\" : \"left\")\n columns.push({ key: `c${c}`, label: label || `Column ${c + 1}`, type, unit, align })\n cellText.forEach((row, r) => {\n parsed[r][c] = type === \"number\" ? (numbers[r]?.value ?? null) : type === \"date\" ? dates[r] : row[c]\n })\n }\n\n const rows: TableRow[] = body.map((row, r) => ({\n text: Object.fromEntries(columns.map((col, c) => [col.key, cellText[r][c]])),\n value: Object.fromEntries(columns.map((col, c) => [col.key, parsed[r][c]])),\n cells: Array.from({ length: width }, (_, c) => (row.children[c]?.children ?? []) as PhrasingContent[]),\n }))\n\n return { node, columns, rows }\n}\n\nconst unitKey = (unit?: Unit) => `${unit?.prefix ?? \"\"}|${unit?.suffix ?? \"\"}`\n\n/**\n * Series are only plotted together when they share a unit and a comparable scale.\n * Anything else gets its own chart — two scales on one plot mislead.\n */\nfunction groupSeries(model: TableModel, numeric: TableColumn[], rowIndices: number[]): ChartSpec[] {\n const byUnit = new Map<string, TableColumn[]>()\n for (const column of numeric) {\n const key = unitKey(column.unit)\n byUnit.set(key, [...(byUnit.get(key) ?? []), column])\n }\n const charts: ChartSpec[] = []\n for (const group of byUnit.values()) {\n const peak = (column: TableColumn) =>\n Math.max(0, ...rowIndices.map((r) => Math.abs((model.rows[r].value[column.key] as number | null) ?? 0)))\n const sorted = [...group].sort((a, b) => peak(b) - peak(a))\n let cluster: TableColumn[] = []\n for (const column of sorted) {\n if (cluster.length && peak(cluster[0]) > peak(column) * 30) {\n charts.push({ seriesKeys: cluster.map((c) => c.key), unit: cluster[0].unit })\n cluster = []\n }\n cluster.push(column)\n }\n if (cluster.length) charts.push({ seriesKeys: cluster.map((c) => c.key), unit: cluster[0].unit })\n }\n // Keep the document's column order within and across charts.\n const order = (key: string) => model.columns.findIndex((c) => c.key === key)\n for (const chart of charts) chart.seriesKeys.sort((a, b) => order(a) - order(b))\n return charts.sort((a, b) => order(a.seriesKeys[0]) - order(b.seriesKeys[0]))\n}\n\n/** Chooses how a table is best shown, from its shape and the types of its columns. */\nexport function planViz(model: TableModel): VizPlan {\n const { columns, rows } = model\n const all = rows.map((_, i) => i)\n const table = (reason: string, candidates: VizKind[] = []): VizPlan => ({\n kind: \"table\",\n charts: [],\n rowIndices: all,\n candidates: [\"table\", ...candidates],\n reason,\n })\n\n if (rows.length === 0 || columns.length < 2) return table(\"too small to visualise\")\n\n let numeric = columns.filter((c) => c.type === \"number\")\n const dateColumn = columns.find((c) => c.type === \"date\")\n const texts = columns.filter((c) => c.type === \"text\")\n let label: TableColumn | undefined = dateColumn ?? texts[0]\n // A leading numeric column that only ever rises (1, 4, 8, 16…) is an ordered axis, not a measure.\n let ordinal = false\n if (!label && rows.length >= 3 && numeric.length >= 2 && numeric[0] === columns[0]) {\n const steps = rows.map((row) => row.value[columns[0].key] as number | null)\n if (steps.every((v, i) => v != null && (i === 0 || v > steps[i - 1]!))) {\n label = columns[0]\n numeric = numeric.slice(1)\n ordinal = true\n }\n }\n const otherTexts = texts.filter((c) => c !== label)\n const avgLength = (column: TableColumn) =>\n rows.reduce((sum, row) => sum + row.text[column.key].length, 0) / rows.length\n\n // A single row of numbers is a row of headline figures.\n if (rows.length === 1 && numeric.length >= 2 && numeric.length <= 6) {\n return {\n kind: \"stats\",\n labelKey: label?.key,\n charts: [{ seriesKeys: numeric.map((c) => c.key) }],\n rowIndices: all,\n candidates: [\"stats\", \"table\"],\n reason: \"one row of figures\",\n }\n }\n if (rows.length < 2) return table(\"single row\")\n\n if (numeric.length === 0) {\n if (dateColumn && otherTexts.length + (texts.includes(label!) ? 0 : texts.length) >= 1 && rows.length >= 3) {\n return {\n kind: \"timeline\",\n labelKey: dateColumn.key,\n charts: [],\n rowIndices: all,\n candidates: [\"timeline\", \"table\"],\n reason: \"dated events without figures\",\n }\n }\n if (columns.length === 2 && rows.length <= 12 && avgLength(columns[0]) <= 28) {\n return {\n kind: \"facts\",\n labelKey: columns[0].key,\n charts: [],\n rowIndices: all,\n candidates: [\"facts\", \"table\"],\n reason: \"short key–value pairs\",\n }\n }\n return table(\"no numeric columns\")\n }\n\n if (!label) {\n if (numeric.length >= 2 && rows.length >= 5) {\n return {\n kind: \"scatter\",\n labelKey: numeric[0].key,\n charts: [{ seriesKeys: [numeric[1].key], unit: numeric[1].unit }],\n rowIndices: all,\n candidates: [\"scatter\", \"table\"],\n reason: \"two measures and no category\",\n }\n }\n return table(\"no label column\")\n }\n\n // A lone column of bare years, in order, beside descriptions is a sequence of\n // events. Drawn as bars it would compare the years as if they were amounts.\n if (!dateColumn && numeric.length === 1 && label.type === \"text\" && rows.length >= 3 && avgLength(label) >= 16) {\n const years = rows.map((row) => (/^\\d{4}$/.test(row.text[numeric[0].key]) ? (row.value[numeric[0].key] as number) : NaN))\n const plausible = years.every((year) => year >= 1000 && year <= 2100)\n const ordered = years.every((year, i) => i === 0 || year >= years[i - 1]) || years.every((year, i) => i === 0 || year <= years[i - 1])\n if (plausible && ordered) {\n return {\n kind: \"timeline\",\n labelKey: numeric[0].key,\n charts: [],\n rowIndices: all,\n candidates: [\"timeline\", \"table\"],\n reason: \"years in order beside descriptions\",\n }\n }\n }\n\n // Records with as much description as measurement read better as a table.\n if (otherTexts.length >= numeric.length || otherTexts.some((c) => avgLength(c) > 40)) {\n // …unless they are dated, in which case they are a sequence of events.\n if (dateColumn && rows.length >= 3 && numeric.length <= 1) {\n return {\n kind: \"timeline\",\n labelKey: dateColumn.key,\n charts: [],\n rowIndices: all,\n candidates: [\"timeline\", \"table\"],\n reason: \"dated events with descriptions\",\n }\n }\n return table(\"descriptive columns outweigh figures\", dateColumn ? [\"timeline\"] : [])\n }\n\n const labels = rows.map((row) => row.text[label.key])\n let rowIndices = all.filter((r) => !(rows.length > 2 && SUMMARY_LABEL.test(labels[r])))\n if (new Set(rowIndices.map((r) => labels[r])).size < rowIndices.length) {\n return table(\"repeated labels — these are records, not categories\")\n }\n if (rowIndices.length < 2) return table(\"too few rows\")\n\n const charts = groupSeries(model, numeric, rowIndices)\n if (charts.length > 3 || charts.some((c) => c.seriesKeys.length > 8)) {\n return table(\"too many unrelated measures\", [\"bar\"])\n }\n const single = charts.length === 1 && charts[0].seriesKeys.length === 1\n const count = rowIndices.length\n\n if (label.type === \"date\" || ordinal) {\n const axis = label.key\n const times = rowIndices.map((r) => rows[r].value[axis] as number)\n if (times[0] > times[times.length - 1]) rowIndices = [...rowIndices].reverse()\n // One measure per plot: a trend once there are enough periods, bars to compare a few.\n const alone = charts.every((chart) => chart.seriesKeys.length === 1)\n const kind: VizKind = alone ? (count >= 8 ? \"area\" : \"bar\") : count >= 4 ? \"line\" : \"bar\"\n return {\n kind,\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [...new Set<VizKind>([kind, \"line\", \"bar\", \"area\", \"table\"])],\n reason: `${count} ${ordinal ? \"steps\" : \"periods\"} × ${numeric.length} measure(s)`,\n }\n }\n\n if (count > 30) return table(\"too many categories for a readable chart\", [\"bar\"])\n\n if (single) {\n const column = columns.find((c) => c.key === charts[0].seriesKeys[0])!\n const values = rowIndices.map((r) => (rows[r].value[column.key] as number | null) ?? 0)\n if (count <= 4 && otherTexts.length === 0) {\n return {\n kind: \"stats\",\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [\"stats\", \"bar\", \"table\"],\n reason: \"a handful of headline figures\",\n }\n }\n const sum = values.reduce((a, b) => a + b, 0)\n const wholeOf100 = column.unit?.suffix === \"%\" && sum > 98 && sum < 102\n if (count >= 3 && count <= 6 && values.every((v) => v >= 0) && (wholeOf100 || SHARE_HEADER.test(column.label))) {\n return {\n kind: \"donut\",\n labelKey: label.key,\n charts,\n rowIndices,\n candidates: [\"donut\", \"bar\", \"table\"],\n reason: \"parts of a whole\",\n }\n }\n }\n\n const cells = count * numeric.length\n if (!single && cells > 48) return table(\"too dense to chart\", [\"bar\"])\n const longest = Math.max(...rowIndices.map((r) => labels[r].length))\n return {\n kind: \"bar\",\n labelKey: label.key,\n charts,\n horizontal: single && (count > 7 || longest > 14),\n rowIndices,\n candidates: single && count <= 6 ? [\"bar\", \"donut\", \"stats\", \"table\"] : [\"bar\", \"table\"],\n reason: `${count} categories × ${numeric.length} measure(s)`,\n }\n}\n\n/** Re-plans a table as a specific form, when that form is one of its candidates. */\nexport function withVizKind(plan: VizPlan, model: TableModel, kind: VizKind): VizPlan {\n if (kind === plan.kind || !plan.candidates.includes(kind)) return plan\n const next: VizPlan = { ...plan, kind, reason: `${plan.reason} → overridden to ${kind}` }\n if (kind === \"timeline\") {\n next.labelKey = model.columns.find((c) => c.type === \"date\")?.key ?? plan.labelKey\n next.rowIndices = model.rows.map((_, i) => i)\n }\n if (kind === \"bar\" && plan.charts.length === 0) {\n const numeric = model.columns.filter((c) => c.type === \"number\")\n const label = model.columns.find((c) => c.type !== \"number\")\n if (!numeric.length || !label) return plan\n next.labelKey = label.key\n next.charts = groupSeries(model, numeric, plan.rowIndices)\n }\n if (kind === \"bar\") {\n const longest = Math.max(...next.rowIndices.map((r) => model.rows[r].text[next.labelKey ?? \"\"]?.length ?? 0))\n const single = next.charts.length === 1 && next.charts[0].seriesKeys.length === 1\n next.horizontal = single && (next.rowIndices.length > 7 || longest > 14)\n }\n return next\n}\n\n/** Compact, unit-aware number formatting for axes, tooltips and stat tiles. */\nexport function formatValue(value: number, unit?: Unit): string {\n const abs = Math.abs(value)\n const body =\n abs >= 10000\n ? new Intl.NumberFormat(\"en\", { notation: \"compact\", maximumFractionDigits: 1 }).format(value)\n : new Intl.NumberFormat(\"en\", { maximumFractionDigits: abs < 10 ? 2 : abs < 100 ? 1 : 0 }).format(value)\n const sign = body.startsWith(\"-\") ? \"−\" : \"\"\n return `${sign}${unit?.prefix ?? \"\"}${body.replace(/^-/, \"\")}${unit?.suffix ?? \"\"}`\n}\n","import type { Blockquote, Heading, List, Paragraph, PhrasingContent, Root, RootContent } from \"mdast\"\nimport type { Frontmatter } from \"../parse\"\nimport type {\n Block,\n DocumentPlan,\n Hero,\n ImageMeta,\n ImageRef,\n Judgements,\n ListVariant,\n Section,\n SectionLayout,\n TocEntry,\n} from \"../types\"\nimport { buildTableModel, planViz, withVizKind } from \"./tables\"\nimport { countWords, imagesOnly, slugify, toText, wordsIn } from \"./text\"\n\nexport type ImageMetaMap = Record<string, ImageMeta | null | undefined>\n\nexport type PlanInput = {\n root: Root\n frontmatter?: Frontmatter\n /** Natural image sizes keyed by the URL written in the markdown. */\n images?: ImageMetaMap\n judgements?: Judgements\n /**\n * Put in front of every id the plan generates. Needed when several documents\n * share one page, so their section anchors cannot collide.\n */\n idPrefix?: string\n}\n\nconst BADGE_URL = /shields\\.io|badgen\\.net|badge\\.fury\\.io|\\/badge(s)?[./]|badge\\.svg|travis-ci|circleci\\.com|codecov\\.io|coveralls\\.io|img\\.shields/i\nconst LOGO_NAME = /logo|icon|brand|wordmark|avatar|emblem/i\nconst BANNER_NAME = /banner|cover|hero|header|splash|masthead/i\n\n/** Every image URL in the document, for size probing. */\nexport function collectImageUrls(root: Root): string[] {\n const urls = new Set<string>()\n const visit = (node: unknown) => {\n const n = node as { type?: string; url?: string; children?: unknown[] }\n if (n.type === \"image\" && n.url) urls.add(n.url)\n n.children?.forEach(visit)\n }\n visit(root)\n return [...urls]\n}\n\nfunction isBadge(image: ImageRef): boolean {\n if (BADGE_URL.test(image.src)) return true\n return !!image.meta && image.meta.height <= 40 && image.meta.width <= 320\n}\n\nfunction classifyHeroImage(image: ImageRef): Hero[\"variant\"] | null {\n const meta = image.meta\n // An image that could not be measured cannot be trusted to carry the page.\n if (!meta || meta.width < 48) return null\n const aspect = meta.width / meta.height\n const name = `${image.src.split(/[?#]/)[0]} ${image.alt}`\n const vector = /\\.svg$/i.test(image.src.split(/[?#]/)[0])\n if (LOGO_NAME.test(name) && !(meta.width >= 1600 && aspect >= 1.8)) return \"logo\"\n if (meta.width >= 1200 && aspect >= 1.5 && !vector) return \"banner\"\n if (BANNER_NAME.test(name) && meta.width >= 900 && aspect >= 1.5) return \"banner\"\n if (vector || Math.max(meta.width, meta.height) <= 640) return \"logo\"\n return \"figure\"\n}\n\ntype Context = {\n images: ImageMetaMap\n judgements: Judgements\n slugs: Set<string>\n prefix: string\n sectionDepth: number\n splitCount: number\n dataCount: number\n}\n\nfunction imageRef(found: { image: { url: string; alt?: string | null; title?: string | null }; href?: string }, ctx: Context): ImageRef {\n return {\n src: found.image.url,\n alt: found.image.alt ?? \"\",\n title: found.image.title ?? undefined,\n href: found.href,\n meta: ctx.images[found.image.url] ?? undefined,\n }\n}\n\nfunction planList(node: List): { variant: ListVariant; columns: 1 | 2 } {\n const items = node.children\n if (items.some((item) => item.checked != null)) return { variant: \"checklist\", columns: 1 }\n const nested = items.some((item) => item.children.some((child) => child.type === \"list\" || child.type === \"code\"))\n const words = items.map((item) => wordsIn(item))\n const total = words.reduce((a, b) => a + b, 0)\n const longest = Math.max(0, ...words)\n // Short lists are a moment of emphasis; long ones are body copy that happens to be bulleted.\n if (!nested && items.length >= 2 && items.length <= 8 && longest <= 28 && total <= 130) {\n return { variant: \"feature\", columns: items.length >= 4 && longest <= 12 ? 2 : 1 }\n }\n return { variant: \"body\", columns: 1 }\n}\n\nconst ATTRIBUTION = /^\\s*(?:[—–]|--|~)\\s*(.+)$/\n\nfunction planQuote(node: Blockquote): Extract<Block, { kind: \"quote\" }> {\n let children = node.children as RootContent[]\n const first = children[0]\n\n // GitHub-style alerts: > [!NOTE]\n if (first?.type === \"paragraph\" && first.children[0]?.type === \"text\") {\n const match = /^\\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]\\s*/i.exec(first.children[0].value)\n if (match) {\n const rest: PhrasingContent[] = [\n { type: \"text\", value: first.children[0].value.slice(match[0].length) },\n ...first.children.slice(1),\n ]\n const body = rest.some((child) => toText(child).trim()) ? [{ ...first, children: rest }] : []\n return { kind: \"quote\", variant: \"callout\", calloutType: match[1].toLowerCase(), children: [...body, ...children.slice(1)] }\n }\n }\n\n let attribution: string | undefined\n const last = children[children.length - 1]\n if (last?.type === \"paragraph\") {\n const whole = ATTRIBUTION.exec(toText(last))\n if (whole && children.length > 1) {\n attribution = whole[1].trim()\n children = children.slice(0, -1)\n } else {\n // Attribution on the final line of the same paragraph.\n const breakAt = last.children.findLastIndex((child) => child.type === \"break\")\n const tail = breakAt >= 0 ? last.children.slice(breakAt + 1) : []\n const lineMatch = tail.length ? ATTRIBUTION.exec(toText(tail)) : /\\n\\s*(?:[—–]|--|~)\\s*([^\\n]+)$/.exec(toText(last))\n if (lineMatch && breakAt >= 0) {\n attribution = lineMatch[1].trim()\n children = [...children.slice(0, -1), { ...last, children: last.children.slice(0, breakAt) }]\n } else if (lineMatch && last.children.length === 1 && last.children[0].type === \"text\") {\n attribution = lineMatch[1].trim()\n const value = last.children[0].value.replace(/\\n\\s*(?:[—–]|--|~)\\s*[^\\n]+$/, \"\")\n children = [...children.slice(0, -1), { ...last, children: [{ type: \"text\", value }] }]\n }\n }\n }\n\n const words = wordsIn(children)\n const simple = children.every((child) => child.type === \"paragraph\")\n return { kind: \"quote\", variant: simple && words <= 45 ? \"pull\" : \"aside\", children, attribution }\n}\n\nfunction buildBlocks(nodes: RootContent[], ctx: Context, sectionId: string): Block[] {\n const blocks: Block[] = []\n for (const node of nodes) {\n switch (node.type) {\n case \"heading\":\n blocks.push({\n kind: \"heading\",\n depth: Math.max(1, node.depth - ctx.sectionDepth),\n id: ctx.prefix + slugify(toText(node), ctx.slugs),\n children: node.children,\n })\n break\n case \"paragraph\": {\n const found = imagesOnly(node.children)\n const refs = found?.map((f) => imageRef(f, ctx))\n if (refs && !refs.every(isBadge)) {\n const previous = blocks[blocks.length - 1]\n const images = previous?.kind === \"media\" ? [...previous.images, ...refs] : refs\n const media: Block = {\n kind: \"media\",\n images,\n variant: images.length >= 3 ? \"slideshow\" : images.length === 2 ? \"pair\" : \"single\",\n }\n if (previous?.kind === \"media\") blocks[blocks.length - 1] = media\n else blocks.push(media)\n } else blocks.push({ kind: \"content\", node })\n break\n }\n case \"list\":\n blocks.push({ kind: \"list\", node, ...planList(node) })\n break\n case \"blockquote\":\n blocks.push(planQuote(node))\n break\n case \"code\":\n blocks.push({ kind: \"code\", lang: node.lang ?? undefined, value: node.value })\n break\n case \"table\": {\n const id = `${sectionId}-data-${++ctx.dataCount}`\n const table = buildTableModel(node)\n const viz = planViz(table)\n const wanted = ctx.judgements.viz?.[id]\n blocks.push({ kind: \"data\", id, table, viz: wanted ? withVizKind(viz, table, wanted) : viz })\n break\n }\n case \"thematicBreak\":\n blocks.push({ kind: \"rule\" })\n break\n default:\n blocks.push({ kind: \"content\", node })\n }\n }\n // A rule earns its place only between two pieces of content. At the start or\n // end of a section or card — where the layout draws its own line — or beside\n // a heading, which is a break already, it is one line too many.\n const breaks = (block?: Block) => !block || block.kind === \"rule\" || block.kind === \"heading\"\n return blocks.filter((block, index) => block.kind !== \"rule\" || !(breaks(blocks[index - 1]) || breaks(blocks[index + 1])))\n}\n\ntype RawSection = { heading?: Heading; nodes: RootContent[] }\n\ntype Shape = {\n words: number\n images: ImageRef[]\n heavy: boolean\n quoteOnly: boolean\n cards: { heading: Heading; nodes: RootContent[] }[] | null\n cardIntro: RootContent[]\n}\n\nfunction measure(raw: RawSection, ctx: Context): Shape {\n const { nodes } = raw\n const images = nodes.flatMap((node) =>\n node.type === \"paragraph\" ? (imagesOnly(node.children) ?? []).map((f) => imageRef(f, ctx)).filter((i) => !isBadge(i)) : [],\n )\n const prose = nodes.filter((node) => node.type !== \"table\" && node.type !== \"code\")\n const heavy = nodes.some((node) => node.type === \"table\" || node.type === \"code\" || node.type === \"math\")\n const quotes = nodes.filter((node) => node.type === \"blockquote\")\n const quoteOnly =\n quotes.length === 1 &&\n !heavy &&\n images.length === 0 &&\n wordsIn(quotes[0]) <= 70 &&\n wordsIn(nodes.filter((node) => node !== quotes[0])) <= 25 &&\n !/^\\[!/.test(toText(quotes[0]).trim())\n\n // Sub-sections that are each a short blurb read as a set of cards.\n const subDepth = ctx.sectionDepth + 1\n const firstSub = nodes.findIndex((node) => node.type === \"heading\" && node.depth === subDepth)\n let cards: Shape[\"cards\"] = null\n if (firstSub >= 0 && !nodes.some((node) => node.type === \"heading\" && node.depth > subDepth)) {\n const groups: { heading: Heading; nodes: RootContent[] }[] = []\n for (const node of nodes.slice(firstSub)) {\n if (node.type === \"heading\") groups.push({ heading: node, nodes: [] })\n else groups[groups.length - 1].nodes.push(node)\n }\n const blurb = (group: { nodes: RootContent[] }) =>\n group.nodes.length > 0 &&\n wordsIn(group.nodes) <= 70 &&\n group.nodes.every(\n (node) => node.type === \"paragraph\" || (node.type === \"list\" && node.children.length <= 5 && wordsIn(node) <= 50),\n ) &&\n group.nodes.filter((node) => node.type === \"paragraph\" && imagesOnly(node.children)).length <= 1\n if (groups.length >= 2 && groups.length <= 12 && groups.every(blurb)) cards = groups\n }\n\n return {\n words: wordsIn(prose),\n images,\n heavy,\n quoteOnly,\n cards,\n cardIntro: firstSub >= 0 ? nodes.slice(0, firstSub) : nodes,\n }\n}\n\n/** Layouts the content can fill, best fit first. `prose` always works. */\nfunction rankLayouts(raw: RawSection, shape: Shape): { layouts: SectionLayout[]; reason: string } {\n const { words, images, heavy } = shape\n const layouts: SectionLayout[] = []\n let reason = \"default reading layout\"\n const pick = (layout: SectionLayout, why: string) => {\n if (!layouts.length) reason = why\n if (!layouts.includes(layout)) layouts.push(layout)\n }\n const lead = images[0]?.meta\n const aspect = lead ? lead.width / lead.height : 0\n const simple = !heavy && !shape.cards\n\n if (shape.quoteOnly) pick(\"quote\", \"a single short quotation\")\n if (simple && images.length === 1 && lead && lead.width >= 1400 && aspect >= 1.3 && words <= 90) {\n pick(\"fullbleed\", `large ${lead.width}×${lead.height} image with ${words} words`)\n }\n if (simple && images.length === 1 && lead && lead.width >= 320 && words >= 20 && words <= 260) {\n pick(\"split\", `one image beside ${words} words`)\n }\n // A long run of prose before the sub-sections is an article with asides, not a card deck.\n const cardsLead = !!shape.cards && (wordsIn(shape.cardIntro) <= 90 || shape.cards.length >= 4)\n if (cardsLead) pick(\"cards\", `${shape.cards!.length} short sub-sections`)\n if (!heavy && images.length >= 2 && words <= 80) pick(\"showcase\", `${images.length} images, little text`)\n // Only a titled section is promoted to a statement: untitled opening text that\n // was not made a hero should not be dressed up as one.\n if (simple && raw.heading && images.length === 0 && words <= 45 && raw.nodes.length <= 3) {\n pick(\"statement\", `only ${words} words — give it room`)\n }\n pick(\"prose\", reason)\n\n // Looser fits: not the first choice, but still valid when picked by hand.\n if (shape.cards) pick(\"cards\", \"\")\n if (simple && images.length >= 1 && lead && lead.width >= 1000 && aspect >= 1.2 && words <= 160) pick(\"fullbleed\", \"\")\n if (simple && images.length >= 1 && lead && words >= 8 && words <= 400) pick(\"split\", \"\")\n if (!heavy && images.length >= 1 && words <= 160) pick(\"showcase\", \"\")\n if (simple && images.length === 0 && words > 0 && words <= 110 && raw.nodes.length <= 5) pick(\"statement\", \"\")\n return { layouts, reason }\n}\n\nfunction buildSection(raw: RawSection, ctx: Context, index: number): Section {\n const titleText = raw.heading ? toText(raw.heading).trim() : \"\"\n const id = ctx.prefix + (raw.heading ? slugify(titleText, ctx.slugs) : slugify(\"introduction\", ctx.slugs))\n const shape = measure(raw, ctx)\n const { layouts, reason } = rankLayouts(raw, shape)\n const wanted = ctx.judgements.layouts?.[id]\n const layout = wanted && layouts.includes(wanted) ? wanted : layouts[0]\n\n let blocks: Block[]\n let feature: ImageRef | undefined\n let flip: boolean | undefined\n\n if (layout === \"cards\" && shape.cards) {\n blocks = [\n ...buildBlocks(shape.cardIntro, ctx, id),\n {\n kind: \"cards\",\n items: shape.cards.map((card) => ({\n id: ctx.prefix + slugify(toText(card.heading), ctx.slugs),\n title: card.heading.children,\n blocks: buildBlocks(card.nodes, ctx, id),\n })),\n },\n ]\n } else if (layout === \"split\" || layout === \"fullbleed\") {\n feature = shape.images[0]\n // The feature image leaves the flow; any further images stay where they were.\n let taken = false\n const rest = raw.nodes.filter((node) => {\n if (taken || node.type !== \"paragraph\") return true\n const found = imagesOnly(node.children)\n if (found?.length === 1 && found[0].image.url === feature!.src) return !(taken = true)\n return true\n })\n blocks = buildBlocks(rest, ctx, id)\n if (layout === \"split\") flip = ctx.splitCount++ % 2 === 1\n } else {\n blocks = buildBlocks(raw.nodes, ctx, id)\n }\n\n const tone = layout === \"quote\" ? \"invert\" : layout === \"statement\" ? \"tint\" : \"plain\"\n return {\n id,\n title: raw.heading?.children ?? [],\n titleText,\n layout,\n tone,\n blocks,\n feature,\n flip,\n words: shape.words,\n reason: wanted && layout === wanted ? `chosen: ${wanted}` : `${reason}${index === 0 && !raw.heading ? \" (introduction)\" : \"\"}`,\n alternatives: layouts,\n }\n}\n\nconst asString = (value: unknown) => (typeof value === \"string\" && value.trim() ? value.trim() : undefined)\n\n/**\n * Turns a parsed document into a layout plan. Pure and deterministic: the same\n * tree, image sizes and judgements always yield the same plan.\n */\nexport function planDocument(input: PlanInput): DocumentPlan {\n const { root } = input\n const frontmatter = input.frontmatter ?? {}\n const nodes = root.children\n const headings = nodes.filter((node): node is Heading => node.type === \"heading\")\n\n // The title is a leading H1 — one that comes before any other heading.\n const titleNode = headings[0]?.depth === 1 ? headings[0] : undefined\n const sectionHeadings = headings.filter((h) => h !== titleNode)\n const sectionDepth = sectionHeadings.length ? Math.min(...sectionHeadings.map((h) => h.depth)) : 2\n\n const ctx: Context = {\n images: input.images ?? {},\n judgements: input.judgements ?? {},\n slugs: new Set([\"top\"]),\n prefix: input.idPrefix ?? \"\",\n sectionDepth,\n splitCount: 0,\n dataCount: 0,\n }\n\n const firstSection = nodes.findIndex((node) => node.type === \"heading\" && node !== titleNode && node.depth === sectionDepth)\n const preamble = (firstSection >= 0 ? nodes.slice(0, firstSection) : nodes).filter((node) => node !== titleNode)\n const body = firstSection >= 0 ? nodes.slice(firstSection) : []\n\n // --- Hero -----------------------------------------------------------------\n const badges: ImageRef[] = []\n const lead: PhrasingContent[][] = []\n let heroImage: ImageRef | undefined\n let heroVariant: Hero[\"variant\"] = \"plain\"\n const intro: RootContent[] = []\n let leadOpen = true\n let leadWords = 0\n\n const coverUrl = asString(frontmatter.image) ?? asString(frontmatter.cover) ?? asString(frontmatter.banner)\n if (coverUrl) {\n const candidate: ImageRef = { src: coverUrl, alt: \"\", meta: ctx.images[coverUrl] ?? undefined }\n const variant = classifyHeroImage(candidate)\n if (variant) [heroImage, heroVariant] = [candidate, variant]\n }\n\n for (const node of preamble) {\n if (node.type === \"paragraph\") {\n const found = imagesOnly(node.children)\n if (found) {\n const refs = found.map((f) => imageRef(f, ctx))\n if (refs.every(isBadge)) {\n badges.push(...refs)\n continue\n }\n if (!heroImage && refs.length === 1 && ctx.judgements.heroImageUsable !== false) {\n const variant = classifyHeroImage(refs[0])\n if (variant) {\n ;[heroImage, heroVariant] = [refs[0], variant]\n continue\n }\n }\n } else if (leadOpen && lead.length < 2) {\n const words = countWords(toText(node))\n if (leadWords + words <= 70) {\n lead.push((node as Paragraph).children)\n leadWords += words\n continue\n }\n }\n }\n // The lead is the opening run of short paragraphs; anything else ends it.\n if (!(node.type === \"paragraph\" && imagesOnly(node.children))) leadOpen = false\n intro.push(node)\n }\n\n // A hero is never forced: without a title or an image fit to lead the page,\n // the opening paragraphs are simply the start of the text.\n const frontTitle = asString(frontmatter.title)\n const hasHero = !!titleNode || !!frontTitle || !!heroImage\n if (!hasHero) {\n intro.splice(0, intro.length, ...preamble)\n lead.length = 0\n badges.length = 0\n leadWords = 0\n }\n\n const description = asString(frontmatter.description) ?? asString(frontmatter.subtitle) ?? asString(frontmatter.summary)\n if (hasHero && description && !lead.length) lead.push([{ type: \"text\", value: description }])\n\n const title: PhrasingContent[] = titleNode?.children ?? (frontTitle ? [{ type: \"text\", value: frontTitle }] : [])\n const eyebrowParts = [asString(frontmatter.category), asString(frontmatter.author)]\n const date = frontmatter.date\n if (date instanceof Date) eyebrowParts.push(date.toISOString().slice(0, 10))\n else if (asString(date)) eyebrowParts.push(asString(date))\n\n const hero: Hero = {\n variant: heroImage ? heroVariant : \"plain\",\n title,\n titleText: toText(title).trim(),\n lead,\n image: heroImage,\n badges,\n eyebrow: (hasHero && eyebrowParts.filter(Boolean).join(\" · \")) || undefined,\n }\n\n // --- Sections ---------------------------------------------------------------\n const raws: RawSection[] = []\n if (intro.length) raws.push({ nodes: intro })\n for (const node of body) {\n if (node.type === \"heading\" && node.depth === sectionDepth) raws.push({ heading: node, nodes: [] })\n else raws[raws.length - 1].nodes.push(node)\n }\n const sections = raws.map((raw, index) => buildSection(raw, ctx, index))\n\n // Two tinted bands in a row would merge into one; keep the rhythm.\n for (let i = 1; i < sections.length; i++) {\n if (sections[i].tone === \"tint\" && sections[i - 1].tone === \"tint\") sections[i].tone = \"plain\"\n }\n\n // --- Navigation ---------------------------------------------------------------\n const toc: TocEntry[] = []\n for (const section of sections) {\n if (!section.titleText) continue\n toc.push({ id: section.id, text: section.titleText, depth: 0 })\n const subs: { id: string; text: string }[] = []\n for (const block of section.blocks) {\n if (block.kind === \"heading\" && block.depth === 1) subs.push({ id: block.id, text: toText(block.children) })\n if (block.kind === \"cards\") for (const item of block.items) subs.push({ id: item.id, text: toText(item.title) })\n }\n for (const sub of subs) toc.push({ ...sub, depth: 1 })\n }\n const titled = sections.filter((s) => s.titleText).length\n const words = sections.reduce((sum, s) => sum + s.words, 0) + leadWords\n\n return {\n hero,\n sections,\n toc: toc.length > 40 ? toc.filter((entry) => entry.depth === 0) : toc,\n showToc: titled >= 3,\n words,\n readingTime: Math.max(1, Math.round(words / 220)),\n }\n}\n","import type { CSSProperties } from \"react\"\nimport type { Appearance, FontPairingId, PaletteId, ThemeChoice } from \"../types\"\n\nexport type PaletteTokens = {\n bg: string\n /** Cards, code blocks and tinted section bands. */\n surface: string\n fg: string\n /** Secondary text. */\n muted: string\n border: string\n accent: string\n accentFg: string\n /**\n * A second colour, where the theme has one: quotations are set in it — a band\n * across the page for a quotation that is a section of its own, a block for one\n * inside an article — so the page has more than one note to play.\n */\n highlight?: { bg: string; fg: string }\n}\n\n/**\n * Chart drawing styles: `linework` is monochrome print-style ink with hatching,\n * `instrument` has rounded, saturated marks on calm surfaces, `soft` adds\n * gradients and depth, and `flat` is plain solid colour.\n */\nexport type ChartStyle = \"linework\" | \"instrument\" | \"soft\" | \"flat\"\n\n/**\n * How a text-led hero is set: `wash` tints the page with a soft glow of the\n * accent; `block` is a cover in the accent colour itself; `ink` is a cover in\n * the theme's darkest tone, the page's colours reversed.\n */\nexport type HeroTone = \"wash\" | \"block\" | \"ink\"\n\n/** The families the themes fall into by subject — how the picker is arranged. */\nexport type ThemeTopic = \"writing\" | \"official\" | \"technology\" | \"lifestyle\" | \"wellbeing\" | \"culture\"\n\nexport const themeTopics: { id: ThemeTopic; name: string }[] = [\n { id: \"writing\", name: \"Writing\" },\n { id: \"official\", name: \"Business & official\" },\n { id: \"technology\", name: \"Technology\" },\n { id: \"lifestyle\", name: \"Places & lifestyle\" },\n { id: \"wellbeing\", name: \"Nature & health\" },\n { id: \"culture\", name: \"Culture & play\" },\n]\n\nexport type Theme = {\n id: PaletteId\n name: string\n /** The family of subjects it belongs to. */\n topic: ThemeTopic\n /**\n * The kinds of document this theme suits — and all the classifier reads when\n * choosing one. Name subjects, not moods: measured against documents of known\n * subject, subjects alone choose better, and in half the time, than subjects\n * with colours and typefaces beside them.\n */\n description: string\n /** Its key colours in a few plain words, for people reading the list. */\n look: string\n /** Typography used unless the caller picks another. */\n fonts: FontPairingId\n /** 0 = soft and rounded … 1 = sharp and formal. Sets the corner radius. */\n formality: number\n /** How charts are drawn under this theme. */\n chart: ChartStyle\n /** How the opening of the page is set off from the rest. */\n hero: HeroTone\n /** Fallback matching when no classifier is configured. */\n keywords: RegExp\n light: PaletteTokens\n dark: PaletteTokens\n}\n\nconst tokens = (\n bg: string,\n surface: string,\n fg: string,\n muted: string,\n border: string,\n accent: string,\n accentFg: string,\n highlight?: string,\n highlightFg?: string,\n): PaletteTokens => ({\n bg,\n surface,\n fg,\n muted,\n border,\n accent,\n accentFg,\n ...(highlight && highlightFg ? { highlight: { bg: highlight, fg: highlightFg } } : {}),\n})\n\nexport const themes: Record<PaletteId, Theme> = {\n paper: {\n id: \"paper\",\n name: \"Paper\",\n topic: \"writing\",\n description: \"essays, short stories, fiction, memoir, opinion, local history\",\n look: \"cream paper, terracotta accent\",\n fonts: \"editorial\",\n formality: 0.5,\n chart: \"flat\",\n hero: \"wash\",\n keywords: /\\b(essay|story|chapter|novel|memoir|poem|letter|journal|philosophy|opinion|notebook|writing)\\b/gi,\n light: tokens(\"#f5eedf\", \"#ebe1cc\", \"#1f1b16\", \"#4e473c\", \"#d6c9b0\", \"#a23f17\", \"#ffffff\"),\n dark: tokens(\"#1b1612\", \"#29221b\", \"#f3ecdf\", \"#b8ad9d\", \"#3f352a\", \"#f0a070\", \"#1a120c\"),\n },\n ink: {\n id: \"ink\",\n name: \"Ink\",\n topic: \"writing\",\n description: \"meeting notes, memos, minutes, plain lists\",\n look: \"white page, black cover, no colour\",\n fonts: \"modern\",\n formality: 0.7,\n chart: \"linework\",\n hero: \"ink\",\n keywords: /\\b(design|minimal|typography|studio|portfolio|notes|memo|manifesto)\\b/gi,\n light: tokens(\"#ffffff\", \"#f1f1f2\", \"#000000\", \"#505056\", \"#d4d4d8\", \"#111111\", \"#ffffff\"),\n dark: tokens(\"#000000\", \"#161618\", \"#ffffff\", \"#a8a8b0\", \"#2e2e33\", \"#f4f4f5\", \"#0a0a0a\"),\n },\n ocean: {\n id: \"ocean\",\n name: \"Ocean\",\n topic: \"official\",\n description: \"business reports, finance, sales, strategy, investors\",\n look: \"cool white, corporate blue cover, amber\",\n fonts: \"modern\",\n formality: 0.6,\n chart: \"soft\",\n hero: \"block\",\n keywords: /\\b(revenue|profit|quarter|q[1-4]|market|investor|financial|growth|customers?|sales|strategy|kpi|forecast|budget|margin|earnings|business)\\b/gi,\n light: tokens(\"#eef4fa\", \"#dce8f4\", \"#0a1f33\", \"#374c63\", \"#bfd2e5\", \"#0a55a0\", \"#ffffff\", \"#f2b84b\", \"#0a1f33\"),\n dark: tokens(\"#0a1a30\", \"#12284a\", \"#eaf2fb\", \"#a0b6ce\", \"#24406b\", \"#6cb8ff\", \"#06121d\", \"#f2b84b\", \"#0a1a30\"),\n },\n forest: {\n id: \"forest\",\n name: \"Forest\",\n topic: \"wellbeing\",\n description: \"nature, environment, outdoors, gardening, biology\",\n look: \"sage green page, deep forest green\",\n fonts: \"friendly\",\n formality: 0.35,\n chart: \"instrument\",\n hero: \"wash\",\n keywords: /\\b(nature|climate|forest|garden|plants?|sustainab\\w+|environment\\w*|organic|wildlife|hiking|ecology|carbon|farm\\w*|valley|trail)\\b/gi,\n light: tokens(\"#e3ecd8\", \"#f5f9ef\", \"#14241a\", \"#3c5040\", \"#c3d2b5\", \"#1f6b35\", \"#ffffff\"),\n dark: tokens(\"#0f2417\", \"#183422\", \"#e8f2e4\", \"#abc1ad\", \"#2b4b36\", \"#8fdc9c\", \"#0b160e\"),\n },\n sunset: {\n id: \"sunset\",\n name: \"Sunset\",\n topic: \"lifestyle\",\n description: \"travel journals, itineraries, destinations, city guides\",\n look: \"apricot page, burnt orange cover\",\n fonts: \"elegant\",\n formality: 0.4,\n chart: \"soft\",\n hero: \"block\",\n keywords: /\\b(travel|trip|beach|hotel|festival|holiday|itinerary|adventure|temples?|shrine|guesthouse|city|walks?|journey|island)\\b/gi,\n light: tokens(\"#fde3cf\", \"#fff5ec\", \"#2a130a\", \"#694133\", \"#efc3a7\", \"#a32c0c\", \"#ffffff\"),\n dark: tokens(\"#24101a\", \"#361a27\", \"#fdeee6\", \"#cfa79b\", \"#4e2839\", \"#ff9a62\", \"#200e06\"),\n },\n violet: {\n id: \"violet\",\n name: \"Violet\",\n topic: \"technology\",\n description: \"tech products, startups, AI, launches, pricing pages\",\n look: \"lavender page, vivid purple cover, lime\",\n fonts: \"friendly\",\n formality: 0.25,\n chart: \"instrument\",\n hero: \"block\",\n keywords: /\\b(product|startup|launch|ai|model|platform|features?|app|saas|creative|innovation|roadmap|pricing|beta)\\b/gi,\n light: tokens(\"#eae4ff\", \"#f8f5ff\", \"#17112e\", \"#4e456e\", \"#cdc2f4\", \"#5a2be0\", \"#ffffff\", \"#c8f560\", \"#17112e\"),\n dark: tokens(\"#170f3a\", \"#231856\", \"#f0ebff\", \"#b3a9db\", \"#382a78\", \"#b39dff\", \"#120b2a\", \"#c8f560\", \"#170f3a\"),\n },\n terminal: {\n id: \"terminal\",\n name: \"Terminal\",\n topic: \"technology\",\n description: \"software docs, READMEs, API and developer guides, framework comparisons\",\n look: \"cool grey, dark slate cover, teal, amber\",\n fonts: \"technical\",\n formality: 0.6,\n chart: \"flat\",\n hero: \"ink\",\n keywords: /\\b(install|npm|api|cli|usage|config\\w*|repository|function|library|docker|git|sdk|endpoint|compile|typescript|python|server|deploy\\w*)\\b/gi,\n light: tokens(\"#edf2f3\", \"#dde6e9\", \"#0b1a20\", \"#354c55\", \"#c0cfd4\", \"#0a6f6a\", \"#ffffff\", \"#ffb000\", \"#0b1a20\"),\n dark: tokens(\"#0a1114\", \"#131f24\", \"#e2ecef\", \"#97acb5\", \"#22333b\", \"#45dcc2\", \"#04201b\", \"#ffb000\", \"#0a1114\"),\n },\n chambers: {\n id: \"chambers\",\n name: \"Chambers\",\n topic: \"official\",\n description: \"legal documents, contracts, policy, regulation\",\n look: \"ivory, navy cover, gold\",\n fonts: \"classic\",\n formality: 0.9,\n chart: \"flat\",\n hero: \"block\",\n keywords: /\\b(agreement|contract|clause|shall|court|law|legal|policy|regulation|act|statute|party|parties|compliance|hereby|pursuant|terms)\\b/gi,\n light: tokens(\"#f6f1e4\", \"#ebe3cf\", \"#111a2e\", \"#3f485c\", \"#d1c7ad\", \"#1f3a6e\", \"#ffffff\", \"#c9a54f\", \"#111a2e\"),\n dark: tokens(\"#0e1526\", \"#18223b\", \"#efe9da\", \"#a8aebd\", \"#2b3654\", \"#d4b46a\", \"#1a1405\", \"#27468a\", \"#f4eee0\"),\n },\n academia: {\n id: \"academia\",\n name: \"Academia\",\n topic: \"official\",\n description: \"scholarly papers, research, theses, lecture notes, science explainers\",\n look: \"parchment, oxblood red\",\n fonts: \"scholarly\",\n formality: 0.9,\n chart: \"linework\",\n hero: \"wash\",\n keywords: /\\b(research|study|abstract|hypothesis|methodology|findings|literature|thesis|theorem|proof|citation|journal|university|experiment)\\b/gi,\n light: tokens(\"#f3ebd6\", \"#e8dec2\", \"#1c1710\", \"#4d4536\", \"#d1c5a3\", \"#7b1e22\", \"#ffffff\"),\n dark: tokens(\"#1c1410\", \"#2a1e18\", \"#f1e8d3\", \"#b7aa8f\", \"#42322a\", \"#f09a94\", \"#240808\"),\n },\n blueprint: {\n id: \"blueprint\",\n name: \"Blueprint\",\n topic: \"technology\",\n description: \"engineering specs, architecture, hardware, proposals\",\n look: \"pale drafting blue, cobalt cover, marker yellow\",\n fonts: \"geometric\",\n formality: 0.8,\n chart: \"flat\",\n hero: \"block\",\n keywords: /\\b(specification|architecture|system|design doc|requirements?|protocol|diagram|engineering|hardware|sensor|circuit|tolerance|proposal|rfc)\\b/gi,\n light: tokens(\"#dee8fb\", \"#f4f8ff\", \"#0a1836\", \"#394a6f\", \"#b7caee\", \"#1140c0\", \"#ffffff\", \"#ffd54a\", \"#0a1836\"),\n dark: tokens(\"#0a1d4d\", \"#122a68\", \"#e6eeff\", \"#aabce3\", \"#24408c\", \"#8fb4ff\", \"#05102a\", \"#ffd54a\", \"#0a1d4d\"),\n },\n midnight: {\n id: \"midnight\",\n name: \"Midnight\",\n topic: \"culture\",\n description: \"night-time, space, astronomy, science fiction, games, nightlife\",\n look: \"periwinkle, deep indigo cover, electric cyan, magenta\",\n fonts: \"geometric\",\n formality: 0.4,\n chart: \"instrument\",\n hero: \"ink\",\n keywords: /\\b(space|galaxy|stars?|orbit|planet|astronom\\w+|game|gaming|cyber\\w*|future|sci-?fi|night|dark|dusk|cinematic|neon|milky way)\\b/gi,\n light: tokens(\"#e3e3fa\", \"#f5f5ff\", \"#100f2e\", \"#464572\", \"#c1c0ed\", \"#3a2bd0\", \"#ffffff\", \"#ff5ad6\", \"#100f2e\"),\n dark: tokens(\"#060620\", \"#101038\", \"#ecebff\", \"#a3a2d4\", \"#25256a\", \"#5ee6ff\", \"#03141a\", \"#ff5ad6\", \"#060620\"),\n },\n rose: {\n id: \"rose\",\n name: \"Rose\",\n topic: \"lifestyle\",\n description: \"weddings, beauty, skincare, fashion, celebrations\",\n look: \"blush pink page, deep rose\",\n fonts: \"luxe\",\n formality: 0.3,\n chart: \"soft\",\n hero: \"wash\",\n keywords: /\\b(wedding|bride|beauty|fashion|skincare|romance|love|invitation|bouquet|engagement|anniversary|bridal)\\b/gi,\n light: tokens(\"#fbdfe3\", \"#fff4f5\", \"#2b1218\", \"#673e4e\", \"#eebcc5\", \"#a8234f\", \"#ffffff\"),\n dark: tokens(\"#2a0f1a\", \"#3b1826\", \"#fdeaee\", \"#d1a3b1\", \"#55263a\", \"#ff9bb8\", \"#2a0a14\"),\n },\n sand: {\n id: \"sand\",\n name: \"Sand\",\n topic: \"lifestyle\",\n description: \"interiors, craft, slow living, quiet luxury\",\n look: \"stone beige, muted olive\",\n fonts: \"luxe\",\n formality: 0.7,\n chart: \"flat\",\n hero: \"wash\",\n keywords: /\\b(interior|furniture|ceramics?|craft|atelier|linen|slow living|minimalis[mt]|gallery|curated|artisan)\\b/gi,\n light: tokens(\"#e9e0cf\", \"#f6f1e6\", \"#25211a\", \"#4c4639\", \"#cdc1a8\", \"#525f27\", \"#ffffff\"),\n dark: tokens(\"#201d14\", \"#2d291d\", \"#f0e9d9\", \"#beb59c\", \"#433d2c\", \"#c3d184\", \"#171a08\"),\n },\n citrus: {\n id: \"citrus\",\n name: \"Citrus\",\n topic: \"culture\",\n description: \"children, schools, community events, playful guides\",\n look: \"sunny yellow page, bold orange cover\",\n fonts: \"rounded\",\n formality: 0.05,\n chart: \"instrument\",\n hero: \"block\",\n keywords: /\\b(kids?|children|school|classroom|play|fun|games?|party|club|camp|volunteers?|community|activities|learn\\w*)\\b/gi,\n light: tokens(\"#ffe98a\", \"#fff8d6\", \"#1f1900\", \"#564c14\", \"#efcf4c\", \"#9e3400\", \"#ffffff\"),\n dark: tokens(\"#1f1a02\", \"#302805\", \"#fff8d0\", \"#c7bb7c\", \"#4a3f0c\", \"#ffd23f\", \"#1c1600\"),\n },\n crimson: {\n id: \"crimson\",\n name: \"Crimson\",\n topic: \"official\",\n description: \"news, journalism, press releases, politics\",\n look: \"newsprint white, red masthead\",\n fonts: \"gazette\",\n formality: 0.85,\n chart: \"flat\",\n hero: \"block\",\n keywords: /\\b(news|report(ed|er|ing)?|press release|journalis\\w+|election|government|minister|breaking|investigation|headline|spokesperson|announced)\\b/gi,\n light: tokens(\"#ffffff\", \"#f4eded\", \"#110808\", \"#5a4c4c\", \"#dbcaca\", \"#ad0f1b\", \"#ffffff\"),\n dark: tokens(\"#120909\", \"#211313\", \"#f8efef\", \"#b9a6a6\", \"#3a2626\", \"#ff6b73\", \"#170204\"),\n },\n slate: {\n id: \"slate\",\n name: \"Slate\",\n topic: \"official\",\n description: \"operations manuals, industrial reports, safety procedures\",\n look: \"steel grey, safety orange cover, hazard yellow\",\n fonts: \"slab\",\n formality: 0.75,\n chart: \"flat\",\n hero: \"block\",\n keywords: /\\b(manual|procedure|maintenance|safety|inspection|operations?|runbook|checklist|equipment|construction|logistics|warehouse|incident)\\b/gi,\n light: tokens(\"#dfe4e8\", \"#f2f5f7\", \"#121b21\", \"#3c4a53\", \"#bac6cd\", \"#9a3a07\", \"#ffffff\", \"#ffd400\", \"#121b21\"),\n dark: tokens(\"#12181c\", \"#1d262c\", \"#e5ecf0\", \"#a4b2bb\", \"#313e47\", \"#ff9248\", \"#1e0d02\", \"#ffd400\", \"#12181c\"),\n },\n lagoon: {\n id: \"lagoon\",\n name: \"Lagoon\",\n topic: \"wellbeing\",\n description: \"healthcare, medicine, patient information, fitness, wellbeing\",\n look: \"aqua page, calm teal, coral\",\n fonts: \"modern\",\n formality: 0.45,\n chart: \"instrument\",\n hero: \"wash\",\n keywords: /\\b(health\\w*|medical|patients?|clinic\\w*|treatment|symptoms?|doctor|nurse|therapy|wellness|wellbeing|diagnos\\w+|care|vaccine)\\b/gi,\n light: tokens(\"#d6f0f1\", \"#f3fbfb\", \"#08242a\", \"#305258\", \"#acd9dc\", \"#086673\", \"#ffffff\", \"#ff7a66\", \"#08242a\"),\n dark: tokens(\"#062a31\", \"#0c3a43\", \"#e2f6f7\", \"#a4cbd0\", \"#18525b\", \"#5fdbe6\", \"#031a1d\", \"#ff8a78\", \"#062a31\"),\n },\n plum: {\n id: \"plum\",\n name: \"Plum\",\n topic: \"culture\",\n description: \"arts, music, theatre, film, exhibitions\",\n look: \"orchid mauve page, deep plum cover, gold\",\n fonts: \"elegant\",\n formality: 0.5,\n chart: \"soft\",\n hero: \"block\",\n keywords: /\\b(art|artist|music|concert|theatre|theater|film|exhibition|gallery|opera|dance|festival|programme|culture|orchestra)\\b/gi,\n light: tokens(\"#f0dcee\", \"#fbf3fa\", \"#24101f\", \"#5b3e55\", \"#dbbdd6\", \"#7a1f6b\", \"#ffffff\", \"#e2b23a\", \"#24101f\"),\n dark: tokens(\"#221023\", \"#331a35\", \"#f7e9f3\", \"#c6a6bf\", \"#4d2b4e\", \"#f3addc\", \"#2a0a22\", \"#e2b23a\", \"#221023\"),\n },\n poster: {\n id: \"poster\",\n name: \"Poster\",\n topic: \"culture\",\n description: \"sports, tournaments, events, bold announcements, manifestos\",\n look: \"stark black and white, electric blue block, neon yellow\",\n fonts: \"poster\",\n formality: 1,\n chart: \"flat\",\n hero: \"block\",\n keywords: /\\b(event|tickets?|match|league|team|championship|tournament|race|drop|lineup|kick-?off|season|score|win)\\b/gi,\n light: tokens(\"#ffffff\", \"#efefef\", \"#000000\", \"#4d4d4d\", \"#111111\", \"#1a3cff\", \"#ffffff\", \"#e8ff3a\", \"#000000\"),\n dark: tokens(\"#000000\", \"#151515\", \"#ffffff\", \"#b0b0b0\", \"#5a5a5a\", \"#e8ff3a\", \"#101400\", \"#1a3cff\", \"#ffffff\"),\n },\n espresso: {\n id: \"espresso\",\n name: \"Espresso\",\n topic: \"lifestyle\",\n description: \"food, recipes, coffee, restaurants, small makers\",\n look: \"latte page, dark roast cover, caramel\",\n fonts: \"slab\",\n formality: 0.5,\n chart: \"flat\",\n hero: \"ink\",\n keywords: /\\b(recipe|food|restaurant|cook\\w*|bak\\w+|coffee|cafe|menu|ingredients?|wine|taste|kitchen|dinner|chef|flavou?r)\\b/gi,\n light: tokens(\"#ecdcc6\", \"#f8efe2\", \"#2a180d\", \"#574031\", \"#d5bea1\", \"#8a4310\", \"#ffffff\"),\n dark: tokens(\"#1f130b\", \"#2e1e13\", \"#f5e8d9\", \"#c1a994\", \"#46311f\", \"#ecab66\", \"#241204\"),\n },\n console: {\n id: \"console\",\n name: \"Console\",\n topic: \"technology\",\n description: \"changelogs, release notes, incident reports, security write-ups, logs\",\n look: \"black screen, phosphor green, amber\",\n fonts: \"mono\",\n formality: 0.95,\n chart: \"linework\",\n hero: \"ink\",\n keywords: /\\b(changelog|release notes|patch|exploit|vulnerabilit\\w+|cve|payload|shell|kernel|stack trace|commit|binary|ctf|debug\\w*)\\b/gi,\n light: tokens(\"#e8f0e1\", \"#f5f9f1\", \"#0c160b\", \"#40543b\", \"#c2d3b6\", \"#17701a\", \"#ffffff\", \"#ffb000\", \"#0c160b\"),\n dark: tokens(\"#030803\", \"#0b150b\", \"#d7f5d0\", \"#88aa82\", \"#1d331d\", \"#5cf25c\", \"#021002\", \"#ffb000\", \"#030803\"),\n },\n}\n\nexport const themeList = Object.values(themes)\n\nexport type FontPairing = {\n id: FontPairingId\n name: string\n /** The headline typeface and its character, in a few words — read by the classifier. */\n look: string\n heading: string\n body: string\n mono: string\n /** Google Fonts `family=` parameters. */\n google: string[]\n headingWeight: number\n /** Heading letter-spacing in em. */\n tracking: number\n}\n\nconst MONO = `\"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace`\nconst MONO_GOOGLE = \"JetBrains+Mono:wght@400;600\"\nconst SANS = \"system-ui, sans-serif\"\nconst SERIF = \"Georgia, serif\"\n\nexport const fontPairings: Record<FontPairingId, FontPairing> = {\n editorial: {\n id: \"editorial\",\n name: \"Editorial\",\n look: \"Fraunces, literary serif\",\n heading: `\"Fraunces\", ${SERIF}`,\n body: `\"Newsreader\", ${SERIF}`,\n mono: MONO,\n google: [\"Fraunces:opsz,wght@9..144,400..700\", \"Newsreader:ital,opsz,wght@0,6..72,400..600;1,6..72,400..600\", MONO_GOOGLE],\n headingWeight: 560,\n tracking: -0.02,\n },\n modern: {\n id: \"modern\",\n name: \"Modern\",\n look: \"Inter, neutral sans\",\n heading: `\"Inter Tight\", \"Inter\", ${SANS}`,\n body: `\"Inter\", ${SANS}`,\n mono: MONO,\n google: [\"Inter+Tight:wght@500..800\", \"Inter:wght@400..700\", MONO_GOOGLE],\n headingWeight: 680,\n tracking: -0.03,\n },\n technical: {\n id: \"technical\",\n name: \"Technical\",\n look: \"Space Grotesk, engineered sans\",\n heading: `\"Space Grotesk\", ${SANS}`,\n body: `\"IBM Plex Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Space+Grotesk:wght@400..700\", \"IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.03,\n },\n elegant: {\n id: \"elegant\",\n name: \"Elegant\",\n look: \"Playfair Display, display serif\",\n heading: `\"Playfair Display\", ${SERIF}`,\n body: `\"Source Sans 3\", ${SANS}`,\n mono: MONO,\n google: [\"Playfair+Display:ital,wght@0,400..800;1,400..800\", \"Source+Sans+3:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.015,\n },\n friendly: {\n id: \"friendly\",\n name: \"Friendly\",\n look: \"Bricolage Grotesque, warm sans\",\n heading: `\"Bricolage Grotesque\", ${SANS}`,\n body: `\"DM Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Bricolage+Grotesque:opsz,wght@12..96,400..800\", \"DM+Sans:ital,opsz,wght@0,9..40,400..700;1,9..40,400..700\", MONO_GOOGLE],\n headingWeight: 700,\n tracking: -0.03,\n },\n classic: {\n id: \"classic\",\n name: \"Classic\",\n look: \"Spectral, book serif\",\n heading: `\"Spectral\", ${SERIF}`,\n body: `\"Public Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Spectral:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500\", \"Public+Sans:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.012,\n },\n scholarly: {\n id: \"scholarly\",\n name: \"Scholarly\",\n look: \"EB Garamond, old-style serif\",\n heading: `\"EB Garamond\", ${SERIF}`,\n body: `\"EB Garamond\", ${SERIF}`,\n mono: MONO,\n google: [\"EB+Garamond:ital,wght@0,400..700;1,400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.01,\n },\n geometric: {\n id: \"geometric\",\n name: \"Geometric\",\n look: \"Sora, geometric sans\",\n heading: `\"Sora\", ${SANS}`,\n body: `\"Manrope\", ${SANS}`,\n mono: MONO,\n google: [\"Sora:wght@400..700\", \"Manrope:wght@400..700\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.035,\n },\n luxe: {\n id: \"luxe\",\n name: \"Luxe\",\n look: \"Cormorant Garamond, fashion serif\",\n heading: `\"Cormorant Garamond\", ${SERIF}`,\n body: `\"Jost\", ${SANS}`,\n mono: MONO,\n google: [\"Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500\", \"Jost:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 500,\n tracking: -0.01,\n },\n rounded: {\n id: \"rounded\",\n name: \"Rounded\",\n look: \"Nunito, rounded sans\",\n heading: `\"Nunito\", ${SANS}`,\n body: `\"Nunito Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Nunito:wght@400;600;700;800;900\", \"Nunito+Sans:ital,wght@0,400;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 800,\n tracking: -0.02,\n },\n gazette: {\n id: \"gazette\",\n name: \"Gazette\",\n look: \"DM Serif Display, newspaper serif\",\n heading: `\"DM Serif Display\", ${SERIF}`,\n body: `\"Libre Franklin\", ${SANS}`,\n mono: MONO,\n google: [\"DM+Serif+Display:ital@0;1\", \"Libre+Franklin:ital,wght@0,400;0,500;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 400,\n tracking: -0.01,\n },\n slab: {\n id: \"slab\",\n name: \"Slab\",\n look: \"Zilla Slab, sturdy slab serif\",\n heading: `\"Zilla Slab\", ${SERIF}`,\n body: `\"Work Sans\", ${SANS}`,\n mono: MONO,\n google: [\"Zilla+Slab:ital,wght@0,400;0,500;0,600;0,700;1,400\", \"Work+Sans:ital,wght@0,400;0,500;0,600;1,400\", MONO_GOOGLE],\n headingWeight: 600,\n tracking: -0.015,\n },\n poster: {\n id: \"poster\",\n name: \"Poster\",\n look: \"Archivo Black, heavy headlines\",\n heading: `\"Archivo Black\", \"Archivo\", ${SANS}`,\n body: `\"Archivo\", ${SANS}`,\n mono: MONO,\n google: [\"Archivo+Black\", \"Archivo:ital,wght@0,400;0,500;0,600;0,700;1,400\", MONO_GOOGLE],\n headingWeight: 400,\n tracking: -0.035,\n },\n mono: {\n id: \"mono\",\n name: \"Mono\",\n look: \"JetBrains Mono, monospace\",\n heading: MONO,\n body: `\"IBM Plex Sans\", ${SANS}`,\n mono: MONO,\n google: [\"JetBrains+Mono:wght@400;500;600;700\", \"IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400\"],\n headingWeight: 600,\n tracking: -0.04,\n },\n}\n\nexport const fontPairingList = Object.values(fontPairings)\n\nexport function googleFontsUrl(pairing: FontPairing): string {\n return `https://fonts.googleapis.com/css2?${pairing.google.map((family) => `family=${family}`).join(\"&\")}&display=swap`\n}\n\n/**\n * What the classifier reads when choosing a theme: the subjects it suits, and\n * nothing else. Kept terse on purpose — classifier latency grows with every\n * character here, across every theme.\n */\nexport function describeTheme(theme: Theme): string {\n return theme.description\n}\n\n/** The complete default choice a theme stands for. */\nexport function themeChoice(id: PaletteId): ThemeChoice {\n const theme = themes[id]\n return { palette: id, fonts: theme.fonts, formality: theme.formality }\n}\n\n/**\n * The theme whose vocabulary stands out in the text, or `null` when none does.\n * This is evidence from the words themselves, independent of any classifier.\n */\nexport function matchTheme(text: string, stats: { codeBlocks: number; tables: number }): PaletteId | null {\n const words = Math.max(200, text.split(/\\s+/).length)\n let best: PaletteId | null = null\n let bestScore = 0.6\n for (const theme of themeList) {\n let score = ((text.match(theme.keywords) ?? []).length / words) * 100\n // Code and tables hint at a genre, but only words can confirm it.\n if (theme.id === \"terminal\") score += Math.min(3, stats.codeBlocks * 0.75)\n if (theme.id === \"ocean\" && score > 0) score += Math.min(0.6, stats.tables * 0.15)\n if (score > bestScore) [best, bestScore] = [theme.id, score]\n }\n return best\n}\n\n/**\n * Picks a theme from the words in the document — the fallback when no classifier\n * is configured, and what the loader is drawn in while the classifier decides.\n */\nexport function guessTheme(text: string, stats: { codeBlocks: number; tables: number }): ThemeChoice {\n return themeChoice(matchTheme(text, stats) ?? \"paper\")\n}\n\n/**\n * CSS custom properties for a theme. They use shadcn's token names, so every\n * shadcn component inside the scope picks the theme up without changes.\n */\nexport function themeVars(choice: ThemeChoice, appearance: Appearance): CSSProperties {\n const tokens = themes[choice.palette][appearance]\n const fonts = fontPairings[choice.fonts]\n const radius = 0.25 + (1 - choice.formality) * 0.75\n const soft = `color-mix(in oklab, ${tokens.accent} 12%, ${tokens.bg})`\n const vars: Record<string, string> = {\n \"--background\": tokens.bg,\n \"--foreground\": tokens.fg,\n \"--card\": tokens.surface,\n \"--card-foreground\": tokens.fg,\n \"--popover\": appearance === \"dark\" ? tokens.surface : tokens.bg,\n \"--popover-foreground\": tokens.fg,\n \"--primary\": tokens.accent,\n \"--primary-foreground\": tokens.accentFg,\n \"--secondary\": tokens.surface,\n \"--secondary-foreground\": tokens.fg,\n \"--muted\": tokens.surface,\n \"--muted-foreground\": tokens.muted,\n \"--accent\": soft,\n \"--accent-foreground\": tokens.fg,\n \"--border\": tokens.border,\n \"--input\": tokens.border,\n \"--ring\": tokens.accent,\n \"--radius\": `${radius.toFixed(3)}rem`,\n \"--smd-soft\": soft,\n \"--smd-bg\": tokens.bg,\n \"--smd-fg\": tokens.fg,\n \"--smd-surface\": tokens.surface,\n \"--smd-accent\": tokens.accent,\n \"--smd-accent-fg\": tokens.accentFg,\n ...(tokens.highlight ? { \"--smd-highlight\": tokens.highlight.bg, \"--smd-highlight-fg\": tokens.highlight.fg } : {}),\n // The other mode's accent is the one made to be read on this mode's text colour — which is what an `ink` cover is set on.\n \"--smd-hero-accent\": themes[choice.palette][appearance === \"dark\" ? \"light\" : \"dark\"].accent,\n \"--smd-font-heading\": fonts.heading,\n \"--smd-font-body\": fonts.body,\n \"--smd-font-mono\": fonts.mono,\n \"--smd-heading-weight\": String(fonts.headingWeight),\n \"--smd-tracking\": `${fonts.tracking}em`,\n colorScheme: appearance,\n }\n return vars as CSSProperties\n}\n","import { toText } from \"./analyze/text\"\nimport { describeTheme, themeList, themes } from \"./theme/themes\"\nimport type { DocumentPlan, Judgements, PaletteId, VizKind } from \"./types\"\n\n/**\n * Question shapes of a \"jev\"-compatible classifier endpoint: the request carries\n * a `state` (the text to judge) and named questions, each answered independently.\n */\nexport type ClassifierQuestion =\n | { type: \"choice\"; criteria: Record<string, string> }\n | { type: \"noul\"; criteria: { true: string; false: string } }\n | { type: \"score\"; criteria: string[] }\n\nexport type ClassifierRequest = {\n state: string\n questions: Record<string, ClassifierQuestion>\n}\n\nexport type ClassifierAnswer =\n | { type: \"choice\"; choice: string; confidence: number; probabilities?: Record<string, number> }\n | { type: \"noul\"; noul: number }\n | { type: \"score\"; score: number; confidence?: number }\n\nexport type ClassifierAnswers = Record<string, ClassifierAnswer>\n\n/** Anything that can answer a classifier request — usually a call to your own proxy route. */\nexport type Classify = (request: ClassifierRequest, signal?: AbortSignal) => Promise<ClassifierAnswers>\n\nconst cache = new Map<string, Promise<ClassifierAnswers>>()\n\n/**\n * A classifier that posts to `endpoint`. Point it at a route on your own server\n * that adds the API key (see `createClassifierHandler` in `stunning-md/server`) —\n * a key placed in browser code is public.\n */\nexport function createClassifier(options: { endpoint: string; headers?: Record<string, string> }): Classify {\n return (request) => {\n // Identical questions share one request, so it is never aborted on behalf of a single caller.\n const key = `${options.endpoint}\\n${JSON.stringify(request)}`\n const hit = cache.get(key)\n if (hit) return hit\n const pending = fetch(options.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(request),\n }).then(async (response) => {\n if (!response.ok) throw new Error(`classifier responded ${response.status}`)\n const data = (await response.json()) as { answers?: ClassifierAnswers }\n if (!data.answers) throw new Error(\"classifier response has no answers\")\n return data.answers\n })\n cache.set(key, pending)\n pending.catch(() => cache.delete(key))\n return pending\n }\n}\n\n/** Viz forms that plot figures against each other, as opposed to listing them. */\nconst COMPARATIVE: VizKind[] = [\"bar\", \"line\", \"area\", \"donut\", \"scatter\", \"stats\"]\n\nconst COMPARE_QUESTION: ClassifierQuestion = {\n type: \"noul\",\n criteria: {\n true: \"the numbers are measurements meant to be compared with each other, ranked, or read as a trend\",\n false: \"the numbers are identifiers, codes, settings, prices or specifications that are looked up one at a time\",\n },\n}\n\nconst WHOLE_QUESTION: ClassifierQuestion = {\n type: \"noul\",\n criteria: {\n true: \"the rows are parts that together make up one whole, such as shares of a total or a budget breakdown\",\n false: \"the rows are independent items that do not add up to a meaningful whole\",\n },\n}\n\n/**\n * A compact summary of the document: enough for tone and topic, small enough to\n * be cheap. `prose` should be running text only — code and table cells mislead.\n */\nexport function documentDigest(plan: DocumentPlan, prose: string): string {\n const headings = plan.sections.map((s) => s.titleText).filter(Boolean).slice(0, 12)\n return [\n plan.hero.titleText && `Title: ${plan.hero.titleText}`,\n headings.length && `Sections: ${headings.join(\" | \")}`,\n `Excerpt: ${prose.replace(/\\s+/g, \" \").slice(0, 400)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\")\n}\n\n/** How much probability a keyword match is worth when weighing the classifier's theme options. */\nconst HINT_WEIGHT = 0.25\n\nexport type JudgeOptions = {\n /** Minimum score before the classifier's theme is accepted over the keyword guess. */\n confidence?: number\n /** The theme the document's own keywords point to, if any (see `matchTheme`). */\n themeHint?: PaletteId\n /** Leave the theme question out — for a document whose theme is already settled. */\n skipTheme?: boolean\n /** Ask the theme question and nothing else. */\n themeOnly?: boolean\n /** What the document was written in answer to, if anything — it helps place the subject. */\n context?: string\n /** Requests in flight at once. Default 3. */\n concurrency?: number\n /** Upper bound on tables sent for a form judgement. */\n maxTables?: number\n signal?: AbortSignal\n /**\n * Called as each answer lands, with the judgements so far and what is still\n * outstanding: `\"theme\"`, `\"hero\"`, or the id of a data block.\n */\n onProgress?: (progress: JudgeProgress) => void\n}\n\nexport type JudgeProgress = { judgements: Judgements; pending: string[] }\n\n/**\n * Asks the classifier the questions structure cannot answer: which theme —\n * colours and typefaces together — suits the content, whether a table's numbers\n * are there to be compared or just looked up, and whether the leading image\n * deserves to be the hero.\n * Questions are asked top-down — theme, hero, then tables in document order —\n * so the top of the page settles first. A failed request simply leaves its\n * judgement unset.\n */\nexport async function judgeDocument(\n plan: DocumentPlan,\n prose: string,\n classify: Classify,\n options: JudgeOptions = {},\n): Promise<Judgements> {\n const threshold = options.confidence ?? 0.4\n const judgements: Judgements = {}\n // A few requests at a time, in order, so small classifier deployments are not\n // flooded by a document full of tables.\n const queue: (() => Promise<void>)[] = []\n const pending = new Set<string>()\n const report = () =>\n options.onProgress?.({ judgements: { ...judgements, viz: judgements.viz && { ...judgements.viz } }, pending: [...pending] })\n const attempt = (key: string, request: ClassifierRequest, apply: (answers: ClassifierAnswers) => void) => {\n pending.add(key)\n queue.push(() =>\n classify(request, options.signal)\n .then(apply, () => undefined)\n .then(() => {\n pending.delete(key)\n if (!options.signal?.aborted) report()\n }),\n )\n }\n\n // One question settles the whole look. Each option spells out the theme's\n // colours and typefaces as well as the content it suits.\n if (!options.skipTheme) attempt(\n \"theme\",\n {\n state: documentDigest(plan, prose) + (options.context ? `\\nWritten in answer to: ${options.context.replace(/\\s+/g, \" \").slice(0, 300)}` : \"\"),\n questions: { theme: { type: \"choice\", criteria: Object.fromEntries(themeList.map((t) => [t.id, describeTheme(t)])) } },\n },\n ({ theme }) => {\n if (theme?.type !== \"choice\") return\n // The document's own vocabulary counts as evidence too: it settles close\n // calls, but cannot overturn a classifier that is sure of itself.\n const odds: Record<string, number> = theme.probabilities ?? { [theme.choice]: theme.confidence }\n let best = \"\"\n let bestScore = 0\n for (const id of Object.keys(odds)) {\n const score = odds[id] + (id === options.themeHint ? HINT_WEIGHT : 0)\n if (id in themes && score > bestScore) [best, bestScore] = [id, score]\n }\n if (!best || bestScore < threshold) return\n const chosen = themes[best as PaletteId]\n judgements.theme = { palette: chosen.id, fonts: chosen.fonts, formality: chosen.formality }\n },\n )\n\n const image = plan.hero.image\n if (image && !options.themeOnly) {\n attempt(\n \"hero\",\n {\n state: [\n `Document: ${plan.hero.titleText}`,\n `First image — alt text: \"${image.alt}\", file: ${image.src.split(/[?#]/)[0].split(\"/\").pop()}`,\n image.meta && `size: ${image.meta.width}×${image.meta.height}px`,\n `Text right after it: ${toText(plan.hero.lead.flat()).slice(0, 200)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n questions: {\n hero: {\n type: \"noul\",\n criteria: {\n true: \"the image is a logo, banner, cover photo or key visual that represents the whole document\",\n false: \"the image is a badge, screenshot, diagram, chart or incidental illustration that belongs in the body\",\n },\n },\n },\n },\n ({ hero }) => {\n // Only a clear \"no\" demotes the image; the structural checks already vouched for it.\n if (hero?.type === \"noul\" && hero.noul < 0.2) judgements.heroImageUsable = false\n },\n )\n }\n\n const tables = plan.sections\n .flatMap((section) =>\n section.blocks\n .flatMap((block) => (block.kind === \"cards\" ? block.items.flatMap((item) => item.blocks) : [block]))\n .flatMap((block) => (block.kind === \"data\" ? [{ section, block }] : [])),\n )\n .filter(({ block }) => block.viz.candidates.length > 1)\n .slice(0, options.themeOnly ? 0 : (options.maxTables ?? 8))\n\n for (const { section, block } of tables) {\n const { columns, rows } = block.table\n const { viz } = block\n if (!COMPARATIVE.includes(viz.kind)) continue\n const lines = [\n columns.map((c) => c.label).join(\" | \"),\n ...rows.slice(0, 12).map((row) => columns.map((c) => row.text[c.key]).join(\" | \")),\n ]\n // Whether numbers are measurements or mere reference values, and whether rows\n // form a whole, depends on what they mean — which the column types cannot say.\n const partsPossible = viz.candidates.includes(\"donut\") && viz.rowIndices.length >= 3\n attempt(\n block.id,\n {\n state: `Section: ${section.titleText || plan.hero.titleText}\\n${lines.join(\"\\n\")}`,\n questions: { compare: COMPARE_QUESTION, ...(partsPossible ? { whole: WHOLE_QUESTION } : {}) },\n },\n ({ compare, whole }) => {\n const set = (kind: VizKind) => ((judgements.viz ??= {})[block.id] = kind)\n if (compare?.type === \"noul\" && compare.noul < 0.12) return set(\"table\")\n if (whole?.type !== \"noul\") return\n if (whole.noul >= 0.9 && viz.kind !== \"donut\") set(\"donut\")\n else if (whole.noul <= 0.12 && viz.kind === \"donut\") set(\"bar\")\n },\n )\n }\n\n const worker = async () => {\n for (let task = queue.shift(); task; task = queue.shift()) {\n if (options.signal?.aborted) return\n await task()\n }\n }\n report()\n // The theme question is by far the heaviest and everything visible depends on\n // it, so it runs alone; the lighter questions then share the connection.\n await queue.shift()?.()\n await Promise.all(Array.from({ length: options.concurrency ?? 3 }, worker))\n return judgements\n}\n"],"mappings":"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","names":["tokens","children","words"]}