Skip to content

Latest commit

 

History

History
159 lines (131 loc) · 11.3 KB

File metadata and controls

159 lines (131 loc) · 11.3 KB

AGENTS.md

1. 项目概述

名称: Linux Lab(linux-learning)— 交互式 Linux 学习平台

技术栈:

  • 框架: React 19 + TypeScript 6 + Vite 7
  • 样式: Tailwind CSS 3(自定义 glass / accent / terminal 主题色板)
  • 状态管理: Zustand 5(带 persist 中间件,localStorage key = linux-lab-storage
  • 路由: React Router v7(HashRouter,不是 BrowserRouter,便于静态托管)
  • 终端: xterm.js + @xterm/addon-fit + @xterm/addon-web-links
  • 内容渲染: marked(Markdown) + dompurify(XSS 防护)
  • 测试: vitest(当前仅 1 个文件 tests/VirtualFS.test.ts
  • 包管理: pnpm(pnpm-lock.yaml 严禁手动修改

主要用途: 浏览器内模拟 bash / zsh / fish 三大 Shell,配有虚拟文件系统(VirtualFS)和 22 节分阶段课程(5 个阶段:Linux 基础 → 命令行核心 → Shell 深入 → 脚本编程 → 系统管理),用户可在终端中边学边练。

环境要求:

  • Node.js: 20+
  • pnpm: 10+
  • 模块类型: ESM(package.json"type": "module"

2. 目录结构

linux-learning/
├── public/                    # 静态资源(Vite 原样复制)
├── dist/                      # 构建产物(gitignore,不要改动)
├── src/
│   ├── main.tsx               # 入口,挂载 React 根
│   ├── App.tsx                # 根组件 + HashRouter + 路由表 + resize
│   ├── index.css              # Tailwind base + 自定义工具类 + 主题变量 + 动画 keyframes
│   ├── components/            # UI 组件(按职责分子目录)
│   │   ├── feedback/          # 反馈组件 (EmptyState / LoadingDots / Skeleton)
│   │   ├── glass/             # 毛玻璃原子组件 (GlassButton / GlassPanel)
│   │   ├── layout/            # 布局组件 (Sidebar / LiquidToolbar)
│   │   ├── lesson/            # 课程渲染器 (LessonViewer)
│   │   └── terminal/          # xterm 终端封装 (Terminal / injectCommand)
│   ├── pages/                 # 页面级组件 (Home)
│   ├── lessons/               # 课程元数据 + Markdown 内容
│   │   ├── lessons.ts         # 22 篇课程 + 5 个 stage + 导航辅助函数
│   │   └── content.ts         # 课程正文 Markdown + runnableSnippets
│   ├── shells/                # Shell 模拟核心
│   │   ├── VirtualFS.ts       # 内存虚拟文件系统(FHS 目录树)
│   │   ├── shells.ts          # bash/zsh/fish 抽象 + tokenizer + 执行器
│   │   ├── commands.ts        # 内置命令注册表 `builtinCommands`
│   │   ├── interactive.ts     # InteractiveSession 抽象 + nano/vim/htop 工厂
│   │   └── types.ts           # 命令上下文类型 + ANSI 颜色工具
│   └── store/
│       └── useAppStore.ts     # 全局 Zustand store(主题 / 进度 / 笔记 / shell)
├── tests/
│   └── VirtualFS.test.ts      # vitest 单元测试(VirtualFS.mkdir)
├── index.html                 # Vite 入口 HTML
├── vite.config.ts             # Vite 配置(仅 react 插件)
├── tsconfig.json              # 项目引用根
├── tsconfig.app.json          # 应用代码 TS 配置(strict)
├── tsconfig.node.json         # Node 环境 TS 配置
├── tailwind.config.js         # Tailwind 主题(glass/accent/terminal + 动画 keyframes)
├── postcss.config.js          # PostCSS(tailwindcss + autoprefixer)
└── eslint.config.js           # Flat config(推荐规则集 + react-hooks + react-refresh)

3. 开发命令

所有命令在 linux-learning/ 目录下执行:

命令 用途
pnpm dev 启动 Vite 开发服务器(HMR)
pnpm build tsc -b 类型检查,再 vite build 打包到 dist/
pnpm lint 运行 ESLint 检查(eslint .
pnpm preview 本地预览 dist/ 构建产物
pnpm test 运行 vitest 测试
pnpm test:watch 监听模式下运行 vitest

注意: Vite 构建产物约 741KB JS + 28KB CSS(未分块),单 chunk 超过 500KB 警告属于正常,不需要额外配置。

CI/CD 流程

项目使用 GitHub Actions(.github/workflows/ci.yml)自动化质量检查:

  • 触发条件: push 到 main 分支或 PR 到 main
  • 环境: Ubuntu latest + Node.js 20 + pnpm 10
  • 流程: pnpm install --frozen-lockfilepnpm lintpnpm buildpnpm test

注意: CI 使用 --frozen-lockfile 确保依赖版本一致,本地开发也应保持 pnpm-lock.yaml 不变。


4. 编码规范

命名约定

  • 组件文件: PascalCase(如 Terminal.tsx),目录也用 PascalCase(如 components/terminal/
  • 工具/逻辑文件: camelCase(如 useAppStore.tsVirtualFS.tsshells.ts
  • 变量/函数: camelCase,英文(即使注释是中文)
  • 类型/接口: PascalCase(LessonMetaShellInfoExecResult
  • 常量: UPPER_SNAKE 仅限真正常量(如 PATH),普通配置对象用 camelCase
  • 导出: 组件和工具函数统一用 named exportexport function Foo()),避免默认导出;只有 App.tsx 用 default export

代码风格

  • 严格 TypeScript: 开启 noUnusedLocals / noUnusedParameters / noFallthroughCasesInSwitch / verbatimModuleSyntax / erasableSyntaxOnly
    • 类型导入必须用 import type { ... }
    • 不能使用 enum、namespace、参数属性erasableSyntaxOnly 限制)
  • React: 全部函数组件 + Hooks;不写 class 组件
  • 样式: 优先 Tailwind 工具类;玻璃拟态用自定义类(glass-softglassglass-strong);不要新建全局 CSS,能用 @apply 或工具类解决就用它们
  • 可访问性: 所有 icon-only 按钮必须有 aria-label;折叠展开按钮必须有 aria-expanded;模态面板必须有 role="dialog" aria-modal="true";优先级 focus-visible 焦点环需保持
  • 微动画: 用 tailwind.config.js 里的 animate-fade-in / animate-slide-up / animate-route-fade 等预定义动画;不要随意内联关键帧
  • 不可变性: Zustand 的 set 调用务必返回新对象/数组,不要直接 mutate(如已完成列表用 Array.from(new Set(...))
  • 注释: 文件头用中文一行说明用途;行内注释可用中文;变量/函数命名仍用英文
  • 错误处理: 不能省略;命令未找到统一返回 exitCode: 127 并打印提示

路径与导入

  • 不使用 path alias(@/ 之类),全部相对路径 ../../shells/...
  • verbatimModuleSyntax: true — 类型和值必须分开导入

5. 禁止改动的文件

文件/目录 原因
linux-learning/pnpm-lock.yaml 自动生成的锁文件,手动改会导致依赖错乱
linux-learning/node_modules/ 自动安装的依赖
linux-learning/dist/ Vite 构建产物(已 gitignore)
linux-learning/src/index.css 中的 @tailwind 指令段 改顺序会破坏产物;自定义样式追加在 @layer
linux-learning/.gitignore 仅在新增需忽略的产物/日志时可加
linux-learning/tailwind.config.jscontent 字段 错误配置会导致样式丢失
linux-learning/tsconfig*.json 的核心编译选项 改动前必须确认影响面

6. AI 注意事项(常见陷阱)

  1. 路由用 HashRouter,新增路由不要切换为 BrowserRouter — 项目是为静态托管(无服务端 fallback)设计的。
  2. Shell 模拟是基于 in-memory VirtualFS,刷新页面会重置文件系统;持久化只覆盖 theme / sidebar / lesson progress / notes(看 useAppStore.ts 的 persist 配置)。
  3. 新增内置命令必须:
    • src/shells/commands.tsbuiltinCommands 注册(key 为命令名)
    • 如有别名,加入同对象的 aliases 数组(shells.tsrunSingle 会查找)
    • 返回 { exitCode: number },输出通过 ctx.print() / ctx.printRaw() 写入
    • 交互式命令(全屏 TUI)则注册到 src/shells/interactive.tsinteractiveCommands,实现 InteractiveSession 接口;runSingle 会识别并返回 ExecResult.session 触发 Terminal 切换 fullscreen 模式
  4. 新增课程只需改 src/lessons/lessons.tslessons 数组(一并加上 stage),正文内容放 content.tsApp.tsx 路由 /lesson/:id 会自动匹配。注意:课程 ID 同时出现在 lessons.ts 元数据 + content.ts 的 commands 区 + content 区三处,修改时务必三处同步。
  5. 终端交互:在 Terminal.tsx 中已有历史 / Tab 补全 / 光标移动 / fullscreen 模式路由,改这块逻辑前先读完整个 ~500 行组件,避免重复造轮子或破坏现有 ref(inputRef / cursorPosRef / historyRef / sessionRef / modeRef 等)。
  6. verbatimModuleSyntax + erasableSyntaxOnly 双重约束下,import { Foo } 中若 Foo 只是类型会编译失败 — 必须改 import type { Foo };同理禁止 enum Foo { A } 写法。
  7. Tailwind 主题色板:所有 UI 颜色统一走 accent.* / glass.* / terminal.*,不要在 JSX 里写裸色值(如 #0a84ff),保持主题可统一替换。
  8. 持久化 storeresetProgress() 已封装在 store 里,调试时别直接 localStorage.removeItem('linux-lab-storage') 后忘了它属于 zustand persist。
  9. i18n:当前界面文案为中文,不要自己抽出 i18n 框架(项目没引入),保持中文内联字符串即可。
  10. 依赖引入:能复用现有库(xtermmarkeddompurifyzustandreact-router-dom)就复用,不要未经确认引入新依赖(除非用户明确要求)。
  11. emoji 使用:用户/AI 全局规则允许且项目本身大量使用 emoji(🌱 ⚡ 🐚 📜 🛠️ 🐟 ✏️ 等),可以放心使用。
  12. commit message:Conventional Commits 格式(feat: / fix: / refactor: / docs: 等),一次提交只做一件事。
  13. InteractiveSession 设计:所有全屏 TUI 命令必须实现 interactive.tsInteractiveSession 接口(render / onKey / isActive / finalize)。禁止在普通 CommandHandler 里用 ctx.print 模拟全屏 — 那会与 prompt 输出混在一起,破坏 Terminal 模式切换。
  14. Terminal 模式切换modeRef === 'fullscreen'handleKey 早期 return 拦截所有键事件路由到 sessionRef.current.onKey不要在 fullscreen 模式里写 prompt 处理逻辑,反之亦然。xterm.onResize 期间 session 需要重渲染时手动调 sessionRef.current.render()
  15. VirtualFS 写入副作用nano / vim 通过 fs.writeFile 持久化编辑内容;改 VirtualFS 行为前先跑 nano README.md + cat README.md 烟测。
  16. 空文件 / 不存在文件nano / vim 对不存在的文件会调 fs.writeFile(path, '') 先 touch 一份空文件再编辑;不要改成"只读"行为,否则 nano 会因 readFile 返回 null 而出错。
  17. 测试:只有 tests/VirtualFS.test.ts 一个测试文件;新增 Vitest 测试时参考其结构(describe / it / beforeEach / expect)。需运行测试时用 pnpm test