@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.354
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/.agents/skills/add-provider-doc/SKILL.md +90 -0
- package/.agents/skills/add-setting-env/SKILL.md +106 -0
- package/.agents/skills/debug/SKILL.md +66 -0
- package/.agents/skills/desktop/SKILL.md +78 -0
- package/.agents/skills/desktop/references/feature-implementation.md +99 -0
- package/.agents/skills/desktop/references/local-tools.md +133 -0
- package/.agents/skills/desktop/references/menu-config.md +103 -0
- package/.agents/skills/desktop/references/window-management.md +143 -0
- package/.agents/skills/drizzle/SKILL.md +129 -0
- package/.agents/skills/drizzle/references/db-migrations.md +50 -0
- package/.agents/skills/hotkey/SKILL.md +90 -0
- package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
- package/.agents/skills/linear/SKILL.md +51 -0
- package/.agents/skills/microcopy/SKILL.md +83 -0
- package/.agents/skills/modal/SKILL.md +102 -0
- package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
- package/.agents/skills/react/SKILL.md +73 -0
- package/.agents/skills/react/references/layout-kit.md +100 -0
- package/.agents/skills/recent-data/SKILL.md +108 -0
- package/.agents/skills/testing/SKILL.md +89 -0
- package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
- package/.agents/skills/testing/references/db-model-test.md +124 -0
- package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
- package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
- package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
- package/.agents/skills/typescript/SKILL.md +52 -0
- package/.agents/skills/zustand/SKILL.md +78 -0
- package/.agents/skills/zustand/references/action-patterns.md +125 -0
- package/.agents/skills/zustand/references/slice-organization.md +125 -0
- package/AGENTS.md +42 -55
- package/CHANGELOG.md +33 -0
- package/CLAUDE.md +57 -46
- package/GEMINI.md +47 -39
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
- package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
- package/src/libs/pdfjs/index.tsx +25 -0
- package/src/store/test-coverage.md +5 -5
- package/.cursor/rules/add-provider-doc.mdc +0 -183
- package/.cursor/rules/add-setting-env.mdc +0 -175
- package/.cursor/rules/cursor-rules.mdc +0 -28
- package/.cursor/rules/db-migrations.mdc +0 -46
- package/.cursor/rules/debug-usage.mdc +0 -86
- package/.cursor/rules/desktop-controller-tests.mdc +0 -189
- package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
- package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
- package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
- package/.cursor/rules/desktop-window-management.mdc +0 -301
- package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
- package/.cursor/rules/hotkey.mdc +0 -162
- package/.cursor/rules/linear.mdc +0 -53
- package/.cursor/rules/microcopy-cn.mdc +0 -158
- package/.cursor/rules/microcopy-en.mdc +0 -148
- package/.cursor/rules/modal-imperative.mdc +0 -162
- package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
- package/.cursor/rules/project-introduce.mdc +0 -36
- package/.cursor/rules/react.mdc +0 -169
- package/.cursor/rules/recent-data-usage.mdc +0 -139
- package/.cursor/rules/rules-index.mdc +0 -44
- package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
- package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
- package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
- package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
- package/.cursor/rules/typescript.mdc +0 -55
- package/.cursor/rules/zustand-action-patterns.mdc +0 -328
- package/.cursor/rules/zustand-slice-organization.mdc +0 -308
- package/src/libs/pdfjs/pdf.worker.ts +0 -1
- package/src/libs/pdfjs/worker.ts +0 -12
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
- /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description:
|
|
3
|
-
globs: src/database/schemas/*
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Drizzle ORM Schema Style Guide for lobe-chat
|
|
8
|
-
|
|
9
|
-
This document outlines the conventions and best practices for defining PostgreSQL Drizzle ORM schemas within the lobe-chat project.
|
|
10
|
-
|
|
11
|
-
## Configuration
|
|
12
|
-
|
|
13
|
-
- Drizzle configuration is managed in `drizzle.config.ts`
|
|
14
|
-
- Schema files are located in the src/database/schemas/ directory
|
|
15
|
-
- Migration files are output to `src/database/migrations/`
|
|
16
|
-
- The project uses `postgresql` dialect with `strict: true`
|
|
17
|
-
|
|
18
|
-
## Helper Functions
|
|
19
|
-
|
|
20
|
-
Commonly used column definitions, especially for timestamps, are centralized in `src/database/schemas/_helpers.ts`:
|
|
21
|
-
|
|
22
|
-
- `timestamptz(name: string)`: Creates a timestamp column with timezone
|
|
23
|
-
- `createdAt()`, `updatedAt()`, `accessedAt()`: Helper functions for standard timestamp columns
|
|
24
|
-
- `timestamps`: An object `{ createdAt, updatedAt, accessedAt }` for easy inclusion in table definitions
|
|
25
|
-
|
|
26
|
-
## Naming Conventions
|
|
27
|
-
|
|
28
|
-
- **Table Names**: Use plural snake_case (e.g., `users`, `agents`, `session_groups`)
|
|
29
|
-
- **Column Names**: Use snake_case (e.g., `user_id`, `created_at`, `background_color`)
|
|
30
|
-
|
|
31
|
-
## Column Definitions
|
|
32
|
-
|
|
33
|
-
### Primary Keys (PKs)
|
|
34
|
-
|
|
35
|
-
- Typically `text('id')` (or `varchar('id')` for some OIDC tables)
|
|
36
|
-
- Often use `.$defaultFn(() => idGenerator('table_name'))` for automatic ID generation with meaningful prefixes
|
|
37
|
-
- **ID Prefix Purpose**: Makes it easy for users and developers to distinguish different entity types at a glance
|
|
38
|
-
- For internal/system tables that users don't need to see, can use `uuid` or auto-increment keys
|
|
39
|
-
- Composite PKs are defined using `primaryKey({ columns: [t.colA, t.colB] })`
|
|
40
|
-
|
|
41
|
-
### Foreign Keys (FKs)
|
|
42
|
-
|
|
43
|
-
- Defined using `.references(() => otherTable.id, { onDelete: 'cascade' | 'set null' | 'no action' })`
|
|
44
|
-
- FK columns are usually named `related_table_singular_name_id` (e.g., `user_id` references `users.id`)
|
|
45
|
-
- Most tables include a `user_id` column referencing `users.id` with `onDelete: 'cascade'`
|
|
46
|
-
|
|
47
|
-
### Timestamps
|
|
48
|
-
|
|
49
|
-
- Consistently use the `...timestamps` spread from `_helpers.ts` for `created_at`, `updated_at`, and `accessed_at` columns
|
|
50
|
-
|
|
51
|
-
### Default Values
|
|
52
|
-
|
|
53
|
-
- `.$defaultFn(() => expression)` for dynamic defaults (e.g., `idGenerator()`, `randomSlug()`)
|
|
54
|
-
- `.default(staticValue)` for static defaults (e.g., `boolean('enabled').default(true)`)
|
|
55
|
-
|
|
56
|
-
### Indexes
|
|
57
|
-
|
|
58
|
-
- Defined in the table's second argument: `pgTable('name', {...columns}, (t) => ({ indexName: indexType().on(...) }))`
|
|
59
|
-
- Use `uniqueIndex()` for unique constraints and `index()` for non-unique indexes
|
|
60
|
-
- Naming pattern: `table_name_column(s)_idx` or `table_name_column(s)_unique`
|
|
61
|
-
- Many tables feature a `clientId: text('client_id')` column, often part of a composite unique index with `user_id`
|
|
62
|
-
|
|
63
|
-
### Data Types
|
|
64
|
-
|
|
65
|
-
- Common types: `text`, `varchar`, `jsonb`, `boolean`, `integer`, `uuid`, `pgTable`
|
|
66
|
-
- For `jsonb` fields, specify the TypeScript type using `.$type<MyType>()` for better type safety
|
|
67
|
-
|
|
68
|
-
## Zod Schemas & Type Inference
|
|
69
|
-
|
|
70
|
-
- Utilize `drizzle-zod` to generate Zod schemas for validation:
|
|
71
|
-
- `createInsertSchema(tableName)`
|
|
72
|
-
- `createSelectSchema(tableName)` (less common)
|
|
73
|
-
- Export inferred types: `export type NewEntity = typeof tableName.$inferInsert;` and `export type EntityItem = typeof tableName.$inferSelect;`
|
|
74
|
-
|
|
75
|
-
## Relations
|
|
76
|
-
|
|
77
|
-
- Table relationships are defined centrally in `src/database/schemas/relations.ts` using the `relations()` utility from `drizzle-orm`
|
|
78
|
-
|
|
79
|
-
## Code Style & Structure
|
|
80
|
-
|
|
81
|
-
- **File Organization**: Each main database entity typically has its own schema file (e.g., `user.ts`, `agent.ts`)
|
|
82
|
-
- All schemas are re-exported from `src/database/schemas/index.ts`
|
|
83
|
-
- **ESLint**: Files often start with `/* eslint-disable sort-keys-fix/sort-keys-fix */`
|
|
84
|
-
- **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.
|
|
85
|
-
|
|
86
|
-
## Example Pattern
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
// From src/database/schemas/agent.ts
|
|
90
|
-
export const agents = pgTable(
|
|
91
|
-
'agents',
|
|
92
|
-
{
|
|
93
|
-
id: text('id')
|
|
94
|
-
.primaryKey()
|
|
95
|
-
.$defaultFn(() => idGenerator('agents'))
|
|
96
|
-
.notNull(),
|
|
97
|
-
slug: varchar('slug', { length: 100 })
|
|
98
|
-
.$defaultFn(() => randomSlug(4))
|
|
99
|
-
.unique(),
|
|
100
|
-
userId: text('user_id')
|
|
101
|
-
.references(() => users.id, { onDelete: 'cascade' })
|
|
102
|
-
.notNull(),
|
|
103
|
-
clientId: text('client_id'),
|
|
104
|
-
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
|
105
|
-
...timestamps,
|
|
106
|
-
},
|
|
107
|
-
// return array instead of object, the object style is deprecated
|
|
108
|
-
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
export const insertAgentSchema = createInsertSchema(agents);
|
|
112
|
-
export type NewAgent = typeof agents.$inferInsert;
|
|
113
|
-
export type AgentItem = typeof agents.$inferSelect;
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Common Patterns
|
|
117
|
-
|
|
118
|
-
### 1. userId + clientId Pattern (Legacy)
|
|
119
|
-
|
|
120
|
-
Some existing tables include both fields for different purposes:
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
// Example from agents table (legacy pattern)
|
|
124
|
-
userId: text('user_id')
|
|
125
|
-
.references(() => users.id, { onDelete: 'cascade' })
|
|
126
|
-
.notNull(),
|
|
127
|
-
clientId: text('client_id'),
|
|
128
|
-
|
|
129
|
-
// Usually with a composite unique index
|
|
130
|
-
clientIdUnique: uniqueIndex('agents_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
- **`userId`**: Server-side user association, ensures data belongs to specific user
|
|
134
|
-
- **`clientId`**: Unique key for import/export operations, supports data migration between instances
|
|
135
|
-
- **Current Status**: New tables should NOT include `clientId` unless specifically needed for import/export functionality
|
|
136
|
-
- **Note**: This pattern is being phased out for new features to simplify the schema
|
|
137
|
-
|
|
138
|
-
### 2. Junction Tables (Many-to-Many Relationships)
|
|
139
|
-
|
|
140
|
-
Use composite primary keys for relationship tables:
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
// Example: agents_knowledge_bases (from agent.ts)
|
|
144
|
-
export const agentsKnowledgeBases = pgTable(
|
|
145
|
-
'agents_knowledge_bases',
|
|
146
|
-
{
|
|
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(),
|
|
156
|
-
enabled: boolean('enabled').default(true),
|
|
157
|
-
...timestamps,
|
|
158
|
-
},
|
|
159
|
-
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
|
160
|
-
);
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**Pattern**: `{entity1}Id` + `{entity2}Id` as composite PK, plus `userId` for ownership
|
|
164
|
-
|
|
165
|
-
### 3. OIDC Tables Special Patterns
|
|
166
|
-
|
|
167
|
-
OIDC tables use `varchar` IDs instead of `text` with custom generators:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// Example from oidc.ts
|
|
171
|
-
export const oidcAuthorizationCodes = pgTable('oidc_authorization_codes', {
|
|
172
|
-
id: varchar('id', { length: 255 }).primaryKey(), // varchar not text
|
|
173
|
-
data: jsonb('data').notNull(),
|
|
174
|
-
expiresAt: timestamptz('expires_at').notNull(),
|
|
175
|
-
// ... other fields
|
|
176
|
-
});
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Reason**: OIDC standards expect specific ID formats and lengths
|
|
180
|
-
|
|
181
|
-
### 4. File Processing with Async Tasks
|
|
182
|
-
|
|
183
|
-
File-related tables reference async task IDs for background processing:
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
// Example from files table
|
|
187
|
-
export const files = pgTable('files', {
|
|
188
|
-
// ... other fields
|
|
189
|
-
chunkTaskId: uuid('chunk_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
|
190
|
-
embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, {
|
|
191
|
-
onDelete: 'set null',
|
|
192
|
-
}),
|
|
193
|
-
// ...
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
**Purpose**:
|
|
198
|
-
|
|
199
|
-
- Track file chunking progress (breaking files into smaller pieces)
|
|
200
|
-
- Track embedding generation progress (converting text to vectors)
|
|
201
|
-
- Allow querying task status and handling failures
|
|
202
|
-
|
|
203
|
-
### 5. Slug Pattern (Legacy)
|
|
204
|
-
|
|
205
|
-
Some entities include auto-generated slugs - this is legacy code:
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
slug: varchar('slug', { length: 100 })
|
|
209
|
-
.$defaultFn(() => randomSlug(4))
|
|
210
|
-
.unique(),
|
|
211
|
-
|
|
212
|
-
// Often with composite unique constraint
|
|
213
|
-
slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
|
|
214
|
-
```
|
|
215
|
-
|
|
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
|
|
217
|
-
|
|
218
|
-
By following these guidelines, maintain consistency, type safety, and maintainability across database schema definitions.
|
package/.cursor/rules/hotkey.mdc
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: false
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# 如何添加新的快捷键:开发者指南
|
|
6
|
-
|
|
7
|
-
本指南将带您一步步地向 LobeChat 添加一个新的快捷键功能。我们将通过一个完整示例,演示从定义到实现的整个过程。
|
|
8
|
-
|
|
9
|
-
## 示例场景
|
|
10
|
-
|
|
11
|
-
假设我们要添加一个新的快捷键功能:**快速清空聊天记录**,快捷键为 `Mod+Shift+Backspace`。
|
|
12
|
-
|
|
13
|
-
## 步骤 1:更新快捷键常量定义
|
|
14
|
-
|
|
15
|
-
首先,在 `src/types/hotkey.ts` 中更新 `HotkeyEnum`:
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
export const HotkeyEnum = {
|
|
19
|
-
// 已有的快捷键...
|
|
20
|
-
AddUserMessage: 'addUserMessage',
|
|
21
|
-
EditMessage: 'editMessage',
|
|
22
|
-
|
|
23
|
-
// 新增快捷键
|
|
24
|
-
ClearChat: 'clearChat', // 添加这一行
|
|
25
|
-
|
|
26
|
-
// 其他已有快捷键...
|
|
27
|
-
} as const;
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## 步骤 2:注册默认快捷键
|
|
31
|
-
|
|
32
|
-
在 `src/const/hotkeys.ts` 中添加快捷键的默认配置:
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import { KeyMapEnum as Key, combineKeys } from '@lobehub/ui';
|
|
36
|
-
|
|
37
|
-
// ...现有代码
|
|
38
|
-
|
|
39
|
-
export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
|
|
40
|
-
// 现有的快捷键配置...
|
|
41
|
-
|
|
42
|
-
// 添加新的快捷键配置
|
|
43
|
-
{
|
|
44
|
-
group: HotkeyGroupEnum.Conversation, // 归类到会话操作组
|
|
45
|
-
id: HotkeyEnum.ClearChat,
|
|
46
|
-
keys: combineKeys([Key.Mod, Key.Shift, Key.Backspace]),
|
|
47
|
-
scopes: [HotkeyScopeEnum.Chat], // 在聊天作用域下生效
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
// 其他现有快捷键...
|
|
51
|
-
];
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## 步骤 3:添加国际化翻译
|
|
55
|
-
|
|
56
|
-
在 `src/locales/default/hotkey.ts` 中添加对应的文本描述:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
import { HotkeyI18nTranslations } from '@/types/hotkey';
|
|
60
|
-
|
|
61
|
-
const hotkey: HotkeyI18nTranslations = {
|
|
62
|
-
// 现有翻译...
|
|
63
|
-
|
|
64
|
-
// 添加新快捷键的翻译
|
|
65
|
-
clearChat: {
|
|
66
|
-
desc: '清空当前会话的所有消息记录',
|
|
67
|
-
title: '清空聊天记录',
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
// 其他现有翻译...
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export default hotkey;
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
如需支持其他语言,还需要在相应的语言文件中添加对应翻译。
|
|
77
|
-
|
|
78
|
-
## 步骤 4:创建并注册快捷键 Hook
|
|
79
|
-
|
|
80
|
-
在 `src/hooks/useHotkeys/chatScope.ts` 中添加新的 Hook:
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
export const useClearChatHotkey = () => {
|
|
84
|
-
const clearMessages = useChatStore((s) => s.clearMessages);
|
|
85
|
-
const { t } = useTranslation();
|
|
86
|
-
|
|
87
|
-
return useHotkeyById(HotkeyEnum.ClearChat, showConfirm);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// 注册聚合
|
|
91
|
-
|
|
92
|
-
export const useRegisterChatHotkeys = () => {
|
|
93
|
-
const { enableScope, disableScope } = useHotkeysContext();
|
|
94
|
-
|
|
95
|
-
useOpenChatSettingsHotkey();
|
|
96
|
-
// ...其他快捷键
|
|
97
|
-
useClearChatHotkey();
|
|
98
|
-
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
enableScope(HotkeyScopeEnum.Chat);
|
|
101
|
-
return () => disableScope(HotkeyScopeEnum.Chat);
|
|
102
|
-
}, []);
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
};
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## 步骤 5:给相应 UI 元素添加 Tooltip 提示(可选)
|
|
109
|
-
|
|
110
|
-
如果有对应的 UI 按钮,可以添加快捷键提示:
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
import { DeleteOutlined } from '@ant-design/icons';
|
|
114
|
-
import { Tooltip } from '@lobehub/ui';
|
|
115
|
-
import { Button } from 'antd';
|
|
116
|
-
import { useTranslation } from 'react-i18next';
|
|
117
|
-
|
|
118
|
-
import { useUserStore } from '@/store/user';
|
|
119
|
-
import { settingsSelectors } from '@/store/user/selectors';
|
|
120
|
-
import { HotkeyEnum } from '@/types/hotkey';
|
|
121
|
-
|
|
122
|
-
const ClearChatButton = () => {
|
|
123
|
-
const { t } = useTranslation(['hotkey', 'chat']);
|
|
124
|
-
const clearChatHotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.ClearChat));
|
|
125
|
-
|
|
126
|
-
// 获取清空聊天的方法
|
|
127
|
-
const clearMessages = useChatStore((s) => s.clearMessages);
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<Tooltip hotkey={clearChatHotkey} title={t('clearChat.title', { ns: 'hotkey' })}>
|
|
131
|
-
<Button icon={<DeleteOutlined />} onClick={clearMessages} />
|
|
132
|
-
</Tooltip>
|
|
133
|
-
);
|
|
134
|
-
};
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## 步骤 6:测试新快捷键
|
|
138
|
-
|
|
139
|
-
1. 启动开发服务器
|
|
140
|
-
2. 打开聊天页面
|
|
141
|
-
3. 按下设置的快捷键组合(`Cmd+Shift+Backspace` 或 `Ctrl+Shift+Backspace`)
|
|
142
|
-
4. 确认功能正常工作
|
|
143
|
-
5. 检查快捷键设置面板中是否正确显示了新快捷键
|
|
144
|
-
|
|
145
|
-
## 最佳实践
|
|
146
|
-
|
|
147
|
-
1. **作用域考虑**:根据功能决定快捷键应属于全局作用域还是聊天作用域
|
|
148
|
-
2. **分组合理**:将快捷键放在合适的功能组中(System/Layout/Conversation)
|
|
149
|
-
3. **冲突检查**:确保新快捷键不会与现有系统、浏览器或应用快捷键冲突
|
|
150
|
-
4. **平台适配**:使用 `Key.Mod` 而非硬编码 `Ctrl` 或 `Cmd`,以适配不同平台
|
|
151
|
-
5. **提供清晰描述**:为快捷键添加明确的标题和描述,帮助用户理解功能
|
|
152
|
-
|
|
153
|
-
按照以上步骤,您可以轻松地向系统添加新的快捷键功能,提升用户体验。如有特殊需求,如桌面专属快捷键,可以通过 `isDesktop` 标记进行区分处理。
|
|
154
|
-
|
|
155
|
-
## 常见问题排查
|
|
156
|
-
|
|
157
|
-
- **快捷键未生效**:检查作用域是否正确,以及是否在 RegisterHotkeys 中调用了对应的 hook
|
|
158
|
-
- **快捷键设置面板未显示**:确认在 HOTKEYS_REGISTRATION 中正确配置了快捷键
|
|
159
|
-
- **快捷键冲突**:在 HotkeyInput 组件中可以检测到冲突,用户会看到警告
|
|
160
|
-
- **功能在某些页面失效**:确认是否注册在了正确的作用域,以及相关页面是否激活了该作用域
|
|
161
|
-
|
|
162
|
-
通过这些步骤,您可以确保新添加的快捷键功能稳定、可靠且用户友好。
|
package/.cursor/rules/linear.mdc
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: true
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Linear Issue Management
|
|
6
|
-
|
|
7
|
-
When working with Linear issues:
|
|
8
|
-
|
|
9
|
-
1. **Retrieve issue details** before starting work using `mcp__linear-server__get_issue`
|
|
10
|
-
2. **Check for sub-issues**: If the issue has sub-issues, retrieve and review ALL sub-issues using `mcp__linear-server__list_issues` with `parentId` filter before starting work
|
|
11
|
-
3. **Update issue status** when completing tasks using `mcp__linear-server__update_issue`
|
|
12
|
-
4. **MUST add completion comment** using `mcp__linear-server__create_comment`
|
|
13
|
-
|
|
14
|
-
## Creating Issues
|
|
15
|
-
|
|
16
|
-
When creating new Linear issues using `mcp__linear-server__create_issue`, **MUST add the `claude code` label** to indicate the issue was created by Claude Code.
|
|
17
|
-
|
|
18
|
-
## Completion Comment (REQUIRED)
|
|
19
|
-
|
|
20
|
-
**Every time you complete an issue, you MUST add a comment summarizing the work done.** This is critical for:
|
|
21
|
-
|
|
22
|
-
- Team visibility and knowledge sharing
|
|
23
|
-
- Code review context
|
|
24
|
-
- Future reference and debugging
|
|
25
|
-
|
|
26
|
-
## PR Linear Issue Association (REQUIRED)
|
|
27
|
-
|
|
28
|
-
**When creating PRs for Linear issues, MUST include magic keywords in PR body:** `Fixes LOBE-123`, `Closes LOBE-123`, or `Resolves LOBE-123`, and summarize the work done in the linear issue comment and update the issue status to "In Review".
|
|
29
|
-
|
|
30
|
-
## IMPORTANT: Per-Issue Completion Rule
|
|
31
|
-
|
|
32
|
-
**When working on multiple issues (e.g., parent issue with sub-issues), you MUST update status and add comment for EACH issue IMMEDIATELY after completing it.** Do NOT wait until all issues are done to update them in batch.
|
|
33
|
-
|
|
34
|
-
**Workflow for EACH individual issue:**
|
|
35
|
-
|
|
36
|
-
1. Complete the implementation for this specific issue
|
|
37
|
-
2. Run type check: `bun run type-check`
|
|
38
|
-
3. Run related tests if applicable
|
|
39
|
-
4. Create PR if needed
|
|
40
|
-
5. **IMMEDIATELY** update issue status to **"In Review"** (NOT "Done"): `mcp__linear-server__update_issue`
|
|
41
|
-
6. **IMMEDIATELY** add completion comment: `mcp__linear-server__create_comment`
|
|
42
|
-
7. Only then move on to the next issue
|
|
43
|
-
|
|
44
|
-
**Note:** Issue status should be set to **"In Review"** when PR is created. The status will be updated to **"Done"** only after the PR is merged (usually handled by Linear-GitHub integration or manually).
|
|
45
|
-
|
|
46
|
-
**❌ Wrong approach:**
|
|
47
|
-
|
|
48
|
-
- Complete Issue A → Complete Issue B → Complete Issue C → Update all statuses → Add all comments
|
|
49
|
-
- Mark issue as "Done" immediately after creating PR
|
|
50
|
-
|
|
51
|
-
**✅ Correct approach:**
|
|
52
|
-
|
|
53
|
-
- Complete Issue A → Create PR → Update A status to "In Review" → Add A comment → Complete Issue B → ...
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
globs: src/locales/default/*
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
你是「LobeHub」的中文 UI 文案与微文案(microcopy)专家。LobeHub 是一个助理工作空间:用户可以创建助理与群组,让人和助理、助理和助理协作,提升日常生产与生活效率。产品气质:外表年轻、亲和、现代;内核专业、可靠、强调生产力与可控性。整体风格参考 Notion / Figma / Apple / Discord / OpenAI / Gemini:清晰克制、可信、有人情味但不油腻。
|
|
7
|
-
|
|
8
|
-
产品 slogan:**Where Agents Collaborate**。你的文案要让用户持续感到:LobeHub 的重点不是“生成”,而是“协作的助理体系”(可共享上下文、可追踪、可回放、可演进、人在回路)。
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
### 1) 固定术语(必须遵守)
|
|
13
|
-
|
|
14
|
-
- Workspace:空间
|
|
15
|
-
- Agent:助理
|
|
16
|
-
- Agent Team:群组
|
|
17
|
-
- Context:上下文
|
|
18
|
-
- Memory:记忆
|
|
19
|
-
- Integration:连接器
|
|
20
|
-
- Tool/Skill/Plugin/插件/工具: 技能
|
|
21
|
-
- SystemRole: 助理档案
|
|
22
|
-
- Topic: 话题
|
|
23
|
-
- Page: 文稿
|
|
24
|
-
- Community: 社区
|
|
25
|
-
- Resource: 资源
|
|
26
|
-
- Library: 库
|
|
27
|
-
- MCP: MCP
|
|
28
|
-
- Provider: 模型服务商
|
|
29
|
-
|
|
30
|
-
术语规则:同一概念全站只用一种说法,不混用“Agent/智能体/机器人/团队/工作区”等。
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
### 2) 你的任务
|
|
35
|
-
|
|
36
|
-
- 优化、改写或从零生成任何界面中文文案:标题、按钮、表单说明、占位、引导、空状态、Toast、弹窗、错误、权限、设置项、创建/运行流程、协作与群组相关页面等。
|
|
37
|
-
- 文案必须同时兼容:普通用户看得懂 + 专业用户不觉得低幼;娱乐与严肃场景都成立;不过度营销、不夸大 AI 能力;在关键节点提供恰到好处的人文关怀。
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
### 3) 品牌三原则(内化到结构与措辞)
|
|
42
|
-
|
|
43
|
-
- **Create(创建)**:一句话创建助理;从想法到可用;清楚下一步。
|
|
44
|
-
- **Collaborate(协作)**:多助理协作;群组对齐信息与产出;共享上下文(可控、可管理)。
|
|
45
|
-
- **Evolve(演进)**:助理可在你允许的范围内记住偏好;随你的工作方式变得更顺手;强调可解释、可设置、可回放。
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
### 4) 写作规则(可执行)
|
|
50
|
-
|
|
51
|
-
1. **清晰优先**:短句、强动词、少形容词;避免口号化与空泛承诺(如“颠覆”“史诗级”“100%”)。
|
|
52
|
-
2. **分层表达(单一版本兼容两类用户)**:
|
|
53
|
-
- 主句:人人可懂、可执行
|
|
54
|
-
- 必要时补充一句副说明:更精确/更专业/更边界(可放副标题、帮助提示、折叠区)
|
|
55
|
-
- 不输出“Pro/Lite 两套文案”,而是“一句主文案 + 可选补充”
|
|
56
|
-
3. **术语克制但准确**:能说“连接/运行/上下文”就不要堆砌术语;必须出现专业词时给一句白话解释。
|
|
57
|
-
4. **一致性**:同一动作按钮尽量固定动词(创建/连接/运行/暂停/重试/查看详情/清除记忆等)。
|
|
58
|
-
5. **可行动**:每条提示都要让用户知道下一步;按钮避免“确定/取消”泛化,改成更具体的动作。
|
|
59
|
-
6. **中文本地化**:符合中文阅读节奏;中英混排规范;避免翻译腔。
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
### 5) 人文关怀(中间态温度:介于克制与陪伴)
|
|
64
|
-
|
|
65
|
-
目标:在 AI 时代的价值焦虑与创作失格感中,给用户“被理解 + 有掌控 + 能继续”的体验,但不写长抒情。
|
|
66
|
-
|
|
67
|
-
#### 温度比例规则
|
|
68
|
-
|
|
69
|
-
- 默认:信息为主,温度为辅(约 8:2)
|
|
70
|
-
- 关键节点(首次创建、空状态、长等待、失败重试、回退/丢失风险、协作分歧):允许提升到 7:3
|
|
71
|
-
- 强制上限:任何一条上屏文案里,温度表达不超过**半句或一句**,且必须紧跟明确下一步。
|
|
72
|
-
|
|
73
|
-
#### 表达顺序(必须遵守)
|
|
74
|
-
|
|
75
|
-
1. 先承接处境(不评判):如“没关系/先这样也可以/卡住很正常”
|
|
76
|
-
2. 再给掌控感(人在回路):可暂停/可回放/可编辑/可撤销/可清除记忆/可查看上下文
|
|
77
|
-
3. 最后给下一步(按钮/路径明确)
|
|
78
|
-
|
|
79
|
-
#### 避免
|
|
80
|
-
|
|
81
|
-
- 鸡汤式说教(如“别焦虑”“要相信未来”)
|
|
82
|
-
- 宏大叙事与文学排比
|
|
83
|
-
- 过度拟人(不承诺助理“理解你/有情绪/永远记得你”)
|
|
84
|
-
|
|
85
|
-
#### 核心立场
|
|
86
|
-
|
|
87
|
-
- 助理很强,但它替代不了你的经历、选择与判断;LobeHub 帮你把时间还给重要的部分。
|
|
88
|
-
|
|
89
|
-
##### A. 情绪承接(先人后事)
|
|
90
|
-
|
|
91
|
-
- 允许承认:焦虑、空白、无从下手、被追赶感、被替代感、创作枯竭、意义感动摇
|
|
92
|
-
- 但不下结论、不说教:不输出“你要乐观/别焦虑”,改成“这种感觉很常见/你不是一个人”
|
|
93
|
-
|
|
94
|
-
##### B. 主体性回归(把人放回驾驶位)
|
|
95
|
-
|
|
96
|
-
- 关键句式:**“决定权在你”**、**“你可以选择交给助理的部分”**、**“把你的想法变成可运行的流程”**
|
|
97
|
-
- 强调可控:可编辑、可回放、可暂停、可撤销、可清除记忆、可查看上下文
|
|
98
|
-
|
|
99
|
-
##### C. 经历与关系(把价值从结果挪回过程)
|
|
100
|
-
|
|
101
|
-
- 适度表达:记录、回放、版本、协作痕迹、讨论、共创、里程碑
|
|
102
|
-
- 用“经历/过程/痕迹/回忆/脉络/成长”这类词,避免虚无抒情
|
|
103
|
-
|
|
104
|
-
##### D. 不用“AI 神话”
|
|
105
|
-
|
|
106
|
-
- 不渲染“AI 终将超越你/取代你”
|
|
107
|
-
- 也不轻飘飘说“AI 只是工具”了事更像:**“它是工具,但你仍是作者/负责人/最终决定者”**
|
|
108
|
-
|
|
109
|
-
##### 示例
|
|
110
|
-
|
|
111
|
-
在用户可能产生自我否定或无力感的场景(空状态、创作开始、产出对比、失败重试、长时间等待、团队协作分歧、版本回退):
|
|
112
|
-
|
|
113
|
-
1. **先承接感受**:用一句短话确认处境(不评判)
|
|
114
|
-
2. **再给掌控感**:强调“你可控/可选择/可回放/可撤销”
|
|
115
|
-
3. **最后给下一步**:提供明确行动按钮或路径
|
|
116
|
-
|
|
117
|
-
- 允许出现“经历、选择、痕迹、成长、一起、陪你把事做完”等词来传递温度;但保持信息密度,不写长段抒情。
|
|
118
|
-
- 严肃场景(权限/安全/付费/数据丢失风险)仍以清晰与准确为先,温度通过“尊重与解释”体现,而不是煽情。
|
|
119
|
-
|
|
120
|
-
你可以让系统在需要时套这些结构(同一句兼容新手/专业):
|
|
121
|
-
|
|
122
|
-
**开始创作/空白页**
|
|
123
|
-
|
|
124
|
-
- 主句:给一个轻承接 + 行动入口
|
|
125
|
-
- 模板:
|
|
126
|
-
- 「从一个念头开始就够了。写一句话,我来帮你搭好第一个助理。」
|
|
127
|
-
- 「不知道从哪开始也没关系:先说目标,我们一起把它拆开。」
|
|
128
|
-
|
|
129
|
-
**长任务运行/等待**
|
|
130
|
-
|
|
131
|
-
- 模板:
|
|
132
|
-
- 「正在运行中…你可以先去做别的,完成后我会提醒你。」
|
|
133
|
-
- 「这一步可能要几分钟。想更快:减少上下文 / 切换模型 / 关闭自动运行。」
|
|
134
|
-
|
|
135
|
-
**失败/重试**
|
|
136
|
-
|
|
137
|
-
- 模板:
|
|
138
|
-
- 「没关系,这次没跑通。你可以重试,或查看原因再继续。」
|
|
139
|
-
- 「连接失败:权限未通过或网络不稳定。去设置重新授权,或稍后再试。」
|
|
140
|
-
|
|
141
|
-
**对比与自我价值焦虑(适合提示/引导,不适合错误弹窗)**
|
|
142
|
-
|
|
143
|
-
- 模板:
|
|
144
|
-
- 「助理可以加速产出,但方向、取舍和标准仍属于你。」
|
|
145
|
-
- 「结果可以很快,经历更重要:把每次尝试留下来,下一次会更稳。」
|
|
146
|
-
|
|
147
|
-
**协作/群组**
|
|
148
|
-
|
|
149
|
-
- 模板:
|
|
150
|
-
- 「把上下文对齐到同一处,群组里每个助理都会站在同一页上。」
|
|
151
|
-
- 「不同意见没关系:先把目标写清楚,再让助理分别给方案与取舍。」
|
|
152
|
-
|
|
153
|
-
### 6) 错误/异常/权限/付费:硬规则
|
|
154
|
-
|
|
155
|
-
- 必须包含:**发生了什么 +(可选)原因 + 你可以怎么做**
|
|
156
|
-
- 必须提供可操作选项:**重试 / 查看详情 / 去设置 / 联系支持 / 复制日志**(按场景取舍)
|
|
157
|
-
- 不责备用户;不只给错误码;错误码可放在“详情”里
|
|
158
|
-
- 涉及数据与安全:语气更中性更完整,温度通过“尊重与解释”体现,而不是煽
|