返回 oh-my-ppt
CLAUDE.md
根目录 / CLAUDE.md
1 # CLAUDE.md
2
3 ## Project
4
5 Oh My PPT — 本地优先的 AI 幻灯片生成与编辑工具。Electron + React + TypeScript。
6
7 ## Commands
8
9 ```bash
10 pnpm dev # 开发
11 pnpm build # 不要跑构建
12 pnpm run typecheck:node # 跑 node 类型检查
13 pnpm run typecheck:web # 跑 renderer 类型检查
14 pnpm typecheck # 跑类型检查
15 pnpm lint # 不要跑ESLint
16 pnpm format # 不要跑Prettier
17 ```
18
19 > 不要跑 `npm run lint` / `npm run build`。
20
21 ## Code Style
22
23 - Prettier: `singleQuote`, `no semi`, `printWidth: 100`, `trailingComma: none`
24 - 路径别名: `@shared/*` → `src/shared/*`, `@renderer/*` → `src/renderer/src/*`
25
26 ## Execution Rules
27
28 - 先判断问题落在哪条链路:生成、编辑、导入、导出或运行时;不要只修当前可见入口
29 - 公共规则变更要确认生成和编辑都覆盖,包括整页编辑、deck 编辑、selector 编辑
30 - 改 runtime asset 时,同步检查 session asset 兼容/刷新机制
31 - 修 bug 优先补最小定向回归测试,必要时覆盖相邻入口
32 - 验证时跑最相关的最小测试集;不要跑 `npm run lint` 或 `npm run build`
33
34 ## Testing
35
36 - 框架:Vitest + happy-dom,测试文件放 `tests/unit/` 下,按功能域分子目录,文件名 `*.test.ts`
37 - 跑测试:`pnpm test`,跑单个文件:`pnpm test -- tests/unit/xxx/foo.test.ts`
38 - 修 bug 或加功能时,必须补对应测试到 `tests/unit/`;测试不通过就继续修代码直到通过
39 - 注意:样式ui改动不需要写测试
40
41 ## React 组件编写规范
42
43 ### 核心原则
44
45 #### 1. 逻辑内聚,少传 props
46 - **能写在组件内的逻辑就写在组件内**,不要通过 props 从父组件传进来
47 - 事件处理、数据获取、状态管理,都优先写在组件自己里面
48
49 ```jsx
50 // ✅ 好
51 function ProductCard({ id }) {
52 const [count, setCount] = useState(0)
53 const handleBuy = () => { /* 逻辑写这里 */ }
54 return <button onClick={handleBuy}>购买</button>
55 }
56
57 // ❌ 坏
58 function ProductCard({ count, onBuy }) { /* 逻辑都从外面传 */ }
59 ```
60
61 #### 2. 跨组件状态用 Zustand
62 - 多个组件需要共享的数据 → 放 zustand store
63 - 不要通过 props 一层层传
64
65 ```jsx
66 const useStore = create((set) => ({
67 user: null,
68 setUser: (user) => set({ user })
69 }))
70
71 // 任何组件直接拿来用,不用传 props
72 const user = useStore(state => state.user)
73 ```
74
75 #### 3. 复用逻辑抽成自定义 Hook
76 - 多个组件都需要**相同的有状态逻辑**时,抽成自定义 Hook
77 - Hook 放在 `hooks/` 目录下,以 `use` 开头
78
79 ```jsx
80 // hooks/useProductData.js
81 function useProductData(productId) {
82 const [product, setProduct] = useState(null)
83 const [loading, setLoading] = useState(false)
84
85 useEffect(() => {
86 fetchProduct(productId).then(setProduct)
87 }, [productId])
88
89 return { product, loading }
90 }
91
92 // 组件中使用
93 function ProductCard({ id }) {
94 const { product, loading } = useProductData(id)
95 // 不用从 props 传 product 和 loading
96 }
97 ```
98
99 #### 4. 什么情况才用 props?
100 只传这两类东西:
101 - **配置项**:`size`, `disabled`, `variant`
102 - **纯展示数据**:`title`, `description`
103
104 ## 简单检查
105 写代码前问一句:*"这个逻辑/状态能不能直接写在当前组件里?"*
106 - 能 → 就写里面
107 - 不能,但多个组件都需要 → 放 zustand 或抽成自定义 Hook
108 - 实在不行 → 才传 props
109
110 ## 记住
111 **组件要自己管自己,别当父组件的提线木偶。**
112
113
113 lines MARKDOWN