Files
kintone-customize-manager/AGENTS.md
2026-03-12 11:03:47 +08:00

365 lines
8.2 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.

# AGENTS.md
Kintone Customize Manager 项目开发指南。
## 1. 构建命令
### 开发
```bash
# 启动开发服务器HMR + 热重载)
npm run dev
# 类型检查
npx tsc --noEmit
```
### 构建
```bash
# 构建生产版本
npm run build
# 打包应用
npm run package:win # Windows
npm run package:mac # macOS
npm run package:linux # Linux
```
### 代码质量
```bash
# ESLint 检查
npm run lint
# 格式化代码
npm run format
```
## 2. 项目结构
```
kintone-customize-manager/
├── src/
│ ├── main/ # Electron 主进程
│ │ ├── index.ts # 主进程入口
│ │ ├── ipc-handlers.ts # IPC 通信处理
│ │ ├── storage.ts # 文件系统操作
│ │ ├── kintone-api.ts # Kintone API 封装
│ │ ├── updater.ts # 自动更新逻辑
│ │ └── config.ts # 配置管理
│ ├── preload/ # Preload 脚本
│ │ └── index.ts # 暴露 API 到渲染进程
│ └── renderer/ # React 渲染进程
│ └── src/
│ ├── main.tsx # React 入口
│ ├── App.tsx # 根组件
│ ├── components/ # React 组件
│ ├── hooks/ # 自定义 Hooks
│ ├── stores/ # Zustand Stores
│ ├── utils/ # 工具函数
│ └── types/ # TypeScript 类型
├── resources/ # 应用资源(图标等)
└── build/ # 构建配置
```
## 3. 路径别名
| 别名 | 路径 |
|------|------|
| `@renderer/*` | `src/renderer/src/*` |
| `@main/*` | `src/main/*` |
| `@preload/*` | `src/preload/*` |
使用示例:
```typescript
import { useStore } from '@renderer/stores'
import { ipcHandler } from '@main/ipc-handlers'
```
## 4. 代码风格
### 导入顺序
```typescript
// 1. Node.js 内置模块
import { join } from 'path'
import { app, BrowserWindow } from 'electron'
// 2. 第三方库
import React, { useState, useEffect } from 'react'
import { Button, Layout } from 'antd'
// 3. 项目内部模块(使用别名)
import { useDomainStore } from '@renderer/stores'
import { formatDate } from '@renderer/utils'
// 4. 相对导入
import './styles.css'
```
### 命名规范
| 类型 | 规范 | 示例 |
|------|------|------|
| 组件文件 | PascalCase | `DomainManager.tsx` |
| 工具函数文件 | camelCase | `formatDate.ts` |
| Store 文件 | camelCase + Store | `domainStore.ts` |
| 类型文件 | camelCase | `types.ts` |
| 组件名 | PascalCase | `DomainManager` |
| 函数/变量 | camelCase | `handleSubmit` |
| 常量 | UPPER_SNAKE_CASE | `MAX_FILE_SIZE` |
| 类型/接口 | PascalCase | `DomainConfig` |
### TypeScript 规范
```typescript
// 显式类型定义
interface DomainConfig {
id: string
name: string
domain: string
username: string
authType: 'password' | 'api_token'
createdAt: string
}
// 函数返回类型
function createWindow(): void { }
// 异步函数
async function fetchDomains(): Promise<Domain[]> { }
// 避免使用 any使用 unknown 或具体类型
function parseResponse(data: unknown): DomainConfig {
// 类型守卫
if (typeof data !== 'object' || data === null) {
throw new Error('Invalid response')
}
return data as DomainConfig
}
```
### React 组件规范
```typescript
// 函数组件优先
interface DomainListProps {
domains: Domain[]
onSelect: (domain: Domain) => void
}
function DomainList({ domains, onSelect }: DomainListProps) {
const [selectedId, setSelectedId] = useState<string | null>(null)
// Hooks 放在组件顶部
const { token } = theme.useToken()
// 事件处理函数使用 useCallback
const handleClick = useCallback((domain: Domain) => {
setSelectedId(domain.id)
onSelect(domain)
}, [onSelect])
return (
<div>
{/* JSX */}
</div>
)
}
export default DomainList
```
### Zustand Store 规范
```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface DomainState {
domains: Domain[]
currentDomain: Domain | null
addDomain: (domain: Domain) => void
removeDomain: (id: string) => void
setCurrentDomain: (domain: Domain | null) => void
}
export const useDomainStore = create<DomainState>()(
persist(
(set) => ({
domains: [],
currentDomain: null,
addDomain: (domain) => set((state) => ({
domains: [...state.domains, domain]
})),
removeDomain: (id) => set((state) => ({
domains: state.domains.filter(d => d.id !== id)
})),
setCurrentDomain: (domain) => set({ currentDomain: domain })
}),
{ name: 'domain-storage' }
)
)
```
## 5. 错误处理
### 主进程错误处理
```typescript
// IPC 处理错误
ipcMain.handle('fetch-domains', async () => {
try {
const domains = await fetchDomainsFromApi()
return { success: true, data: domains }
} catch (error) {
console.error('Failed to fetch domains:', error)
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
}
}
})
```
### 渲染进程错误处理
```typescript
// 使用 Result 模式
type Result<T> =
| { success: true; data: T }
| { success: false; error: string }
async function handleFetch(): Promise<Result<Domain[]>> {
try {
const result = await window.api.fetchDomains()
if (!result.success) {
message.error(result.error)
}
return result
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error'
message.error(errorMsg)
return { success: false, error: errorMsg }
}
}
```
## 6. IPC 通信规范
### Preload 暴露 API
```typescript
// preload/index.ts
const api = {
// 使用 invoke 进行双向通信
fetchDomains: () => ipcRenderer.invoke('fetch-domains'),
// 使用 send 进行单向通信
notify: (message: string) => ipcRenderer.send('notify', message),
// 监听事件
onUpdate: (callback: (info: UpdateInfo) => void) =>
ipcRenderer.on('update-available', (_, info) => callback(info))
}
contextBridge.exposeInMainWorld('api', api)
```
### 类型定义
```typescript
// preload/index.d.ts
interface ElectronAPI {
fetchDomains: () => Promise<Result<Domain[]>>
notify: (message: string) => void
onUpdate: (callback: (info: UpdateInfo) => void) => void
}
declare global {
interface Window {
electron: ElectronAPI
api: ElectronAPI
}
}
```
## 7. UI 组件规范
### 使用 LobeHub UI + Ant Design
```typescript
// 使用 antd-style 进行样式
import { createStyles } from 'antd-style'
const useStyles = createStyles(({ token, css }) => ({
container: css`
padding: ${token.paddingLG}px;
background: ${token.colorBgContainer};
border-radius: ${token.borderRadiusLG}px;
`
}))
function MyComponent() {
const { styles } = useStyles()
return <div className={styles.container}>...</div>
}
```
### 国际化
```typescript
// 使用中文默认
import zhCN from 'antd/locale/zh_CN'
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
```
## 8. 安全规范
### 密码存储
```typescript
// 使用 safeStorage 加密存储
import { safeStorage } from 'electron'
// 加密
const encrypted = safeStorage.encryptString(password)
// 解密
const decrypted = safeStorage.decryptString(encrypted)
```
### CSP 配置
```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;" />
```
## 9. fnm 环境配置
所有 npm/npx 命令需要先加载 fnm 环境:
```bash
# 方式一:使用 wrapper 脚本
~/.config/opencode/node-fnm-wrapper.sh npm run dev
# 方式二:手动加载
eval "$(fnm env --use-on-cd)" && npm run dev
```
## 10. 注意事项
1. **ESM Only**: LobeHub UI 仅支持 ESM确保 `tsconfig.json``"module": "ESNext"`
2. **React 19**: 必须使用 `@types/react@^19.0.0``@types/react-dom@^19.0.0`
3. **CSS 方案**: 使用 `antd-style`,不使用 Tailwind CSS
4. **Context Isolation**: 必须启用 `contextIsolation: true`
5. **禁止类型断言**: 避免使用 `as any`,优先使用类型守卫