# xiaolai/type-review architecture

> Architecture diagram of the GitHub repository xiaolai/type-review, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-26). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/xiaolai/type-review
- Repository: https://github.com/xiaolai/type-review

## Overview

TYPE is a privacy-focused typing-practice web app. A typist enters practice, keyboard input reaches the session, and the engine evaluates the run, computes metrics, and records results for adaptive planning and statistics. Passage selection supports adaptive drills, generated benchmarks, curated text, and user text. The UI also exposes settings, keyboard and sound options, shareable results, and in-app reading. The graph follows the README and sampled entry points; unsampled feature wiring is shown only where documented.

Main entry points: [`src/ui/App.tsx`](https://github.com/xiaolai/type-review/blob/main/src/ui/App.tsx) (App shell), [`src/engine/session/session.ts`](https://github.com/xiaolai/type-review/blob/main/src/engine/session/session.ts) (Session), [`src/io/key-event-bus.ts`](https://github.com/xiaolai/type-review/blob/main/src/io/key-event-bus.ts) (Key event bus), [`src/io/corpus/session-adapter.ts`](https://github.com/xiaolai/type-review/blob/main/src/io/corpus/session-adapter.ts) (Session corpus adapter)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_experience["App experience"]
  node_app["App shell<br/>[App.tsx]"]
  node_router["Hash router<br/>[router.ts]"]
  node_practice["Practice stage<br/>[PracticeStage.tsx]"]
  node_results["Results view<br/>[Results.tsx]"]
  node_library["Text library<br/>[Library.tsx]"]
  node_reading["Articles<br/>[ArticleView.tsx]"]
  node_settings["Settings<br/>[Settings.tsx]"]
end

subgraph group_engine["Typing engine"]
  node_session["Session<br/>[session.ts]"]
  node_typing["Text input<br/>[text-input.ts]"]
  node_adaptive["Adaptive planner<br/>[index.ts]"]
  node_metrics["Run metrics<br/>[run-metrics.ts]"]
end

subgraph group_content["Passage sources"]
  node_corpus["Corpus engine<br/>[index.ts]"]
  node_adapter["Session corpus adapter<br/>[session-adapter.ts]"]
  node_sources["Curated sources<br/>[composite.ts]"]
  node_usertext[("User text store")]
end

subgraph group_device["Device services"]
  node_input["Keyboard input<br/>[input-handler.ts]"]
  node_keybus["Key event bus<br/>[key-event-bus.ts]"]
  node_profile[("Profile persistence<br/>[index.ts]")]
  node_sounds["Key sounds<br/>[key-sounds.ts]"]
  node_keyboard["On-screen keyboard"]
end

subgraph group_insights["Stats and sharing"]
  node_statistics["Statistics dashboard<br/>[Stats.tsx]"]
  node_share["Share card<br/>[Share.tsx]"]
  node_siteview["Site statistics<br/>[SiteStats.tsx]"]
  node_statsapi["Stats endpoint<br/>[stats.ts]"]
end

node_typist(("Typist"))
node_analytics[("Cloudflare Analytics")]

node_typist -->|"uses"| node_app
node_app -->|"navigates"| node_router
node_app -->|"renders"| node_practice
node_app -->|"renders"| node_results
node_app -->|"renders"| node_library
node_app -->|"renders"| node_settings
node_app -->|"renders"| node_reading
node_typist -->|"presses keys"| node_keybus
node_keybus -->|"dispatches events"| node_input
node_input -->|"sends keystrokes"| node_session
node_app -->|"bootstraps"| node_session
node_session -->|"feeds characters"| node_typing
node_session -->|"requests passages"| node_adapter
node_adapter -->|"selects entries"| node_sources
node_sources -->|"reads user text"| node_usertext
node_adapter -->|"generates fallback"| node_corpus
node_session -->|"plans lessons"| node_adaptive
node_session -->|"computes results"| node_metrics
node_session -->|"provides run state"| node_results
node_app -->|"loads and saves"| node_profile
node_app -->|"renders"| node_statistics
node_statistics -->|"reads run history"| node_session
node_app -->|"renders"| node_share
node_app -->|"renders"| node_siteview
node_siteview -->|"requests counts"| node_statsapi
node_statsapi -->|"queries metrics"| node_analytics
node_app -->|"configures"| node_keyboard
node_keybus -->|"dispatches events"| node_keyboard
node_app -->|"configures"| node_sounds
node_keybus -->|"dispatches events"| node_sounds
node_app -->|"manages uploads"| node_usertext

click node_app "https://github.com/xiaolai/type-review/blob/main/src/ui/App.tsx"
click node_router "https://github.com/xiaolai/type-review/blob/main/src/ui/router.ts"
click node_practice "https://github.com/xiaolai/type-review/blob/main/src/ui/components/PracticeStage.tsx"
click node_results "https://github.com/xiaolai/type-review/blob/main/src/ui/Results.tsx"
click node_library "https://github.com/xiaolai/type-review/blob/main/src/ui/Library.tsx"
click node_reading "https://github.com/xiaolai/type-review/blob/main/src/ui/articles/ArticleView.tsx"
click node_settings "https://github.com/xiaolai/type-review/blob/main/src/ui/Settings.tsx"
click node_session "https://github.com/xiaolai/type-review/blob/main/src/engine/session/session.ts"
click node_typing "https://github.com/xiaolai/type-review/blob/main/src/engine/typing/text-input.ts"
click node_adaptive "https://github.com/xiaolai/type-review/blob/main/src/engine/adaptive/index.ts"
click node_metrics "https://github.com/xiaolai/type-review/blob/main/src/engine/metrics/run-metrics.ts"
click node_corpus "https://github.com/xiaolai/type-review/blob/main/src/engine/corpus/index.ts"
click node_adapter "https://github.com/xiaolai/type-review/blob/main/src/io/corpus/session-adapter.ts"
click node_sources "https://github.com/xiaolai/type-review/blob/main/src/io/corpus/composite.ts"
click node_usertext "https://github.com/xiaolai/type-review/blob/main/src/io/corpus/user-corpus-store.ts"
click node_input "https://github.com/xiaolai/type-review/blob/main/src/io/input-handler.ts"
click node_keybus "https://github.com/xiaolai/type-review/blob/main/src/io/key-event-bus.ts"
click node_profile "https://github.com/xiaolai/type-review/blob/main/src/io/persistence/index.ts"
click node_sounds "https://github.com/xiaolai/type-review/blob/main/src/io/key-sounds.ts"
click node_keyboard "https://github.com/xiaolai/type-review/blob/main/src/ui/components/OnScreenKeyboard.tsx"
click node_statistics "https://github.com/xiaolai/type-review/blob/main/src/ui/Stats.tsx"
click node_share "https://github.com/xiaolai/type-review/blob/main/src/ui/Share.tsx"
click node_siteview "https://github.com/xiaolai/type-review/blob/main/src/ui/SiteStats.tsx"
click node_statsapi "https://github.com/xiaolai/type-review/blob/main/functions/api/stats.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_app,node_router,node_practice,node_results,node_library,node_reading,node_settings toneBlue
class node_session,node_typing,node_adaptive,node_metrics,node_analytics toneAmber
class node_corpus,node_adapter,node_sources,node_usertext toneMint
class node_input,node_keybus,node_profile,node_sounds,node_keyboard toneRose
class node_statistics,node_share,node_siteview,node_statsapi,node_typist toneIndigo
```

## Components

### App experience

- [App shell](https://github.com/xiaolai/type-review/blob/main/src/ui/App.tsx): `src/ui/App.tsx`
- [Hash router](https://github.com/xiaolai/type-review/blob/main/src/ui/router.ts): `src/ui/router.ts`
- [Practice stage](https://github.com/xiaolai/type-review/blob/main/src/ui/components/PracticeStage.tsx): `src/ui/components/PracticeStage.tsx`
- [Results view](https://github.com/xiaolai/type-review/blob/main/src/ui/Results.tsx): `src/ui/Results.tsx`
- [Text library](https://github.com/xiaolai/type-review/blob/main/src/ui/Library.tsx): `src/ui/Library.tsx`
- [Articles](https://github.com/xiaolai/type-review/blob/main/src/ui/articles/ArticleView.tsx): `src/ui/articles/ArticleView.tsx`
- [Settings](https://github.com/xiaolai/type-review/blob/main/src/ui/Settings.tsx): `src/ui/Settings.tsx`

### Typing engine

- [Session](https://github.com/xiaolai/type-review/blob/main/src/engine/session/session.ts): `src/engine/session/session.ts`
- [Text input](https://github.com/xiaolai/type-review/blob/main/src/engine/typing/text-input.ts): `src/engine/typing/text-input.ts`
- [Adaptive planner](https://github.com/xiaolai/type-review/blob/main/src/engine/adaptive/index.ts): `src/engine/adaptive/index.ts`
- [Run metrics](https://github.com/xiaolai/type-review/blob/main/src/engine/metrics/run-metrics.ts): `src/engine/metrics/run-metrics.ts`

### Passage sources

- [Corpus engine](https://github.com/xiaolai/type-review/blob/main/src/engine/corpus/index.ts): `src/engine/corpus/index.ts`
- [Session corpus adapter](https://github.com/xiaolai/type-review/blob/main/src/io/corpus/session-adapter.ts): `src/io/corpus/session-adapter.ts`
- [Curated sources](https://github.com/xiaolai/type-review/blob/main/src/io/corpus/composite.ts): `src/io/corpus/composite.ts`
- [User text store](https://github.com/xiaolai/type-review/blob/main/src/io/corpus/user-corpus-store.ts): `src/io/corpus/user-corpus-store.ts`

### Device services

- [Keyboard input](https://github.com/xiaolai/type-review/blob/main/src/io/input-handler.ts): `src/io/input-handler.ts`
- [Key event bus](https://github.com/xiaolai/type-review/blob/main/src/io/key-event-bus.ts): `src/io/key-event-bus.ts`
- [Profile persistence](https://github.com/xiaolai/type-review/blob/main/src/io/persistence/index.ts): `src/io/persistence/index.ts`
- [Key sounds](https://github.com/xiaolai/type-review/blob/main/src/io/key-sounds.ts): `src/io/key-sounds.ts`
- [On-screen keyboard](https://github.com/xiaolai/type-review/blob/main/src/ui/components/OnScreenKeyboard.tsx): `src/ui/components/OnScreenKeyboard.tsx`

### Stats and sharing

- [Statistics dashboard](https://github.com/xiaolai/type-review/blob/main/src/ui/Stats.tsx): `src/ui/Stats.tsx`
- [Share card](https://github.com/xiaolai/type-review/blob/main/src/ui/Share.tsx): `src/ui/Share.tsx`
- [Site statistics](https://github.com/xiaolai/type-review/blob/main/src/ui/SiteStats.tsx): `src/ui/SiteStats.tsx`
- [Stats endpoint](https://github.com/xiaolai/type-review/blob/main/functions/api/stats.ts): `functions/api/stats.ts`

### Other components

- **Typist**
- **Cloudflare Analytics**

## Connections

- Typist → App shell: uses
- App shell → Hash router: navigates
- App shell → Practice stage: renders
- App shell → Results view: renders
- App shell → Text library: renders
- App shell → Settings: renders
- App shell → Articles: renders
- Typist → Key event bus: presses keys
- Key event bus → Keyboard input: dispatches events
- Keyboard input → Session: sends keystrokes
- App shell → Session: bootstraps
- Session → Text input: feeds characters
- Session → Session corpus adapter: requests passages
- Session corpus adapter → Curated sources: selects entries
- Curated sources → User text store: reads user text
- Session corpus adapter → Corpus engine: generates fallback
- Session → Adaptive planner: plans lessons
- Session → Run metrics: computes results
- Session → Results view: provides run state
- App shell → Profile persistence: loads and saves
- App shell → Statistics dashboard: renders
- Statistics dashboard → Session: reads run history
- App shell → Share card: renders
- App shell → Site statistics: renders
- Site statistics → Stats endpoint: requests counts
- Stats endpoint → Cloudflare Analytics: queries metrics
- App shell → On-screen keyboard: configures
- Key event bus → On-screen keyboard: dispatches events
- App shell → Key sounds: configures
- Key event bus → Key sounds: dispatches events
- App shell → User text store: manages uploads
