Skip to content

Free the lab editor's keyboard, keep code readable when the compiler fails, and localise presets - #14

Merged
renrenmimi merged 2 commits into
masterfrom
fix/compiler-lab
Oct 10, 2026
Merged

renrenmimi merged 2 commits into
masterfrom
fix/compiler-lab

Conversation

@renrenmimi

Copy link
Copy Markdown
Owner

Branches from #9 (commit 2148ad8), so it can be merged before or after it, and independently of #10–#13.

Summary

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

  • A4: the editor was a keyboard trap. Tab and Shift+Tab always inserted two spaces, with no way out, which breaks WCAG 2.1.2. A selection was replaced, and undo was lost.
  • A6: the "static view" covered the code with a blurred overlay, and "Open in Playground" opened an empty Playground.
  • A7 (lab controls): in the light theme, the selected tab and enabled switches were 1.84–2.43:1 on the dark lab frame.
  • A8 (.tsl-st-note): the status note, timing and type-bar hint were 3.4–3.6:1.
  • A16: ten presets had English-only comments, so a Chinese reader saw English after a click.
  • A17: old squiggles were drawn at old offsets on the new text for about 285 ms after typing.
  • A18: the whole diagnostics list was a live region, the tabs pattern was incomplete, and the editor had no focus style.
  • A19 (editor): 12px text on phones made iOS zoom in.
  • A24: public/tslab/typescript.js and libs.json (11.6 MB) were tracked and deployed, but nothing loads them.
  • A25: "About 11 MB" was shown, but the real transfer is about 2 MB.
  • A27 (lab copy): the version badge tooltip was English-only. The empty-output message said "fix the errors above first", which echoes the misconception that errors block emit.
  • A28:
    • An unset strict switch showed as off, although the worker treats it as on.
    • The spinner stood still under reduced motion.
    • useTsLab subscribed in an effect, which can miss an update.
  • B23 (lab copy): the JS tab's note used 标注 where the course term is 注解.

Changes

  • lib/tslab.tsx
    • Keyboard:
      • Tab indents, and indents every selected line when the selection spans lines. Shift+Tab outdents and never inserts.
      • Both edits go through insertText, so the browser's undo history is kept.
      • Esc, then Tab or Shift+Tab, lets focus leave the editor. A hint referenced by aria-describedby says so.
    • Loading and failure:
      • Idle, loading and failure states are a .tsl-banner above the editor; the code stays readable and selectable.
      • On failure, the Playground link is …/play/#src=<encoded current code>, and the type bar and status bar say the compiler is not running.
    • Diagnostics: diagnostics remember the source they were computed for. Squiggles disappear as soon as the code changes, and the list is marked stale until the new result arrives.
    • Screen readers:
      • A visually hidden role="status" announces one summary ("1 error") only when it changes.
      • The tabs have aria-controls, a tabpanel, a roving tabindex and Arrow/Home/End keys.
    • Presets and switches: TsLabPreset.code is Loc<string>. An unset strict switch is shown as on.
    • Copy:
      • The download hint reads "About 2 MB to download, once."
      • The empty-output message is neutral: "The compiler wrote no output for this code."
      • The tooltip is bilingual, and the zh erasure note uses 注解.
  • lib/tslab-client.tsx: useTsLab uses useSyncExternalStore, with a snapshot that changes reference on every publish.
  • app/globals.css, section 14 (the lab) only:
    • .tsl-banner replaces .tsl-overlay.
    • In the light theme the lab redefines --acc, --acc-ink, --acc-soft and --acc-border to the dark theme's values.
    • The quiet texts are raised to 62–66% white.
    • Added .tsl-ta:focus-visible, .tsl-keyhint, .tsl-sr and .tsl-diags[data-stale].
    • Editor text is 16px on screens of 720px or less.
    • Under reduced motion, the spinner runs at 2s infinite !important.
  • app/types, functions, structural, utility, type-magic page.tsx: only the ten preset constants listed in A16 change. The English code is byte-for-byte the same, and the Chinese copy translates only the comments.
  • public/tslab/typescript.js and libs.json are removed. .gitignore now ignores /public/tslab/* except worker.js.
  • Test: test/tslab-editor.test.tsx.

Verification

  • npx tsc --noEmit --incremental false: clean. npm test: 7 files, 48 tests pass. npm run build: succeeds.
  • Headless Chrome against a local next start:
    • Keyboard (/types): Tab inserts 2 characters, and ⌘Z restores the original text. Shift+Tab at column 0 changes nothing. Esc then Tab moves focus out of the editor.
    • Squiggles: right after typing two spaces there are 0 squiggles and the list is stale. After the result, the squiggle covers qty again and the status region says "1 error".
    • Fallback: with worker.js blocked, the banner shows the failure and no overlay is rendered. The editor's centre hits the textarea, so the code is selectable. The Playground link is …/play/#src=%2F%2F%20Not%20one%20type…, and the type bar and status say the compiler is not running.
    • Playground: opening typescriptlang.org/play/#src=… with a sample that has a Chinese comment loads exactly that code into the Playground editor.
    • Light-theme contrast (/tsconfig, colours resolved on a canvas): selected tab 9.37:1, enabled strict switch 8.2:1, status note 6.33:1, type-bar hint 6.42:1.
    • Chinese mode: clicking every preset on /types, /functions, /structural, /utility and /type-magic leaves no English comment.
    • Phone (390, reduced motion): highlight layer and textarea are both 16px, and the spinner runs infinite 2s. /tslab/typescript.js and /tslab/libs.json return 404.

🤖 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>
…fails, localise presets

- Tab no longer traps focus: Esc, then Tab or Shift+Tab, leaves the editor, and a hint shown
  while it is focused says so. Tab indents (every selected line when the selection spans
  lines), Shift+Tab outdents and never inserts spaces, and both go through insertText so the
  browser's undo history survives.
- Loading and failure states are a banner above the editor instead of an overlay that blurred
  the code. The Playground link carries the current code (#src=), and while the compiler is
  not running the type bar and the status bar say so.
- Diagnostics are tied to the source they were computed for: squiggles disappear the moment
  the code changes and come back with the new result, and the list is shown as stale
  meanwhile.
- Screen readers hear one "N errors" summary from a status region instead of the whole list.
  The tabs follow the WAI-ARIA pattern (aria-controls, tabpanel, roving tabindex, arrow keys,
  Home and End), and the editor has a visible focus ring.
- The ten presets with comments now come in both languages (TsLabPreset.code is
  Loc<string>), so a Chinese reader no longer gets English comments after a click.
- The lab keeps the dark theme's accent inside its always-dark frame in the light theme, and
  the status note, timing and type-bar hints are raised above 6:1.
- The editor uses 16px text on narrow screens, the spinner keeps turning slowly under reduced
  motion, an unset strict switch shows as on as the worker treats it, and useTsLab subscribes
  through useSyncExternalStore.
- "About 11 MB" becomes "About 2 MB to download", the empty output message no longer implies
  that errors block emit, and the version badge's tooltip is bilingual.
- Stop tracking public/tslab/typescript.js and libs.json (11.6 MB that nothing loads) and
  ignore everything in public/tslab except worker.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 10, 2026 02:16
@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 2:17am 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

chatgpt-codex-connector Bot commented Oct 10, 2026 •

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 ✅ Completed 2026-10-10T02:20:41.492196Z a563b75 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a563b75570

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/tslab.tsx
setMs(r.ms);
setChecking(false);
const n = r.diagnostics.filter((d) => d.severity === "error").length;
setLiveSummary(n > 0 ? L(TXT.errorCount(n)) : L(TXT.noErrors));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recompute the live summary after language changes

When a lab has already compiled and the reader switches between English and Chinese, this stores the translated summary in state, but the checking effect does not depend on lang or L. If the source is unchanged—or the learner has edited it, so language switching preserves it—the hidden role="status" continues exposing text such as “1 error” in the previous language. Store the error count instead and translate during render, or update the summary when the language changes.

Useful? React with 👍 / 👎.

Comment thread lib/tslab.tsx
[text, lang],
);

if (text === null) return <div className="tsl-diag-idle">{pending}</div>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop showing a pending emit after compiler failure

When the worker fails before the user has opened an output tab, output.js and output.dts remain null, while the emit effect refuses to run unless ts.status === "ready". Opening Compiled JS or Declaration .d.ts therefore displays “checking…” indefinitely even though the failure banner and status bar say the compiler is not running. Handle the failed state separately instead of treating every null output as pending.

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview — a563b755 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