{"id":50756461,"url":"https://github.com/lujun9972/type-practice","last_synced_at":"2026-06-11T05:30:30.002Z","repository":{"id":362309876,"uuid":"1255507870","full_name":"lujun9972/type-practice","owner":"lujun9972","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-03T14:49:59.000Z","size":193,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-03T16:03:05.031Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lujun9972.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-31T23:00:50.000Z","updated_at":"2026-06-03T14:53:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lujun9972/type-practice","commit_stats":null,"previous_names":["lujun9972/type-practice"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lujun9972/type-practice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lujun9972%2Ftype-practice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lujun9972%2Ftype-practice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lujun9972%2Ftype-practice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lujun9972%2Ftype-practice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lujun9972","download_url":"https://codeload.github.com/lujun9972/type-practice/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lujun9972%2Ftype-practice/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34184779,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-11T05:30:29.453Z","updated_at":"2026-06-11T05:30:29.991Z","avatar_url":"https://github.com/lujun9972.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Type Practice\n\n面向小学生的中英文打字练习游戏。从素材库选择文章，逐段打字练习，实时显示准确率和速度。通过 XP 经验值、等级、连击、每日目标等游戏化机制激励持续练习。\n\n## Features\n\n### 素材管理\n\n- **素材库** — 手动创建、URL 抓取、AI 生成三种方式添加素材，支持查看详情、编辑、删除\n- **素材导入导出** — 一键导出为 JSON 文件，支持全部/按标签/手动选择三种导出范围；导入时自动检测冲突，逐个选择保留本地、使用导入的或两个都保留\n- **唐诗300首** — 内置300首经典唐诗（白居易、李白、孟浩然、王勃等），开箱即练\n- **标签分类** — 按标签筛选素材，随机练习模式一键开始\n\n### 打字练习\n\n- **分段打字** — 文章自动按句号分段，逐段解锁，图片在打完前置段落后显示\n- **拼音模式** — 切换到拼音输入模式，练习汉字拼音输入，支持声调显示和 IME 组合处理\n- **拼音提示** — 遇到不会读的汉字，点击 Hint 显示拼音（不限次数）\n- **随机练习** — 🎲 按钮从当前筛选结果中随机选一篇，增加练习多样性\n- **进度保存** — 关闭浏览器后再次打开，提示继续或重新开始\n\n### 内容获取\n\n- **URL 抓取** — 粘贴网页链接，自动提取正文和图片作为打字素材\n- **AI 生成** — 输入话题，LLM 自动生成适合小学生的阅读内容，可指定字数范围\n\n### 打字统计\n\n- 每段显示准确率、用时，完成后汇总速度（字/分钟）\n\n### 游戏化系统\n\n- **XP 经验值** — 每打对一个字获得 1 XP，即时反馈\n- **等级系统** — Lv.1（打字新手）到 Lv.10（传说），10 个等级逐步成长\n- **每日目标** — 每天自选轻松（80 XP）、正常（150 XP）、挑战（300 XP）三档目标\n- **连击奖励** — 连续练习天数记录，每坚持 7 天获得 1 个修复道具（最多 3 个）\n- **修复道具** — 断连时可消耗道具修复连击记录\n\n### 管理保护\n\n管理页面支持密码认证，防止误操作修改素材库。\n\n## Tech Stack\n\n| 层 | 技术 |\n|---|------|\n| Frontend | Vue 3 + TypeScript + Vite |\n| Backend | Python FastAPI + uvicorn |\n| Testing | Vitest (frontend, 128 tests) + pytest (backend, 114 tests) |\n| Content Extraction | readability + custom extractor |\n| Pinyin | pinyin-pro |\n| LLM | DeepSeek / OpenAI-compatible API |\n\n## Architecture\n\n```\n┌─────────────────┐       ┌─────────────────────────────┐\n│   Browser SPA   │       │        FastAPI Server        │\n│   (Vue 3)       │──────▶│                              │\n│                 │ REST  │  ┌─────┐  ┌──────┐  ┌─────┐ │\n│  Pages:         │  API  │  │Store│  │Stats │  │LLM  │ │\n│  - Home         │◀──────│  │(JSON│  │Store │  │Proxy│ │\n│  - Play         │       │  │ File)│  │(JSON)│  │     │ │\n│  - Admin        │       │  └─────┘  └──────┘  └─────┘ │\n│  - Settings     │       │                              │\n│                 │       │  Auth Middleware (token)      │\n└─────────────────┘       └─────────────────────────────┘\n```\n\n- **Frontend**: Vue 3 SPA，Vite 开发服务器代理 `/api` 到后端\n- **Backend**: FastAPI 单文件应用，JSON 文件持久化，无数据库依赖\n- **Auth**: 密码设置后生成 token，受保护端点通过中间件验证\n- **Production**: 后端直接 serve 前端 `dist/` 静态文件\n\n## Project Structure\n\n```\ntype-practice/\n├── frontend/                # Vue 3 SPA\n│   ├── src/\n│   │   ├── api/             # API client (materials, stats, config)\n│   │   ├── components/      # TypingSegment, TypingSession\n│   │   ├── engine/          # TypingEngine + PinyinEngine\n│   │   └── pages/           # HomePage, PlayPage, AdminPage, SettingsPage\n│   └── tests/               # Vitest unit + component tests\n├── backend/                 # FastAPI server\n│   ├── app/\n│   │   ├── main.py          # API endpoints + auth + import/export\n│   │   ├── stats.py         # Gamification data layer (XP, levels, streaks)\n│   │   ├── extractor.py     # URL content extraction\n│   │   ├── splitter/        # Text → segment splitting\n│   │   └── store.py         # JSON file persistence\n│   ├── data/\n│   │   └── tang300-export.json  # 唐诗300首导入文件\n│   └── tests/               # pytest integration tests\n├── dev.sh                   # Development start/stop script\n├── prod.sh                  # Production build \u0026 deploy script\n└── package.json             # Dev script orchestration\n```\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js \u003e= 18\n- Python \u003e= 3.11\n\n### Install\n\n```bash\n# Frontend\ncd frontend \u0026\u0026 npm install \u0026\u0026 cd ..\n\n# Backend\ncd backend \u0026\u0026 pip install -e \".[dev]\" \u0026\u0026 cd ..\n```\n\n### Run (Development)\n\n```bash\nnpm run dev\n```\n\nFrontend runs at `http://localhost:5173`, backend at `http://localhost:8000`.\n\nAlternatively, use the dev script:\n\n```bash\n./dev.sh start    # Start both servers\n./dev.sh status   # Check if running\n./dev.sh stop     # Stop both servers\n```\n\n### Run (Production)\n\n```bash\n./prod.sh build   # Build frontend\n./prod.sh start   # Start production server (serves frontend + API on port 8000)\n```\n\n### Test\n\n```bash\nnpm test\n```\n\nRuns both backend (pytest) and frontend (Vitest) test suites.\n\n## Configuration\n\n### Application Settings\n\n访问设置页面 (`/settings`) 可配置：\n\n- **打字模式** — 普通打字 / 拼音输入\n- **跳过标点** — 打字时是否忽略标点符号\n- **跳过限制** — 每篇素材最多可 Skip 几段\n\n### Admin Auth\n\n管理页面 (`/admin`) 需要密码认证。首次访问时设置密码，之后每次需要登录。Token 存储在 `sessionStorage`，关闭标签页即失效。\n\n### LLM Integration\n\nAI 生成功能需要一个 OpenAI-compatible LLM API：\n\n| 环境变量 | 默认值 | 说明 |\n|----------|--------|------|\n| `LLM_BASE_URL` | `https://api.deepseek.com` | API base URL |\n| `LLM_API_KEY` | — | API Key（也可在设置页面配置） |\n| `LLM_MODEL` | `deepseek-v4-flash` | 模型名称 |\n\n```bash\nLLM_API_KEY=your-key-here npm run dev\n```\n\n## Security\n\n- **Admin 认证** — 管理页面密码保护，token 存储在服务端内存，重启后清空\n- **前端 token** — `sessionStorage` 存储，标签页关闭即失效\n- **受保护端点** — 所有写操作（创建/编辑/删除素材、导入导出、修改配置）均需有效 token\n- **密码存储** — SHA-256 + salt，不明文存储\n\n## Author\n\nDarkSun ([@lujun9972](https://github.com/lujun9972))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flujun9972%2Ftype-practice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flujun9972%2Ftype-practice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flujun9972%2Ftype-practice/lists"}