Files
robot-shanghai/CLAUDE.md
T
2026-09-08 14:19:44 +08:00

188 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
本文件记录 Claude Code 在本仓库中开发、创建文件和修改代码时必须遵守的项目规范。规范以当前仓库的代码、配置和 README 为准;如配置发生变化,优先同步更新本文件。
## 项目概览
- 技术栈:React 19、TypeScript、Vite 8、React Router 7、Tailwind CSS 4、Zustand。
- 包管理与脚本:使用 Bun。
- 代码检查与格式化:使用 Biome。
- 提交前检查:Husky + lint-staged。
- 运行环境:易班 H5 课程模板,包含 videoajax.js 封装、1920 x 1080 整体缩放、横屏访问提示和移动端 vconsole 调试。
## 常用命令
以 [package.json](package.json) 为准:
```bash
bun dev # 启动开发服务器
bun run build # 类型检查并构建生产版本
bun run preview # 预览生产构建
bun run lint # 代码检查
bun run lint:fix # 自动修复代码问题
```
修改代码后,至少运行 `bun run lint`。如果改动影响路由、构建配置、页面结构、类型系统或模板能力,还应运行 `bun run build`。
## 代码风格
以 [biome.json](biome.json) 为准:
- 使用 Biome 进行 JS/TS/JSON 等文件的格式化与检查。
- 使用 tab 缩进。
- JavaScript/TypeScript 字符串使用双引号。
- 导入整理交给 Biome 的 organizeImports。
- 当前 CSS linter/formatter 关闭,不要擅自引入新的 CSS 格式化标准。
- 写代码时匹配周围代码的命名、注释密度和组织方式,不为了“统一”改写无关文件。
## 文件创建与目录归属
新增文件前先判断职责归属,优先沿用现有目录结构:
- `src/components/ui/`:无课程业务和全局状态依赖的基础 UI copied source。
- `src/components/common/`:模板通用组件和适配器;其中 `GuideDialog`、`GuideSpotlight` 负责连接 Zustand、CourseCanvas 与交互内核。新增通用组件后,按需在 `src/components/common/index.ts` 维护导出。
- `src/components/business/`:业务组合组件,不放通用基础能力。
- `src/course/canvas/`:设计稿画布、缩放、viewport 和课程 Portal 根节点。
- `src/course/orientation/`:横竖屏访问控制。
- `src/course/interactions/`:由 CLI 复制的受控课程交互内核,不依赖具体页面、Store 或 common 适配器。
- `src/pages/<PageName>/`:页面组件。页面目录入口使用 `index.tsx`,页面组件使用默认导出。
- 模块内私有组件:仅本模块使用、其他模块用不到的,放到本模块目录的 `components/` 子目录(没有就创建);被多个模块或组件复用才下沉到 `src/components/common/`(通用基础能力)或 `business/`(业务组合),按组件核心类型区分。该规则约束新代码,存量平铺在页面目录下的模块内组件不强制迁移。
- `src/router/index.tsx`:集中维护路由配置。新增页面优先复用 `lazyRoute` 和 `RouteFallback`。
- `src/stores/`:Zustand 状态管理。
- `src/utils/`:通用工具、hooks 或平台封装。
- `src/types/`:只放跨多个模块复用的全局共享类型。
- `src/styles/`:全局样式和重置样式。
### 类型放置规则
组件、页面、store、API 的对外 `props` 与跨文件共享的 `interface`、`type` 放在 `.types.ts`;文件内私有的 helper 类型留在实现文件即可:
- 组件类型放在同组件目录的 `组件名.types.ts`,例如 `Button/Button.types.ts`。
- 页面类型如确实需要,放在页面目录的 `PageName.types.ts`。
- store 类型统一放在 `src/stores/types/*.types.ts`,例如 `src/stores/types/user.types.ts`。
- API 模块类型放在对应模块的 `*.types.ts`,例如 `src/api/client.types.ts`。
- 只有跨多个页面、组件、store、utils 复用的全局共享类型,才放入 `src/types/`。
- 不要把组件 props、store slice state 或组件内部布局类型放入 `src/types/`,避免全局类型目录变成杂物目录。
## Tailwind、cn 与样式提取规则
项目已有 [src/lib/cn.ts](src/lib/cn.ts) 中的 `cn`,用于合并 Tailwind class 并处理冲突([src/utils/tailwind.ts](src/utils/tailwind.ts) 只是 re-export)。使用时遵守以下边界。
### 默认策略
- 短的静态 Tailwind class 可以直接写在 JSX 的 `className` 中。
- 不为了“看起来统一”强行把所有 class 都包进 `cn`。
- 不为了少量 class 新增多层封装、过度抽象或大量碎片常量。
### 什么时候使用 cn
在以下场景使用 `cn`:
- 需要合并外部传入的 `className`。
- 条件 class 需要和其他 class 合并。
- 需要通过 `tailwind-merge` 解决 Tailwind 冲突合并。
- 需要组合 CSS Module、固定 class、条件 class 或外部 `className`。
示例:
```tsx
<div className={cn(styles.panel, active && "opacity-100", className)} />
```
### 什么时候不使用 cn
以下场景不要使用 `cn`:
```tsx
<div className="flex items-center justify-center" />
<div className={active ? "h-2 w-8 bg-current" : "h-2 w-2 bg-current/20"} />
```
单个固定字符串,或在两个完整字符串之间二选一的三元且不需要再合并其他 class 时,直接写 `className`。
### 什么时候提取样式
只有在以下情况才提取 class:
- Tailwind 字符串过长,导致 JSX 难读。
- 同一组 class 在同一文件或多个相近组件中重复出现。
- 条件组合已经让 `cn(...)` 变得很长。
- 提取后能更清楚表达布局或业务意图。
- Tailwind 直接实现过于麻烦、复杂或冗余,此时优先考虑 CSS Module、已有组件 API 或更简单的结构。
提取位置:
- 组件/页面私有样式常量优先放在当前文件内。
- 同目录多个文件共享时,可放同目录局部文件。
- 跨组件复用的样式能力,再考虑沉淀为通用组件、工具或 CSS Module。
### 组件与 CSS 类的边界
「何时提取 class」见上一节;本节只解决复用一段 UI 时该抽组件还是抽 CSS 类。
组件/页面文件超过 500 行时,先审视是否该拆分(这是触发思考的信号,不是强制拆分线);拆出的子组件按「文件创建与目录归属」放置。
重复出现时优先抽组件,CSS 类是兜底,但避免「为整洁而抽」。抽组件必须同时满足:
- 同一组「标记 + 样式 + 行为」在 2 处以上真实重复(第一次出现不抽)。
- 组件有清晰可命名的语义,能一句话说清它是什么。
- 复用的是整体而非几个 class;props 少且稳定(一般 ≤3~4 个)。
以下情况改用 CSS 类或局部常量,而不是抽组件:
- 只复用样式、不复用标记/行为,内容由数据或富文本决定。
- 仅同一文件内的小重复。
- 给第三方/框架等无法插入组件的位置加样式。
- Tailwind 表达不了或 class 串过长,但元素本身不够独立语义。
反模式(不要抽):只被用一次仅为缩短 JSX;组件 props 塞满 className/style 覆盖;名字说不清职责(`CardTopSection`、`Wrapper` 之类)。
## 动画使用规则
项目使用 CSS/Tailwind 和 GSAP 时遵守以下边界:
- 普通状态过渡、hover、active、简单淡入淡出及单元素入场默认使用 CSS/Tailwind。
- 只有动画需要时间轴编排、暂停/继续/倒放、运行时动态计算、音视频同步或复杂路径时,才使用 GSAP。
- 不因项目已经安装 GSAP 而重写现有 CSS 动画,也不为简单动画建立 GSAP preset 或通用封装。
- CSS 与 GSAP 不要在同一交互周期内控制同一元素的同一属性,尤其是 `transform` 和 `opacity`。
- GSAP 动画通过 `src/utils/motion.ts` 使用 `useGSAP` 管理生命周期,并处理 `prefers-reduced-motion` 降级。
- 不使用 GSAP 修改 `CourseCanvas`、画布 portal 根节点或 dnd-kit 正在控制的 `transform`。
## 导入与导出
- `@` 指向 `src`,新增代码优先使用 `@` 避免过长相对路径。
- 同目录内局部文件可以继续使用相对路径。
- 保持现有 barrel export 风格:通用组件、utils、stores 等需要对外复用时,再维护对应 `index.ts`。
- 不要为了导出方便把不该公开的内部实现暴露出去。
## 路由与页面
- 路由集中在 [src/router/index.tsx](src/router/index.tsx)。
- 新页面优先使用目录入口懒加载,例如 `lazyRoute(() => import("../pages/About"))`。
- 页面组件使用默认导出。
- 新增路由时同步考虑 `HydrateFallback: RouteFallback`。
- 不要绕开现有 hash router 和懒加载模式,除非用户明确要求调整路由架构。
## 模板能力约束
开发时要尊重模板已有能力:
- 页面按 `1920 x 1080` 设计稿开发,整体缩放由 `src/course/canvas` 的 `CourseCanvas` 组件承担。
- 横屏访问提示由 `src/course/orientation` 的 `OrientationGuard` 组件承担。
- 易班能力通过 `src/utils/yiban.ts` 和 `public/videoajax/` 相关脚本封装。
- 移动端调试使用 `src/utils/vconsole.ts`。
- 图片按钮由 `src/components/common/ImageButton` 提供;视频播放与视频弹层由 `src/components/common/Video` 提供。
- 拖拽交互由 `src/course/interactions/drag-drop` 提供(基于 dnd-kit)。
- 音频能力由 `src/utils/audio` 提供(`GameAudioProvider` / `useGameAudio` / `AudioManager`)。
- GSAP 复杂动画通过 `src/utils/motion.ts` 统一引入,边界见「动画使用规则」。
- 新增通用组件时,同步考虑是否需要在 [src/pages/Components/index.tsx](src/pages/Components/index.tsx) 增加示例。
## Claude Code 工作准则
- 编辑前先阅读相关文件,查找现有模式和可复用实现。
- 优先复用已有组件、工具、store 和样式方案。
- 不随意替换技术栈、引入新依赖或新增全局抽象。
- 新建文件时先判断目录归属、类型放置位置、是否需要导出、是否需要示例页。
- 修改配置、路由、构建、全局样式等影响范围较大的内容前,应确认需求明确。
- 不绕过 lint/build 结果;如果校验失败,必须如实报告失败命令、错误摘要和影响范围。