Loading…
a327ex.com

Anchor Website 2

Opus 4.8

Summary

Second major session on the Anchor 2 reimplementation of a327ex.com (renderer at a327ex-site/renderer/, a fork of ricochet-template tracked inside the a327ex-site repo). Began on homepage inline messages, then pivoted strategy — abandon the Ricochet-theme plan and faithfully reproduce the current live theme — and carried that through theme translation, dark mode, full chrome fidelity, engine-level text rendering, all of Phase B (block constructs), Phase C (raw-HTML logs), a heading-size correction, a page scrollbar, and inline emoji.

Strategy pivot — faithful new-theme.css, swappable theme layer:

  • Stopped entangling content-fidelity with Ricochet theming. New plan: reproduce the live theme faithfully (not pixel-perfect) using static/new-theme.css + static/new-theme.js + server/templates.lua as the literal spec, built as a SWAPPABLE theme layer so Ricochet / dark / old-posts become value swaps later. Embeds are the one deliberate divergence (iframes → native cards).
  • Reframed the goal as capability (a faithful in-engine content renderer for small projects the user would rather build in Anchor 2 than HTML5), not the look.
  • Read the full new-theme.css (28KB) + new-theme.js (feed buildFeed/renderMessage/renderLink, embeds, spoilers, lightbox) to extract the construct→treatment spec.

Homepage inline messages (pre-pivot step 1):

  • homepage.lua: messages render full content inline (loaded + laid out via canvas_load/canvas_layout), longer-form entries stay compact rows. Later reworked under feed fidelity (date below content, italic time, separators).

Converter robustness fixes:

  • ::image directive folded alt="…"/width=wide attributes into the path → crash on texture load. Fixed convert.lua to take only the URL token (args:match('^(%S+)')); percent-encoded filenames with spaces survive (decode after token-split).
  • canvas_load now pcall-guards image_load (bad/missing asset warns instead of crashing boot); layout_image_element treats a missing image as zero-size.

Phase A — theme translation (theme.lua rewritten):

  • Theme registry + theme_apply(name). Color roles are stable color_new objects mutated in place so a color-only swap needs no relayout (colors are 0–255 RGBA here — color_new defaults 255, __call packs via rgba()).
  • site_light from new-theme.css :root; site_dark from templates.lua [data-theme="dark"]; they differ only in colors (shared FONTS/METRICS/GAP_BEFORE/TREAT).
  • Title rendered as a synthetic h1 element prepended in load_page.

Dark mode:

  • Dark palette lives in server/templates.lua (:root[data-theme="dark"]), not the CSS. F2 toggles light/dark (color-only swap). Routed sidebar + homepage chrome colors off Ricochet palette onto prose.color.*. Defaults to dark (site follows prefers-color-scheme).

Chrome fidelity:

  • Breadcrumb restored — the live site DOES have nav.crumbs ("home / section / slug"); re-added as a small dim crumb text kind above the title (I had wrongly removed it).
  • Feed faithful rework (homepage.lua): all Arimo (no mono); link rows = title left / "date · TAG" right (tag italic-dim; logs show italic time; notes/posts date-only); messages = full content then a right-aligned date row below (date + italic time, clickable); <hr> separators except adjacent same-kind links. Added small_italic font.
  • Sidebar rework (sidebar.lua): lowercase home/search + collapsible logs/messages/notes/posts with triangle carets (layer_triangle), 12px Arial, blue active, guide lines; dropped INDEX/counts/+−.
  • Column layout (main.lua): the site's flex layout — left column (viewport−800)/1.8 with the sidebar right-aligned in it, content at left_col_w + 48; empty space far-left/far-right. Content width is the real 704. Fixed text/image left-alignment (was centered, misaligning from feed rows).
  • Vertical alignment: shared COLUMN_TOP so sidebar home, homepage crumb, and article crumb start on the same line.

Engine text rendering (SHARED Anchor2/engine/src/anchor.c — built; binary copied to renderer/anchor.exe):

  • "Rough" text root cause: layer_draw_text placed glyph quads at fractional positions sampled GL_LINEAR → blur. Added pixel-snap (roundf on glyph quads, cursor advances fractionally). Also coverage gamma in font_blit_to_buffer, a TrueType interpreter-version at FT init (FT_Property_Set, #include <freetype/ftmodapi.h>), font_rebuild_all(), and Lua binding font_render_config(snap, interp, gamma) for live A/B. User chose snap on / interp 40 / gamma 1.6 (snap was the real win).
  • Switched renderer to system Arial (C:/Windows/Fonts/arial*.ttf), Arimo fallback. Weight fix: site's 600 (strong/h2/h3) → Arial renders 700, matched with the bold face.
  • Code-block cursor engine_set_cursor('hand') over pannable blocks; shift+wheel pans (wheel-alone reverted — hijacked page scroll).

Phase B — block constructs (recursive container layout in canvas.lua):

  • B1: recursive layout_list/draw_list + containers; blockquote (left border, indent, italic+quote color via ctx), hr, inline-code pill.
  • B2: lists — list/item with disc/numbered gutter markers, ~28px indent, nesting; converter → indexed line array + recursive parse_list.
  • B3: code blocks — mono, faint bg; no-wrap + horizontal scroll (stencil-clipped, drag + shift+wheel, indicator, canvas_code_at).
  • B4: tables — browser-style min-content auto-layout (cell_min_w) so narrow columns keep their longest word while wide columns shrink.
  • B5: <details> — collapsible box, +/− marker, click-toggle (canvas_details_at, relayout). Folded in the log-skeleton HTML parse (<details>/<summary><code>/<pre><code> + entity decode) → all 176 logs render collapsible tool-call sections.

Phase C — raw-HTML logs:

  • Blockquotes parse recursively (flush_quoteparse_blocks) — fixed a reported "table inside a log not rendering" (table was inside a > quote). Tables/lists/code/nested-quotes inside quotes now work.
  • Entity decoding in all prose (moved html_unescape into parse_inline). Inline HTML in prose turned out to be code/examples (kept literal); one KaTeX-HTML-blob edge case left messy.

Heading sizes:

  • .entry h1=17.6/16/14 never applies on the live site — the new theme wraps content in <main>, not <div class="entry"> (that's the old /posts/* theme). So headings render at browser defaults (2em/1.5em/1.17em = 28/21/16 at 14px). Set those, removed the heading bottom-border (heading_rule = false), tightened top margins. (Verified the live CSS via WebFetch, then found the <main> vs .entry mismatch.)

Page scrollbar (main.lua): right-edge scrollbar_geom — thumb sized to visible fraction, draggable, click-track-to-jump, overflow-only, both views.

Inline emoji (Twemoji): scanned content for emoji-presentation codepoints (216), downloaded 208 Twemoji 72×72 PNGs → assets/emoji/<hex>.png, wrote data/emoji_index.lua. Codepoint-aware tokenizer (utf8_at); codepoints with a PNG become inline emoji tokens drawn as small baseline-aligned images (emoji_image lazy cache). Text-presentation symbols stay font glyphs. Single-codepoint only.

State at session end: Phases A/B/C + chrome + engine text + headings + scrollbar + emoji done. Next: D (embeds), E (spoilers), F (feed layout polish), G (lightbox / theme toggle / old-posts skin / web). The Anchor2 engine SOURCE change (anchor.c) is built and its binary is deployed to the renderer, but the source commit/push to the PUBLIC Anchor2 repo is intentionally left pending (private-session policy).

🔒 Only the summary of this log is public. Private because it contains too many website internal details.