mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-05-07 02:03:55 -05:00
dev
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0215fd9e4d |
fix(theme): bridge into site-head.html and defend against Quarto's clobber
Two issues caught by local rendering of /about/license.html in dark mode:
1) The active source for the inline FOUC-prevention script is
`shared/config/site-head.html`, NOT `shared/scripts/theme-persist.js`.
Quarto inlines the contents of site-head.html into <head>; the
standalone .js file is documentation-only ("kept identical for
documentation/testability"). The previous patch only touched the
.js file, so the .quarto-dark body-class bridge never reached the
rendered page. Mirror the fix into site-head.html.
2) Even with the bridge in place, Quarto's own
`toggleBodyColor(mode)` runs *after* this script and re-asserts
the body class based on `mode`. When no `quarto-color-scheme`
value is in localStorage (typical first visit), Quarto resolves
`mode = 'light'` and clobbers our `.quarto-dark` class — even
though the OS preference resolved `data-bs-theme="dark"` already.
Result: dark `data-bs-theme` background with `.quarto-dark`-keyed
CSS variables stuck at light defaults (the "Open by design."
invisibility on /about/license.html, low-contrast hero/body text
across about/community/newsletter pages).
Defense: a MutationObserver on `body.class` and `html[data-bs-theme]`
re-asserts the bridge whenever Quarto (or any other script) changes
it. Cheap, idempotent, and surgical.
Verified locally with playwright + the about/license.html render:
bodyClasses: ["nav-fixed", "fullcontent", "quarto-dark"] ← was quarto-light
h1Color: rgb(229, 231, 235) ← was rgb(26,26,46)
bodyAbText: "#e5e7eb" ← was "#1a1a2e"
|
||
|
|
d759f3c4c2 |
fix(theme): bridge Quarto's 'alternate'/'default' to data-bs-theme
Quarto's built-in toggle stores its color-scheme choice as
'alternate'/'default' under the same localStorage key (`quarto-color-scheme`)
that the shared theme-persist shim reads. The shim only recognized
'dark'/'light', so once a reader clicked the toggle it would, on the next
load, fall back to OS preference and apply data-bs-theme=light while
Quarto correctly enabled the dark stylesheet (or vice versa). The result
was a half-themed page — most visible to readers on macOS dark mode whose
stored choice was 'default' (light): Bootstrap's CSS-variable dark mode
kicked in via data-bs-theme=dark, but the dark-mode SCSS layer never
loaded, leaving a dark navbar against a light sidebar/content/announcement.
theme-persist now accepts both vocabularies on read (alternate→dark,
default→light) and wraps quartoToggleColorScheme so the html attribute
syncs immediately after a click instead of waiting for the next reload.
The wrapper is a no-op on non-Quarto subsites (StaffML/Next.js).
Quarto's startup still checks `=== 'alternate'`, so we do NOT rewrite
Quarto's stored values — only mirror them onto <html>.
Single shared file in include-in-header propagates to all 8 Quarto
subsites: book vol1+vol2, labs, kits, slides, instructors, site, mlsysim.
Verified with Playwright across the full vol1↔vol2 navigation + toggle
sequence and across {OS=dark|light} × {storage=null|default|alternate}
matrix: 5/10 mismatches before, 0 after.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
cf94da0edf |
fix(site-head): prevent nested HTML comment from leaking into page body
The outer comment in shared/config/site-head.html contained the literal `<!-- MLSB_BUILD_STAMP -->` token. HTML comments cannot nest, so the inner `-->` terminated the outer block early, leaking `` ` token.\n--> `` as visible text on every site that includes this head file (instructors, slides, labs, etc.). Rewrote the comment to describe the placeholder without writing it verbatim. |
||
|
|
73967f7c42 |
PR-2: Visual polish (announcement bars, theme persistence, dev-mirror fix, audit script) (#1405)
* fix(dev-mirror): compute prefix from dev-side depth in rewrite-dev-urls.sh The previous implementation hard-coded PREFIX="../" for any non-root subsite, which silently mis-rewrote every absolute mlsysbook.ai link on the dev preview for nested subsites (vol1, vol2 — they live at /book/vol1/ and /book/vol2/ on dev). The most visible symptom was the navbar title-href landing one level too shallow: clicking the navbar title from inside Vol I went to /book/ instead of the unified landing page at the dev root. Fix: derive PREFIX from the number of path segments in the calling subsite's dev-side path (book/vol1 → 2 hops → '../../') and use the mlsysbook.ai key (not the dev-path) for self-link detection. Add an explicit error if the caller passes a subsite name that is not in the SUBSITES map, instead of silently producing wrong rewrites. Sample rewrites with the fix: vol1 page https://mlsysbook.ai/ → ../../ vol1 page https://mlsysbook.ai/vol2/ → ../../book/vol2/ vol1 page https://mlsysbook.ai/kits/ → ../../kits/ kits page https://mlsysbook.ai/ → ../ kits page https://mlsysbook.ai/vol1/ → ../book/vol1/ Live builds are unaffected — they use the original absolute URLs. * feat(book): per-volume announcement bars (Crimson / ETH-Blue) Split the shared book announcement bar into two volume-scoped files so each volume gets audience-appropriate copy AND inherits the right brand tint. Vol I keeps the Harvard-Crimson tint (its theme accent) and the Foundations-flavored content; Vol II picks up the ETH-Blue tint (its theme accent) and Scale-flavored content that leads with the new volume launch and the cross-ecosystem build path. Files: - announcement-vol1.yml — new, Vol I copy, no hard-coded color (uses `type: primary` so .announcement / .alert-primary get $accent = $brand-crimson via theme-harvard.scss) - announcement-vol2.yml — new, Vol II copy, same pattern but theme feeds $accent = $brand-eth-blue via theme-eth.scss - announcement.yml — emptied to a no-op with a deprecation note; keep for one release cycle to avoid breaking any external metadata reference, then delete The CSS that translates `type: primary` into the per-theme tint already lived in book/quarto/assets/styles/_base-styles.scss (`.announcement { background: linear-gradient(... lighten($accent, 52%) ...) }`). No SCSS changes needed — the previous behavior of a single shared bar just hid that the tint was already theme-driven. Resolves the "Vol II announcement should be ETH-themed" QA note. * feat(theme): cross-site dark-mode persistence + FOUC guard Make dark-mode preference flow seamlessly across every subsite under mlsysbook.ai (Quarto-built and Next.js alike) and eliminate the theme-flash that dark-mode readers see on first paint. Quarto subsites (book / labs / kits / slides / instructors / mlsysim / tinytorch / unified site): - shared/config/site-head.html now inlines a tiny pre-paint script that reads `quarto-color-scheme` from localStorage (or falls back to OS preference) and applies `data-bs-theme`, `data-quarto-color-scheme`, and `style.color-scheme` on <html> BEFORE any other script runs. Eliminates the visible flash that was happening because Quarto's own toggle script runs late. - Listens for `storage` events so a toggle in tab A propagates to tab B without a refresh. - Inlined deliberately: the script is tiny, must be synchronous in <head> to avoid the flash, and inlining sidesteps per-subsite asset path differences. Canonical externalized source kept at shared/scripts/theme-persist.js for documentation/testability — if you change one, mirror to the other. StaffML (Next.js): - public/theme-bootstrap.js now reads the Quarto-side key as a fallback when StaffML has no local preference, so a user toggling dark mode on the book lands here in dark mode on first visit. - components/ThemeProvider.tsx mirrors writes back to `quarto-color-scheme`, so navigating onward to any Quarto subsite inherits StaffML's choice. Both subsystems retain their own keys as primary so each app's behavior is unchanged in isolation. The `quarto-color-scheme` key is the bridge contract — keep it stable across all theme code paths. * test(audit): Playwright site-audit script (sidebar / darkmode / assets) Single Playwright-driven QA script that the release-prep plan needs in three flavors. Implemented as one CLI with three subcommands so the shared boilerplate (browser launch, URL list, output dirs, screenshot naming) lives in one place and the per-site source-of-truth list does too. Subcommands: sidebar Assert every Quarto subsite exposes a populated, visible #quarto-sidebar / .sidebar-navigation. Skips sites that intentionally have no sidebar (landing, slides, StaffML). Catches the regression where Vol I/II builds dropped the sidebar after a config refactor. darkmode Force dark-mode via localStorage + data attributes, scroll top→bottom in 800px chunks (so lazy content renders), and screenshot full-page into _audit/darkmode/<site>.png for eyeball review. Surfaces "half-themed" widgets that CSS linters can't find (announcement bar, footer tiles, code blocks, etc.). assets Listen for failed network requests + 4xx/5xx responses on every site URL. Catches the broken <img> embeds reported during dev-mirror review (TinyTorch big-picture PDF viewer, Vol II cover) before they hit production. Targets dev / live / local with --target. Use --only <substring> to narrow scope. JSON report written to _audit/<cmd>.json for CI ingest. Exits non-zero on issues so it can become a blocking CI check once the baseline is clean. Requires `npm i -D playwright && npx playwright install chromium`. |
||
|
|
8f09e80c4c |
PR-3: Scripts, audits, cleanup (build stamp, PDF dropdown, 404s, mirror guard, dedup, RELEASE-PREP) (#1406)
* feat(footer): build-time "last updated" stamp
Add a small build-time stamp to the page footer ("Last updated YYYY-MM-DD
· <site> · <commit>") so readers can see at a glance that the site is
fresh. Quarto's per-page `date-modified` already exists for chapter
pages, but it doesn't capture site-level rebuilds (theme tweaks,
navbar changes, deploy reruns).
Pieces:
- shared/scripts/inject-build-stamp.sh: wraps a token-replace over a
build directory. Search-and-replace on `<!-- MLSB_BUILD_STAMP -->`
means sites that haven't adopted the token are unaffected — opt-in
rollout per subsite.
- book/quarto/config/shared/html/footer-common.yml: token added next
to the existing copyright line in the shared book footer.
- shared/config/footer-site.yml: token added next to the copyright
in the unified-site footer.
- shared/config/site-head.html: minimal CSS for `.mlsb-build-stamp`
(small, neutral, dark-mode aware).
- .github/workflows/kits-publish-live.yml: representative wiring —
runs the stamp step after build and before deploy. Other publish-
live workflows can adopt the step the same way as they roll
through release-prep validation.
* feat(navbar): expose paper.pdf for TinyTorch / MLSys·im / StaffML
Each of these subsites already builds a companion paper.tex in CI and
ships the PDF alongside the HTML site. Surface those papers in the
navbar dropdowns where readers actually look for them:
Build menu:
- TinyTorch → site
- TinyTorch Paper (file-pdf icon, opens in new tab)
→ /tinytorch/assets/downloads/TinyTorch-Paper.pdf
- MLSys·im → site
- MLSys·im Paper (file-pdf icon, opens in new tab)
→ /mlsysim/mlsysim-paper.pdf
Prepare menu (after a separator):
- StaffML Paper (file-pdf icon, opens in new tab)
→ /staffml/downloads/StaffML-Paper.pdf
Paper URLs are intentionally kept in lockstep with the build steps in
tinytorch-publish-live (assets/downloads/), mlsysim-publish-live
(site root), and staffml-publish-live (out/downloads/). If a build
path moves, both the workflow and this navbar entry need to move
together — there is no single source.
* feat(404): per-site 404 pages for slides / instructors / unified site
The book, kits, labs, mlsysim, and tinytorch subsites already have
flavored 404.qmd pages that route lost readers to the right
neighborhood. Add the missing three so every subsite under
mlsysbook.ai has a coherent recovery experience instead of falling
back to GitHub Pages' default white-page 404.
- slides/404.qmd — slide-deck flavored copy, pointers back to
the deck index, the volumes, and the hub.
- instructors/404.qmd — instructor-flavored copy, pointers to the
course map, slides, and both volumes.
- site/404.qmd — landing-page flavored copy, the most
ecosystem-wide nav (links to every subsite)
because this is the most common 404 source
for inbound links from the legacy single-
volume mlsysbook.ai.
StaffML already has its own React not-found.tsx so no work needed.
TinyTorch's legacy Sphinx 404.md is preserved for now (still wired on
the Sphinx site that hasn't migrated yet).
* ci(precommit): block subsite-mirror drift on shared assets
Add a pre-commit hook that runs `shared/scripts/sync-mirrors.sh --check`
on every commit. The hook fails if any of the per-subsite real-file
mirrors (subscribe-modal.js, theme SCSS partials, logo) has drifted
from its canonical source in `shared/`.
Why a guard, not just a sync: Quarto's resource-copy step preserves
symlinks instead of dereferencing them, so we have to keep real
copies. Without the guard, "I'll edit the canonical and forget to
re-sync" silently re-introduces the duplicate-divergence bug we just
spent effort fixing. `always_run: true` because a mirror can drift via
deletion of the canonical, not just by editing the canonical itself.
To re-sync after a deliberate change:
bash shared/scripts/sync-mirrors.sh
* refactor(audit): duplicate-file finder + clean up obvious leftover
Add shared/scripts/find-duplicates.py as a periodic duplication
auditor. It SHA-1 hashes every source-y file across the ecosystem
roots, groups identical contents, subtracts the intentional groups
declared in shared/scripts/sync-mirrors.sh, and reports the rest as
unintended duplicates. JSON report written to .audit/duplicates.json
for CI ingest later; --strict makes it exit non-zero.
Defaults err on the side of being useful out of the box:
- Skips symlinks (those are deliberate aliases, not duplicates).
- Skips small files (<256B) — LICENSE stubs, .gitkeep, etc.
- Skips _site / _build / node_modules / .next / out / .git.
- Source-y suffix list (.js, .ts, .scss, .css, .html, .yml, .py, .sh).
Binary assets (images, PDFs) are NOT scanned because their dup
story is different (logos, icons are intentionally repeated).
Initial-cleanup pass:
- Delete tinytorch/scripts/cleanup_repo_history.sh — byte-identical
leftover; the canonical version lives at
tinytorch/tools/maintenance/cleanup_history.sh and is the one
referenced by tinytorch/tools/maintenance/README.md.
After this commit the only remaining unintended duplicate is
runHistoryProvider.ts in three vscode-ext packages (kits / labs /
tinytorch). Promoting that into a shared vscode-ext package is real
refactor work — out of scope for release-prep, captured for later.
Add .audit/ and _audit/ (the latter from the Playwright site-audit
script) to .gitignore.
* docs(release-prep): handoff notes covering all five PR groupings
Add a single document at the worktree root that walks through what
this branch contains, why each piece is there, the recommended PR
split (PR-1 safety-net, PR-2 visual polish, PR-3 scripts/audits/
cleanup, PR-4 TinyTorch prep, PR-5 cutover skeletons), what was
intentionally LEFT OUT (and why), and what verification was done
locally vs. what still needs the dev mirror to exercise.
Treat this as the cover memo for the staged-rollout foundation
work; once the five PRs are individually merged into dev, this file
will outlive the branch but the per-PR sections still document why
each piece exists for anyone debugging months from now.
|
||
|
|
4b840ff957 |
refactor(site): shared styles system and fix subscribe modal for subsites
- Create shared/ directory with centralized SCSS partials and site-head.html so site subsites (about, community, newsletter) no longer duplicate inline header config or reference book-only styles - Fix subscribe modal: change script src from broken relative path (../../book/quarto/assets/scripts/subscribe-modal.js) to absolute URL (https://mlsysbook.ai/vol1/assets/scripts/subscribe-modal.js) which the rewrite-dev-urls.sh script converts for dev preview automatically |