mirror of
https://github.com/lobehub/lobe-chat.git
synced 2025-12-20 01:12:52 +08:00
📝 docs: add some cursor rules & optimize codebase indexing (#7999)
* 📝 docs: add some cursor rules & optimize codebase indexing * 📝 docs: some code reviews issue by greptile
This commit is contained in:
94
.cursor/rules/backend-architecture.mdc
Normal file
94
.cursor/rules/backend-architecture.mdc
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: src/services/**/*,src/database/**/*,src/server/**/*
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# LobeChat 后端技术架构指南
|
||||||
|
|
||||||
|
本指南旨在阐述 LobeChat 项目的后端分层架构,重点介绍各核心目录的职责以及它们之间的协作方式。
|
||||||
|
|
||||||
|
## 目录结构映射
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── server/
|
||||||
|
│ ├── routers/ # tRPC API 路由定义
|
||||||
|
│ └── services/ # 业务逻辑服务层
|
||||||
|
│ └── */impls/ # 平台特定实现
|
||||||
|
├── database/
|
||||||
|
│ ├── models/ # 数据模型 (单表 CRUD)
|
||||||
|
│ ├── repositories/ # 仓库层 (复杂查询/聚合)
|
||||||
|
│ └── schemas/ # Drizzle ORM 表定义
|
||||||
|
└── services/ # 客户端服务 (调用 tRPC 或直接访问 Model)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心架构分层
|
||||||
|
|
||||||
|
LobeChat 的后端设计注重模块化、可测试性和灵活性,以适应不同的运行环境(如浏览器端 PGLite、服务端远程 PostgreSQL 以及 Electron 桌面应用)。
|
||||||
|
|
||||||
|
其主要分层如下:
|
||||||
|
|
||||||
|
1. **客户端服务层 (`src/services`)**:
|
||||||
|
* 位于 [src/services/](mdc:src/services)。
|
||||||
|
* 这是客户端业务逻辑的核心层,负责封装各种业务操作和数据处理逻辑。
|
||||||
|
* **环境适配**: 根据不同的运行环境,服务层会选择合适的数据访问方式:
|
||||||
|
* **本地数据库模式**: 直接调用 `Model` 层进行数据操作,适用于浏览器 PGLite 和本地 Electron 应用。
|
||||||
|
* **远程数据库模式**: 通过 `tRPC` 客户端调用服务端 API,适用于需要云同步的场景。
|
||||||
|
* **类型转换**: 对于简单的数据类型转换,直接在此层进行类型断言,如 `this.pluginModel.query() as Promise<LobeTool[]>`(参见 [src/services/plugin/client.ts](mdc:src/services/plugin/client.ts))。
|
||||||
|
* 每个服务模块通常包含 `client.ts`(本地模式)、`server.ts`(远程模式)和 `type.ts`(接口定义)文件。
|
||||||
|
|
||||||
|
2. **API 接口层 (`TRPC`)**:
|
||||||
|
* 位于 [src/server/routers/](mdc:src/server/routers)。
|
||||||
|
* 使用 `tRPC` 构建类型安全的 API。Router 根据运行时环境(如 Edge Functions, Node.js Lambda)进行组织。
|
||||||
|
* 负责接收客户端请求,并将其路由到相应的 `Service` 层进行处理。
|
||||||
|
|
||||||
|
3. **服务端服务层 (`server/services`)**:
|
||||||
|
* 位于 [src/server/services/](mdc:src/server/services)。
|
||||||
|
* 核心职责是封装独立的、可复用的业务逻辑单元。这些服务应易于测试。
|
||||||
|
* **平台差异抽象**: 一个关键特性是通过其内部的 `impls` 子目录(例如 [src/server/services/file/impls/](mdc:src/server/services/file/impls) 包含 `s3.ts` 和 `local.ts`)来抹平不同运行环境带来的差异(例如云端使用 S3 存储,桌面版使用本地文件系统)。这使得上层(如 `tRPC` routers)无需关心底层具体实现。
|
||||||
|
* 目标是使 `tRPC` router 层的逻辑尽可能纯粹,专注于请求处理和业务流程编排。
|
||||||
|
* 服务会调用 `Repository` 层或直接调用 `Model` 层进行数据持久化和检索,也可能调用其他服务。
|
||||||
|
|
||||||
|
4. **仓库层 (`Repositories`)**:
|
||||||
|
* 位于 [src/database/repositories/](mdc:src/database/repositories)。
|
||||||
|
* 主要处理**复杂的跨表查询和数据聚合**逻辑,特别是当需要从**多个 `Model`** 获取数据并进行组合时。
|
||||||
|
* 与 `Model` 层不同,`Repository` 层专注于复杂的业务查询场景,而不涉及简单的领域模型转换。
|
||||||
|
* 当业务逻辑涉及多表关联、复杂的数据统计或需要事务处理时,会使用 `Repository` 层。
|
||||||
|
* 如果数据操作简单(仅涉及单个 `Model`),则通常直接在 `src/services` 层调用 `Model` 并进行简单的类型断言。
|
||||||
|
|
||||||
|
5. **模型层 (`Models`)**:
|
||||||
|
* 位于 [src/database/models/](mdc:src/database/models) (例如 [src/database/models/plugin.ts](mdc:src/database/models/plugin.ts) 和 [src/database/models/document.ts](mdc:src/database/models/document.ts))。
|
||||||
|
* 提供对数据库中各个表(由 [src/database/schemas/](mdc:src/database/schemas) 中的 Drizzle ORM schema 定义)的基本 CRUD (创建、读取、更新、删除) 操作和简单的查询能力。
|
||||||
|
* `Model` 类专注于单个数据表的直接操作,**不涉及复杂的领域模型转换**,这些转换通常在上层的 `src/services` 中通过类型断言完成。
|
||||||
|
|
||||||
|
6. **数据库 (`Database`)**:
|
||||||
|
* **客户端模式 (浏览器/PWA)**: 使用 PGLite (基于 WASM 的 PostgreSQL),数据存储在用户浏览器本地。
|
||||||
|
* **服务端模式 (云部署)**: 使用远程 PostgreSQL 数据库。
|
||||||
|
* **Electron 桌面应用**:
|
||||||
|
* Electron 客户端会启动一个本地 Node.js 服务。
|
||||||
|
* 本地服务通过 `tRPC` 与 Electron 的渲染进程通信。
|
||||||
|
* 数据库选择依赖于是否开启**云同步**功能:
|
||||||
|
* **云同步开启**: 连接到远程 PostgreSQL 数据库。
|
||||||
|
* **云同步关闭**: 使用 PGLite (通过 Node.js 的 WASM 实现) 在本地存储数据。
|
||||||
|
|
||||||
|
## 数据流向说明
|
||||||
|
|
||||||
|
### 浏览器/PWA 模式
|
||||||
|
```
|
||||||
|
UI (React) → Zustand State → Model Layer → PGLite (本地数据库)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 服务端模式
|
||||||
|
```
|
||||||
|
UI (React) → Zustand State → tRPC Client → tRPC Routers → Services → Repositories/Models → Remote PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
### Electron 桌面应用模式
|
||||||
|
```
|
||||||
|
UI (Electron Renderer) → Zustand State → tRPC Client → 本地 Node.js 服务 → tRPC Routers → Services → Repositories/Models → PGLite/Remote PostgreSQL (取决于云同步设置)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
27
.cursor/rules/cursor-ux-optimize.mdc
Normal file
27
.cursor/rules/cursor-ux-optimize.mdc
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
## Formatting Response
|
||||||
|
|
||||||
|
This is about how you should format your responses.
|
||||||
|
|
||||||
|
### Render Markdown Table
|
||||||
|
|
||||||
|
- Be aware that the cursor chat you are in can't render markdown table correctly.
|
||||||
|
- IMPORTANT: Tables need to be rendered in plain text and not markdown
|
||||||
|
|
||||||
|
When rendering tables, do not use markdown table syntax or plain text alone. Instead, place the entire table inside a code/text block (using triple backticks). This ensures the table formatting is preserved and readable in the chat interface.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
+----+---------+-----------+
|
||||||
|
| ID | Name | Role |
|
||||||
|
+----+---------+-----------+
|
||||||
|
| 1 | Alice | Admin |
|
||||||
|
| 2 | Bob | User |
|
||||||
|
| 3 | Charlie | Moderator |
|
||||||
|
+----+---------+-----------+
|
||||||
|
```
|
||||||
8
.cursor/rules/define-database-model.mdc
Normal file
8
.cursor/rules/define-database-model.mdc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: src/database/models/**/*
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
1. first read [lobe-chat-backend-architecture.mdc](mdc:.cursor/rules/lobe-chat-backend-architecture.mdc)
|
||||||
|
2. refer to the [_template.ts](mdc:src/database/models/_template.ts) to create new model
|
||||||
|
3. if an operation involves multiple models or complex queries, consider defining it in the `repositories` layer under `src/database/repositories/`
|
||||||
202
.cursor/rules/drizzle-schema-style-guide.mdc
Normal file
202
.cursor/rules/drizzle-schema-style-guide.mdc
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: src/database/schemas/*
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# Drizzle ORM Schema Style Guide for lobe-chat
|
||||||
|
|
||||||
|
This document outlines the conventions and best practices for defining PostgreSQL Drizzle ORM schemas within the lobe-chat project.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- Drizzle configuration is managed in [drizzle.config.ts](mdc:drizzle.config.ts)
|
||||||
|
- Schema files are located in the src/database/schemas/ directory
|
||||||
|
- Migration files are output to `src/database/migrations/`
|
||||||
|
- The project uses `postgresql` dialect with `strict: true`
|
||||||
|
|
||||||
|
## Helper Functions
|
||||||
|
|
||||||
|
Commonly used column definitions, especially for timestamps, are centralized in [src/database/schemas/_helpers.ts](mdc:src/database/schemas/_helpers.ts):
|
||||||
|
- `timestamptz(name: string)`: Creates a timestamp column with timezone
|
||||||
|
- `createdAt()`, `updatedAt()`, `accessedAt()`: Helper functions for standard timestamp columns
|
||||||
|
- `timestamps`: An object `{ createdAt, updatedAt, accessedAt }` for easy inclusion in table definitions
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
- **Table Names**: Use plural snake_case (e.g., `users`, `agents`, `session_groups`)
|
||||||
|
- **Column Names**: Use snake_case (e.g., `user_id`, `created_at`, `background_color`)
|
||||||
|
|
||||||
|
## Column Definitions
|
||||||
|
|
||||||
|
### Primary Keys (PKs)
|
||||||
|
- Typically `text('id')` (or `varchar('id')` for some OIDC tables)
|
||||||
|
- Often use `.$defaultFn(() => idGenerator('table_name'))` for automatic ID generation with meaningful prefixes
|
||||||
|
- **ID Prefix Purpose**: Makes it easy for users and developers to distinguish different entity types at a glance
|
||||||
|
- For internal/system tables that users don't need to see, can use `uuid` or auto-increment keys
|
||||||
|
- Composite PKs are defined using `primaryKey({ columns: [t.colA, t.colB] })`
|
||||||
|
|
||||||
|
### Foreign Keys (FKs)
|
||||||
|
- Defined using `.references(() => otherTable.id, { onDelete: 'cascade' | 'set null' | 'no action' })`
|
||||||
|
- FK columns are usually named `related_table_singular_name_id` (e.g., `user_id` references `users.id`)
|
||||||
|
- Most tables include a `user_id` column referencing `users.id` with `onDelete: 'cascade'`
|
||||||
|
|
||||||
|
### Timestamps
|
||||||
|
- Consistently use the `...timestamps` spread from [_helpers.ts](mdc:src/database/schemas/_helpers.ts) for `created_at`, `updated_at`, and `accessed_at` columns
|
||||||
|
|
||||||
|
### Default Values
|
||||||
|
- `.$defaultFn(() => expression)` for dynamic defaults (e.g., `idGenerator()`, `randomSlug()`)
|
||||||
|
- `.default(staticValue)` for static defaults (e.g., `boolean('enabled').default(true)`)
|
||||||
|
|
||||||
|
### Indexes
|
||||||
|
- Defined in the table's second argument: `pgTable('name', {...columns}, (t) => ({ indexName: indexType().on(...) }))`
|
||||||
|
- Use `uniqueIndex()` for unique constraints and `index()` for non-unique indexes
|
||||||
|
- Naming pattern: `table_name_column(s)_idx` or `table_name_column(s)_unique`
|
||||||
|
- Many tables feature a `clientId: text('client_id')` column, often part of a composite unique index with `user_id`
|
||||||
|
|
||||||
|
### Data Types
|
||||||
|
- Common types: `text`, `varchar`, `jsonb`, `boolean`, `integer`, `uuid`, `pgTable`
|
||||||
|
- For `jsonb` fields, specify the TypeScript type using `.$type<MyType>()` for better type safety
|
||||||
|
|
||||||
|
## Zod Schemas & Type Inference
|
||||||
|
|
||||||
|
- Utilize `drizzle-zod` to generate Zod schemas for validation:
|
||||||
|
- `createInsertSchema(tableName)`
|
||||||
|
- `createSelectSchema(tableName)` (less common)
|
||||||
|
- Export inferred types: `export type NewEntity = typeof tableName.$inferInsert;` and `export type EntityItem = typeof tableName.$inferSelect;`
|
||||||
|
|
||||||
|
## Relations
|
||||||
|
|
||||||
|
- Table relationships are defined centrally in [src/database/schemas/relations.ts](mdc:src/database/schemas/relations.ts) using the `relations()` utility from `drizzle-orm`
|
||||||
|
|
||||||
|
## Code Style & Structure
|
||||||
|
|
||||||
|
- **File Organization**: Each main database entity typically has its own schema file (e.g., [user.ts](mdc:src/database/schemas/user.ts), [agent.ts](mdc:src/database/schemas/agent.ts))
|
||||||
|
- All schemas are re-exported from [src/database/schemas/index.ts](mdc:src/database/schemas/index.ts)
|
||||||
|
- **ESLint**: Files often start with `/* eslint-disable sort-keys-fix/sort-keys-fix */`
|
||||||
|
- **Comments**: Use JSDoc-style comments to explain the purpose of tables and complex columns, fields that are self-explanatory do not require jsdoc explanations, such as id, user_id, etc.
|
||||||
|
|
||||||
|
## Example Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// From src/database/schemas/agent.ts
|
||||||
|
export const agents = pgTable(
|
||||||
|
'agents',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => idGenerator('agents'))
|
||||||
|
.notNull(),
|
||||||
|
slug: varchar('slug', { length: 100 })
|
||||||
|
.$defaultFn(() => randomSlug(4))
|
||||||
|
.unique(),
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
clientId: text('client_id'),
|
||||||
|
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
// return array instead of object, the object style is deprecated
|
||||||
|
(t) => [
|
||||||
|
uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const insertAgentSchema = createInsertSchema(agents);
|
||||||
|
export type NewAgent = typeof agents.$inferInsert;
|
||||||
|
export type AgentItem = typeof agents.$inferSelect;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### 1. userId + clientId Pattern (Legacy)
|
||||||
|
Some existing tables include both fields for different purposes:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example from agents table (legacy pattern)
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
clientId: text('client_id'),
|
||||||
|
|
||||||
|
// Usually with a composite unique index
|
||||||
|
clientIdUnique: uniqueIndex('agents_client_id_user_id_unique').on(t.clientId, t.userId),
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`userId`**: Server-side user association, ensures data belongs to specific user
|
||||||
|
- **`clientId`**: Unique key for import/export operations, supports data migration between instances
|
||||||
|
- **Current Status**: New tables should NOT include `clientId` unless specifically needed for import/export functionality
|
||||||
|
- **Note**: This pattern is being phased out for new features to simplify the schema
|
||||||
|
|
||||||
|
### 2. Junction Tables (Many-to-Many Relationships)
|
||||||
|
Use composite primary keys for relationship tables:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example: agents_knowledge_bases (from agent.ts)
|
||||||
|
export const agentsKnowledgeBases = pgTable(
|
||||||
|
'agents_knowledge_bases',
|
||||||
|
{
|
||||||
|
agentId: text('agent_id').references(() => agents.id, { onDelete: 'cascade' }).notNull(),
|
||||||
|
knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, { onDelete: 'cascade' }).notNull(),
|
||||||
|
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
||||||
|
enabled: boolean('enabled').default(true),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(t) => [
|
||||||
|
primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern**: `{entity1}Id` + `{entity2}Id` as composite PK, plus `userId` for ownership
|
||||||
|
|
||||||
|
### 3. OIDC Tables Special Patterns
|
||||||
|
OIDC tables use `varchar` IDs instead of `text` with custom generators:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example from oidc.ts
|
||||||
|
export const oidcAuthorizationCodes = pgTable('oidc_authorization_codes', {
|
||||||
|
id: varchar('id', { length: 255 }).primaryKey(), // varchar not text
|
||||||
|
data: jsonb('data').notNull(),
|
||||||
|
expiresAt: timestamptz('expires_at').notNull(),
|
||||||
|
// ... other fields
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reason**: OIDC standards expect specific ID formats and lengths
|
||||||
|
|
||||||
|
### 4. File Processing with Async Tasks
|
||||||
|
File-related tables reference async task IDs for background processing:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example from files table
|
||||||
|
export const files = pgTable('files', {
|
||||||
|
// ... other fields
|
||||||
|
chunkTaskId: uuid('chunk_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
||||||
|
embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Purpose**:
|
||||||
|
- Track file chunking progress (breaking files into smaller pieces)
|
||||||
|
- Track embedding generation progress (converting text to vectors)
|
||||||
|
- Allow querying task status and handling failures
|
||||||
|
|
||||||
|
### 5. Slug Pattern (Legacy)
|
||||||
|
Some entities include auto-generated slugs - this is legacy code:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
slug: varchar('slug', { length: 100 })
|
||||||
|
.$defaultFn(() => randomSlug(4))
|
||||||
|
.unique(),
|
||||||
|
|
||||||
|
// Often with composite unique constraint
|
||||||
|
slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
|
||||||
|
```
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
By following these guidelines, maintain consistency, type safety, and maintainability across database schema definitions.
|
||||||
6
.cursor/rules/i18n/i18n-auto-attached.mdc
Normal file
6
.cursor/rules/i18n/i18n-auto-attached.mdc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: src/locales/**/*
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
read [i18n.mdc](mdc:.cursor/rules/i18n/i18n.mdc)
|
||||||
183
.cursor/rules/i18n/i18n.mdc
Normal file
183
.cursor/rules/i18n/i18n.mdc
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
---
|
||||||
|
description: i18n workflow and troubleshooting
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# LobeChat Internationalization (i18n) Guide
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
LobeChat uses **react-i18next** for internationalization with a well-structured namespace approach:
|
||||||
|
|
||||||
|
- **Default language**: Chinese (zh-CN) - serves as the source language
|
||||||
|
- **Supported locales**: 18 languages including English, Japanese, Korean, Arabic, etc.
|
||||||
|
- **Framework**: react-i18next with Next.js app router
|
||||||
|
- **Translation automation**: [@lobehub/i18n-cli](mdc:package.json) for automated translations, config file: .i18nrc.js
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/locales/
|
||||||
|
├── default/ # Source language files (zh-CN)
|
||||||
|
│ ├── index.ts # Namespace exports
|
||||||
|
│ ├── common.ts # Common translations
|
||||||
|
│ ├── chat.ts # Chat-related translations
|
||||||
|
│ ├── setting.ts # Settings translations
|
||||||
|
│ └── ... # Other namespace files
|
||||||
|
└── resources.ts # Type definitions and locale config
|
||||||
|
|
||||||
|
locales/ # Translated files
|
||||||
|
├── en-US/ # English translations
|
||||||
|
│ ├── common.json # Common translations
|
||||||
|
│ ├── chat.json # Chat translations
|
||||||
|
│ ├── setting.json # Settings translations
|
||||||
|
│ └── ... # Other namespace JSON files
|
||||||
|
├── ja-JP/ # Japanese translations
|
||||||
|
│ ├── common.json
|
||||||
|
│ ├── chat.json
|
||||||
|
│ └── ...
|
||||||
|
└── ... # Other language folders
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow for Adding New Translations
|
||||||
|
|
||||||
|
### 1. Add New Translation Keys
|
||||||
|
|
||||||
|
**Step 1**: Add translation key to the appropriate namespace file in [src/locales/default/](mdc:src/locales/default)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example: src/locales/default/common.ts
|
||||||
|
export default {
|
||||||
|
// ... existing keys
|
||||||
|
newFeature: {
|
||||||
|
title: "新功能标题",
|
||||||
|
description: "功能描述文案",
|
||||||
|
button: "操作按钮",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2**: If creating a new namespace, export it in [src/locales/default/index.ts](mdc:src/locales/default/index.ts)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import newNamespace from "./newNamespace";
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
// ... existing namespaces
|
||||||
|
newNamespace,
|
||||||
|
} as const;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Translation Process
|
||||||
|
|
||||||
|
**Development Mode** (Recommended):
|
||||||
|
|
||||||
|
- Manually add Chinese translations to corresponding JSON files in `locales/zh-CN/namespace.json`, this avoids running slow automation during development
|
||||||
|
- Don't auto add translations for other language like English etc, most of developer is Chinese,
|
||||||
|
|
||||||
|
**Production Mode**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate translations for all languages
|
||||||
|
npm run i18n
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage in Components
|
||||||
|
|
||||||
|
### Basic Usage with Hooks
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const MyComponent = () => {
|
||||||
|
const { t } = useTranslation("common"); // namespace
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{t("newFeature.title")}</h1>
|
||||||
|
<p>{t("newFeature.description")}</p>
|
||||||
|
<button>{t("newFeature.button")}</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Parameters
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const { t } = useTranslation("common");
|
||||||
|
|
||||||
|
// Translation key with interpolation
|
||||||
|
<p>{t("welcome.message", { name: "John" })}</p>;
|
||||||
|
|
||||||
|
// Corresponding locale file:
|
||||||
|
// welcome: { message: '欢迎 {{name}} 使用!' }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Namespaces
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const { t } = useTranslation(['common', 'chat']);
|
||||||
|
|
||||||
|
// Access different namespaces
|
||||||
|
<button>{t('common:save')}</button>
|
||||||
|
<span>{t('chat:typing')}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Safety
|
||||||
|
|
||||||
|
The project uses TypeScript for type-safe translations with auto-generated types from [src/locales/resources.ts](mdc:src/locales/resources.ts):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { DefaultResources, NS, Locales } from "@/locales/resources";
|
||||||
|
|
||||||
|
// Available types:
|
||||||
|
// - NS: Available namespace keys ('common' | 'chat' | 'setting' | ...)
|
||||||
|
// - Locales: Supported locale codes ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||||
|
|
||||||
|
// Type-safe namespace usage
|
||||||
|
const namespace: NS = "common"; // ✅ Valid
|
||||||
|
const locale: Locales = "en-US"; // ✅ Valid
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Namespace Organization
|
||||||
|
|
||||||
|
- **common**: Shared UI elements (buttons, labels, actions)
|
||||||
|
- **chat**: Chat-specific features
|
||||||
|
- **setting**: Configuration and settings
|
||||||
|
- **error**: Error messages and handling
|
||||||
|
- **[feature]**: Feature-specific or page specific namespaces
|
||||||
|
|
||||||
|
### 2. Key Naming Conventions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Hierarchical structure
|
||||||
|
export default {
|
||||||
|
modal: {
|
||||||
|
confirm: {
|
||||||
|
title: "确认操作",
|
||||||
|
message: "确定要执行此操作吗?",
|
||||||
|
actions: {
|
||||||
|
confirm: "确认",
|
||||||
|
cancel: "取消",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ❌ Avoid: Flat structure
|
||||||
|
export default {
|
||||||
|
modalConfirmTitle: "确认操作",
|
||||||
|
modalConfirmMessage: "确定要执行此操作吗?",
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Missing Translation Keys
|
||||||
|
|
||||||
|
- Check if the key exists in src/locales/default/namespace.ts
|
||||||
|
- Ensure proper namespace import in component
|
||||||
|
- Ensure new namespaces are exported in [src/locales/default/index.ts](mdc:src/locales/default/index.ts)
|
||||||
72
.cursor/rules/packages/lobe-ui.mdc
Normal file
72
.cursor/rules/packages/lobe-ui.mdc
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
description: @lobehub/ui components list
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
## @lobehub/ui Components
|
||||||
|
|
||||||
|
- General
|
||||||
|
ActionIcon
|
||||||
|
ActionIconGroup
|
||||||
|
Block
|
||||||
|
Button
|
||||||
|
Icon
|
||||||
|
- Data Display
|
||||||
|
Avatar
|
||||||
|
Collapse
|
||||||
|
FileTypeIcon
|
||||||
|
FluentEmoji
|
||||||
|
GuideCard
|
||||||
|
Highlighter
|
||||||
|
Hotkey
|
||||||
|
Image
|
||||||
|
List
|
||||||
|
Markdown
|
||||||
|
MaterialFileTypeIcon
|
||||||
|
Mermaid
|
||||||
|
Segmented
|
||||||
|
Snippet
|
||||||
|
SortableList
|
||||||
|
Tag
|
||||||
|
Tooltip
|
||||||
|
Video
|
||||||
|
- Data Entry
|
||||||
|
AutoComplete
|
||||||
|
CodeEditor
|
||||||
|
ColorSwatches
|
||||||
|
CopyButton
|
||||||
|
DatePicker
|
||||||
|
EditableText
|
||||||
|
EmojiPicker
|
||||||
|
Form
|
||||||
|
FormModal
|
||||||
|
HotkeyInput
|
||||||
|
ImageSelect
|
||||||
|
Input
|
||||||
|
SearchBar
|
||||||
|
Select
|
||||||
|
SliderWithInput
|
||||||
|
ThemeSwitch
|
||||||
|
- Feedback
|
||||||
|
Alert
|
||||||
|
Drawer
|
||||||
|
Modal
|
||||||
|
- Layout
|
||||||
|
DraggablePanel
|
||||||
|
Footer
|
||||||
|
Grid
|
||||||
|
Header
|
||||||
|
Layout
|
||||||
|
MaskShadow
|
||||||
|
ScrollShadow
|
||||||
|
- Navigation
|
||||||
|
Burger
|
||||||
|
Dropdown
|
||||||
|
Menu
|
||||||
|
SideNav
|
||||||
|
Tabs
|
||||||
|
Toc
|
||||||
|
- Theme
|
||||||
|
ConfigProvider
|
||||||
|
FontLoader
|
||||||
|
ThemeProvider
|
||||||
126
.cursor/rules/packages/react-layout-kit.mdc
Normal file
126
.cursor/rules/packages/react-layout-kit.mdc
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
description: react flex layout package `react-layout-kit` usage
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# React Layout Kit 使用指南
|
||||||
|
|
||||||
|
`react-layout-kit` 是一个功能丰富的 React flex 布局组件库,在 lobe-chat 项目中被广泛使用。以下是重点组件的使用方法:
|
||||||
|
|
||||||
|
## Flexbox 组件
|
||||||
|
|
||||||
|
Flexbox 是最常用的布局组件,用于创建弹性布局,类似于 CSS 的 `display: flex`。
|
||||||
|
|
||||||
|
### 基本用法
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { Flexbox } from 'react-layout-kit';
|
||||||
|
|
||||||
|
// 默认垂直布局
|
||||||
|
<Flexbox>
|
||||||
|
<div>子元素1</div>
|
||||||
|
<div>子元素2</div>
|
||||||
|
</Flexbox>
|
||||||
|
|
||||||
|
// 水平布局
|
||||||
|
<Flexbox horizontal>
|
||||||
|
<div>左侧元素</div>
|
||||||
|
<div>右侧元素</div>
|
||||||
|
</Flexbox>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 常用属性
|
||||||
|
|
||||||
|
- `horizontal`: 布尔值,设置为水平方向布局
|
||||||
|
- `flex`: 数值或字符串,控制 flex 属性
|
||||||
|
- `gap`: 数值,设置子元素之间的间距
|
||||||
|
- `align`: 对齐方式,如 'center', 'flex-start' 等
|
||||||
|
- `justify`: 主轴对齐方式,如 'space-between', 'center' 等
|
||||||
|
- `padding`: 内边距值
|
||||||
|
- `paddingInline`: 水平内边距值
|
||||||
|
- `paddingBlock`: 垂直内边距值
|
||||||
|
- `width/height`: 设置宽高,通常用 `'100%'` 或具体像素值
|
||||||
|
- `style`: 自定义样式对象
|
||||||
|
|
||||||
|
### 实际应用示例
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 经典三栏布局
|
||||||
|
<Flexbox horizontal height={'100%'} width={'100%'}>
|
||||||
|
{/* 左侧边栏 */}
|
||||||
|
<Flexbox
|
||||||
|
width={260}
|
||||||
|
style={{
|
||||||
|
borderRight: `1px solid ${theme.colorBorderSecondary}`,
|
||||||
|
height: '100%',
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SidebarContent />
|
||||||
|
</Flexbox>
|
||||||
|
|
||||||
|
{/* 中间内容区 */}
|
||||||
|
<Flexbox
|
||||||
|
flex={1}
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 主要内容 */}
|
||||||
|
<Flexbox flex={1} padding={24} style={{ overflowY: 'auto' }}>
|
||||||
|
<MainContent />
|
||||||
|
</Flexbox>
|
||||||
|
|
||||||
|
{/* 底部区域 */}
|
||||||
|
<Flexbox
|
||||||
|
style={{
|
||||||
|
borderTop: `1px solid ${theme.colorBorderSecondary}`,
|
||||||
|
padding: '16px 24px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Footer />
|
||||||
|
</Flexbox>
|
||||||
|
</Flexbox>
|
||||||
|
</Flexbox>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Center 组件
|
||||||
|
|
||||||
|
Center 是对 Flexbox 的封装,使子元素水平和垂直居中。
|
||||||
|
|
||||||
|
### 基本用法
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { Center } from 'react-layout-kit';
|
||||||
|
|
||||||
|
<Center width={'100%'} height={'100%'}>
|
||||||
|
<Content />
|
||||||
|
</Center>
|
||||||
|
```
|
||||||
|
|
||||||
|
Center 组件继承了 Flexbox 的所有属性,同时默认设置了居中对齐。主要用于快速创建居中布局。
|
||||||
|
|
||||||
|
### 实际应用示例
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 登录页面居中布局
|
||||||
|
<Flexbox height={'100%'} width={'100%'}>
|
||||||
|
<Center height={'100%'} width={'100%'}>
|
||||||
|
<LoginForm />
|
||||||
|
</Center>
|
||||||
|
</Flexbox>
|
||||||
|
|
||||||
|
// 图标居中显示
|
||||||
|
<Center className={styles.icon} flex={'none'} height={40} width={40}>
|
||||||
|
<Icon icon={icon} size={24} />
|
||||||
|
</Center>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. 使用 `flex={1}` 让组件填充可用空间
|
||||||
|
2. 使用 `gap` 代替传统的 margin 设置元素间距
|
||||||
|
3. 嵌套 Flexbox 创建复杂布局
|
||||||
|
4. 设置 `overflow: 'auto'` 使内容可滚动
|
||||||
|
5. 使用 `horizontal` 创建水平布局,默认为垂直布局
|
||||||
|
6. 与 `antd-style` 的 `useTheme` hook 配合使用创建主题响应式的布局
|
||||||
46
.cursor/rules/project-introduce.mdc
Normal file
46
.cursor/rules/project-introduce.mdc
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
## Project Description
|
||||||
|
|
||||||
|
You are developing an open-source, modern-design AI chat framework: lobe chat.
|
||||||
|
|
||||||
|
Emoji logo: 🤯
|
||||||
|
|
||||||
|
|
||||||
|
## Project Technologies Stack
|
||||||
|
|
||||||
|
read [package.json](mdc:package.json) to know all npm packages you can use.
|
||||||
|
read [folder-structure.mdx](mdc:docs/development/basic/folder-structure.mdx) to learn project structure.
|
||||||
|
|
||||||
|
The project uses the following technologies:
|
||||||
|
|
||||||
|
- pnpm as package manager
|
||||||
|
- Next.js 15 for frontend and backend, using app router instead of pages router
|
||||||
|
- react 19, using hooks, functional components, react server components
|
||||||
|
- TypeScript programming language
|
||||||
|
- antd, @lobehub/ui for component framework
|
||||||
|
- antd-style for css-in-js framework
|
||||||
|
- react-layout-kit for flex layout
|
||||||
|
- react-i18next for i18n
|
||||||
|
- lucide-react, @ant-design/icons for icons
|
||||||
|
- @lobehub/icons for AI provider/model logo icon
|
||||||
|
- @formkit/auto-animate for react list animation
|
||||||
|
- zustand for global state management
|
||||||
|
- nuqs for type-safe search params state manager
|
||||||
|
- SWR for react data fetch
|
||||||
|
- aHooks for react hooks library
|
||||||
|
- dayjs for date and time library
|
||||||
|
- lodash-es for utility library
|
||||||
|
- zod for data validation
|
||||||
|
- TRPC for type safe backend
|
||||||
|
- PGLite for client DB and PostgreSQL for backend DB
|
||||||
|
- Drizzle ORM
|
||||||
|
- Vitest for testing, testing-library for react component test
|
||||||
|
- Prettier for code formatting
|
||||||
|
- ESLint for code linting
|
||||||
|
- Cursor AI for code editing and AI coding assistance
|
||||||
|
|
||||||
|
Note: All tools and libraries used are the latest versions. The application only needs to be compatible with the latest browsers;
|
||||||
68
.cursor/rules/react-component.mdc
Normal file
68
.cursor/rules/react-component.mdc
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs: *.tsx
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# react component 编写指南
|
||||||
|
|
||||||
|
- 如果要写复杂样式的话用 antd-style ,简单的话可以用 style 属性直接写内联样式
|
||||||
|
- 如果需要 flex 布局或者居中布局应该使用 react-layout-kit
|
||||||
|
- 选择组件库中的组件时优先使用 [lobe-ui.mdc](mdc:.cursor/rules/package-usage/lobe-ui.mdc) 有的,然后才是 antd 的,不知道 @lobehub/ui 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜
|
||||||
|
|
||||||
|
## 访问 theme 的两种方式
|
||||||
|
|
||||||
|
### 使用 antd-style 的 useTheme hook
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useTheme } from 'antd-style';
|
||||||
|
|
||||||
|
const MyComponent = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
color: theme.colorPrimary,
|
||||||
|
backgroundColor: theme.colorBgContainer,
|
||||||
|
padding: theme.padding,
|
||||||
|
borderRadius: theme.borderRadius
|
||||||
|
}}>
|
||||||
|
使用主题 token 的组件
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 antd-style 的 createStyles
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const useStyles = createStyles(({ css, token }) => {
|
||||||
|
return {
|
||||||
|
container: css`
|
||||||
|
background-color: ${token.colorBgContainer};
|
||||||
|
border-radius: ${token.borderRadius}px;
|
||||||
|
padding: ${token.padding}px;
|
||||||
|
color: ${token.colorText};
|
||||||
|
`,
|
||||||
|
title: css`
|
||||||
|
font-size: ${token.fontSizeLG}px;
|
||||||
|
font-weight: ${token.fontWeightStrong};
|
||||||
|
margin-bottom: ${token.marginSM}px;
|
||||||
|
`,
|
||||||
|
content: css`
|
||||||
|
font-size: ${token.fontSize}px;
|
||||||
|
line-height: ${token.lineHeight};
|
||||||
|
`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const Card: FC<CardProps> = ({ title, content }) => {
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flexbox className={styles.container}>
|
||||||
|
<div className={styles.title}>{title}</div>
|
||||||
|
<div className={styles.content}>{content}</div>
|
||||||
|
</Flexbox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
6
.cursorindexingignore
Normal file
6
.cursorindexingignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||||
|
locales/
|
||||||
|
apps/desktop/resources/locales/
|
||||||
|
**/__snapshots__/
|
||||||
|
**/fixtures/
|
||||||
|
src/database/migrations/
|
||||||
Reference in New Issue
Block a user