Tailwind v4 dropped the v3 preflight rule that set `cursor: pointer` on
`button, [role="button"]`, so every <button> in the web app fell back to
the UA default. <a> elements (nav links, GitHub icon) were unaffected, which
is why only the locale switcher and dark-mode toggle looked broken in the header.
Restore the rule once in globals.css instead of adding `cursor-pointer` to
each of the 18 buttons. `:not(:disabled)` keeps disabled playback controls
on the default cursor.
Closes#594
`fs.cpSync` in `copyChapterAssets` terminates the Node process on Windows
with Node 22. Running `npm run extract` (the `predev` hook of `npm run dev`)
prints "Source: root chapter folders (17)" and then exits with
STATUS_STACK_BUFFER_OVERRUN (exit code 0xC0000409) without any JS-level
error, so the course data is never written and the dev server never starts.
Replace it with a small recursive copy helper built on `fs.readdirSync` +
`fs.copyFileSync`, which is what `fs.cpSync` does internally minus the
native path that crashes here.
Verified on Windows 11 / Node v22.20.0:
- before: exit 0xC0000409, extraction aborted, no course-assets output
- after: exit 0, "17 versions / 16 diffs / 51 docs", 74 asset files written
Clarify that specialized tools structure the model-harness interface for safer arguments, precise permissions, and shared logging. Keep the English, Chinese, and Japanese lessons in sync and regenerate the web content.\n\nRefs #549
The "Message Growth" visualization (message-flow.tsx) had three issues:
1. Flicker at len=0/1. The setInterval kept firing while the array was
full, queuing multiple setCount(0) resets that snapped the counter
back mid-regrowth; and index-keyed chips inside AnimatePresence
collided with still-exiting nodes on reset, leaving stuck invisible
chips (len=1 rendered 8 chips at opacity 0). Replaced with a single
self-scheduling setTimeout chain and removed AnimatePresence/exit so
chips unmount cleanly on reset.
2. Horizontal scrollbar at len=8. Switched overflow-x-auto to flex-wrap
and tightened chip padding/gap (px-2.5->px-2, gap-1.5->gap-1) so all
8 chips fit on one line within the existing column width.
3. Inconsistent entrance animation. Dropped the width:0->auto slide in
favor of a unified scale+opacity zoom with layout for smooth reflow.
Refs #450
- initialize dark mode state after mount from document to avoid mismatched first render state.
- render a placeholder before mount to reduce theme icon hydration mismatch risk.