WikiJS Math Rendering + Dome Cookbook Markup — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make math render on the live buckyverse Dome Cookbook page by cleaning its transcription into consistent KaTeX markup and enabling WikiJS’s server-side KaTeX renderer.
Architecture: Re-transcribe the 37 math-bearing pages via OpenRouter with a KaTeX-strict prompt (preserving the 11 prose pages and the page-14 ICOSAHEDRON fix), rebuild index.md, re-push; the user enables KaTeX / disables MathJax in the WikiJS Admin UI; re-push to re-render; verify KaTeX renders live.
Tech Stack: OpenRouter qwen2.5-vl-72b (vision), python3, the repo’s dev/wikijs/scripts/push-page-to-prod.exs, curl for live verification.
Note on TDD: Content/ops task — no unit tests. Each task’s verify step is a concrete inspection or a live-HTML check. Commit after each content task.
Working directory: repo root /Volumes/Storage/belmendo/Projects/buckyverse.org, branch pluck/def-1-codexis-dome-cookbook-render-math.
Constants:
IMG_DIR=dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.imagesOCR_DIR=/private/tmp/claude-501/-Volumes-Storage-belmendo-Projects-buckyverse-org/af6dd1a6-1d28-431b-83a8-c30aec38ea28/scratchpad/ocr_imgs(downscaled OCR copies from the original ingest; regenerate if absent)- Math pages (re-transcribe): 05 06 08 09 11 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
- Prose pages (preserve as-is): 01 02 03 04 07 10 12 14 31 47 48
- Live page:
https://www.buckyverse.org/en/dome_cookbook_of_geodesic_geometry/index
Task 1: Re-transcribe the 37 math-bearing pages with KaTeX-strict markup
Files:
-
Create (scratch):
<scratchpad>/dome_transcribe_katex.py -
Modify:
dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images/page-NN.txt(only the 37 math pages) -
Step 1: Confirm OCR downscaled copies exist (regenerate if missing)
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
OCR="/private/tmp/claude-501/-Volumes-Storage-belmendo-Projects-buckyverse-org/af6dd1a6-1d28-431b-83a8-c30aec38ea28/scratchpad/ocr_imgs"
if [ "$(ls "$OCR"/page-*.png 2>/dev/null | wc -l | tr -d ' ')" != "48" ]; then
mkdir -p "$OCR"
for p in dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images/page-*.png; do
magick "$p" -resize 1000x "$OCR/$(basename "$p")" 2>/dev/null
done
fi
ls "$OCR"/page-*.png | wc -l # expect 48
- Step 2: Write the KaTeX-strict transcription script
Write to the scratchpad, dome_transcribe_katex.py. It loads the OpenRouter key from ~/Projects/codexis/.env, transcribes ONLY the pages passed as CLI args, and overwrites their .txt:
import base64, json, os, sys, time, urllib.request
IMG_DIR = "dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images"
OCR_DIR = "/private/tmp/claude-501/-Volumes-Storage-belmendo-Projects-buckyverse-org/af6dd1a6-1d28-431b-83a8-c30aec38ea28/scratchpad/ocr_imgs"
MODEL = "qwen/qwen2.5-vl-72b-instruct"
URL = "https://openrouter.ai/api/v1/chat/completions"
PROMPT = (
"This is one page of a hand-lettered 1975 booklet on geodesic dome geometry, dense with "
"hand-written mathematics. Transcribe the page as clean Markdown. CRITICAL: wrap EVERY "
"mathematical expression in KaTeX delimiters — inline math in single dollar signs $...$, and "
"standalone/displayed equations in double dollar signs $$...$$ on their own line. Use standard "
"LaTeX inside: \\sin \\cos \\tan \\cot, \\sqrt{}, \\frac{}{}, \\overline{} for overbars, "
"\\angle for angle, \\therefore, \\Delta, ^\\circ for degrees, subscripts a_{1}, and \\times. "
"Every equation, angle value, chord factor, and numeric result must be inside math delimiters "
"(e.g. write $\\sin 60^\\circ = 0.86603$, not sin 60 = .86603). Transcribe headings and prose "
"normally (no math delimiters on plain words). For a diagram or a large table, write a one- or "
"two-line italic description prefixed with 'Figure:' or 'Table:' instead of redrawing it. Do not "
"invent content. Output only the transcription, no preamble."
)
def load_key():
for line in open(os.path.expanduser("~/Projects/codexis/.env")):
line = line.strip()
if line.startswith("OPENROUTER_API_KEY="):
return line.split("=", 1)[1]
raise SystemExit("no OPENROUTER_API_KEY")
KEY = load_key()
def transcribe(png_path):
b64 = base64.b64encode(open(png_path, "rb").read()).decode()
payload = {"model": MODEL, "messages": [{"role": "user", "content": [
{"type": "text", "text": PROMPT},
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + b64}}]}],
"temperature": 0}
req = urllib.request.Request(URL, data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json", "Authorization": "Bearer " + KEY})
with urllib.request.urlopen(req, timeout=180) as r:
return json.loads(r.read().decode())["choices"][0]["message"]["content"].strip()
def main():
pages = sys.argv[1:] # e.g. 05 06 08 ...
for n in pages:
png = os.path.join(OCR_DIR, f"page-{n}.png")
if not os.path.exists(png):
png = os.path.join(IMG_DIR, f"page-{n}.png")
out = os.path.join(IMG_DIR, f"page-{n}.txt")
print(f"transcribing page-{n} ...", flush=True)
for attempt in (1, 2, 3):
try:
t = time.time()
text = transcribe(png)
if text:
open(out, "w").write(text)
print(f" ok ({len(text)} chars, {time.time()-t:.1f}s)", flush=True)
break
except Exception as e:
print(f" attempt {attempt} failed: {e}", flush=True)
time.sleep(5 * attempt)
print("done", flush=True)
if __name__ == "__main__":
main()
- Step 3: Smoke-test one dense math page (page 40) and inspect quality
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
cp "$IMG_DIR/page-40.txt" /tmp/page-40.txt.bak 2>/dev/null || true
python3 "<scratchpad>/dome_transcribe_katex.py" 40
sed -n '1,30p' dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images/page-40.txt
Confirm formulas are wrapped in $…$/$$…$$ with valid LaTeX and the numbers match the page image. If wrong, adjust the prompt and re-run before doing the rest.
- Step 4: Re-transcribe all 37 math pages
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
python3 "<scratchpad>/dome_transcribe_katex.py" \
05 06 08 09 11 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 \
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
Runs ~4–5 min (OpenRouter ~6 s/page). Re-run any page that logs a failure.
- Step 5: Verify every math page now has math delimiters, prose pages untouched
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
IMG=dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images
for n in 05 06 08 09 11 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46; do
[ "$(grep -c '\$' "$IMG/page-$n.txt")" -ge 1 ] || echo "NO MATH MARKUP: page-$n"
done; echo "math check done"
# prose pages must be unchanged vs git
git diff --name-only -- "$IMG"/page-01.txt "$IMG"/page-14.txt | grep . && echo "WARN: prose page changed" || echo "prose pages untouched"
Expected: “math check done” with no NO-MATH lines; “prose pages untouched” (page 14’s ICOSAHEDRON fix intact).
- Step 6: Sanity-check KaTeX validity of a few pages
Read page-13.txt, page-30.txt, page-40.txt. Confirm: balanced $/$$, no obviously broken LaTeX (\frac with two args, closed \overline{}, ^\circ for degrees). Hand-fix any malformed expression.
- Step 7: Commit
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
git add dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images/*.txt
git commit -m "feat: re-transcribe Dome Cookbook math pages with KaTeX markup"
Task 2: Rebuild index.md and re-push (pre-KaTeX)
Files:
-
Modify:
dome_cookbook_of_geodesic_geometry/index.md -
Step 1: Rebuild the body from the header + updated transcriptions
Reuse the original assembly (keep the header through ## Pages, re-append 48 page sections with heading demotion, then the footer):
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
IMG=dome_cookbook_of_geodesic_geometry/DomeCookbookofGeodesicGeometry.images
OUT=dome_cookbook_of_geodesic_geometry/index.md
REL=DomeCookbookofGeodesicGeometry.images
# Header ends at the "## Pages" line — find it dynamically
HEADEND=$(grep -n '^## Pages$' "$OUT" | head -1 | cut -d: -f1)
head -"$HEADEND" "$OUT" > "$OUT.tmp"
for i in $(seq -w 1 48); do
n=$((10#$i))
{
printf '\n### Page %s\n\n' "$n"
printf '\n\n' "$n" "$REL" "$i"
sed -E 's/^#{1,3} /#### /' "$IMG/page-$i.txt"
printf '\n'
} >> "$OUT.tmp"
done
cat >> "$OUT.tmp" <<'EOF'
---
*Source: David Kruschke, *Dome Cookbook of Geodesic Geometry*, 2nd edition, 1975. Page images rendered with `pdftoppm`; page text transcribed with a vision model (qwen2.5-vl). Diagrams and tables are reproduced as page images.*
File: [DomeCookbookofGeodesicGeometry-Kruschke2ndEdition1975.pdf](DomeCookbookofGeodesicGeometry-Kruschke2ndEdition1975.pdf)
EOF
mv "$OUT.tmp" "$OUT"
grep -c '^### Page ' "$OUT" # expect 48
grep -c '^!\[Page ' "$OUT" # expect 48
- Step 2: Verify frontmatter + math markup landed in the page
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
OUT=dome_cookbook_of_geodesic_geometry/index.md
awk 'NR>1 && /^description:/{sub(/^description: /,"");print "desc len:", length($0)}' "$OUT" # <=255
echo "pages-with-math-in-index: $(grep -c '\$' "$OUT")" # should be well above the old 12
head -1 "$OUT" # frontmatter opens with ---
- Step 3: Commit
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
git add dome_cookbook_of_geodesic_geometry/index.md
git commit -m "feat: rebuild Dome Cookbook index.md with KaTeX math markup"
- Step 4: Re-push the page to prod (dry-run then real)
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
ASDF_ERLANG_VERSION=28.4.1 ASDF_ELIXIR_VERSION=1.19.5-otp-28 \
elixir dev/wikijs/scripts/push-page-to-prod.exs --dry-run \
"dome_cookbook_of_geodesic_geometry/index=dome_cookbook_of_geodesic_geometry"
ASDF_ERLANG_VERSION=28.4.1 ASDF_ELIXIR_VERSION=1.19.5-otp-28 \
elixir dev/wikijs/scripts/push-page-to-prod.exs \
"dome_cookbook_of_geodesic_geometry/index=dome_cookbook_of_geodesic_geometry"
Expected: ✓ updated. (Math will still show raw $$ until Task 3 enables KaTeX — that’s expected here.)
Task 3: User enables KaTeX in the WikiJS Admin UI (gate)
Files: none (production WikiJS config, user-applied).
- Step 1: Ask the user to toggle the renderer
Message the user: In Administration → Rendering → Markdown, enable “KaTeX” and disable “MathJax”, then save. (Reversible; a single renderer toggle.) Wait for their confirmation before continuing.
- Step 2: Confirm the toggle took effect (renderer state)
After the user confirms, verify via the tooling that KaTeX is now enabled (read-only inspection; do not write config):
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
bash dev/wikijs/scripts/inspect-prod.sh 2>/dev/null | grep -iE 'katex|mathjax' || \
echo "(inspect-prod has no renderer view — rely on the live-page verification in Task 4)"
If the inspection can’t show renderer state, proceed to Task 4 (the live page is the real proof).
Task 4: Re-render, verify KaTeX renders live, spot-check geodesic_math
Files: none (verification + one re-push to trigger re-render).
- Step 1: Re-push the Dome Cookbook page to force a KaTeX re-render
WikiJS re-renders a page on save. Enabling the renderer does NOT re-render existing pages, so re-push:
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
ASDF_ERLANG_VERSION=28.4.1 ASDF_ELIXIR_VERSION=1.19.5-otp-28 \
elixir dev/wikijs/scripts/push-page-to-prod.exs \
"dome_cookbook_of_geodesic_geometry/index=dome_cookbook_of_geodesic_geometry"
- Step 2: Verify the live page renders KaTeX (not raw
$$)
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
curl -s "https://www.buckyverse.org/en/dome_cookbook_of_geodesic_geometry/index" > /tmp/dome-live.html
echo "http: $(curl -s -o /dev/null -w '%{http_code}' https://www.buckyverse.org/en/dome_cookbook_of_geodesic_geometry/index)"
echo "katex spans: $(grep -o 'class="katex' /tmp/dome-live.html | wc -l | tr -d ' ')" # expect > 0
echo "raw \$\$ leftover in body: $(grep -o '\$\$[^$]' /tmp/dome-live.html | wc -l | tr -d ' ')" # expect ~0
Expected: HTTP 200, katex spans > 0, raw $$ ~0. If KaTeX spans are 0, the renderer toggle didn’t take — return to Task 3.
- Step 3: Spot-check the wiki-wide effect on geodesic_math
geodesic_math needs re-rendering too. Re-render by re-pushing its page (it already has valid math markup in its index.md):
cd /Volumes/Storage/belmendo/Projects/buckyverse.org
ASDF_ERLANG_VERSION=28.4.1 ASDF_ELIXIR_VERSION=1.19.5-otp-28 \
elixir dev/wikijs/scripts/push-page-to-prod.exs \
"geodesic_math_and_how_to_use_it/index=geodesic_math_and_how_to_use_it"
curl -s "https://www.buckyverse.org/en/geodesic_math_and_how_to_use_it/index" | grep -o 'class="katex' | wc -l # expect > 0
Report the result. Note that a full wiki-wide re-render (all other math pages) is a follow-up beyond this pluck — only Dome Cookbook and geodesic_math are re-rendered here.
Task 5: Re-file the deferred codexis ebook build
Files: Modify work/DEFERRED.md (via the dev-xp_work deferred: record flow, on the base branch during housekeeping).
Note for the pluck controller: DEF-1 was re-scoped by this pluck to the WikiJS math fix. The original “full codexis LaTeX/ebook build” is still wanted (per the user’s “Both” choice) — record it as a fresh
DEF-Nso it isn’t lost when DEF-1 is yanked.
- Step 1: Record the codexis ebook build as a new deferred item
Content: “Full codexis LaTeX/ebook build of the Dome Cookbook” — init codexis project, OCR→LaTeX with math environments, handle the hand-drawn diagrams (TikZ or embedded page-image figures), codexis build --all (EPUB/PDF/mobi/kfx where math renders as real typeset math), codexis deploy --target buckyverse. Why: downloadable typeset editions with rendered math, independent of the wiki. Source: split from DEF-1 (the wiki-side math fix shipped separately).
- Step 2: Commit (handled by the record flow:
xp_work: record DEF-N).
Verification checklist (whole plan)
- 37 math pages re-transcribed with
$…$/$$…$$; 11 prose pages (incl. page-14 fix) unchanged. -
index.mdrebuilt: 48 page sections, math markup present, valid frontmatter. - Page re-pushed; user enabled KaTeX / disabled MathJax.
- Live Dome Cookbook page: HTTP 200,
class="katex"spans > 0, no raw$$. - geodesic_math re-rendered and shows KaTeX (wiki-wide proof).
- Codexis ebook build re-filed as a deferred item.