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

8.2 KiB
Raw Blame History

AGENTS.md

Kintone Customize Manager 项目开发指南。

1. 构建命令

开发

# 启动开发服务器HMR + 热重载)
npm run dev

# 类型检查
npx tsc --noEmit

构建

# 构建生产版本
npm run build

# 打包应用
npm run package:win    # Windows
npm run package:mac    # macOS
npm run package:linux  # Linux

代码质量

# 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/*

使用示例:

import { useStore } from '@renderer/stores'
import { ipcHandler } from '@main/ipc-handlers'

4. 代码风格

导入顺序

// 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 规范

// 显式类型定义
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 组件规范

// 函数组件优先
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 规范

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. 错误处理

主进程错误处理

// 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' 
    }
  }
})

渲染进程错误处理

// 使用 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

// 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)

类型定义

// 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

// 使用 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>
}

国际化

// 使用中文默认
import zhCN from 'antd/locale/zh_CN'

<ConfigProvider locale={zhCN}>
  <App />
</ConfigProvider>

8. 安全规范

密码存储

// 使用 safeStorage 加密存储
import { safeStorage } from 'electron'

// 加密
const encrypted = safeStorage.encryptString(password)

// 解密
const decrypted = safeStorage.decryptString(encrypted)

CSP 配置

<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 环境:

# 方式一:使用 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,优先使用类型守卫