HASH cf0f1a5d6f13
DATE 2026-07-18
SUBJECT web: readme falls back to plain text when it defeats the grid, keyvalue
hard-wraps
FILES 3 CHANGED HASH cf0f1a5d6f13
DATE 2026-07-18
SUBJECT web: readme falls back to plain text
when it defeats the grid, keyvalue
hard-wraps
FILES 3 CHANGED
┌─ ooknet-design/src/components/KeyValue/KeyValue.test.ts ───────────────────┐
│ diff --git a/ooknet-design/src/components/KeyValue/KeyValue.test.ts b/ooknet-desig │
│ n/src/components/KeyValue/KeyValue.test.ts │
│ index b0f7214..87abedd 100644 │
│ --- a/ooknet-design/src/components/KeyValue/KeyValue.test.ts │
│ +++ b/ooknet-design/src/components/KeyValue/KeyValue.test.ts │
│ @@ -3,6 +3,11 @@ import { buildKeyValue } from "./KeyValue"; │
│ import { len } from "../../lib/ascii"; │
│ │
│ describe("buildKeyValue", () => { │
│ + it("hard-wraps single tokens wider than the value column", () => { │
│ + const out = buildKeyValue([["SUBJECT", "feat(hosts:{ooksmedia,ooksdesk,ooknod │
│ e,ookstest})"]], 48); │
│ + for (const line of out.split("\n")) expect(len(line)).toBeLessThanOrEqual(48) │
│ ; │
│ + }); │
│ + │
│ it("aligns values to the widest key plus gap", () => { │
│ const out = buildKeyValue([["AUTHOR", "OOKNET"], ["PUBLISHED", "2026-07-16"]] │
│ , 48); │
│ const lines = out.split("\n"); │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...nts/KeyValue/KeyValue.test.ts ───┐
│ diff --git a/ooknet-design/src/components/Ke │
│ yValue/KeyValue.test.ts b/ooknet-design/src/ │
│ components/KeyValue/KeyValue.test.ts │
│ index b0f7214..87abedd 100644 │
│ --- a/ooknet-design/src/components/KeyValue/ │
│ KeyValue.test.ts │
│ +++ b/ooknet-design/src/components/KeyValue/ │
│ KeyValue.test.ts │
│ @@ -3,6 +3,11 @@ import { buildKeyValue } fr │
│ om "./KeyValue"; │
│ import { len } from "../../lib/ascii"; │
│ │
│ describe("buildKeyValue", () => { │
│ + it("hard-wraps single tokens wider than t │
│ he value column", () => { │
│ + const out = buildKeyValue([["SUBJECT", │
│ "feat(hosts:{ooksmedia,ooksdesk,ooknode,ooks │
│ test})"]], 48); │
│ + for (const line of out.split("\n")) exp │
│ ect(len(line)).toBeLessThanOrEqual(48); │
│ + }); │
│ + │
│ it("aligns values to the widest key plus │
│ gap", () => { │
│ const out = buildKeyValue([["AUTHOR", " │
│ OOKNET"], ["PUBLISHED", "2026-07-16"]], 48); │
│ const lines = out.split("\n"); │
└──────────────────────────────────────────────┘
┌─ ooknet-design/src/components/KeyValue/KeyValue.ts ────────────────────────┐
│ diff --git a/ooknet-design/src/components/KeyValue/KeyValue.ts b/ooknet-design/src │
│ /components/KeyValue/KeyValue.ts │
│ index f6abd4b..2b0d0f4 100644 │
│ --- a/ooknet-design/src/components/KeyValue/KeyValue.ts │
│ +++ b/ooknet-design/src/components/KeyValue/KeyValue.ts │
│ @@ -1,6 +1,6 @@ │
│ // Labeled key/value rows with values word-wrapped under their column - │
│ // the pattern ArticleMeta hand-rolls, as a reusable block. │
│ -import { checkGrid, len, pad, wrap } from "../../lib/ascii"; │
│ +import { checkGrid, len, pad, wrapHard } from "../../lib/ascii"; │
│ │
│ export interface Props { │
│ entries: [string, string][]; │
│ @@ -14,7 +14,7 @@ export function buildKeyValue(entries: [string, string][], width │
│ : number, keyW?: │
│ │
│ const lines: string[] = []; │
│ for (const [key, value] of entries) { │
│ - wrap(value, valW) │
│ + wrapHard(value, valW) │
│ .split("\n") │
│ .forEach((ln, i) => { │
│ lines.push(pad(i === 0 ? key : "", kw) + ln); │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...mponents/KeyValue/KeyValue.ts ───┐
│ diff --git a/ooknet-design/src/components/Ke │
│ yValue/KeyValue.ts b/ooknet-design/src/compo │
│ nents/KeyValue/KeyValue.ts │
│ index f6abd4b..2b0d0f4 100644 │
│ --- a/ooknet-design/src/components/KeyValue/ │
│ KeyValue.ts │
│ +++ b/ooknet-design/src/components/KeyValue/ │
│ KeyValue.ts │
│ @@ -1,6 +1,6 @@ │
│ // Labeled key/value rows with values word- │
│ wrapped under their column - │
│ // the pattern ArticleMeta hand-rolls, as a │
│ reusable block. │
│ -import { checkGrid, len, pad, wrap } from " │
│ ../../lib/ascii"; │
│ +import { checkGrid, len, pad, wrapHard } fr │
│ om "../../lib/ascii"; │
│ │
│ export interface Props { │
│ entries: [string, string][]; │
│ @@ -14,7 +14,7 @@ export function buildKeyVa │
│ lue(entries: [string, string][], width: numb │
│ er, keyW?: │
│ │
│ const lines: string[] = []; │
│ for (const [key, value] of entries) { │
│ - wrap(value, valW) │
│ + wrapHard(value, valW) │
│ .split("\n") │
│ .forEach((ln, i) => { │
│ lines.push(pad(i === 0 ? key : "", │
│ kw) + ln); │
└──────────────────────────────────────────────┘
┌─ ooknet-design/src/pages/git/[repo]/index.astro ───────────────────────────┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/index.astro b/ooknet-design/src/pa │
│ ges/git/[repo]/index.astro │
│ index ba0a270..1bd6fad 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/index.astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/index.astro │
│ @@ -6,11 +6,12 @@ import Frame from "../../../components/Frame/Frame.astro"; │
│ import Pre from "../../../components/Pre/Pre.astro"; │
│ import Menu from "../../../components/Menu/Menu.astro"; │
│ import GitListing from "../../../components/GitListing/GitListing.astro"; │
│ +import CodeFrame from "../../../components/CodeFrame/CodeFrame.astro"; │
│ import Timeline from "../../../components/Timeline/Timeline.astro"; │
│ import Button from "../../../components/Button/Button.astro"; │
│ import Prose from "../../../components/Prose/Prose.astro"; │
│ import ArticleFooter from "../../../components/ArticleFooter/ArticleFooter.astro" │
│ ; │
│ -import { len, row2, ruleD } from "../../../lib/ascii"; │
│ +import { len, row2, ruleD, toGrid } from "../../../lib/ascii"; │
│ import { FRAME_W, MOBILE_FRAME_W } from "../../../lib/config"; │
│ import { renderMarkdown } from "../../../lib/markdown"; │
│ import { REPOS } from "../../../git.config"; │
│ @@ -29,7 +30,16 @@ const branch = info.branch; │
│ const entries = dirEntries(repo.path, "", branch); │
│ const log = repoLog(repo.path, 5, `/git/${repo.slug}/`, branch); │
│ const readme = readReadme(repo.path, branch); │
│ -const readmeHtml = readme ? renderMarkdown(readme) : null; │
│ +// foreign readmes can defeat the grid (wide tables, exotic markdown); │
│ +// degrade to plain text rather than failing the whole build │
│ +let readmeHtml: string | null = null; │
│ +if (readme) { │
│ + try { │
│ + readmeHtml = renderMarkdown(readme); │
│ + } catch { │
│ + readmeHtml = null; │
│ + } │
│ +} │
│ │
│ const section = (title: string, right: string) => (w: number) => │
│ [row2(title, right, w), ruleD(w)].join("\n"); │
│ @@ -85,6 +95,14 @@ const variants: [string, number][] = [ │
│ <Pre>{" "}</Pre> │
│ </Fragment> │
│ )} │
│ + {readme && !readmeHtml && ( │
│ + <Fragment> │
│ + <Frame build={section("README", "plain - defeats the grid")} /> │
│ + <Pre>{" "}</Pre> │
│ + <CodeFrame code={toGrid(readme)} label="README.MD" /> │
│ + <Pre>{" "}</Pre> │
│ + </Fragment> │
│ + )} │
│ <Frame build={section("LOG", "most recent first")} /> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../pages/git/[repo]/index.astro ───┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/index.astro b/ooknet-design/src/pages/gi │
│ t/[repo]/index.astro │
│ index ba0a270..1bd6fad 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/ind │
│ ex.astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/ind │
│ ex.astro │
│ @@ -6,11 +6,12 @@ import Frame from "../../. │
│ ./components/Frame/Frame.astro"; │
│ import Pre from "../../../components/Pre/Pr │
│ e.astro"; │
│ import Menu from "../../../components/Menu/ │
│ Menu.astro"; │
│ import GitListing from "../../../components │
│ /GitListing/GitListing.astro"; │
│ +import CodeFrame from "../../../components/ │
│ CodeFrame/CodeFrame.astro"; │
│ import Timeline from "../../../components/T │
│ imeline/Timeline.astro"; │
│ import Button from "../../../components/But │
│ ton/Button.astro"; │
│ import Prose from "../../../components/Pros │
│ e/Prose.astro"; │
│ import ArticleFooter from "../../../compone │
│ nts/ArticleFooter/ArticleFooter.astro"; │
│ -import { len, row2, ruleD } from "../../../ │
│ lib/ascii"; │
│ +import { len, row2, ruleD, toGrid } from ". │
│ ./../../lib/ascii"; │
│ import { FRAME_W, MOBILE_FRAME_W } from ".. │
│ /../../lib/config"; │
│ import { renderMarkdown } from "../../../li │
│ b/markdown"; │
│ import { REPOS } from "../../../git.config" │
│ ; │
│ @@ -29,7 +30,16 @@ const branch = info.branc │
│ h; │
│ const entries = dirEntries(repo.path, "", b │
│ ranch); │
│ const log = repoLog(repo.path, 5, `/git/${r │
│ epo.slug}/`, branch); │
│ const readme = readReadme(repo.path, branch │
│ ); │
│ -const readmeHtml = readme ? renderMarkdown( │
│ readme) : null; │
│ +// foreign readmes can defeat the grid (wid │
│ e tables, exotic markdown); │
│ +// degrade to plain text rather than failin │
│ g the whole build │
│ +let readmeHtml: string | null = null; │
│ +if (readme) { │
│ + try { │
│ + readmeHtml = renderMarkdown(readme); │
│ + } catch { │
│ + readmeHtml = null; │
│ + } │
│ +} │
│ │
│ const section = (title: string, right: stri │
│ ng) => (w: number) => │
│ [row2(title, right, w), ruleD(w)].join("\ │
│ n"); │
│ @@ -85,6 +95,14 @@ const variants: [string, │
│ number][] = [ │
│ <Pre>{" "}</Pre> │
│ </Fragment> │
│ )} │
│ + {readme && !readmeHtml && ( │
│ + <Fragment> │
│ + <Frame build={section("README", "plai │
│ n - defeats the grid")} /> │
│ + <Pre>{" "}</Pre> │
│ + <CodeFrame code={toGrid(readme)} labe │
│ l="README.MD" /> │
│ + <Pre>{" "}</Pre> │
│ + </Fragment> │
│ + )} │
│ <Frame build={section("LOG", "most recent │
│ first")} /> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
└──────────────────────────────────────────────┘
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET