Skip to content

Improve site tooling and fix a CSS favicon URL bug that manifests on non-empty baseurls (not in production).#162

Merged
KubaO merged 9 commits into
twinbasic:mainfrom
KubaO:staging
May 27, 2026
Merged

Improve site tooling and fix a CSS favicon URL bug that manifests on non-empty baseurls (not in production).#162
KubaO merged 9 commits into
twinbasic:mainfrom
KubaO:staging

Conversation

@KubaO
Copy link
Copy Markdown
Collaborator

@KubaO KubaO commented May 27, 2026

No description provided.

KubaO added 9 commits May 27, 2026 15:28
Drop the Rouge-class indirection in builder/highlight.mjs. The new
builder/highlight-theme.mjs parses the vendored .theme files under
builder/themes/, derives a deterministic Symbol-keyed palette, and
emits _site/assets/css/tb-highlight.css at build time. Per-span
class names switch from Rouge tokens (k, s, mi) to a palette scheme
(c1, c2, ...); same-coloured symbols collapse to one classId (the
five Literal* symbols fold to one rule).

builder/highlight.mjs shrinks from ~470 lines to ~190 -- the
SCOPE_TO_ROUGE_CLASS table, the ~120 lines of per-language quirks,
JS_BUILTINS, the C/C++ token re-tokeniser, and the bestRougeClass
walker all delete. The run-coalescing, line-continuation absorb,
and block-comment merge logic stay; the lc/cm class checks now
resolve via theme.classForSymbol.

Asset wiring: writePhase takes a generatedAssets parameter, the
orchestrator threads highlighter.themeCss through it, and the file
lands at <destRoot>/assets/css/tb-highlight.css alongside the
verbatim-copied theme tree. template.mjs adds the <link> in the
head between just-the-docs-combined.css and just-the-docs-head-nav.css;
book.mjs and pdf.mjs swap rouge.css -> tb-highlight.css for the PDF
tree. scripts/extract_theme_colors.py, scripts/themes/*.css, and
builder/assets/css/rouge.css delete. The docs/_sass/custom/_twinbasic-*.scss
partials stay -- Phase 10 commit 7 (Jekyll source set deletion) will
take them when it lands.
New builder/mermaid.mjs runs as the first orchestrator step (before
Phase 1's discover), walks docs/assets/images/mmd/*.mmd, and
invokes mmdc via npx (rooted at builder/) for any source whose
.svg sibling is missing or older. The .mmd is now the canonical
input; the SVG is a build artifact regenerated whenever the source
changes -- the hand-export workflow (Typora) is no longer needed.
Idempotent: subsequent builds with no source changes are a 2 ms
mtime-check no-op.

Adds @mermaid-js/mermaid-cli as a builder/ devDependency. The site
already pulls in puppeteer at the repo root for the PDF render
step (CI calls 'npx puppeteer browsers install chrome --install-deps'),
so mermaid.mjs discovers that cached Chrome under
~/.cache/puppeteer/ and passes it to mmdc via PUPPETEER_EXECUTABLE_PATH
-- no second Chrome download. mermaid-cli's bundled puppeteer-core
accepts the version that's already there.

A missing mmdc (someone hasn't run 'npm install' in builder/) or
missing Chrome cache downgrades to a graceful warning -- the
existing on-disk SVG is retained, the build continues with an
informative line pointing at the fix.

Regenerates the existing diagram SVG with '-b transparent' (mmdc's
default flag), flipping 'background-color: white' to
'background-color: transparent' in the rendered <style>. Otherwise
byte-identical to the prior hand-export.
builder/highlight.mjs now emits the <button class="copy-code">
HTML inside each <div class="highlighter-rouge"> wrapper at build
time; the chrome's existing CSS positions it absolutely over the
top-right corner. Gated by enable_copy_code_button in _config.yml
(default true) -- the orchestrator threads the flag through
initHighlighter({ copyButton }).

builder/assets/js/just-the-docs.js retires the runtime DOM-injection
loop (the upstream processCodeBlocks step that walked
.highlighter-rouge and appended a button per block). The click
handler stays: it now selects 'button.copy-code' and walks up to
the matching code block via .closest(), then runs the same
writeText + icon-swap on click. Net JS reduction ~10 lines.

builder/assets/css/print.css hides the button for the PDF render
path -- the PDF doesn't need interactive chrome.

Verified end-to-end: an http-server preview of docs/_site/ shows
the button inside every code block, clicking swaps the clipboard
icon to the 'copied' variant, and pages with multiple blocks
(Tutorials/Arrays.html, 13 buttons) all wire correctly via
.closest() -- each button reads from the right code block.
builder/offline.mjs's deriveOfflineSearchDataJs now parses the
pretty-printed search-data JSON and re-stringifies it without
indentation before wrapping as window.SEARCH_DATA. The online
search-data.json keeps its pretty-printed shape (Phase 6
unchanged); only the offline tree's wrapper is minified.

Real-world reduction on the current tree: 2.80 MB -> 2.70 MB
(~100 KB / 3.6%). The PLAN-11 estimate of ~1.1 MB was optimistic;
most of the file is content payload (preserved verbatim by
JSON.parse/stringify), only the per-entry 4-space indentation
collapses. ~75 KB from indent removal across 2587 entries plus
~25 KB from various brace/comma spacing.

The lunr consumer (initSearch in just-the-docs.js) reads
window.SEARCH_DATA via Object.entries; minification is invisible
to it. Verified that the rewritten file parses cleanly and the
2587 entries are intact.
builder/offline.mjs's deriveOfflineJtdJs is now AST-driven. Parses
the upstream just-the-docs.js with acorn, walks for
FunctionDeclaration nodes named navLink / initSearch, and string-
slices the canonical replacements at the node ranges -- so non-
patched regions stay byte-identical to upstream. Survives cosmetic
upstream edits (variable renames, statement reorders inside the
function body) that would have broken anchored regex matches.

No regex fallback ships: just-the-docs.js is a vendored asset in
builder/assets/js/, re-extracted only on deliberate gem-bump
operations. A parse error at build time is a clear signal to fix
the asset (or the AST patcher's expectations) at the moment of the
bump, not silently to switch code paths. PLAN-11 §7.D13 updated to
reflect this.

Verified the AST output is byte-identical to the prior regex-
patched bytes on the current just-the-docs version (19,963 bytes,
identical). End-to-end smoke via http-server preview of
docs/_site-offline/: window.SEARCH_DATA is set with 2,587 entries,
nav-active highlight resolves the current page correctly under
file://, search query 'Const' returns 60 hits.

Adds acorn ^8.0 and acorn-walk ^8.0 to builder/package.json.
The vendored just-the-docs-combined.css carries a hard-coded
`background-image: url("/favicon.png")` for the .site-logo
element. Under a root deployment (docs.twinbasic.com) this resolves
fine, but under a baseurl deployment (kubao.github.io/twinBASIC-docs/)
it 404s on `https://kubao.github.io/favicon.png` -- the top-left
twinBASIC icon disappears.

write.mjs's copyTheme now takes the baseurl and, when non-empty,
runs each copied .css file through a small rewriter that prepends
the baseurl to root-absolute url() references. Protocol-relative
`url("//...")` is left alone (negative lookahead). On the
current tree the only affected reference is .site-logo's favicon
path; the rewrite is idempotent on the next build (we always start
from the immutable builder/assets/css/ source).

The offline plugin already handles baseurl-prefixed paths correctly
(it strips the baseurl prefix during URL resolution before mapping
to a relative file-tree path), so the offline CSS still ends up
with the right `url("../../favicon.png")` form regardless of
which baseurl the online build was generated with.

Verified end-to-end:
  - --baseurl /twinBASIC-docs -> CSS has url("/twinBASIC-docs/favicon.png")
  - no flag                   -> CSS keeps url("/favicon.png")
  - offline pass               -> CSS has url("../../favicon.png")
  - check.bat clean in both modes.
@KubaO KubaO merged commit 79c8ff7 into twinbasic:main May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant