Anchor Website 8
Opus 4.8
Summary
Per-post fidelity pass on the in-engine a327ex.com renderer (a327ex-site/renderer/). The user
went through posts one-by-one against the live site, reporting issues; each was fixed in turn.
Session ended mid-pass (context window) — the immediate next step is running fetch_media.py for
the now-wired auto-generated YouTube CC + postering the converted mockup videos.
Old /posts/* layout — drop the sidebar + clickable crumb:
- On the live site old Blot posts render WITHOUT the sidebar (
skip_crumbs+ the Blot header bar); the renderer kept the sidebar. Addedcontent_view()in main.lua returning(content_x, content_w, show_sidebar); old posts use a centered Blot container,draw()/relayout()/wheel routing gate onshow_sidebar. - The
home / section / slugcrumb became a dedicated{type='crumb'}element (elements.lualayout/draw_crumb_element, canvas.lua dispatch); "home" is clickable →show_home(), hit-tested in update() from the laid-out element + scroll. Removed the deadkind=='crumb'text branches. - Follow-ups: dead-centered the old-post column (was Blot-faithful left-of-center, user wanted
centered); fixed videos auto-playing through the navigation (the navigating click bled into the
new page's poster hit-test — consume
ui_mouse_pressed=falseinload_page/show_home); underlined "home"; added date+time top-right on the crumb line (looked up from the index,fmt_crumb_date).
Clickable inline links (engine + renderer): user chose Option A (engine binding) over
os.execute. Added open_url → SDL_OpenURL in Anchor2 anchor.c. Renderer: tokenizer carries
url onto word/ws tokens; _link_rects (per-line merged) built in layout_text_element;
canvas_link_at walks text elements; open_link dispatches internal (a327ex.com/anchor.blot.im or
/section/slug whose slug exists) → load_page, external → open_url. Click consumed so it can't
bleed into video/lightbox.
Whole-site link audit: extracted 1310 real hyperlinks from the data files (excludes code-block
text), classified internal/external, HTTP-checked 515 unique externals, then HAND-VERIFIED suspects
(raw HTTP status is unreliable — claude.ai/X/steamdb/github bot-block). Tooling left in
renderer/tools/ (extract_links.lua → classify_links.py → audit_report.py → make_report.py); report
in renderer/reference/link_audit.md. Fixes applied (user: "fix what you're sure about, surface
doubts"): howtomarketyourgame.com (500/dead) → howtomarketagame.com (verified 200);
opus-4.5 ×6 download links — dot-vs-hyphen path, relinked /media/notes/opus-4.5/ →
/media/notes/opus-4-5/; 7 deleted tweets (syndication-API-verified TweetTombstone) → 4 with wayback
snapshots repointed, 3 with no archive had the dead link removed (anchor text kept; @stimhacked's
::tweet embed directive kept its original URL); gamasutra article gone → web.archive.org snapshot;
explorer.helium.com→world.helium.com, rendertoken→rendernetwork, goldenlight.mirror.xyz→
paragraph.com, boards.4channel.org→4chan.org; rumbleverse (defunct) → Wikipedia. Left per user:
new.a327ex.com (DNS-dead, fine in context), /posts/the_haircutter (fine broken). Also stripped
71 code-junk self-links ([e.target](http://e.target), h.id, plan.md, …) from the logs via
an explicit denylist (real [domain/path](url) self-links like claude.ai/amulet.xyz preserved).
Inline emphasis parser rewrite (the ::sidenote "edit from the future" asides): links inside
_italic_ rendered as literal [text](url) and _vs_ in a URL (TYRONE_vs_COPS) closed the italic
early. Root cause: parse_inline was a flat single-pass scanner with no recursion. Rewrote it: added
find_emph_close (skips [...](...) and `code` when seeking an emphasis closer) and made
emphasis spans RECURSE (inline() + style-merge), so nested links/bold parse and inherit the outer
style. Verified simple cases unchanged. Reconverted all 349 pages; a site-wide sweep found 0
remaining literal ](http…) links.
Sidebar + navigation: added SEC_BODY_GAP (header→entries breathing room). Added n/p
next/prev-post keys — chronological order (reversed index → small_games oldest [1] … newest message
[N]); from the homepage next→first, prev→last; clamps; load_page now also clears the lightbox.
Old-skin heading spacing (two attempts): first head_shrink (top-aligned box trim) only
tightened the gap below — insufficient ("extreme height differences still haven't been fixed").
Replaced with head_overflow (centered overflow): shrink the old-theme h2/h3 box by 2*overflow
and draw the glyph CENTERED in it (overflowing top+bottom), replicating old-blog.css's
line-height:8px. Gaps tighten both sides and become independent of font metrics; gap_before back
to 48. h1 untouched (its line-height:36px was already roomy); new theme head_overflow=0. Draw
applies el._head_oy in draw_text_element.
Image lightbox crispness: a centered odd-dimension image landed on a half-pixel, so the smooth
filter blurred an otherwise 1:1 texture (article images are crisp because el.x is an integer left
edge). Fixed by pixel-snapping the lightbox top-left.
YouTube title + open-on-YouTube: youtube_url(el) (watch?v= / shorts/), draw_video_title
(gradient scrim + title at top of frame, always on the poster + on hover during playback, scales in
fullscreen). Click the title → opens the video on YouTube (open_url). Active-video title click
wired in video_controls_update. The not-downloaded placeholder for youtube/short now reads "click
to watch on YouTube" and the card opens YT — so the 8 unrecoverable embeds are useful again.
Playback speed controls: new engine binding video_set_speed → ma_sound_set_pitch (the video
clock follows audio consumption, so A/V stays synced; PITCH-SHIFTED — pitch-correction deferred as a
separate DSP lift). UI: a 1x speed button on the controls bar + a popup menu (0.5/1/1.25/1.5/2×,
speed_menu_geom mirroring the cc menu); persists across videos. Engine rebuilt; binary copy was
blocked while the renderer ran, copied successfully at end of session.
Auto-generated CC — WIRED, NOT YET PULLED (the explicit NEXT step): fetch_media.py's
make_subs now also fetches YouTube auto-subs (--write-auto-subs, English-only en.* to avoid the
~50-language auto-translation explosion) into a separate subs/auto/ dir, keyed "<lang> (auto)" in
the manifest (only filling gaps where there's no human track), behind a versioned .subs2.done
marker. subs_parse got a dedup pass (drop empty + collapse consecutive duplicates of the rolling
word-by-word cues). video_load_subs prefers human en, then en (auto). ⏳ The user must re-run
python tools/fetch_media.py (network — YouTube anti-bot) to actually pull them; cleanup is a first
pass to refine once the real auto-VTT is seen.
marketing_skills "missing content": the raw <div class="captioned-image-container"> rendered as
literal text — convert.lua now strips lone raw-HTML block tags (applied site-wide). The animated UI
mockups used ::image pointing at .mp4/.webm files (broken even on the live site as
<img src=x.mp4>) — fixed the author's directive mistake by changing those 4 to ::video (across
marketing_skills, self_expression_vesselization, unity_godot_artists_money); they now convert to
video elements and reuse the local-video pipeline (need fetch_media to poster them; side-by-side
captioned layout not reproduced — they stack).
New-tab links (note only): the web build must open EVERY link — including internal/same-site — in
a new tab (target="_blank"); recorded as a WEB-PORT NOTE on open_link and in PLAN.md. Desktop
behavior unchanged (internal → in-app nav is the desktop equivalent).
Files touched: renderer/main.lua, elements.lua, theme.lua, canvas.lua, sidebar.lua, tools/convert.lua, tools/fetch_media.py (+ audit scripts), reference/link_audit.md, PLAN.md; Anchor2/engine/src/anchor.c (open_url + video_set_speed); source edits across ~18 posts + self-link cleanup in several logs. All 349 pages reconverted. PLAN.md has a prominent ⏸ MID-PER-POST-FIDELITY note with the fetch_media-for-auto-CC next step.
🔒 Only the summary of this log is public. Private because it contains too many website internal details.