Fix #182: markdown relative URLs + prose CSS #183

Merged
crueber merged 2 commits from fix/issue-182 into main 2026-09-06 22:57:26 +00:00
Owner

Fixes #182 — rendered markdown resolved src/href verbatim, so relative images/links 404'd against the SPA route.

Resolution at render time — new resolveMarkdownUrls(html, {owner, repo, ref, dir}) in web/src/lib/render-md.js (pure, headless-tested), applied by renderMarkdownHtml(src, ctx?)/renderBody(src, ctx?) BEFORE the DOMPurify gate:

  • images → §9.5 raw-bytes endpoint at same ref; .md/.markdown links → in-app blob view at same ref; other relative links → raw endpoint (bytes, not the blob view — documented trade-off, keeps non-renderables off the 2 MiB cap page)
  • anchors/schemes/protocol-relative untouched; leading / = repo-root; dot-segments normalize with .. clamped at root; nothing decoded/re-encoded; ?query/#frag preserved
  • dangerous schemes pass through byte-identical so the sanitizer still drops them (anti-laundering pins in test)

Call sites: Tree doc tabs (docRef = display short-ref, sha fallback — ref dodged: Solid drops it on components, caught live), Blob (ref + file dir), Release ({ref: tag, dir: root}); threads/previews pass no ctx (no file coordinates — documented).

Sibling fix: RepoClient.urls.raw built /{o}/{r}/raw/…, a page route that never existed (404 — Blob raw pill broken too); now the §9.5 ?raw shape. Resolver mirrors it (pinned both sides).

Prose CSS (.markdown-body, both themes): heading scale+rules, code blocks/inline, bordered tables+zebra, tinted blockquotes, nested+task lists (:has un-bullet), hr, framed images, emerald links; threads/previews move off the dead prose-sm class.

Tests: node --test test/unit/*.test.js 378/378 green (15 new in md-urls.test.js). Browser proof (real Chromium over CDP against a live server + pushed demo repo): README renders both images + navigates md→blob, sub-doc resolves ../ against its dir, release notes resolve at tag, dark+light screenshots, zero console errors, zero failed requests, zero broken images, no javascript: in rendered HTML. No new deps. Decision appended to docs/go/12_web_ui.md.

Fixes #182 — rendered markdown resolved src/href verbatim, so relative images/links 404'd against the SPA route. **Resolution at render time** — new `resolveMarkdownUrls(html, {owner, repo, ref, dir})` in `web/src/lib/render-md.js` (pure, headless-tested), applied by `renderMarkdownHtml(src, ctx?)`/`renderBody(src, ctx?)` BEFORE the DOMPurify gate: - images → §9.5 raw-bytes endpoint at same ref; `.md`/.markdown links → in-app blob view at same ref; other relative links → raw endpoint (bytes, not the blob view — documented trade-off, keeps non-renderables off the 2 MiB cap page) - anchors/schemes/protocol-relative untouched; leading `/` = repo-root; dot-segments normalize with `..` clamped at root; nothing decoded/re-encoded; ?query/#frag preserved - dangerous schemes pass through byte-identical so the sanitizer still drops them (anti-laundering pins in test) **Call sites**: Tree doc tabs (`docRef` = display short-ref, sha fallback — `ref` dodged: Solid drops it on components, caught live), Blob (ref + file dir), Release ({ref: tag, dir: root}); threads/previews pass no ctx (no file coordinates — documented). **Sibling fix**: `RepoClient.urls.raw` built `/{o}/{r}/raw/…`, a page route that never existed (404 — Blob raw pill broken too); now the §9.5 `?raw` shape. Resolver mirrors it (pinned both sides). **Prose CSS** (`.markdown-body`, both themes): heading scale+rules, code blocks/inline, bordered tables+zebra, tinted blockquotes, nested+task lists (:has un-bullet), hr, framed images, emerald links; threads/previews move off the dead `prose-sm` class. **Tests**: `node --test test/unit/*.test.js` 378/378 green (15 new in md-urls.test.js). Browser proof (real Chromium over CDP against a live server + pushed demo repo): README renders both images + navigates md→blob, sub-doc resolves `../` against its dir, release notes resolve at tag, dark+light screenshots, zero console errors, zero failed requests, zero broken images, no `javascript:` in rendered HTML. No new deps. Decision appended to docs/go/12_web_ui.md.
resolveMarkdownUrls(html, {owner, repo, ref, dir}) in render-md.js rewrites
relative src/href at render time (images/other links -> ?raw byte endpoint
at same ref, .md links -> in-app blob view; anchors/schemes untouched,
dot-segments normalize with root clamp, no decode/re-encode). Call sites:
Tree doc tabs (docRef display ref, sha fallback), Blob (ref+dir), Release
(tag ref, root base); threads/previews pass no ctx (no file coordinates).
Sibling fix: urls.raw built a /raw page route that never existed (404);
now the 07_api 9.5 ?raw shape (also fixes the Blob raw pill). Prose pass
on .markdown-body both themes; threads switch from dead prose-sm class.
Headless: md-urls.test.js (15 incl. anti-laundering pins). Browser: README
both themes, zero console errors, zero broken images, md->blob nav.
Docs: decision appended to 12_web_ui.md.
Single-expression dedup; behavior-identical, removes drift risk between
the Node-tested layer and the browser gate. Docs: 12_web_ui.md #182 entry
unchanged.
Sign in to join this conversation.
No description provided.