Anchor Website 1
Fable 5
Opus 4.8
Summary
Started, from scratch, an Anchor 2 reimplementation of the personal website
a327ex.com — rendered in the RICOCHET visual theme, desktop-first — living at
a327ex-site/renderer/ (a fork of ricochet-template). Built the engine support,
the markdown→element converter, article rendering, the sidebar + in-app navigation,
and the homepage shell + feed. Full status/roadmap saved to renderer/PLAN.md.
Task framing & strategy (A vs B):
- Restart of the earlier
Anchor2/siteeffort. Goal: render everything the live site renders, inside Anchor 2, in the Ricochet theme, as close as possible to the site's structure while respecting Ricochet. - Debated "copy the site CSS first, then fix" (A) vs "start from Ricochet, fix toward the site" (B). Chose B: A's gravity is the bland default theme we're replacing (silent omissions that linger); B's errors are concrete/visible and converge. The real resolution: source of truth is per-dimension — site dictates structure (what renders, column width, feed/sidebar), Ricochet dictates look; spacing takes the site's relationships but Ricochet's magnitudes. Also: the site CSS is explicitly throwaway bridge code.
Setup decisions:
- Project folder
a327ex-site/renderer/, tracked inside thea327ex-siterepo (no separate git repo). Old/posts/*must eventually render in their EXACT original dark theme (deferred; old CSS is instatic/old-blog*.css). Font handled the thorough way (engine change), not a hack.
Engine — dynamic glyph atlas (shared Anchor2/engine/src/anchor.c):
- Replaced the fixed ASCII-96 baked atlas with a lazy, multi-page,
codepoint→GlyphInfo cache. On a glyph miss:
FT_Load_Char, pack at a shelf cursor,glTexSubImage2Djust that sub-rect (from a tight buffer — no GL_UNPACK_ROW_LENGTH, WebGL-safe), open-addressing hash cache, 1024² pages (cap 8),.notdeffor missing. ASCII pre-warmed at load. FT_Face + font_data now kept alive (not freed after bake). - Result: any codepoint the font file contains renders (Latin-1, punctuation, CJK e.g. LanaPixel). Caveats noted: font must actually contain the glyph; CJK line-breaking and cross-font fallback are separate renderer-layer concerns.
- Later added a
font_get_ascentLua binding +self.ascenton the framework font object (bothAnchor2/framework/anchor/font.luaandrenderer/anchor/font.lua) to baseline-align mixed-font lines (inline code was sitting above the body baseline because tokens were top-aligned). Engine built; patched binary atrenderer/anchor.exe.
Scaffold: lean fork — the ricochet/ package + framework + fresh patched
anchor.exe + smooth assets (Oswald / IBM Plex Reg+Italic+Medium / Cascadia) + Tabler
icons; skipped rough-mode pixel assets and dev galleries. Proof-of-life main.lua that
doubled as a live non-ASCII font test; verified the file is real UTF-8.
Phase 1 — article renderer:
theme.lua(prose): type scale (Oswald title; Plex Medium h2/h3 — condensed caps too loud for in-article subheads; Plex 18 body; Cascadia code), light color roles, spacing.elements.lua+canvas.luaadapted from the oldAnchor2/siteextraction: styled runs, word-wrap, image fit, scroll (mouse wheel + arrows/j-k), chrome (crumb + Oswald title + hairline). Reads data files viadofile(engine_chdirs into the game dir).
Spacing iteration:
- Started flat (14px gap); user pushed to source values from the real CSS. Transcribed
new-theme.cssmargins (p 16,.entryheadings mt40/mb8.8, etc.) with CSS-style margin collapse — then user chose to revert to the Ricochet baseline (strategy B) and tune by eye. - Built an F1 spacing tuner (live sliders + COPY VALUES → clipboard + console). Baked article values: element_gap 14, line_gap 6, gap_before h2 28 / h3 24 / image 20 (image carries an equal gap below via gap_after + max-collapse).
Converter (tools/convert.lua):
- markdown + Blot frontmatter +
::directives→data/<slug>.luaelement files. Inline parser (**bold**/*italic*/`code`/[text](url)links → blue, non-interactive), block line-scanner,::image(URL-decode +/media→../media), other::TYPE→ dim[TYPE]placeholder, blockquotes → body paragraphs. Serializes with%q. --allconverts + indexes notes/logs/posts/messages = 328 pages →data/index.lua(globally date-sorted, message content previews). Fixed a crash on lines starting with::but no directive name (require non-space after::).
Phase 2 — sidebar + navigation (sidebar.lua):
- Content tree from the index (notes/logs), collapsible (collapsed by default),
date-sorted, truncated rows (
text_truncate, UTF-8 safe, inelements.lua), HOME pin, stencil-clipped scroll, active row = inverted ink fill + blue tick. - In-app navigation: clicking loads + lays out that page (
load_page), two independent scroll panes (wheel routes to the pane under the cursor). - Explored the nav design against the Ricochet reference sheets; offered "open rail" vs "framed panel" via an F2 toggle, user chose open; removed the edge-hugging corner brackets and fixed the header rule that was striking through the section label.
Perf: added off-screen culling in canvas_draw (long logs only pay draw cost for
the visible screenful). Open-click layout hitch for huge logs still pending (caching).
Phase 4 — homepage (homepage.lua):
- Crumb (social links + email) + ARTIFACTS banner (It Follows / SNKRX / BYTEPATH) + separator + unified feed (all four types interleaved by date, tagged NOTE/AI LOG/POST/MSG, clickable). Initial view; HOME pin returns to it.
- After user flagged ratios as "very off" vs the live site, reworked to compact
UI-scale type (like the sidebar, not the article prose scale), dense rows, full
timestamps, and a small mono
ARTIFACTSlabel (was a giant Oswald title). Made the F1 panel contextual (HOME SPACING vs SPACING) withhplive-tunable, immediate-mode.
Wrap-up: plan + status written to renderer/PLAN.md and a project memory pointer
added. Next steps (priority): (1) inline message content in the feed, (2) content
fidelity — code blocks/lists/blockquotes/embeds/spoilers and the rough-rendering logs
(raw HTML), (3) old-theme /posts/*, (4) search + theme toggle + interactive links,
(5) styling polish (STYLING_TODO.md), (6) infra/perf + eventual web delivery, (7) the
renderer is still uncommitted. Controls: F1 spacing tuner; wheel / ↑↓ / j-k scroll.
🔒 Only the summary of this log is public. Private because it contains too many website internal details.