[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80260":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":15,"starSnapshotCount":15,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},80260,"type-review","xiaolai\u002Ftype-review","xiaolai","TYPE — adaptive typing practice that lives in your browser. Live at https:\u002F\u002Ftype.review","https:\u002F\u002Ftype.review",null,"TypeScript",174,27,94,0,34,73,77,141,92.04,"Other",false,"main",true,[],"2026-06-12 04:01:27","# TYPE · [type.review](https:\u002F\u002Ftype.review)\n\nA typing-practice web app that **adapts to you** and **stays out of your way** — no account, no server, no telemetry.\n\n> Adaptive letter-unlock + benchmark runs + bring-your-own text.\n\n## What you get\n\n- **Adaptive mode** — the app teaches you a small alphabet, then unlocks more letters as you get fast and accurate. Drilling weak keys without thinking about it.\n- **Benchmark mode** — runs on real prose, ended by either word count or a timer (configurable).\n- **Real text to type** — a curated library of public-domain quotes and short passages, your own `.txt` \u002F `.md` uploads, or paste a one-off paragraph via the *custom text* affordance.\n- **Per-key + per-finger stats** — a stats dashboard with per-source WPM trends, finger speed\u002Ferror breakdown, daily-run streaks, and milestone tracking. Every run feeds the same model regardless of mode.\n- **An on-screen keyboard** (optional) — colour-coded by your per-key mastery, with live press-state. Mac and Windows layouts. QWERTY, Colemak, Dvorak keymaps.\n- **Mechanical-keyboard sounds** (optional) — synthesised mechvibe + soft packs, plus a real-sample typewriter pack. Synth packs generate on-device with Web Audio.\n- **Sharable results** — `copy share link` on Results emits a `#\u002Fshare\u002F\u003Cpayload>` URL; recipients see a read-only card of the run.\n- **Long-form in-app reading** — the *Why typing matters* essay (and any future essays under `public\u002Farticles\u002F\u003Cslug>\u002Farticle.md`) renders as part of the app.\n- **Four themes** — dark, light, sepia, high-contrast. sRGB fallbacks for pre-2023 browsers.\n- **Your data, on your device** — IndexedDB, no account. One click exports a JSON backup; one click wipes it.\n\n## Try it\n\n[type.review](https:\u002F\u002Ftype.review) — it's a single page that loads, no sign-up, and starts working.\n\n## Privacy\n\n- No analytics, no telemetry, no cookies.\n- No third-party fonts at runtime. The CSS hints at Geist + Geist Mono but the app ships zero font files — if the user agent doesn't already have them, the system-font fallback chain renders the page; no font request is ever made.\n- Your typing history lives in your browser's IndexedDB. The app never sends it anywhere.\n- If you upload your own text in the library, it stays in IndexedDB. It's never uploaded.\n\nSee the in-app `#\u002Fabout` page for the full privacy summary.\n\n## Keyboard shortcuts\n\n- **Type** — just type. The app captures everything that's not a modifier.\n- **Tab** — start with fresh text (new passage).\n- **Enter** on the results screen — start the next run.\n- **Esc** — never used by the app; reserved for your OS \u002F browser.\n\nFor a deeper walkthrough of modes, settings, scoring, and the source picker, see [`#\u002Fguide`](https:\u002F\u002Ftype.review\u002F#\u002Fguide) (or the catalog of everything at [`#\u002Ffeatures`](https:\u002F\u002Ftype.review\u002F#\u002Ffeatures)).\n\n## For contributors\n\nEverything below is for people working on the app itself.\n\n### Stack\n\nSolidJS + TypeScript (strict) · Vite · Vitest + jsdom · Biome · pnpm.\nThe only runtime dep is `solid-js`.\n\n### Quick start\n\n```bash\npnpm install\npnpm dev          # http:\u002F\u002Flocalhost:5173\npnpm test         # full Vitest suite\npnpm test:coverage\npnpm typecheck\npnpm lint         # Biome\npnpm lint:fix\npnpm build        # tsc --noEmit && vite build → dist\u002F\npnpm preview      # serve dist locally\n```\n\n### Architecture in one sentence\n\nA pure TypeScript **engine** (typing loop, metrics, adaptive letter-unlock planner, corpus, session orchestrator) sits at the bottom; thin **io** adapters (DOM input bus, IndexedDB persistence, Web Audio sounds) sit in the middle; a **SolidJS UI** sits on top. The hot keystroke loop never flows through reactive state — keystrokes mutate a plain `Session` and the UI subscribes to RAF-batched snapshots.\n\nSee [`ARCHITECTURE.md`](.\u002FARCHITECTURE.md) for layer rules, the data flow, and the corpus seam.\n\n### Deployment\n\n[type.review](https:\u002F\u002Ftype.review) is served by **Cloudflare Pages**. To redeploy after a build:\n\n```bash\npnpm build\npnpm dlx wrangler pages deploy dist --project-name=type-review --branch=main\n```\n\n(Auth via `CLOUDFLARE_EMAIL` + `CLOUDFLARE_API_KEY` env vars, or `CLOUDFLARE_API_TOKEN`.)\n\nThe repo ships header policies for Netlify \u002F Cloudflare Pages \u002F Vercel:\n\n- **Content-Security-Policy**: `default-src 'none'` baseline, only same-origin scripts\u002Fstyles, no inline anything, `frame-ancestors 'none'`\n- **COOP\u002FCOEP**: `same-origin` + `require-corp` (enables full-resolution `performance.now()`)\n- **Permissions-Policy**: deny camera \u002F microphone \u002F geolocation \u002F payment \u002F usb\n- **X-Frame-Options**: `DENY` · **X-Content-Type-Options**: `nosniff` · **Referrer-Policy**: `no-referrer`\n\nFor Apache \u002F Nginx \u002F S3+CloudFront, translate the same policy to your host. Subresource Integrity is intentionally not configured — all assets are served same-origin.\n\n### Identifiers used on the client\n\n| Surface | Value | Notes |\n|---|---|---|\n| Package name | `type-review` | `package.json` |\n| IndexedDB database | `type-review` (v2) | object stores: `profile`, `user-corpus` |\n| localStorage keys | `type-review:theme`, `type-review:has-saved-profile`, `type-review:show-keyboard`, `type-review:keyboard-layout`, `type-review:sound-pack`, `type-review:sound-volume`, `type-review:corpus-channel` | preferences only — no PII |\n| BroadcastChannel | `type-review` | cross-tab \"another tab saved\" notification |\n| Console log prefix | `type-review: \u003Cstage> failed` | from `src\u002Fui\u002Flog.ts` |\n\nThese are every string a user-agent sees from this app. None of them surface in the UI; the visible brand is **TYPE**.\n\n### Contributing\n\nThis codebase optimises for **clarity over cleverness** and **fail-loud invariants**. Before opening a PR:\n\n1. `pnpm typecheck && pnpm lint && pnpm test` — all must pass (CI enforces).\n2. Don't destructure SolidJS `props` (breaks reactivity). Read via `props.x` inside JSX.\n3. Don't mutate `session.profile` directly — route through `Session.updateSettings` (the only validated write path from UI to engine).\n4. Layer purity is checked by `src\u002Flayer-purity.test.ts` — `engine` may not import `io` or `ui`; `io` may not import `ui`.\n\n### License\n\n[MIT](.\u002FLICENSE). Bundled corpus content (quotes + code snippets) is curated from public-domain sources plus short fair-use quotation snippets; each entry carries its own `license` field — see `LICENSE` and `src\u002Fio\u002Fcorpus\u002Fdata\u002F`.\n","TYPE 是一个基于浏览器的自适应打字练习应用，旨在提供个性化的打字训练体验。其核心功能包括自适应模式，根据用户表现逐步解锁更多字母；基准模式，支持自定义文本或使用内置的经典文段进行测试；以及详细的按键和手指统计分析。技术上采用TypeScript构建，保证了代码质量和性能，并通过IndexedDB本地存储数据，确保用户隐私安全。该工具非常适合需要提高打字速度与准确度的学习者，尤其是那些偏好无干扰、注重个人数据保护的用户。此外，它还提供了多种主题选项、机械键盘音效模拟等增强用户体验的功能。",2,"2026-06-11 03:59:51","CREATED_QUERY"]