@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.355

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.agents/skills/add-provider-doc/SKILL.md +90 -0
  2. package/.agents/skills/add-setting-env/SKILL.md +106 -0
  3. package/.agents/skills/debug/SKILL.md +66 -0
  4. package/.agents/skills/desktop/SKILL.md +78 -0
  5. package/.agents/skills/desktop/references/feature-implementation.md +99 -0
  6. package/.agents/skills/desktop/references/local-tools.md +133 -0
  7. package/.agents/skills/desktop/references/menu-config.md +103 -0
  8. package/.agents/skills/desktop/references/window-management.md +143 -0
  9. package/.agents/skills/drizzle/SKILL.md +129 -0
  10. package/.agents/skills/drizzle/references/db-migrations.md +50 -0
  11. package/.agents/skills/hotkey/SKILL.md +90 -0
  12. package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
  13. package/.agents/skills/linear/SKILL.md +51 -0
  14. package/.agents/skills/microcopy/SKILL.md +83 -0
  15. package/.agents/skills/modal/SKILL.md +102 -0
  16. package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
  17. package/.agents/skills/react/SKILL.md +73 -0
  18. package/.agents/skills/react/references/layout-kit.md +100 -0
  19. package/.agents/skills/recent-data/SKILL.md +108 -0
  20. package/.agents/skills/testing/SKILL.md +89 -0
  21. package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
  22. package/.agents/skills/testing/references/db-model-test.md +124 -0
  23. package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
  24. package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
  25. package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
  26. package/.agents/skills/typescript/SKILL.md +52 -0
  27. package/.agents/skills/zustand/SKILL.md +78 -0
  28. package/.agents/skills/zustand/references/action-patterns.md +125 -0
  29. package/.agents/skills/zustand/references/slice-organization.md +125 -0
  30. package/AGENTS.md +42 -55
  31. package/CHANGELOG.md +60 -0
  32. package/CLAUDE.md +57 -46
  33. package/GEMINI.md +47 -39
  34. package/changelog/v1.json +18 -0
  35. package/locales/en-US/plugin.json +3 -0
  36. package/locales/zh-CN/plugin.json +3 -0
  37. package/package.json +1 -1
  38. package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +3 -11
  39. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +0 -13
  40. package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +0 -25
  41. package/packages/database/src/models/__tests__/topics/topic.create.test.ts +3 -3
  42. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
  43. package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +4 -0
  44. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
  45. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +1 -1
  46. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/InboxItem.tsx +19 -29
  47. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/List.tsx +1 -1
  48. package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +1 -1
  49. package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
  50. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
  51. package/src/libs/pdfjs/index.tsx +25 -0
  52. package/src/locales/default/plugin.ts +3 -0
  53. package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +0 -25
  54. package/src/services/chat/chat.test.ts +19 -19
  55. package/src/services/chat/index.ts +8 -3
  56. package/src/services/chat/mecha/agentConfigResolver.test.ts +72 -55
  57. package/src/services/chat/mecha/agentConfigResolver.ts +28 -4
  58. package/src/services/chat/mecha/contextEngineering.test.ts +21 -14
  59. package/src/services/chat/mecha/contextEngineering.ts +12 -0
  60. package/src/services/chat/types.ts +7 -1
  61. package/src/store/chat/agents/createAgentExecutors.ts +15 -4
  62. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +1 -0
  63. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
  64. package/src/store/test-coverage.md +5 -5
  65. package/.cursor/rules/add-provider-doc.mdc +0 -183
  66. package/.cursor/rules/add-setting-env.mdc +0 -175
  67. package/.cursor/rules/cursor-rules.mdc +0 -28
  68. package/.cursor/rules/db-migrations.mdc +0 -46
  69. package/.cursor/rules/debug-usage.mdc +0 -86
  70. package/.cursor/rules/desktop-controller-tests.mdc +0 -189
  71. package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
  72. package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
  73. package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
  74. package/.cursor/rules/desktop-window-management.mdc +0 -301
  75. package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
  76. package/.cursor/rules/hotkey.mdc +0 -162
  77. package/.cursor/rules/linear.mdc +0 -53
  78. package/.cursor/rules/microcopy-cn.mdc +0 -158
  79. package/.cursor/rules/microcopy-en.mdc +0 -148
  80. package/.cursor/rules/modal-imperative.mdc +0 -162
  81. package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
  82. package/.cursor/rules/project-introduce.mdc +0 -36
  83. package/.cursor/rules/react.mdc +0 -169
  84. package/.cursor/rules/recent-data-usage.mdc +0 -139
  85. package/.cursor/rules/rules-index.mdc +0 -44
  86. package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
  87. package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
  88. package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
  89. package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
  90. package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
  91. package/.cursor/rules/typescript.mdc +0 -55
  92. package/.cursor/rules/zustand-action-patterns.mdc +0 -328
  93. package/.cursor/rules/zustand-slice-organization.mdc +0 -308
  94. package/src/libs/pdfjs/pdf.worker.ts +0 -1
  95. package/src/libs/pdfjs/worker.ts +0 -12
  96. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
  97. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
  98. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
  99. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
  100. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
  101. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
  102. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
  103. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
  104. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
  105. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
  106. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
  107. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
  108. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
  109. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
  110. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
  111. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
  112. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
  113. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
  114. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
  115. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
  116. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
  117. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
  118. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
  119. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
  120. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
  121. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
  122. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
  123. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
  124. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
  125. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
  126. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
  127. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
  128. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
  129. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
  130. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
  131. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
  132. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
  133. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
  134. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
  135. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
  136. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
  137. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
  138. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
  139. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
  140. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
  141. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
  142. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
  143. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
  144. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
@@ -1,189 +0,0 @@
1
- ---
2
- description: 桌面端测试
3
- globs:
4
- alwaysApply: false
5
- ---
6
-
7
- # 桌面端控制器单元测试指南
8
-
9
- ## 测试框架与目录结构
10
-
11
- LobeChat 桌面端使用 Vitest 作为测试框架。控制器的单元测试应放置在对应控制器文件同级的 `__tests__` 目录下,并以原控制器文件名加 `.test.ts` 作为文件名。
12
-
13
- ```plaintext
14
- apps/desktop/src/main/controllers/
15
- ├── __tests__/
16
- │ ├── index.test.ts
17
- │ ├── MenuCtr.test.ts
18
- │ └── ...
19
- ├── McpCtr.ts
20
- ├── MenuCtr.ts
21
- └── ...
22
- ```
23
-
24
- ## 测试文件基本结构
25
-
26
- ```typescript
27
- import { beforeEach, describe, expect, it, vi } from 'vitest';
28
-
29
- import type { App } from '@/core/App';
30
-
31
- import YourController from '../YourControllerName';
32
-
33
- // 模拟依赖
34
- vi.mock('依赖模块', () => ({
35
- 依赖函数: vi.fn(),
36
- }));
37
-
38
- // 模拟 App 实例
39
- const mockApp = {
40
- // 按需模拟必要的 App 属性和方法
41
- } as unknown as App;
42
-
43
- describe('YourController', () => {
44
- let controller: YourController;
45
-
46
- beforeEach(() => {
47
- vi.clearAllMocks();
48
- controller = new YourController(mockApp);
49
- });
50
-
51
- describe('方法名', () => {
52
- it('测试场景描述', async () => {
53
- // 准备测试数据
54
-
55
- // 执行被测方法
56
- const result = await controller.方法名(参数);
57
-
58
- // 验证结果
59
- expect(result).toMatchObject(预期结果);
60
- });
61
- });
62
- });
63
- ```
64
-
65
- ## 模拟外部依赖
66
-
67
- ### 模拟模块函数
68
-
69
- ```typescript
70
- const mockFunction = vi.fn();
71
-
72
- vi.mock('module-name', () => ({
73
- functionName: mockFunction,
74
- }));
75
- ```
76
-
77
- ### 模拟 Node.js 核心模块
78
-
79
- 例如模拟 `child_process.exec` 和 `util.promisify`:
80
-
81
- ```typescript
82
- // 存储模拟的 exec 实现
83
- const mockExecImpl = vi.fn();
84
-
85
- // 模拟 child_process.exec
86
- vi.mock('child_process', () => ({
87
- exec: vi.fn((cmd, callback) => {
88
- return mockExecImpl(cmd, callback);
89
- }),
90
- }));
91
-
92
- // 模拟 util.promisify
93
- vi.mock('util', () => ({
94
- promisify: vi.fn((fn) => {
95
- return async (cmd: string) => {
96
- return new Promise((resolve, reject) => {
97
- mockExecImpl(cmd, (error: Error | null, result: any) => {
98
- if (error) reject(error);
99
- else resolve(result);
100
- });
101
- });
102
- };
103
- }),
104
- }));
105
- ```
106
-
107
- ## 编写有效的测试用例
108
-
109
- ### 测试分类
110
-
111
- 将测试用例分为不同类别,每个类别测试一个特定场景:
112
-
113
- ```typescript
114
- // 成功场景
115
- it('应该成功完成操作', async () => {});
116
-
117
- // 边界条件
118
- it('应该处理边界情况', async () => {});
119
-
120
- // 错误处理
121
- it('应该优雅地处理错误', async () => {});
122
- ```
123
-
124
- ### 设置测试数据
125
-
126
- ```typescript
127
- // 模拟返回值
128
- mockExecImpl.mockImplementation((cmd: string, callback: any) => {
129
- if (cmd === '命令') {
130
- callback(null, { stdout: '成功输出' });
131
- } else {
132
- callback(new Error('错误信息'), null);
133
- }
134
- });
135
- ```
136
-
137
- ### 断言
138
-
139
- 使用 Vitest 的断言函数验证结果:
140
-
141
- ```typescript
142
- // 检查基本值
143
- expect(result.success).toBe(true);
144
-
145
- // 检查对象部分匹配
146
- expect(result.data).toMatchObject({
147
- key: 'value',
148
- });
149
-
150
- // 检查数组
151
- expect(result.items).toHaveLength(2);
152
- expect(result.items[0].name).toBe('expectedName');
153
-
154
- // 检查函数调用
155
- expect(mockFunction).toHaveBeenCalledWith(expectedArgs);
156
- expect(mockFunction).toHaveBeenCalledTimes(1);
157
- ```
158
-
159
- ## 最佳实践
160
-
161
- 1. **隔离测试**:确保每个测试互不影响,使用 `beforeEach` 重置模拟和状态
162
- 2. **全面覆盖**:测试正常流程、边界条件和错误处理
163
- 3. **清晰命名**:测试名称应清晰描述测试内容和预期结果
164
- 4. **避免测试实现细节**:测试应该关注行为而非实现细节,使代码重构不会破坏测试
165
- 5. **模拟外部依赖**:使用 `vi.mock()` 模拟所有外部依赖,减少测试的不确定性
166
-
167
- ## 示例:测试 IPC 事件处理方法
168
-
169
- ```typescript
170
- it('应该正确处理 IPC 事件', async () => {
171
- // 模拟依赖
172
- mockSomething.mockReturnValue({ result: 'success' });
173
-
174
- // 调用 IPC 方法
175
- const result = await controller.ipcMethodName({
176
- param1: 'value1',
177
- param2: 'value2',
178
- });
179
-
180
- // 验证结果
181
- expect(result).toEqual({
182
- success: true,
183
- data: { result: 'success' },
184
- });
185
-
186
- // 验证依赖调用
187
- expect(mockSomething).toHaveBeenCalledWith('value1', 'value2');
188
- });
189
- ```
@@ -1,155 +0,0 @@
1
- ---
2
- description: 当要做 electron 相关工作时
3
- globs:
4
- alwaysApply: false
5
- ---
6
-
7
- # 桌面端新功能实现指南
8
-
9
- ## 桌面端应用架构概述
10
-
11
- LobeChat 桌面端基于 Electron 框架构建,采用主进程-渲染进程架构:
12
-
13
- 1. **主进程 (Main Process)**:
14
- - 位置:`apps/desktop/src/main`
15
- - 职责:控制应用生命周期、系统API交互、窗口管理、后台服务
16
-
17
- 2. **渲染进程 (Renderer Process)**:
18
- - 复用 Web 端代码,位于 `src` 目录
19
- - 通过 IPC 与主进程通信
20
-
21
- 3. **预加载脚本 (Preload)**:
22
- - 位置:`apps/desktop/src/preload`
23
- - 职责:安全地暴露主进程功能给渲染进程
24
-
25
- ## 添加新桌面端功能流程
26
-
27
- ### 1. 确定功能需求与设计
28
-
29
- 首先确定新功能的需求和设计,包括:
30
-
31
- - 功能描述和用例
32
- - 是否需要系统级API(如文件系统、网络等)
33
- - UI/UX设计(如必要)
34
- - 与现有功能的交互方式
35
-
36
- ### 2. 在主进程中实现核心功能
37
-
38
- 1. **创建控制器 (Controller)**
39
- - 位置:`apps/desktop/src/main/controllers/`
40
- - 示例:创建 `NewFeatureCtr.ts`
41
- - 需继承 `ControllerModule`,并设置 `static readonly groupName`(例如 `static override readonly groupName = 'newFeature';`)
42
- - 按 `_template.ts` 模板格式实现,并在 `apps/desktop/src/main/controllers/registry.ts` 的 `controllerIpcConstructors` 中注册,保证类型推导与自动装配
43
-
44
- 2. **定义 IPC 事件处理器**
45
- - 使用 `@IpcMethod()` 装饰器暴露渲染进程可访问的通道
46
- - 通道名称基于 `groupName.methodName` 自动生成,不再手动拼接字符串
47
- - 处理函数可通过 `getIpcContext()` 获取 `sender`、`event` 等上下文信息,并按照需要返回结构化结果
48
-
49
- 3. **实现业务逻辑**
50
- - 可能需要调用 Electron API 或 Node.js 原生模块
51
- - 对于复杂功能,可以创建专门的服务类 (`services/`)
52
-
53
- ### 3. 定义 IPC 通信类型
54
-
55
- 1. **在共享类型定义中添加新类型**
56
- - 位置:`packages/electron-client-ipc/src/types.ts`
57
- - 添加参数类型接口(如 `NewFeatureParams`)
58
- - 添加返回结果类型接口(如 `NewFeatureResult`)
59
-
60
- ### 4. 在渲染进程实现前端功能
61
-
62
- 1. **创建服务层**
63
- - 位置:`src/services/electron/`
64
- - 添加服务方法调用 IPC
65
- - 使用 `ensureElectronIpc()` 生成的类型安全代理,避免手动拼通道名称
66
-
67
- ```typescript
68
- // src/services/electron/newFeatureService.ts
69
- import type { NewFeatureParams } from '@lobechat/electron-client-ipc';
70
-
71
- import { ensureElectronIpc } from '@/utils/electron/ipc';
72
-
73
- const ipc = ensureElectronIpc();
74
-
75
- export const newFeatureService = async (params: NewFeatureParams) => {
76
- return ipc.newFeature.doSomething(params);
77
- };
78
- ```
79
-
80
- 2. **实现 Store Action**
81
- - 位置:`src/store/`
82
- - 添加状态更新逻辑和错误处理
83
-
84
- 3. **添加 UI 组件**
85
- - 根据需要在适当位置添加UI组件
86
- - 通过 Store 或 Service 层调用功能
87
-
88
- ### 5. 如果是新增内置工具,遵循工具实现流程
89
-
90
- 参考 `desktop-local-tools-implement.mdc` 了解更多关于添加内置工具的详细步骤。
91
-
92
- ### 6. 添加测试
93
-
94
- 1. **单元测试**
95
- - 位置:`apps/desktop/src/main/controllers/__tests__/`
96
- - 测试主进程组件功能
97
-
98
- 2. **集成测试**
99
- - 测试 IPC 通信和功能完整流程
100
-
101
- ## 最佳实践
102
-
103
- 1. **安全性考虑**
104
- - 谨慎处理用户数据和文件系统访问
105
- - 适当验证和清理输入数据
106
- - 限制暴露给渲染进程的API范围
107
-
108
- 2. **性能优化**
109
- - 对于耗时操作,考虑使用异步方法
110
- - 大型数据传输考虑分批处理
111
-
112
- 3. **用户体验**
113
- - 为长时间操作添加进度指示
114
- - 提供适当的错误反馈
115
- - 考虑操作的可撤销性
116
-
117
- 4. **代码组织**
118
- - 遵循项目现有的命名和代码风格约定
119
- - 为新功能添加适当的文档和注释
120
- - 功能模块化,避免过度耦合
121
-
122
- ## 示例:实现系统通知功能
123
-
124
- ```typescript
125
- // apps/desktop/src/main/controllers/NotificationCtr.ts
126
- import type {
127
- DesktopNotificationResult,
128
- ShowDesktopNotificationParams,
129
- } from '@lobechat/electron-client-ipc';
130
- import { Notification } from 'electron';
131
-
132
- import { ControllerModule, IpcMethod } from '@/controllers';
133
-
134
- export default class NotificationCtr extends ControllerModule {
135
- static override readonly groupName = 'notification';
136
-
137
- @IpcMethod()
138
- async showDesktopNotification(
139
- params: ShowDesktopNotificationParams,
140
- ): Promise<DesktopNotificationResult> {
141
- if (!Notification.isSupported()) {
142
- return { error: 'Notifications not supported', success: false };
143
- }
144
-
145
- try {
146
- const notification = new Notification({ body: params.body, title: params.title });
147
- notification.show();
148
- return { success: true };
149
- } catch (error) {
150
- console.error('[NotificationCtr] Failed to show notification:', error);
151
- return { error: error instanceof Error ? error.message : 'Unknown error', success: false };
152
- }
153
- }
154
- }
155
- ```
@@ -1,81 +0,0 @@
1
- ---
2
- description:
3
- globs:
4
- alwaysApply: false
5
- ---
6
-
7
- **新增桌面端工具流程:**
8
-
9
- 1. **定义工具接口 (Manifest):**
10
- - **文件:** `src/tools/[tool_category]/index.ts` (例如: `src/tools/local-files/index.ts`)
11
- - **操作:**
12
- - 在 `ApiName` 对象(例如 `LocalFilesApiName`)中添加一个新的、唯一的 API 名称。
13
- - 在 `Manifest` 对象(例如 `LocalFilesManifest`)的 `api` 数组中,新增一个对象来定义新工具的接口。
14
- - **关键字段:**
15
- - `name`: 使用上一步定义的 API 名称。
16
- - `description`: 清晰描述工具的功能,供 Agent 理解和向用户展示。
17
- - `parameters`: 使用 JSON Schema 定义工具所需的输入参数。
18
- - `type`: 通常是 'object'。
19
- - `properties`: 定义每个参数的名称、`description`、`type` (string, number, boolean, array, etc.),使用英文。
20
- - `required`: 一个字符串数组,列出必须提供的参数名称。
21
-
22
- 2. **定义相关类型:**
23
- - **文件 1:** `packages/electron-client-ipc/src/types.ts` (或类似的共享 IPC 类型文件)
24
- - **操作:** 定义传递给 IPC 事件的参数类型接口 (例如: `RenameLocalFileParams`, `MoveLocalFileParams`)。确保与 Manifest 中定义的 `parameters` 一致。
25
- - **文件 2:** `src/tools/[tool_category]/type.ts` (例如: `src/tools/local-files/type.ts`)
26
- - **操作:** 定义此工具执行后,存储在前端 Zustand Store 中的状态类型接口 (例如: `LocalRenameFileState`, `LocalMoveFileState`)。这通常包含操作结果(成功/失败)、错误信息以及相关数据(如旧路径、新路径等)。
27
-
28
- 3. **实现前端状态管理 (Store Action):**
29
- - **文件:** `src/store/chat/slices/builtinTool/actions/[tool_category].ts` (例如: `src/store/chat/slices/builtinTool/actions/localFile.ts`)
30
- - **操作:**
31
- - 导入在步骤 2 中定义的 IPC 参数类型和状态类型。
32
- - 在 Action 接口 (例如: `LocalFileAction`) 中添加新 Action 的方法签名,使用对应的 IPC 参数类型。
33
- - 在 `createSlice` (例如: `localFileSlice`) 中实现该 Action 方法:
34
- - 接收 `id` (消息 ID) 和 `params` (符合 IPC 参数类型)。
35
- - 设置加载状态 (`toggleLocalFileLoading(id, true)`)。
36
- - 调用对应的 `Service` 层方法 (见步骤 4),传递 `params`。
37
- - 使用 `try...catch` 处理 `Service` 调用可能发生的错误。
38
- - **成功时:**
39
- - 调用 `updatePluginState(id, {...})` 更新插件状态,使用步骤 2 中定义的状态类型。
40
- - 调用 `internal_updateMessageContent(id, JSON.stringify({...}))` 更新消息内容,通常包含成功确认信息。
41
- - **失败时:**
42
- - 记录错误 (`console.error`)。
43
- - 调用 `updatePluginState(id, {...})` 更新插件状态,包含错误信息。
44
- - 调用 `internal_updateMessagePluginError(id, {...})` 设置消息的错误状态。
45
- - 调用 `internal_updateMessageContent(id, JSON.stringify({...}))` 更新消息内容,包含错误信息。
46
- - 在 `finally` 块中取消加载状态 (`toggleLocalFileLoading(id, false)`)。
47
- - 返回操作是否成功 (`boolean`)。
48
-
49
- 4. **实现 Service 层 (调用 IPC):**
50
- - **文件:** `src/services/electron/[tool_category]Service.ts` (例如: `src/services/electron/localFileService.ts`)
51
- - **操作:**
52
- - 导入在步骤 2 中定义的 IPC 参数类型。
53
- - 添加一个新的 `async` 方法,方法名通常与 Action 名称对应 (例如: `renameLocalFile`)。
54
- - 方法接收 `params` (符合 IPC 参数类型)。
55
- - 通过 `ensureElectronIpc()` 获取 IPC 代理 (`const ipc = ensureElectronIpc();`),调用与 Manifest 中 `name` 字段匹配的链式方法,并将 `params` 传递过去。
56
- - 定义方法的返回类型,通常是 `Promise<{ success: boolean; error?: string }>`,与后端 Controller 返回的结构一致。
57
-
58
- 5. **实现后端逻辑 (Controller / IPC Handler):**
59
- - **文件:** `apps/desktop/src/main/controllers/[ToolName]Ctr.ts` (例如: `apps/desktop/src/main/controllers/LocalFileCtr.ts`)
60
- - **操作:**
61
- - 导入 Node.js 相关模块 (`fs`, `path` 等) 和 IPC 相关依赖 (`ControllerModule`, `IpcMethod`、参数类型等)。
62
- - 添加一个新的 `async` 方法,方法名通常以 `handle` 开头 (例如: `handleRenameFile`)。
63
- - 使用 `@IpcMethod()` 装饰器将此方法注册为对应 IPC 事件的处理器,确保方法名与 Manifest 中的 `name` 以及 Service 层的链式调用一致。
64
- - 方法的参数应解构自 Service 层传递过来的对象,类型与步骤 2 中定义的 IPC 参数类型匹配。
65
- - 实现核心业务逻辑:
66
- - 进行必要的输入验证。
67
- - 执行文件系统操作或其他后端任务 (例如: `fs.promises.rename`)。
68
- - 使用 `try...catch` 捕获执行过程中的错误。
69
- - 处理特定错误码 (`error.code`) 以提供更友好的错误消息。
70
- - 返回一个包含 `success` (boolean) 和可选 `error` (string) 字段的对象。
71
-
72
- 6. **更新 Agent 文档 (System Role):**
73
- - **文件:** `src/tools/[tool_category]/systemRole.ts` (例如: `src/tools/local-files/systemRole.ts`)
74
- - **操作:**
75
- - 在 `<core_capabilities>` 部分添加新工具的简要描述。
76
- - 如果需要,更新 `<workflow>`。
77
- - 在 `<tool_usage_guidelines>` 部分为新工具添加详细的使用说明,解释其参数、用途和预期行为。
78
- - 如有必要,更新 `<security_considerations>`。
79
- - 如有必要(例如工具返回了新的数据结构或路径),更新 `<response_format>` 中的示例。
80
-
81
- 通过遵循这些步骤,可以系统地将新的桌面端工具集成到 LobeChat 的插件系统中。
@@ -1,209 +0,0 @@
1
- ---
2
- description:
3
- globs:
4
- alwaysApply: false
5
- ---
6
-
7
- # 桌面端菜单配置指南
8
-
9
- ## 菜单系统概述
10
-
11
- LobeChat 桌面应用有三种主要的菜单类型:
12
-
13
- 1. **应用菜单 (App Menu)**:显示在应用窗口顶部(macOS)或窗口标题栏(Windows/Linux)
14
- 2. **上下文菜单 (Context Menu)**:右键点击时显示的菜单
15
- 3. **托盘菜单 (Tray Menu)**:点击系统托盘图标显示的菜单
16
-
17
- ## 菜单相关文件结构
18
-
19
- ```plaintext
20
- apps/desktop/src/main/
21
- ├── menus/ # 菜单定义
22
- │ ├── appMenu.ts # 应用菜单配置
23
- │ ├── contextMenu.ts # 上下文菜单配置
24
- │ └── factory.ts # 菜单工厂函数
25
- ├── controllers/
26
- │ ├── MenuCtr.ts # 菜单控制器
27
- │ └── TrayMenuCtr.ts # 托盘菜单控制器
28
- ```
29
-
30
- ## 菜单配置流程
31
-
32
- ### 1. 应用菜单配置
33
-
34
- 应用菜单在 `apps/desktop/src/main/menus/appMenu.ts` 中定义:
35
-
36
- 1. **导入依赖**
37
-
38
- ```typescript
39
- import { BrowserWindow, Menu, MenuItem, MenuItemConstructorOptions, app } from 'electron';
40
- import { is } from 'electron-util';
41
- ```
42
-
43
- 2. **定义菜单项**
44
- - 使用 `MenuItemConstructorOptions` 类型定义菜单结构
45
- - 每个菜单项可以包含:label, accelerator (快捷键), role, submenu, click 等属性
46
-
47
- 3. **创建菜单工厂函数**
48
-
49
- ```typescript
50
- export const createAppMenu = (win: BrowserWindow) => {
51
- const template = [
52
- // 定义菜单项...
53
- ];
54
-
55
- return Menu.buildFromTemplate(template);
56
- };
57
- ```
58
-
59
- 4. **注册菜单**
60
- - 在 `MenuCtr.ts` 控制器中使用 `Menu.setApplicationMenu(menu)` 设置应用菜单
61
-
62
- ### 2. 上下文菜单配置
63
-
64
- 上下文菜单通常在特定元素上右键点击时显示:
65
-
66
- 1. **在主进程中定义菜单模板**
67
-
68
- ```typescript
69
- // apps/desktop/src/main/menus/contextMenu.ts
70
- export const createContextMenu = () => {
71
- const template = [
72
- // 定义菜单项...
73
- ];
74
-
75
- return Menu.buildFromTemplate(template);
76
- };
77
- ```
78
-
79
- 2. **在适当的事件处理器中显示菜单**
80
-
81
- ```typescript
82
- const menu = createContextMenu();
83
- menu.popup();
84
- ```
85
-
86
- ### 3. 托盘菜单配置
87
-
88
- 托盘菜单在 `TrayMenuCtr.ts` 中配置:
89
-
90
- 1. **创建托盘图标**
91
-
92
- ```typescript
93
- this.tray = new Tray(trayIconPath);
94
- ```
95
-
96
- 2. **定义托盘菜单**
97
-
98
- ```typescript
99
- const contextMenu = Menu.buildFromTemplate([
100
- { label: '显示主窗口', click: this.showMainWindow },
101
- { type: 'separator' },
102
- { label: '退出', click: () => app.quit() },
103
- ]);
104
- ```
105
-
106
- 3. **设置托盘菜单**
107
-
108
- ```typescript
109
- this.tray.setContextMenu(contextMenu);
110
- ```
111
-
112
- ## 多语言支持
113
-
114
- 为菜单添加多语言支持:
115
-
116
- 1. **导入本地化工具**
117
-
118
- ```typescript
119
- import { i18n } from '../locales';
120
- ```
121
-
122
- 2. **使用翻译函数**
123
-
124
- ```typescript
125
- const template = [
126
- {
127
- label: i18n.t('menu.file'),
128
- submenu: [
129
- { label: i18n.t('menu.new'), click: createNew },
130
- // ...
131
- ],
132
- },
133
- // ...
134
- ];
135
- ```
136
-
137
- 3. **在语言切换时更新菜单** 在 `MenuCtr.ts` 中监听语言变化事件并重新创建菜单
138
-
139
- ## 添加新菜单项流程
140
-
141
- 1. **确定菜单位置**
142
- - 决定添加到哪个菜单(应用菜单、上下文菜单或托盘菜单)
143
- - 确定在菜单中的位置(主菜单项或子菜单项)
144
-
145
- 2. **定义菜单项**
146
-
147
- ```typescript
148
- const newMenuItem: MenuItemConstructorOptions = {
149
- label: '新功能',
150
- accelerator: 'CmdOrCtrl+N',
151
- click: (_, window) => {
152
- // 处理点击事件
153
- if (window) window.webContents.send('trigger-new-feature');
154
- },
155
- };
156
- ```
157
-
158
- 3. **添加到菜单模板** 将新菜单项添加到相应的菜单模板中
159
-
160
- 4. **对于与渲染进程交互的功能**
161
- - 使用 `window.webContents.send()` 发送 IPC 消息到渲染进程
162
- - 在渲染进程中监听该消息并处理
163
-
164
- ## 菜单项启用/禁用控制
165
-
166
- 动态控制菜单项状态:
167
-
168
- 1. **保存对菜单项的引用**
169
-
170
- ```typescript
171
- this.menuItems = {};
172
- const menu = Menu.buildFromTemplate(template);
173
- this.menuItems.newFeature = menu.getMenuItemById('new-feature');
174
- ```
175
-
176
- 2. **根据条件更新状态**
177
-
178
- ```typescript
179
- updateMenuState(state) {
180
- if (this.menuItems.newFeature) {
181
- this.menuItems.newFeature.enabled = state.canUseNewFeature;
182
- }
183
- }
184
- ```
185
-
186
- ## 最佳实践
187
-
188
- 1. **使用标准角色**
189
- - 尽可能使用 Electron 预定义的角色(如 `role: 'copy'`)以获得本地化和一致的行为
190
-
191
- 2. **平台特定菜单**
192
- - 使用 `process.platform` 检查为不同平台提供不同菜单
193
-
194
- ```typescript
195
- if (process.platform === 'darwin') {
196
- template.unshift({ role: 'appMenu' });
197
- }
198
- ```
199
-
200
- 3. **快捷键冲突**
201
- - 避免与系统快捷键冲突
202
- - 使用 `CmdOrCtrl` 代替 `Ctrl` 以支持 macOS 和 Windows/Linux
203
-
204
- 4. **保持菜单简洁**
205
- - 避免过多嵌套的子菜单
206
- - 将相关功能分组在一起
207
-
208
- 5. **添加分隔符**
209
- - 使用 `{ type: 'separator' }` 在逻辑上分隔不同组的菜单项