Skip to content

Restore settings after a client re-render, title every page, and self-host the fonts - #10

Merged
renrenmimi merged 2 commits into
masterfrom
fix/shell-settings-and-titles
Oct 10, 2026
Merged

renrenmimi merged 2 commits into
masterfrom
fix/shell-settings-and-titles

Conversation

@renrenmimi

Copy link
Copy Markdown
Owner

Branches from #9 (commit 2148ad8), so it can be merged before or after it.

Summary

Fixes from the 2026-10-09 audit (04-TSer-代码与体验.md):

  • A1 A cold start failed hydration about 6% of the time (React #418) and reset the reader's language, theme and sidebar.
  • A13 English pages downloaded seven Noto Sans SC files (about 279 KB), and the build fetched fonts from Google.
  • A14 Every page had the same <title>; the 404 page was Next.js's white default and highlighted the prologue.
  • A22 (no JavaScript) Every section stayed invisible without scripts.
  • A23 Switching the language blocked the click while the whole page re-rendered.
  • A27 (theme colour) The phone's browser chrome stayed dark in the light theme.
  • A28 (drawer) A drawer opened on a phone stayed open after the window grew past the breakpoint and came back.

Changes

  • app/layout.tsx: the two pre-paint scripts move from <head> to the top of <body>, the same fix AlgoAlgo Raise contrast in both themes, stop idle repaints, show code as typed, and fix print #15 verified. Fonts come from app/fonts through next/font/local, and metadataBase is set for the chapters' canonical URLs.
  • app/theme-provider.tsx, lib/i18n.tsx: the providers read their settings from localStorage, not from <html>, and write data-theme, data-sidebar, data-lang and lang back. The theme also updates <meta name="theme-color">, the language switch runs in startTransition, and the drawer closes when the window grows past the breakpoint.
  • app/<chapter>/layout.tsx (11 new server layouts) and app/chapter-metadata.ts give each chapter its own title, description and canonical URL. app/document-title.tsx keeps the tab title in the reader's language, including after Next.js rewrites it on navigation.
  • app/not-found.tsx: a 404 page inside the site shell, in both languages, listing every chapter. In lib/curriculum.ts, chapterByPath returns PAGE_NOT_FOUND for a path outside the course, so the sidebar highlights nothing and the breadcrumb says "Page not found".
  • app/fonts/: the same Syne, Space Grotesk and JetBrains Mono files as AlgoAlgo and DataData, plus DataData's Noto Sans SC slices. In app/globals.css, only the html[data-lang="zh"] font stacks name Noto Sans SC, and @media (scripting: none) shows every .reveal section.
  • Tests: test/settings-restore.test.tsx, test/titles.test.tsx, test/fonts-and-reveal.test.ts.

Verification

  • npx tsc --noEmit --incremental false: clean. npm test: 9 files, 52 tests pass. npm run build: succeeds with no network font fetch.
  • Headless Chrome against a local next start:
    • Cold starts: 120 runs with fresh contexts, alternating phone and desktop, with zh, light and collapsed stored. Settings were lost 0 times and #418 appeared 0 times. Before the fix the audit saw 7 of 120 on production.
    • Titles: 12 of 12 distinct in English and in Chinese. After client-side navigation to /generics, the Chinese tab reads "泛型 · TSer".
    • 404: /no-such-page returns 404 and renders in Chinese on the dark background. No aria-current is set, the breadcrumb reads "页面不存在", and the page lists 12 chapter links.
    • Fonts: an English page makes 3 font requests and none of them is Noto. A Chinese page loads 20 Noto Sans SC slices. Nothing is fetched from Google.
    • No JavaScript: /narrowing has 0 of 11 sections hidden.
    • Drawer: after a 390 → 1300 → 390 resize, the drawer is closed.

🤖 Generated with Claude Code

renrenmimi and others added 2 commits October 9, 2026 16:07
JSX trims each line of a text block and joins the lines with one space.
The Chinese copy is wrapped across source lines throughout the course, so
it rendered with spaces between Chinese characters and around full-width
punctuation: 1,572 of them in 36 files.

- 1,548 line breaks are joined by a script that walks the TypeScript
  syntax tree and touches only JSX text and JSX attribute strings, never
  code samples, template literals or comments. Breaks next to Latin
  letters, digits, expressions, "——" or operators keep their space.
- 24 more spaces go: 20 after full-width punctuation, 3 between Chinese
  text and a tag whose text is Chinese, and one {" "} between a
  full-width mark and Chinese text.
- test/zh-copy-spacing.test.ts, the check DataData and AlgoAlgo use,
  scans every TSX file and fails on any new stray space; CLAUDE.md states
  the rule.

Apart from the one {" "}, the diff changes nothing but whitespace. The
text every edited file renders differs from master only by deleted
spaces, each next to Chinese text, so the English copy is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t the fonts

- Move the two pre-paint scripts from <head> to the top of <body>, so the webpack runtime
  removing chunk scripts from <head> no longer races hydration (React error #418).
- ThemeProvider, ShellProvider and LangProvider read the settings back from localStorage and
  write data-theme, data-sidebar, data-lang and lang back onto <html>, so even a root that
  React renders again on the client keeps the reader's language, theme and sidebar.
- Switch the language inside startTransition; keep <meta name="theme-color"> in step with the
  theme; close the phone drawer when the window grows past the drawer breakpoint.
- Give every chapter a server layout that exports its own metadata, keep the tab title in the
  reader's language on the client, and add a real 404 page in the site shell. chapterByPath
  returns PAGE_NOT_FOUND for a path outside the course instead of the prologue.
- Self-host Syne, Space Grotesk and JetBrains Mono with next/font/local (the same files as
  AlgoAlgo and DataData), and load Noto Sans SC only through the Chinese font stacks, so
  English pages download none of it.
- Show every section when scripting is off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 10, 2026 01:48
@vercel

vercel Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
tser Ready Ready Preview Oct 10, 2026 1:49am UTC

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-10T01:50:46.620078Z 0cd9871 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@renrenmimi
renrenmimi merged commit 33ea6d7 into master Oct 10, 2026
3 checks passed
@renrenmimi
renrenmimi deleted the fix/shell-settings-and-titles branch October 10, 2026 06:21

This branch was successfully deployed

1 active deployment
Preview — 0cd98713 Deployed Oct 10, 2026 by vercel[bot]
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.

2 participants