┌─ ooknet-design/src/lib/rehype-ascii.ts ─────────────────────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/lib/rehype-ascii.ts b/ooknet-design/src/lib/rehype- │
│ ascii.ts │
│ index f94d6f0..76befbd 100644 │
│ --- a/ooknet-design/src/lib/rehype-ascii.ts │
│ +++ b/ooknet-design/src/lib/rehype-ascii.ts │
│ @@ -232,7 +232,12 @@ function parseFence(text: string): { body: string; captionTex │
│ t: string | null } │
│ : { body: trimmed, captionText: null }; │
│ } │
│ │
│ -export function rehypeAscii() { │
│ +// Lenient mode is for markdown we don't own (repo readmes): a │
│ +// construct that defeats the grid degrades to a plain code frame │
│ +// instead of failing the build. Site content never sets it and keeps │
│ +// failing loudly. │
│ +export function rehypeAscii(opts: { lenient?: boolean } = {}) { │
│ + const lenient = opts.lenient === true; │
│ return (tree: Root) => { │
│ let fig = 0; │
│ │
│ @@ -253,29 +258,38 @@ export function rehypeAscii() { │
│ // through mermaid-ascii. Art wider than the narrow frame │
│ // scrolls. │
│ if (lang === "diagram" || lang === "mermaid") { │
│ - fig++; │
│ const { body, captionText } = parseFence(text); │
│ - const caption = captionText ? `FIG. ${fig} - ${captionText.toUpperCase( │
│ )}` : `FIG. ${fig}`; │
│ - const art = lang === "mermaid" ? mermaidToAscii(body, FRAME_W) : body; │
│ - checkGrid(art, FRAME_W, "diagram"); │
│ - │
│ - node.tagName = "figure"; │
│ - node.properties = { className: ["figure-frame", "diagram-frame"], role: │
│ "img", "aria-label": caption }; │
│ - node.children = [ │
│ - ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ - ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ - { │
│ - type: "element", │
│ - tagName: "pre", │
│ - properties: { className: ["ascii", "diagram-art"], "aria-hidden": " │
│ true" }, │
│ - children: [{ type: "text", value: art }], │
│ - }, │
│ - captionSpan(caption, FRAME_W, "frame-wide"), │
│ - captionSpan(caption, MOBILE_FRAME_W, "frame-narrow"), │
│ - ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ - ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ - ]; │
│ - return; │
│ + const caption = captionText ? `FIG. ${fig + 1} - ${captionText.toUpperC │
│ ase()}` : `FIG. ${fig + 1}`; │
│ + let figChildren: ElementContent[] | null = null; │
│ + try { │
│ + const art = lang === "mermaid" ? mermaidToAscii(body, FRAME_W) : body │
│ ; │
│ + checkGrid(art, FRAME_W, "diagram"); │
│ + figChildren = [ │
│ + ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ + ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ + { │
│ + type: "element", │
│ + tagName: "pre", │
│ + properties: { className: ["ascii", "diagram-art"], "aria-hidden": │
│ "true" }, │
│ + children: [{ type: "text", value: art }], │
│ + }, │
│ + captionSpan(caption, FRAME_W, "frame-wide"), │
│ + captionSpan(caption, MOBILE_FRAME_W, "frame-narrow"), │
│ + ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ + ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ + ]; │
│ + } catch (e) { │
│ + // lenient: fall through and render the fence as a plain │
│ + // code frame instead │
│ + if (!lenient) throw e; │
│ + } │
│ + if (figChildren) { │
│ + fig++; │
│ + node.tagName = "figure"; │
│ + node.properties = { className: ["figure-frame", "diagram-frame"], rol │
│ e: "img", "aria-label": caption }; │
│ + node.children = figChildren; │
│ + return; │
│ + } │
│ } │
│ │
│ node.tagName = "div"; │
│ @@ -292,6 +306,24 @@ export function rehypeAscii() { │
│ const { rows, headerRows } = tableCells(node); │
│ if (!rows.length) return; │
│ │
│ + // A table with too many columns for the frame defeats the │
│ + // grid; lenient mode degrades just this table to a code frame │
│ + // whose rows hard-wrap, so it can't fail. │
│ + let pres: Element[]; │
│ + try { │
│ + pres = [ │
│ + makeTablePre(buildAsciiTable(rows, headerRows, FRAME_W), "frame-wide" │
│ ), │
│ + makeTablePre(buildAsciiTable(rows, headerRows, MOBILE_FRAME_W), "fram │
│ e-narrow"), │
│ + ]; │
│ + } catch (e) { │
│ + if (!lenient) throw e; │
│ + const text = rows.map((r) => r.map((c) => c.text).join(" | ")).join("\n │
│ "); │
│ + pres = [ │
│ + makeCodePre(text, "table", FRAME_W, "frame-wide"), │
│ + makeCodePre(text, "table", MOBILE_FRAME_W, "frame-narrow"), │
│ + ]; │
│ + } │
│ + │
│ // Keep the semantic table for screen readers; the ASCII pres │
│ // are presentation only. │
│ const original: Element = { │
│ @@ -302,11 +334,7 @@ export function rehypeAscii() { │
│ }; │
│ node.tagName = "div"; │
│ node.properties = { className: ["table-frames"] }; │
│ - node.children = [ │
│ - makeTablePre(buildAsciiTable(rows, headerRows, FRAME_W), "frame-wide"), │
│ - makeTablePre(buildAsciiTable(rows, headerRows, MOBILE_FRAME_W), "frame- │
│ narrow"), │
│ - original, │
│ - ]; │
│ + node.children = [...pres, original]; │
│ return; │
│ } │
│ │
│ @@ -340,17 +368,28 @@ export function rehypeAscii() { │
│ const img = kids[0]; │
│ if (kids.length !== 1 || img.type !== "element" || img.tagName !== "img") │
│ return; │
│ │
│ - fig++; │
│ const alt = String((img.properties as Properties | undefined)?.alt ?? "") │
│ .trim(); │
│ - const caption = alt ? `FIG. ${fig} - ${alt.toUpperCase()}` : `FIG. ${fig} │
│ `; │
│ + const caption = alt ? `FIG. ${fig + 1} - ${alt.toUpperCase()}` : `FIG. ${ │
│ fig + 1}`; │
│ + let caps: Element[]; │
│ + try { │
│ + caps = [ │
│ + captionSpan(caption, FRAME_W, "frame-wide"), │
│ + captionSpan(caption, MOBILE_FRAME_W, "frame-narrow"), │
│ + ]; │
│ + } catch (e) { │
│ + // lenient: an unbreakable alt defeats the caption - leave │
│ + // the image as a plain paragraph │
│ + if (!lenient) throw e; │
│ + return; │
│ + } │
│ + fig++; │
│ node.tagName = "figure"; │
│ node.properties = { className: ["figure-frame"] }; │
│ node.children = [ │
│ ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ img, │
│ - captionSpan(caption, FRAME_W, "frame-wide"), │
│ - captionSpan(caption, MOBILE_FRAME_W, "frame-narrow"), │
│ + ...caps, │
│ ruleSpan(FRAME_W, "frame-wide", "fig-rule"), │
│ ruleSpan(MOBILE_FRAME_W, "frame-narrow", "fig-rule"), │
│ ]; │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...esign/src/lib/rehype-ascii.ts ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/lib/rehype-as │
│ cii.ts b/ooknet-design/src/lib/rehype-ascii. │
│ ts │
│ index f94d6f0..76befbd 100644 │
│ --- a/ooknet-design/src/lib/rehype-ascii.ts │
│ +++ b/ooknet-design/src/lib/rehype-ascii.ts │
│ @@ -232,7 +232,12 @@ function parseFence(tex │
│ t: string): { body: string; captionText: str │
│ ing | null } │
│ : { body: trimmed, captionText: null }; │
│ } │
│ │
│ -export function rehypeAscii() { │
│ +// Lenient mode is for markdown we don't ow │
│ n (repo readmes): a │
│ +// construct that defeats the grid degrades │
│ to a plain code frame │
│ +// instead of failing the build. Site conte │
│ nt never sets it and keeps │
│ +// failing loudly. │
│ +export function rehypeAscii(opts: { lenient │
│ ?: boolean } = {}) { │
│ + const lenient = opts.lenient === true; │
│ return (tree: Root) => { │
│ let fig = 0; │
│ │
│ @@ -253,29 +258,38 @@ export function rehype │
│ Ascii() { │
│ // through mermaid-ascii. Art wider │
│ than the narrow frame │
│ // scrolls. │
│ if (lang === "diagram" || lang === │
│ "mermaid") { │
│ - fig++; │
│ const { body, captionText } = par │
│ seFence(text); │
│ - const caption = captionText ? `FI │
│ G. ${fig} - ${captionText.toUpperCase()}` : │
│ `FIG. ${fig}`; │
│ - const art = lang === "mermaid" ? │
│ mermaidToAscii(body, FRAME_W) : body; │
│ - checkGrid(art, FRAME_W, "diagram" │
│ ); │
│ - │
│ - node.tagName = "figure"; │
│ - node.properties = { className: [" │
│ figure-frame", "diagram-frame"], role: "img" │
│ , "aria-label": caption }; │
│ - node.children = [ │
│ - ruleSpan(FRAME_W, "frame-wide", │
│ "fig-rule"), │
│ - ruleSpan(MOBILE_FRAME_W, "frame │
│ -narrow", "fig-rule"), │
│ - { │
│ - type: "element", │
│ - tagName: "pre", │
│ - properties: { className: ["as │
│ cii", "diagram-art"], "aria-hidden": "true" │
│ }, │
│ - children: [{ type: "text", va │
│ lue: art }], │
│ - }, │
│ - captionSpan(caption, FRAME_W, " │
│ frame-wide"), │
│ - captionSpan(caption, MOBILE_FRA │
│ ME_W, "frame-narrow"), │
│ - ruleSpan(FRAME_W, "frame-wide", │
│ "fig-rule"), │
│ - ruleSpan(MOBILE_FRAME_W, "frame │
│ -narrow", "fig-rule"), │
│ - ]; │
│ - return; │
│ + const caption = captionText ? `FI │
│ G. ${fig + 1} - ${captionText.toUpperCase()} │
│ ` : `FIG. ${fig + 1}`; │
│ + let figChildren: ElementContent[] │
│ | null = null; │
│ + try { │
│ + const art = lang === "mermaid" │
│ ? mermaidToAscii(body, FRAME_W) : body; │
│ + checkGrid(art, FRAME_W, "diagra │
│ m"); │
│ + figChildren = [ │
│ + ruleSpan(FRAME_W, "frame-wide │
│ ", "fig-rule"), │
│ + ruleSpan(MOBILE_FRAME_W, "fra │
│ me-narrow", "fig-rule"), │
│ + { │
│ + type: "element", │
│ + tagName: "pre", │
│ + properties: { className: [" │
│ ascii", "diagram-art"], "aria-hidden": "true │
│ " }, │
│ + children: [{ type: "text", │
│ value: art }], │
│ + }, │
│ + captionSpan(caption, FRAME_W, │
│ "frame-wide"), │
│ + captionSpan(caption, MOBILE_F │
│ RAME_W, "frame-narrow"), │
│ + ruleSpan(FRAME_W, "frame-wide │
│ ", "fig-rule"), │
│ + ruleSpan(MOBILE_FRAME_W, "fra │
│ me-narrow", "fig-rule"), │
│ + ]; │
│ + } catch (e) { │
│ + // lenient: fall through and re │
│ nder the fence as a plain │
│ + // code frame instead │
│ + if (!lenient) throw e; │
│ + } │
│ + if (figChildren) { │
│ + fig++; │
│ + node.tagName = "figure"; │
│ + node.properties = { className: │
│ ["figure-frame", "diagram-frame"], role: "im │
│ g", "aria-label": caption }; │
│ + node.children = figChildren; │
│ + return; │
│ + } │
│ } │
│ │
│ node.tagName = "div"; │
│ @@ -292,6 +306,24 @@ export function rehypeA │
│ scii() { │
│ const { rows, headerRows } = tableC │
│ ells(node); │
│ if (!rows.length) return; │
│ │
│ + // A table with too many columns fo │
│ r the frame defeats the │
│ + // grid; lenient mode degrades just │
│ this table to a code frame │
│ + // whose rows hard-wrap, so it can' │
│ t fail. │
│ + let pres: Element[]; │
│ + try { │
│ + pres = [ │
│ + makeTablePre(buildAsciiTable(ro │
│ ws, headerRows, FRAME_W), "frame-wide"), │
│ + makeTablePre(buildAsciiTable(ro │
│ ws, headerRows, MOBILE_FRAME_W), "frame-narr │
│ ow"), │
│ + ]; │
│ + } catch (e) { │
│ + if (!lenient) throw e; │
│ + const text = rows.map((r) => r.ma │
│ p((c) => c.text).join(" | ")).join("\n"); │
│ + pres = [ │
│ + makeCodePre(text, "table", FRAM │
│ E_W, "frame-wide"), │
│ + makeCodePre(text, "table", MOBI │
│ LE_FRAME_W, "frame-narrow"), │
│ + ]; │
│ + } │
│ + │
│ // Keep the semantic table for scre │
│ en readers; the ASCII pres │
│ // are presentation only. │
│ const original: Element = { │
│ @@ -302,11 +334,7 @@ export function rehypeA │
│ scii() { │
│ }; │
│ node.tagName = "div"; │
│ node.properties = { className: ["ta │
│ ble-frames"] }; │
│ - node.children = [ │
│ - makeTablePre(buildAsciiTable(rows │
│ , headerRows, FRAME_W), "frame-wide"), │
│ - makeTablePre(buildAsciiTable(rows │
│ , headerRows, MOBILE_FRAME_W), "frame-narrow │
│ "), │
│ - original, │
│ - ]; │
│ + node.children = [...pres, original] │
│ ; │
│ return; │
│ } │
│ │
│ @@ -340,17 +368,28 @@ export function rehype │
│ Ascii() { │
│ const img = kids[0]; │
│ if (kids.length !== 1 || img.type ! │
│ == "element" || img.tagName !== "img") retur │
│ n; │
│ │
│ - fig++; │
│ const alt = String((img.properties │
│ as Properties | undefined)?.alt ?? "").trim( │
│ ); │
│ - const caption = alt ? `FIG. ${fig} │
│ - ${alt.toUpperCase()}` : `FIG. ${fig}`; │
│ + const caption = alt ? `FIG. ${fig + │
│ 1} - ${alt.toUpperCase()}` : `FIG. ${fig + │
│ 1}`; │
│ + let caps: Element[]; │
│ + try { │
│ + caps = [ │
│ + captionSpan(caption, FRAME_W, " │
│ frame-wide"), │
│ + captionSpan(caption, MOBILE_FRA │
│ ME_W, "frame-narrow"), │
│ + ]; │
│ + } catch (e) { │
│ + // lenient: an unbreakable alt de │
│ feats the caption - leave │
│ + // the image as a plain paragraph │
│ + if (!lenient) throw e; │
│ + return; │
│ + } │
│ + fig++; │
│ node.tagName = "figure"; │
│ node.properties = { className: ["fi │
│ gure-frame"] }; │
│ node.children = [ │
│ ruleSpan(FRAME_W, "frame-wide", " │
│ fig-rule"), │
│ ruleSpan(MOBILE_FRAME_W, "frame-n │
│ arrow", "fig-rule"), │
│ img, │
│ - captionSpan(caption, FRAME_W, "fr │
│ ame-wide"), │
│ - captionSpan(caption, MOBILE_FRAME │
│ _W, "frame-narrow"), │
│ + ...caps, │
│ ruleSpan(FRAME_W, "frame-wide", " │
│ fig-rule"), │
│ ruleSpan(MOBILE_FRAME_W, "frame-n │
│ arrow", "fig-rule"), │
│ ]; │
└──────────────────────────────────────────────┘