# AI Agent 开发指南 本文档为 AI Agent 开发本项目提供详细的规范和模板,帮助快速理解项目结构、技术栈和开发流程。 ## 项目概览 - **项目名称**: Thing - **技术栈**: Tauri 2 + Vue 3 + Vite + Pinia + shadcn-vue + Rust - **平台**: Windows - **包管理**: Bun ## 核心架构 ### 模块系统 项目采用统一的模块注册机制。每个模块通过 `src/modules//index.ts` 导出 `ModuleConfig` 配置,由 `src/modules/registry.ts` 中的 `moduleRegistry` 单例统一管理。 ``` src/modules// ├── index.ts # 模块配置(ModuleConfig) ├── ModuleComponent.vue # 前端组件 src-tauri/src/ # Rust 后端逻辑(按功能分文件/模块目录) ``` #### 模块配置类型 ```typescript // src/types/module.ts interface ModuleConfig { id: string // 唯一标识 name: string // 显示名称 icon: string // 图标标识(对应 icons.ts 中的 key) description: string // 模块描述 category: ModuleCategory // 分类:'network' | 'tool' | 'system' | 'media' defaultEnabled?: boolean // 默认是否启用 builtin?: boolean // 是否内置模块(不可禁用) loader?: () => Promise<{ default: Component }> // 懒加载 component?: Component // 直接组件引用(内置模块) searchItems?: SearchIndexItem[] // 全局搜索项 process?: ModuleProcessConfig // 进程配置(需要子进程的模块) lifecycle?: ModuleLifecycle // 生命周期钩子 order?: number // 排序权重 } ``` #### 注册新模块 1. 创建 `src/modules//index.ts`,导出 `moduleConfig` 2. 在 `src/modules/index.ts` 中添加导入 3. 在 `src/modules/icons.ts` 中添加图标映射 ### IPC 通信 前端通过 Tauri API 调用 Rust 命令: ```typescript import { invoke } from '@tauri-apps/api/core' // 调用 Rust 命令 const result = await invoke('module_command', { param: 'value' }) ``` ### 状态管理 使用 Pinia 管理全局状态,每个模块可拥有独立的 store: ```typescript // src/stores/moduleName.ts import { defineStore } from 'pinia' export const useModuleStore = defineStore('moduleName', { state: () => ({ // 状态定义 }), actions: { // 操作方法 } }) ``` ## 开发规范 ### 命名规范 | 类型 | 格式 | 示例 | |------|------|------| | 组件 | PascalCase | `ProxyManager.vue` | | 函数 | camelCase | `getSystemProxy()` | | 文件 | kebab-case | `proxy-manager.ts` | | 常量 | UPPER_CASE | `MAX_HISTORY_COUNT` | | Rust 模块 | snake_case | `proxy.rs` | | Tauri 命令 | snake_case | `get_system_proxy` | ### 代码风格 - **TypeScript**: 使用 ESLint,配置在 `.eslintrc.cjs` - **Rust**: 使用 `cargo fmt` 格式化 - **提交信息**: Conventional Commits 格式 ## 模块开发模板 ### 前端模块模板 创建 `src/modules//` 目录,包含以下文件: ```typescript // index.ts - 模块配置 import type { ModuleConfig } from '@/types/module' export const moduleConfig: ModuleConfig = { id: 'module-name', name: '模块名称', icon: 'module-name', // 需在 icons.ts 中添加映射 description: '模块描述', category: 'tool', // 'network' | 'tool' | 'system' | 'media' defaultEnabled: true, loader: () => import('./ModuleComponent.vue'), searchItems: [ { title: '功能名称', description: '功能描述', keywords: ['关键词1', '关键词2'] } ], order: 100 } ``` > 注册模块时,还需在 `src/modules/index.ts` 中添加导入,在 `src/modules/icons.ts` 中添加图标映射。 ```vue ``` ### Rust 命令模板 在 `src-tauri/src/` 中创建模块文件(如 `src-tauri/src/my_module.rs` 或 `src-tauri/src/my_module/` 目录),并在 `lib.rs` 中注册: ```rust // src-tauri/src/my_module.rs use serde::Serialize; use tauri::State; #[derive(Serialize)] pub struct CommandResult { pub success: bool, pub data: Option, pub error: Option, } #[tauri::command] pub async fn module_get_data() -> CommandResult> { match fetch_data() { Ok(data) => CommandResult { success: true, data: Some(data), error: None, }, Err(e) => CommandResult { success: false, data: None, error: Some(e.to_string()), }, } } #[tauri::command] pub async fn module_set_data(data: String) -> CommandResult { match save_data(&data) { Ok(_) => CommandResult { success: true, data: Some(true), error: None, }, Err(e) => CommandResult { success: false, data: None, error: Some(e.to_string()), }, } } fn fetch_data() -> Result, Box> { // 实现逻辑 Ok(vec![]) } fn save_data(data: &str) -> Result<(), Box> { // 实现逻辑 Ok(()) } ``` ### 注册 Rust 命令 在 `src-tauri/src/lib.rs` 中注册命令: ```rust // lib.rs mod my_module; use my_module::{module_get_data, module_set_data}; #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}!", name) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![ greet, module_get_data, module_set_data ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` ## 常用 Tauri 插件 项目当前使用的 Tauri 插件(见 `Cargo.toml`): | 插件 | 用途 | |------|------| | `tauri-plugin-autostart` | 开机自启 | | `tauri-plugin-opener` | 打开文件/目录/URL(绕过 IPC scope 限制) | | `tauri-plugin-dialog` | 文件/目录选择对话框 | > 系统托盘(tray)和窗口效果(mica/acrylic)是 Tauri 2 内置能力,无需额外插件。日志系统为自建(`src-tauri/src/logger.rs`),未使用 `tauri-plugin-log`。 ## 进程管理 对于需要管理外部子进程的模块(如 mihomo),使用内置的 `ProcessManager`。下载器模块使用进程内自建下载引擎(`src-tauri/src/download_engine/`),不涉及外部子进程: ### 架构 - **Rust 端** (`src-tauri/src/process_manager.rs`):`ProcessManager` 通过 `std::process::Command` 管理子进程,支持启动、停止、崩溃检测和自动重启 - **前端** (`src/stores/processStore.ts`):Pinia store,通过 `invoke` 调用 Rust 命令,通过 `listen` 接收进程状态变更事件 - **模块配置**:在模块的 `index.ts` 中通过 `ModuleProcessConfig` 声明进程信息 ### 模块配置 ```typescript // src/modules/proxy/index.ts export const moduleConfig: ModuleConfig = { // ... process: { name: 'mihomo', executable: '', // 运行时确定 args: ['-f', 'config.yaml'], autoStart: false, // 模块启用时是否自动启动 restartOnCrash: true, // 崩溃后自动重启 maxRestarts: 3 // 最大重启次数(0 = 不限制) } } ``` ### 前端 API ```typescript import { useProcessStore } from '@/stores/processStore' const processStore = useProcessStore() // 通过模块 ID 启动进程(自动读取模块配置) await processStore.startByModule('proxy') // 停止进程 await processStore.stopByModule('proxy') // 获取进程状态 const status = processStore.getProcessStatus('proxy') // 监听状态变更(在 main.ts 中已初始化) // processStore.initListener() ``` ### Rust 命令 | 命令 | 参数 | 返回值 | |------|------|--------| | `start_process` | `StartProcessParams` | `ProcessInfo` | | `stop_process` | `id: String` | `()` | | `get_process_status` | `id: String` | `Option` | | `get_all_process_status` | - | `Vec` | | `stop_all_processes` | - | `()` | ### 事件 | 事件名 | 载荷 | 触发时机 | |--------|------|----------| | `process-status-changed` | `ProcessInfo` | 进程状态变更(崩溃、重启、停止) | ## 数据存储 ### 配置文件 使用 JSON 格式存储在应用数据目录: ```rust // 获取应用数据目录 let app_dir = app.path().app_data_dir()?; let config_path = app_dir.join("config.json"); ``` ### SQLite 对于需要查询的数据(如剪贴板历史),使用 `rusqlite` crate: ```toml # Cargo.toml rusqlite = { version = "0.30", features = ["bundled"] } ``` ## 权限配置 在 `src-tauri/capabilities/default.json` 中配置权限(实际项目配置): ```json { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", "windows": ["main"], "permissions": [ "core:default", "opener:default", "opener:allow-reveal-item-in-dir", "opener:allow-open-path", "dialog:default", "core:window:allow-minimize", "core:window:allow-maximize", "core:window:allow-close", "core:window:allow-toggle-maximize", "core:window:allow-hide", "core:window:allow-show", "core:window:allow-set-focus", "core:window:allow-start-dragging", "core:window:allow-set-effects", "core:window:allow-set-background-color", "core:window:allow-set-theme" ] } ``` ## 调试指南 ### 前端调试 - 使用 Chrome DevTools:`Ctrl+Shift+I` - 打印日志:`console.log()` ### Rust 调试 - 使用 `println!()` 输出到终端 - 使用 `dbg!()` 宏调试变量 - 使用 Visual Studio Code 的 Rust 调试插件 ### 构建问题排查 1. **Rust 编译错误**:检查 `Cargo.toml` 依赖版本 2. **前端构建错误**:检查 `package.json` 依赖和 TypeScript 类型 3. **Tauri 配置错误**:检查 `tauri.conf.json` 路径和权限配置 ## 常见问题 ### Windows 路径问题 使用 `std::path::Path` 处理路径,避免硬编码分隔符: ```rust let path = Path::new("data").join("config.json"); ``` ### Tauri 2 权限问题 确保在 `capabilities` 中声明所需权限,否则命令调用会失败。 ### WebView2 兼容性 确保用户安装了 Microsoft Edge WebView2 Runtime: - 安装包:https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/ ## 开发流程 1. **创建模块目录**:`src/modules//`,编写组件和 `index.ts` 配置 2. **注册模块**:在 `src/modules/index.ts` 中添加导入,在 `src/modules/icons.ts` 中添加图标映射 3. **实现 Rust 命令**:在 `src-tauri/src/` 中编写后端逻辑,在 `lib.rs` 中注册命令 4. **配置权限**:更新 `capabilities/default.json`(如需要) 5. **测试**:运行 `bun run tauri dev` 测试 6. **构建**:运行 `bun run tauri build` 构建生产版本 ## 模板代码生成 ### 快速生成模块 使用以下命令创建新模块结构: ```bash # 创建前端模块目录 mkdir -p src/modules/new-module # 创建 Rust 模块文件 touch src-tauri/src/new_module.rs ``` ### 模板文件 复制以下模板快速开始: **前端组件模板**: ```vue ``` **Rust 命令模板**: ```rust #[tauri::command] pub async fn new_module_action() -> Result { // 实现逻辑 Ok(serde_json::json!({})) } ``` ## 注意事项 1. **避免阻塞主线程**:Rust 命令使用 `async` 避免阻塞 2. **资源清理**:在组件卸载时清理定时器和事件监听器 3. **错误处理**:所有操作都应有错误处理和用户提示 4. **安全考虑**:避免执行未验证的用户输入作为命令参数 5. **性能优化**:对于高频更新的数据使用 `watch` 和 `computed` ## 参考资源 - [Tauri 2 Documentation](https://v2.tauri.app/) - [Vue 3 Documentation](https://vuejs.org/) - [Pinia Documentation](https://pinia.vuejs.org/) - [shadcn-vue UI Documentation](https://shadcn-vue.com/docs) - [Rust Documentation](https://doc.rust-lang.org/)