@lobehub/lobehub 2.0.0-next.173 → 2.0.0-next.175
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.
- package/.cursor/rules/db-migrations.mdc +10 -4
- package/.cursor/rules/drizzle-schema-style-guide.mdc +33 -17
- package/.cursor/rules/hotkey.mdc +161 -0
- package/.cursor/rules/i18n.mdc +2 -5
- package/.cursor/rules/project-introduce.mdc +3 -2
- package/.cursor/rules/project-structure.mdc +33 -37
- package/.cursor/rules/react.mdc +155 -0
- package/.cursor/rules/recent-data-usage.mdc +138 -0
- package/.cursor/rules/rules-index.mdc +1 -1
- package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +285 -0
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +11 -16
- package/.cursor/rules/typescript.mdc +0 -4
- package/.cursor/rules/zustand-action-patterns.mdc +137 -169
- package/.cursor/rules/zustand-slice-organization.mdc +16 -8
- package/.husky/pre-commit +1 -1
- package/.i18nrc.js +3 -1
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +42 -0
- package/CLAUDE.md +10 -3
- package/GEMINI.md +2 -1
- package/README.md +9 -9
- package/README.zh-CN.md +12 -12
- package/changelog/v1.json +14 -0
- package/docs/development/database-schema.dbml +18 -3
- package/docs/development/state-management/state-management-selectors.mdx +1 -1
- package/glossary.json +8 -0
- package/package.json +3 -3
- package/packages/database/migrations/0063_add_columns_for_several_tables.sql +30 -0
- package/packages/database/migrations/0064_add_agents_session_group_id.sql +7 -0
- package/packages/database/migrations/meta/0063_snapshot.json +9113 -0
- package/packages/database/migrations/meta/0064_snapshot.json +9143 -0
- package/packages/database/migrations/meta/_journal.json +14 -0
- package/packages/database/src/core/migrations.json +39 -0
- package/packages/database/src/schemas/_helpers.ts +5 -1
- package/packages/database/src/schemas/agent.ts +8 -1
- package/packages/database/src/schemas/aiInfra.ts +18 -3
- package/packages/database/src/schemas/message.ts +11 -6
- package/packages/database/src/schemas/topic.ts +17 -2
- package/packages/database/src/schemas/user.ts +5 -2
- package/packages/database/src/schemas/userMemories.ts +9 -8
- package/packages/types/src/topic/thread.ts +24 -0
- package/packages/types/src/user/index.ts +1 -0
- package/packages/types/src/user/onboarding.ts +16 -0
- package/vitest.config.mts +3 -0
- package/.cursor/rules/group-chat.mdc +0 -35
- package/.cursor/rules/react-component.mdc +0 -173
|
@@ -5,20 +5,26 @@ alwaysApply: false
|
|
|
5
5
|
|
|
6
6
|
# Database Migrations Guide
|
|
7
7
|
|
|
8
|
-
## Step1: Generate migrations
|
|
8
|
+
## Step1: Generate migrations
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
bun run db:generate
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
this step will generate
|
|
14
|
+
this step will generate following files:
|
|
15
|
+
|
|
16
|
+
- packages/database/migrations/0046_meaningless_file_name.sql
|
|
17
|
+
- packages/database/migrations/0046_meaningless_file_name.sql
|
|
18
|
+
|
|
19
|
+
and update the following files:
|
|
15
20
|
|
|
16
|
-
- packages/database/migrations/0046_xxx.sql
|
|
17
21
|
- packages/database/migrations/meta/\_journal.json
|
|
22
|
+
- packages/database/src/core/migrations.json
|
|
23
|
+
- docs/development/database-schema.dbml
|
|
18
24
|
|
|
19
25
|
## Step2: optimize the migration sql fileName
|
|
20
26
|
|
|
21
|
-
the migration sql file name is randomly generated, we need to optimize the file name to make it more readable and meaningful. For example, `
|
|
27
|
+
the migration sql file name is randomly generated, we need to optimize the file name to make it more readable and meaningful. For example, `0046_meaningless_file_name.sql` -> `0046_user_add_avatar_column.sql`
|
|
22
28
|
|
|
23
29
|
## Step3: Defensive Programming - Use Idempotent Clauses
|
|
24
30
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description:
|
|
3
3
|
globs: src/database/schemas/*
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
6
|
+
|
|
6
7
|
# Drizzle ORM Schema Style Guide for lobe-chat
|
|
7
8
|
|
|
8
9
|
This document outlines the conventions and best practices for defining PostgreSQL Drizzle ORM schemas within the lobe-chat project.
|
|
@@ -16,7 +17,8 @@ This document outlines the conventions and best practices for defining PostgreSQ
|
|
|
16
17
|
|
|
17
18
|
## Helper Functions
|
|
18
19
|
|
|
19
|
-
Commonly used column definitions, especially for timestamps, are centralized in [src/database/schemas
|
|
20
|
+
Commonly used column definitions, especially for timestamps, are centralized in [src/database/schemas/\_helpers.ts](mdc:src/database/schemas/_helpers.ts):
|
|
21
|
+
|
|
20
22
|
- `timestamptz(name: string)`: Creates a timestamp column with timezone
|
|
21
23
|
- `createdAt()`, `updatedAt()`, `accessedAt()`: Helper functions for standard timestamp columns
|
|
22
24
|
- `timestamps`: An object `{ createdAt, updatedAt, accessedAt }` for easy inclusion in table definitions
|
|
@@ -29,6 +31,7 @@ Commonly used column definitions, especially for timestamps, are centralized in
|
|
|
29
31
|
## Column Definitions
|
|
30
32
|
|
|
31
33
|
### Primary Keys (PKs)
|
|
34
|
+
|
|
32
35
|
- Typically `text('id')` (or `varchar('id')` for some OIDC tables)
|
|
33
36
|
- Often use `.$defaultFn(() => idGenerator('table_name'))` for automatic ID generation with meaningful prefixes
|
|
34
37
|
- **ID Prefix Purpose**: Makes it easy for users and developers to distinguish different entity types at a glance
|
|
@@ -36,24 +39,29 @@ Commonly used column definitions, especially for timestamps, are centralized in
|
|
|
36
39
|
- Composite PKs are defined using `primaryKey({ columns: [t.colA, t.colB] })`
|
|
37
40
|
|
|
38
41
|
### Foreign Keys (FKs)
|
|
42
|
+
|
|
39
43
|
- Defined using `.references(() => otherTable.id, { onDelete: 'cascade' | 'set null' | 'no action' })`
|
|
40
44
|
- FK columns are usually named `related_table_singular_name_id` (e.g., `user_id` references `users.id`)
|
|
41
45
|
- Most tables include a `user_id` column referencing `users.id` with `onDelete: 'cascade'`
|
|
42
46
|
|
|
43
47
|
### Timestamps
|
|
44
|
-
|
|
48
|
+
|
|
49
|
+
- Consistently use the `...timestamps` spread from [\_helpers.ts](mdc:src/database/schemas/_helpers.ts) for `created_at`, `updated_at`, and `accessed_at` columns
|
|
45
50
|
|
|
46
51
|
### Default Values
|
|
52
|
+
|
|
47
53
|
- `.$defaultFn(() => expression)` for dynamic defaults (e.g., `idGenerator()`, `randomSlug()`)
|
|
48
54
|
- `.default(staticValue)` for static defaults (e.g., `boolean('enabled').default(true)`)
|
|
49
55
|
|
|
50
56
|
### Indexes
|
|
57
|
+
|
|
51
58
|
- Defined in the table's second argument: `pgTable('name', {...columns}, (t) => ({ indexName: indexType().on(...) }))`
|
|
52
59
|
- Use `uniqueIndex()` for unique constraints and `index()` for non-unique indexes
|
|
53
60
|
- Naming pattern: `table_name_column(s)_idx` or `table_name_column(s)_unique`
|
|
54
61
|
- Many tables feature a `clientId: text('client_id')` column, often part of a composite unique index with `user_id`
|
|
55
62
|
|
|
56
63
|
### Data Types
|
|
64
|
+
|
|
57
65
|
- Common types: `text`, `varchar`, `jsonb`, `boolean`, `integer`, `uuid`, `pgTable`
|
|
58
66
|
- For `jsonb` fields, specify the TypeScript type using `.$type<MyType>()` for better type safety
|
|
59
67
|
|
|
@@ -97,9 +105,7 @@ export const agents = pgTable(
|
|
|
97
105
|
...timestamps,
|
|
98
106
|
},
|
|
99
107
|
// return array instead of object, the object style is deprecated
|
|
100
|
-
(t) => [
|
|
101
|
-
uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId),
|
|
102
|
-
],
|
|
108
|
+
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
|
103
109
|
);
|
|
104
110
|
|
|
105
111
|
export const insertAgentSchema = createInsertSchema(agents);
|
|
@@ -110,6 +116,7 @@ export type AgentItem = typeof agents.$inferSelect;
|
|
|
110
116
|
## Common Patterns
|
|
111
117
|
|
|
112
118
|
### 1. userId + clientId Pattern (Legacy)
|
|
119
|
+
|
|
113
120
|
Some existing tables include both fields for different purposes:
|
|
114
121
|
|
|
115
122
|
```typescript
|
|
@@ -129,6 +136,7 @@ clientIdUnique: uniqueIndex('agents_client_id_user_id_unique').on(t.clientId, t.
|
|
|
129
136
|
- **Note**: This pattern is being phased out for new features to simplify the schema
|
|
130
137
|
|
|
131
138
|
### 2. Junction Tables (Many-to-Many Relationships)
|
|
139
|
+
|
|
132
140
|
Use composite primary keys for relationship tables:
|
|
133
141
|
|
|
134
142
|
```typescript
|
|
@@ -136,21 +144,26 @@ Use composite primary keys for relationship tables:
|
|
|
136
144
|
export const agentsKnowledgeBases = pgTable(
|
|
137
145
|
'agents_knowledge_bases',
|
|
138
146
|
{
|
|
139
|
-
agentId: text('agent_id')
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
agentId: text('agent_id')
|
|
148
|
+
.references(() => agents.id, { onDelete: 'cascade' })
|
|
149
|
+
.notNull(),
|
|
150
|
+
knowledgeBaseId: text('knowledge_base_id')
|
|
151
|
+
.references(() => knowledgeBases.id, { onDelete: 'cascade' })
|
|
152
|
+
.notNull(),
|
|
153
|
+
userId: text('user_id')
|
|
154
|
+
.references(() => users.id, { onDelete: 'cascade' })
|
|
155
|
+
.notNull(),
|
|
142
156
|
enabled: boolean('enabled').default(true),
|
|
143
157
|
...timestamps,
|
|
144
158
|
},
|
|
145
|
-
(t) => [
|
|
146
|
-
primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
|
|
147
|
-
],
|
|
159
|
+
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
|
148
160
|
);
|
|
149
161
|
```
|
|
150
162
|
|
|
151
163
|
**Pattern**: `{entity1}Id` + `{entity2}Id` as composite PK, plus `userId` for ownership
|
|
152
164
|
|
|
153
165
|
### 3. OIDC Tables Special Patterns
|
|
166
|
+
|
|
154
167
|
OIDC tables use `varchar` IDs instead of `text` with custom generators:
|
|
155
168
|
|
|
156
169
|
```typescript
|
|
@@ -166,6 +179,7 @@ export const oidcAuthorizationCodes = pgTable('oidc_authorization_codes', {
|
|
|
166
179
|
**Reason**: OIDC standards expect specific ID formats and lengths
|
|
167
180
|
|
|
168
181
|
### 4. File Processing with Async Tasks
|
|
182
|
+
|
|
169
183
|
File-related tables reference async task IDs for background processing:
|
|
170
184
|
|
|
171
185
|
```typescript
|
|
@@ -173,17 +187,21 @@ File-related tables reference async task IDs for background processing:
|
|
|
173
187
|
export const files = pgTable('files', {
|
|
174
188
|
// ... other fields
|
|
175
189
|
chunkTaskId: uuid('chunk_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
|
176
|
-
embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, {
|
|
190
|
+
embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, {
|
|
191
|
+
onDelete: 'set null',
|
|
192
|
+
}),
|
|
177
193
|
// ...
|
|
178
194
|
});
|
|
179
195
|
```
|
|
180
196
|
|
|
181
|
-
**Purpose**:
|
|
197
|
+
**Purpose**:
|
|
198
|
+
|
|
182
199
|
- Track file chunking progress (breaking files into smaller pieces)
|
|
183
200
|
- Track embedding generation progress (converting text to vectors)
|
|
184
201
|
- Allow querying task status and handling failures
|
|
185
202
|
|
|
186
203
|
### 5. Slug Pattern (Legacy)
|
|
204
|
+
|
|
187
205
|
Some entities include auto-generated slugs - this is legacy code:
|
|
188
206
|
|
|
189
207
|
```typescript
|
|
@@ -195,8 +213,6 @@ slug: varchar('slug', { length: 100 })
|
|
|
195
213
|
slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
|
|
196
214
|
```
|
|
197
215
|
|
|
198
|
-
**Current usage**: Only used to identify default agents/sessions (legacy pattern)
|
|
199
|
-
**Future refactor**: Will likely be replaced with `isDefault: boolean()` field
|
|
200
|
-
**Note**: Avoid using slugs for new features - prefer explicit boolean flags for status tracking
|
|
216
|
+
**Current usage**: Only used to identify default agents/sessions (legacy pattern) **Future refactor**: Will likely be replaced with `isDefault: boolean()` field **Note**: Avoid using slugs for new features - prefer explicit boolean flags for status tracking
|
|
201
217
|
|
|
202
218
|
By following these guidelines, maintain consistency, type safety, and maintainability across database schema definitions.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: false
|
|
3
|
+
---
|
|
4
|
+
# 如何添加新的快捷键:开发者指南
|
|
5
|
+
|
|
6
|
+
本指南将带您一步步地向 LobeChat 添加一个新的快捷键功能。我们将通过一个完整示例,演示从定义到实现的整个过程。
|
|
7
|
+
|
|
8
|
+
## 示例场景
|
|
9
|
+
|
|
10
|
+
假设我们要添加一个新的快捷键功能:**快速清空聊天记录**,快捷键为 `Mod+Shift+Backspace`。
|
|
11
|
+
|
|
12
|
+
## 步骤 1:更新快捷键常量定义
|
|
13
|
+
|
|
14
|
+
首先,在 `src/types/hotkey.ts` 中更新 `HotkeyEnum`:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
export const HotkeyEnum = {
|
|
18
|
+
// 已有的快捷键...
|
|
19
|
+
AddUserMessage: 'addUserMessage',
|
|
20
|
+
EditMessage: 'editMessage',
|
|
21
|
+
|
|
22
|
+
// 新增快捷键
|
|
23
|
+
ClearChat: 'clearChat', // 添加这一行
|
|
24
|
+
|
|
25
|
+
// 其他已有快捷键...
|
|
26
|
+
} as const;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 步骤 2:注册默认快捷键
|
|
30
|
+
|
|
31
|
+
在 `src/const/hotkeys.ts` 中添加快捷键的默认配置:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { KeyMapEnum as Key, combineKeys } from '@lobehub/ui';
|
|
35
|
+
|
|
36
|
+
// ...现有代码
|
|
37
|
+
|
|
38
|
+
export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
|
|
39
|
+
// 现有的快捷键配置...
|
|
40
|
+
|
|
41
|
+
// 添加新的快捷键配置
|
|
42
|
+
{
|
|
43
|
+
group: HotkeyGroupEnum.Conversation, // 归类到会话操作组
|
|
44
|
+
id: HotkeyEnum.ClearChat,
|
|
45
|
+
keys: combineKeys([Key.Mod, Key.Shift, Key.Backspace]),
|
|
46
|
+
scopes: [HotkeyScopeEnum.Chat], // 在聊天作用域下生效
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// 其他现有快捷键...
|
|
50
|
+
];
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 步骤 3:添加国际化翻译
|
|
54
|
+
|
|
55
|
+
在 `src/locales/default/hotkey.ts` 中添加对应的文本描述:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { HotkeyI18nTranslations } from '@/types/hotkey';
|
|
59
|
+
|
|
60
|
+
const hotkey: HotkeyI18nTranslations = {
|
|
61
|
+
// 现有翻译...
|
|
62
|
+
|
|
63
|
+
// 添加新快捷键的翻译
|
|
64
|
+
clearChat: {
|
|
65
|
+
desc: '清空当前会话的所有消息记录',
|
|
66
|
+
title: '清空聊天记录',
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// 其他现有翻译...
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default hotkey;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
如需支持其他语言,还需要在相应的语言文件中添加对应翻译。
|
|
76
|
+
|
|
77
|
+
## 步骤 4:创建并注册快捷键 Hook
|
|
78
|
+
|
|
79
|
+
在 `src/hooks/useHotkeys/chatScope.ts` 中添加新的 Hook:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
export const useClearChatHotkey = () => {
|
|
83
|
+
const clearMessages = useChatStore((s) => s.clearMessages);
|
|
84
|
+
const { t } = useTranslation();
|
|
85
|
+
|
|
86
|
+
return useHotkeyById(HotkeyEnum.ClearChat, showConfirm);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// 注册聚合
|
|
90
|
+
|
|
91
|
+
export const useRegisterChatHotkeys = () => {
|
|
92
|
+
const { enableScope, disableScope } = useHotkeysContext();
|
|
93
|
+
|
|
94
|
+
useOpenChatSettingsHotkey();
|
|
95
|
+
// ...其他快捷键
|
|
96
|
+
useClearChatHotkey();
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
enableScope(HotkeyScopeEnum.Chat);
|
|
100
|
+
return () => disableScope(HotkeyScopeEnum.Chat);
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 步骤 5:给相应 UI 元素添加 Tooltip 提示(可选)
|
|
108
|
+
|
|
109
|
+
如果有对应的 UI 按钮,可以添加快捷键提示:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { DeleteOutlined } from '@ant-design/icons';
|
|
113
|
+
import { Tooltip } from '@lobehub/ui';
|
|
114
|
+
import { Button } from 'antd';
|
|
115
|
+
import { useTranslation } from 'react-i18next';
|
|
116
|
+
|
|
117
|
+
import { useUserStore } from '@/store/user';
|
|
118
|
+
import { settingsSelectors } from '@/store/user/selectors';
|
|
119
|
+
import { HotkeyEnum } from '@/types/hotkey';
|
|
120
|
+
|
|
121
|
+
const ClearChatButton = () => {
|
|
122
|
+
const { t } = useTranslation(['hotkey', 'chat']);
|
|
123
|
+
const clearChatHotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.ClearChat));
|
|
124
|
+
|
|
125
|
+
// 获取清空聊天的方法
|
|
126
|
+
const clearMessages = useChatStore((s) => s.clearMessages);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Tooltip hotkey={clearChatHotkey} title={t('clearChat.title', { ns: 'hotkey' })}>
|
|
130
|
+
<Button icon={<DeleteOutlined />} onClick={clearMessages} />
|
|
131
|
+
</Tooltip>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 步骤 6:测试新快捷键
|
|
137
|
+
|
|
138
|
+
1. 启动开发服务器
|
|
139
|
+
2. 打开聊天页面
|
|
140
|
+
3. 按下设置的快捷键组合(`Cmd+Shift+Backspace` 或 `Ctrl+Shift+Backspace`)
|
|
141
|
+
4. 确认功能正常工作
|
|
142
|
+
5. 检查快捷键设置面板中是否正确显示了新快捷键
|
|
143
|
+
|
|
144
|
+
## 最佳实践
|
|
145
|
+
|
|
146
|
+
1. **作用域考虑**:根据功能决定快捷键应属于全局作用域还是聊天作用域
|
|
147
|
+
2. **分组合理**:将快捷键放在合适的功能组中(System/Layout/Conversation)
|
|
148
|
+
3. **冲突检查**:确保新快捷键不会与现有系统、浏览器或应用快捷键冲突
|
|
149
|
+
4. **平台适配**:使用 `Key.Mod` 而非硬编码 `Ctrl` 或 `Cmd`,以适配不同平台
|
|
150
|
+
5. **提供清晰描述**:为快捷键添加明确的标题和描述,帮助用户理解功能
|
|
151
|
+
|
|
152
|
+
按照以上步骤,您可以轻松地向系统添加新的快捷键功能,提升用户体验。如有特殊需求,如桌面专属快捷键,可以通过 `isDesktop` 标记进行区分处理。
|
|
153
|
+
|
|
154
|
+
## 常见问题排查
|
|
155
|
+
|
|
156
|
+
- **快捷键未生效**:检查作用域是否正确,以及是否在 RegisterHotkeys 中调用了对应的 hook
|
|
157
|
+
- **快捷键设置面板未显示**:确认在 HOTKEYS_REGISTRATION 中正确配置了快捷键
|
|
158
|
+
- **快捷键冲突**:在 HotkeyInput 组件中可以检测到冲突,用户会看到警告
|
|
159
|
+
- **功能在某些页面失效**:确认是否注册在了正确的作用域,以及相关页面是否激活了该作用域
|
|
160
|
+
|
|
161
|
+
通过这些步骤,您可以确保新添加的快捷键功能稳定、可靠且用户友好。
|
package/.cursor/rules/i18n.mdc
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
globs: *.tsx
|
|
3
3
|
alwaysApply: false
|
|
4
4
|
---
|
|
5
|
+
|
|
5
6
|
# LobeChat Internationalization Guide
|
|
6
7
|
|
|
7
8
|
## Key Points
|
|
@@ -14,7 +15,7 @@ alwaysApply: false
|
|
|
14
15
|
|
|
15
16
|
## Directory Structure
|
|
16
17
|
|
|
17
|
-
```
|
|
18
|
+
```plaintext
|
|
18
19
|
src/locales/
|
|
19
20
|
├── default/ # Source language files (zh-CN)
|
|
20
21
|
│ ├── index.ts # Namespace exports
|
|
@@ -176,7 +177,3 @@ export default {
|
|
|
176
177
|
- Check if the key exists in src/locales/default/namespace.ts
|
|
177
178
|
- Ensure the namespace is correctly imported in the component
|
|
178
179
|
- Ensure new namespaces are exported in src/locales/default/index.ts
|
|
179
|
-
|
|
180
|
-
- 检查键是否存在于 src/locales/default/namespace.ts 中
|
|
181
|
-
- 确保在组件中正确导入命名空间
|
|
182
|
-
- 确保新命名空间已在 src/locales/default/index.ts 中导出
|
|
@@ -17,6 +17,7 @@ logo emoji: 🤯
|
|
|
17
17
|
## Project Technologies Stack
|
|
18
18
|
|
|
19
19
|
- Next.js 16
|
|
20
|
+
- implement spa inside nextjs with `react-router-dom`
|
|
20
21
|
- react 19
|
|
21
22
|
- TypeScript
|
|
22
23
|
- `@lobehub/ui`, antd for component framework
|
|
@@ -29,8 +30,8 @@ logo emoji: 🤯
|
|
|
29
30
|
- SWR for data fetch
|
|
30
31
|
- aHooks for react hooks library
|
|
31
32
|
- dayjs for time library
|
|
32
|
-
-
|
|
33
|
+
- es-toolkit for utility library
|
|
33
34
|
- TRPC for type safe backend
|
|
34
|
-
-
|
|
35
|
+
- Neon PostgreSQL for backend DB
|
|
35
36
|
- Drizzle ORM
|
|
36
37
|
- Vitest for testing
|
|
@@ -25,22 +25,23 @@ lobe-chat/
|
|
|
25
25
|
│ └── zh-CN/
|
|
26
26
|
├── packages/
|
|
27
27
|
│ ├── agent-runtime/
|
|
28
|
+
│ ├── builtin-agents/
|
|
29
|
+
│ ├── builtin-tool-*/ # builtin tool packages
|
|
28
30
|
│ ├── const/
|
|
29
31
|
│ ├── context-engine/
|
|
30
32
|
│ ├── conversation-flow/
|
|
31
33
|
│ ├── database/
|
|
32
|
-
│ │
|
|
33
|
-
│ │
|
|
34
|
-
│ │
|
|
35
|
-
│ │
|
|
34
|
+
│ │ └── src/
|
|
35
|
+
│ │ ├── models/
|
|
36
|
+
│ │ ├── schemas/
|
|
37
|
+
│ │ └── repositories/
|
|
38
|
+
│ ├── desktop-bridge/
|
|
36
39
|
│ ├── electron-client-ipc/
|
|
37
40
|
│ ├── electron-server-ipc/
|
|
38
41
|
│ ├── fetch-sse/
|
|
39
42
|
│ ├── file-loaders/
|
|
40
|
-
│ ├── memory-
|
|
43
|
+
│ ├── memory-user-memory/
|
|
41
44
|
│ ├── model-bank/
|
|
42
|
-
│ │ └── src/
|
|
43
|
-
│ │ └── aiModels/
|
|
44
45
|
│ ├── model-runtime/
|
|
45
46
|
│ │ └── src/
|
|
46
47
|
│ │ ├── core/
|
|
@@ -50,9 +51,6 @@ lobe-chat/
|
|
|
50
51
|
│ ├── python-interpreter/
|
|
51
52
|
│ ├── ssrf-safe-fetch/
|
|
52
53
|
│ ├── types/
|
|
53
|
-
│ │ └── src/
|
|
54
|
-
│ │ ├── message/
|
|
55
|
-
│ │ └── user/
|
|
56
54
|
│ ├── utils/
|
|
57
55
|
│ └── web-crawler/
|
|
58
56
|
├── public/
|
|
@@ -61,24 +59,25 @@ lobe-chat/
|
|
|
61
59
|
│ ├── app/
|
|
62
60
|
│ │ ├── (backend)/
|
|
63
61
|
│ │ │ ├── api/
|
|
64
|
-
│ │ │
|
|
65
|
-
│ │ │
|
|
62
|
+
│ │ │ ├── f/
|
|
63
|
+
│ │ │ ├── market/
|
|
66
64
|
│ │ │ ├── middleware/
|
|
67
65
|
│ │ │ ├── oidc/
|
|
68
66
|
│ │ │ ├── trpc/
|
|
69
67
|
│ │ │ └── webapi/
|
|
70
|
-
│ │ │ ├── chat/
|
|
71
|
-
│ │ │ └── tts/
|
|
72
68
|
│ │ ├── [variants]/
|
|
69
|
+
│ │ │ ├── (auth)/
|
|
73
70
|
│ │ │ ├── (main)/
|
|
74
|
-
│ │ │
|
|
75
|
-
│ │ │
|
|
76
|
-
│ │ │ └──
|
|
77
|
-
│ │ └──
|
|
71
|
+
│ │ │ ├── (mobile)/
|
|
72
|
+
│ │ │ ├── onboarding/
|
|
73
|
+
│ │ │ └── router/
|
|
74
|
+
│ │ └── desktop/
|
|
78
75
|
│ ├── components/
|
|
79
76
|
│ ├── config/
|
|
77
|
+
│ ├── const/
|
|
78
|
+
│ ├── envs/
|
|
80
79
|
│ ├── features/
|
|
81
|
-
│
|
|
80
|
+
│ ├── helpers/
|
|
82
81
|
│ ├── hooks/
|
|
83
82
|
│ ├── layout/
|
|
84
83
|
│ │ ├── AuthProvider/
|
|
@@ -90,23 +89,23 @@ lobe-chat/
|
|
|
90
89
|
│ ├── locales/
|
|
91
90
|
│ │ └── default/
|
|
92
91
|
│ ├── server/
|
|
92
|
+
│ │ ├── featureFlags/
|
|
93
|
+
│ │ ├── globalConfig/
|
|
93
94
|
│ │ ├── modules/
|
|
94
95
|
│ │ ├── routers/
|
|
95
96
|
│ │ │ ├── async/
|
|
96
|
-
│ │ │ ├──
|
|
97
|
-
│ │ │ ├──
|
|
98
|
-
│ │ │ └──
|
|
97
|
+
│ │ │ ├── lambda/
|
|
98
|
+
│ │ │ ├── mobile/
|
|
99
|
+
│ │ │ └── tools/
|
|
99
100
|
│ │ └── services/
|
|
100
101
|
│ ├── services/
|
|
101
|
-
│ │ ├── user/
|
|
102
|
-
│ │ │ ├── client.ts
|
|
103
|
-
│ │ │ └── server.ts
|
|
104
|
-
│ │ └── message/
|
|
105
102
|
│ ├── store/
|
|
106
103
|
│ │ ├── agent/
|
|
107
104
|
│ │ ├── chat/
|
|
108
105
|
│ │ └── user/
|
|
109
106
|
│ ├── styles/
|
|
107
|
+
│ ├── tools/
|
|
108
|
+
│ ├── types/
|
|
110
109
|
│ └── utils/
|
|
111
110
|
└── package.json
|
|
112
111
|
```
|
|
@@ -116,25 +115,22 @@ lobe-chat/
|
|
|
116
115
|
- UI Components: `src/components`, `src/features`
|
|
117
116
|
- Global providers: `src/layout`
|
|
118
117
|
- Zustand stores: `src/store`
|
|
119
|
-
- Client Services: `src/services/`
|
|
120
|
-
- clientDB: `src/services/<domain>/client.ts`
|
|
121
|
-
- serverDB: `src/services/<domain>/server.ts`
|
|
118
|
+
- Client Services: `src/services/`
|
|
122
119
|
- API Routers:
|
|
123
120
|
- `src/app/(backend)/webapi` (REST)
|
|
124
|
-
- `src/server/routers/{
|
|
121
|
+
- `src/server/routers/{async|lambda|mobile|tools}` (tRPC)
|
|
125
122
|
- Server:
|
|
126
|
-
- Services(can access serverDB): `src/server/services`
|
|
127
|
-
- Modules(can't access db): `src/server/modules`
|
|
123
|
+
- Services (can access serverDB): `src/server/services`
|
|
124
|
+
- Modules (can't access db): `src/server/modules`
|
|
125
|
+
- Feature Flags: `src/server/featureFlags`
|
|
126
|
+
- Global Config: `src/server/globalConfig`
|
|
128
127
|
- Database:
|
|
129
128
|
- Schema (Drizzle): `packages/database/src/schemas`
|
|
130
129
|
- Model (CRUD): `packages/database/src/models`
|
|
131
130
|
- Repository (bff-queries): `packages/database/src/repositories`
|
|
132
131
|
- Third-party Integrations: `src/libs` — analytics, oidc etc.
|
|
132
|
+
- Builtin Tools: `src/tools`, `packages/builtin-tool-*`
|
|
133
133
|
|
|
134
134
|
## Data Flow Architecture
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
- **Web with ServerDB**: React UI → Client Service → tRPC Lambda → Server Services → PostgreSQL (Remote)
|
|
138
|
-
- **Desktop**:
|
|
139
|
-
- Cloud sync disabled: Electron UI → Client Service → tRPC Lambda → Local Server Services → PGLite (Node WASM)
|
|
140
|
-
- Cloud sync enabled: Electron UI → Client Service → tRPC Lambda → Cloud Server Services → PostgreSQL (Remote)
|
|
136
|
+
React UI → Store Actions → Client Service → TRPC Lambda → Server Services -> DB Model → PostgreSQL (Remote)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
globs: *.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Component Writing Guide
|
|
8
|
+
|
|
9
|
+
- Use antd-style for complex styles; for simple cases, use the `style` attribute for inline styles
|
|
10
|
+
- Use `Flexbox` and `Center` components from react-layout-kit for flex and centered layouts
|
|
11
|
+
- Component selection priority: src/components > installed component packages > lobe-ui > antd
|
|
12
|
+
- Use selectors to access zustand store data instead of accessing the store directly
|
|
13
|
+
|
|
14
|
+
## Lobe UI Components
|
|
15
|
+
|
|
16
|
+
- If unsure how to use `@lobehub/ui` components or what props they accept, search for existing usage in this project instead of guessing. Most components extend antd components with additional props
|
|
17
|
+
- For specific usage, search online. For example, for ActionIcon visit <https://ui.lobehub.com/components/action-icon>
|
|
18
|
+
- Read `node_modules/@lobehub/ui/es/index.js` to see all available components and their props
|
|
19
|
+
|
|
20
|
+
- General
|
|
21
|
+
- ActionIcon
|
|
22
|
+
- ActionIconGroup
|
|
23
|
+
- Block
|
|
24
|
+
- Button
|
|
25
|
+
- Icon
|
|
26
|
+
- Data Display
|
|
27
|
+
- Accordion
|
|
28
|
+
- Avatar
|
|
29
|
+
- Collapse
|
|
30
|
+
- Empty
|
|
31
|
+
- FileTypeIcon
|
|
32
|
+
- FluentEmoji
|
|
33
|
+
- GroupAvatar
|
|
34
|
+
- GuideCard
|
|
35
|
+
- Highlighter
|
|
36
|
+
- Hotkey
|
|
37
|
+
- Image
|
|
38
|
+
- List
|
|
39
|
+
- Markdown
|
|
40
|
+
- MaterialFileTypeIcon
|
|
41
|
+
- Mermaid
|
|
42
|
+
- Segmented
|
|
43
|
+
- Skeleton
|
|
44
|
+
- Snippet
|
|
45
|
+
- SortableList
|
|
46
|
+
- Tag
|
|
47
|
+
- Tooltip
|
|
48
|
+
- Video
|
|
49
|
+
- Data Entry
|
|
50
|
+
- AutoComplete
|
|
51
|
+
- CodeEditor
|
|
52
|
+
- ColorSwatches
|
|
53
|
+
- CopyButton
|
|
54
|
+
- DatePicker
|
|
55
|
+
- DownloadButton
|
|
56
|
+
- EditableText
|
|
57
|
+
- EmojiPicker
|
|
58
|
+
- Form
|
|
59
|
+
- FormModal
|
|
60
|
+
- HotkeyInput
|
|
61
|
+
- ImageSelect
|
|
62
|
+
- Input
|
|
63
|
+
- SearchBar
|
|
64
|
+
- Select
|
|
65
|
+
- SliderWithInput
|
|
66
|
+
- ThemeSwitch
|
|
67
|
+
- Feedback
|
|
68
|
+
- Alert
|
|
69
|
+
- Drawer
|
|
70
|
+
- Modal
|
|
71
|
+
- Layout
|
|
72
|
+
- DraggablePanel
|
|
73
|
+
- Footer
|
|
74
|
+
- Grid
|
|
75
|
+
- Header
|
|
76
|
+
- Layout
|
|
77
|
+
- MaskShadow
|
|
78
|
+
- ScrollShadow
|
|
79
|
+
- Navigation
|
|
80
|
+
- Burger
|
|
81
|
+
- DraggableSideNav
|
|
82
|
+
- Dropdown
|
|
83
|
+
- Menu
|
|
84
|
+
- SideNav
|
|
85
|
+
- Tabs
|
|
86
|
+
- Toc
|
|
87
|
+
- Theme
|
|
88
|
+
- ConfigProvider
|
|
89
|
+
- FontLoader
|
|
90
|
+
- ThemeProvider
|
|
91
|
+
- Typography
|
|
92
|
+
- Text
|
|
93
|
+
|
|
94
|
+
## Routing Architecture
|
|
95
|
+
|
|
96
|
+
This project uses a **hybrid routing architecture**: Next.js App Router for static pages + React Router DOM for the main SPA.
|
|
97
|
+
|
|
98
|
+
### Route Types
|
|
99
|
+
|
|
100
|
+
```plaintext
|
|
101
|
+
+------------------+--------------------------------+--------------------------------+
|
|
102
|
+
| Route Type | Use Case | Implementation |
|
|
103
|
+
+------------------+--------------------------------+--------------------------------+
|
|
104
|
+
| Next.js App | Auth pages (login, signup, | page.tsx file convention |
|
|
105
|
+
| Router | oauth, reset-password, etc.) | src/app/[variants]/(auth)/ |
|
|
106
|
+
+------------------+--------------------------------+--------------------------------+
|
|
107
|
+
| React Router | Main SPA features | BrowserRouter + Routes |
|
|
108
|
+
| DOM | (chat, discover, settings) | desktopRouter.config.tsx |
|
|
109
|
+
| | | mobileRouter.config.tsx |
|
|
110
|
+
+------------------+--------------------------------+--------------------------------+
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Key Files
|
|
114
|
+
|
|
115
|
+
- Entry point: `src/app/[variants]/page.tsx` - Routes to Desktop or Mobile based on device
|
|
116
|
+
- Desktop router: `src/app/[variants]/router/desktopRouter.config.tsx`
|
|
117
|
+
- Mobile router: `src/app/[variants]/(mobile)/router/mobileRouter.config.tsx`
|
|
118
|
+
- Router utilities: `src/utils/router.tsx`
|
|
119
|
+
|
|
120
|
+
### Router Utilities
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { dynamicElement, redirectElement, ErrorBoundary, RouteConfig } from '@/utils/router';
|
|
124
|
+
|
|
125
|
+
// Lazy load a page component
|
|
126
|
+
element: dynamicElement(() => import('./chat'), 'Desktop > Chat')
|
|
127
|
+
|
|
128
|
+
// Create a redirect
|
|
129
|
+
element: redirectElement('/settings/profile')
|
|
130
|
+
|
|
131
|
+
// Error boundary for route
|
|
132
|
+
errorElement: <ErrorBoundary resetPath="/chat" />
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Adding New Routes
|
|
136
|
+
|
|
137
|
+
1. Add route config to `desktopRouter.config.tsx` or `mobileRouter.config.tsx`
|
|
138
|
+
2. Create page component in the corresponding directory under `(main)/`
|
|
139
|
+
3. Use `dynamicElement()` for lazy loading
|
|
140
|
+
|
|
141
|
+
### Navigation
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// In components - use react-router-dom hooks
|
|
145
|
+
import { useNavigate, useParams } from 'react-router-dom';
|
|
146
|
+
|
|
147
|
+
const navigate = useNavigate();
|
|
148
|
+
navigate('/chat');
|
|
149
|
+
|
|
150
|
+
// From stores - use global navigate
|
|
151
|
+
import { useGlobalStore } from '@/store/global';
|
|
152
|
+
|
|
153
|
+
const navigate = useGlobalStore.getState().navigate;
|
|
154
|
+
navigate?.('/settings');
|
|
155
|
+
```
|