feat(booth): render .md/.txt/.log inline in the gallery, collapsible + closable

Docs used to render as a clumsy link that navigated to a separate page. They
now render in place: build_gallery pre-renders each doc (markdown -> HTML,
plain text raw) and the gallery shows it inside a native <details open>
disclosure that spans the full grid width so prose has a readable measure.

The doc bar carries: a collapse chevron (the whole <details> summary toggles,
works with JS off), a full-page link (still reaches the standalone viewer), a
download link, and a session-close ✕. The ✕ needed stopPropagation +
preventDefault because it lives inside <summary> — otherwise its click would
toggle the disclosure instead of hiding the item. Close is JS (progressive
enhancement); collapse is native.

Two design points:
- Plain text is returned RAW from build_gallery and escaped by the template
  inside <pre>. Pre-escaping in Python plus Jinja autoescape would
  double-encode angle brackets; a test pins the single-escape.
- Inlining is bounded by DOC_MAX_BYTES. A doc over the limit keeps the old
  link-out behaviour rather than being rendered into every index load; a test
  covers the fallback.

The shared .markdown-body / .textview typography moved from doc.html's scoped
<style> into base.html so the inline body and the full-page view render
identically; doc.html keeps only its page-layout wrapper.

Updated the pre-existing test_gallery_links_docs_to_view: it asserted the old
link-out behaviour the operator asked to change, so it now asserts the inline
render plus the surviving full-page and download affordances. 61 pass.
Verified live: markdown renders with headings/table/blockquote/code, txt
preserves whitespace and single-escapes, collapse and ✕-close both work.
This commit is contained in:
vh
2026-08-19 11:06:36 -07:00
parent 95228299bc
commit 299b52458b
5 changed files with 200 additions and 25 deletions
+19 -1
View File
@@ -269,13 +269,31 @@ def build_gallery(child: Path) -> list[dict]:
if rel in sidecars:
continue
p = by_rel[rel]
dkind = doc_kind(p.name)
rendered = None
rendered_html = False
# Pre-render docs so the gallery can show them INLINE (collapsible)
# instead of linking out to a separate page. Bounded by DOC_MAX_BYTES:
# a giant log stays a download link rather than being inlined into every
# index render. Markdown → HTML (marked safe in the template); plain text
# is returned RAW and the template escapes it inside <pre> — pre-escaping
# here would double-encode under Jinja autoescape.
if dkind is not None:
try:
if p.stat().st_size <= DOC_MAX_BYTES:
text = p.read_text(errors="replace")
rendered, rendered_html = render_doc(text, dkind)
except OSError:
rendered = None
items.append(
{
"name": rel,
"kind": classify(p.name),
"doc": doc_kind(p.name),
"doc": dkind,
"url": quote(rel, safe="/"),
"caption": caption.get(rel),
"rendered": rendered,
"rendered_html": rendered_html,
}
)
return items