# 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 { } // 避免使用 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(null) // Hooks 放在组件顶部 const { token } = theme.useToken() // 事件处理函数使用 useCallback const handleClick = useCallback((domain: Domain) => { setSelectedId(domain.id) onSelect(domain) }, [onSelect]) return (
{/* JSX */}
) } 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()( 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 = | { success: true; data: T } | { success: false; error: string } async function handleFetch(): Promise> { 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> 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
...
} ``` ### 国际化 ```typescript // 使用中文默认 import zhCN from 'antd/locale/zh_CN' ``` ## 8. 安全规范 ### 密码存储 ```typescript // 使用 safeStorage 加密存储 import { safeStorage } from 'electron' // 加密 const encrypted = safeStorage.encryptString(password) // 解密 const decrypted = safeStorage.decryptString(encrypted) ``` ### CSP 配置 ```html ``` ## 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`,优先使用类型守卫