@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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Desktop Window Management Guide
|
|
2
|
+
|
|
3
|
+
## Window Management Overview
|
|
4
|
+
|
|
5
|
+
1. Window creation and configuration
|
|
6
|
+
2. Window state management (size, position, maximize)
|
|
7
|
+
3. Multi-window coordination
|
|
8
|
+
4. Window event handling
|
|
9
|
+
|
|
10
|
+
## File Structure
|
|
11
|
+
|
|
12
|
+
```plaintext
|
|
13
|
+
apps/desktop/src/main/
|
|
14
|
+
├── appBrowsers.ts # Core window management
|
|
15
|
+
├── controllers/
|
|
16
|
+
│ └── BrowserWindowsCtr.ts # Window controller
|
|
17
|
+
└── modules/
|
|
18
|
+
└── browserWindowManager.ts # Window manager module
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Window Creation
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
export const createMainWindow = () => {
|
|
25
|
+
const mainWindow = new BrowserWindow({
|
|
26
|
+
width: 1200,
|
|
27
|
+
height: 800,
|
|
28
|
+
minWidth: 600,
|
|
29
|
+
minHeight: 400,
|
|
30
|
+
webPreferences: {
|
|
31
|
+
preload: path.join(__dirname, '../preload/index.js'),
|
|
32
|
+
contextIsolation: true,
|
|
33
|
+
nodeIntegration: false,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (isDev) {
|
|
38
|
+
mainWindow.loadURL('http://localhost:3000');
|
|
39
|
+
} else {
|
|
40
|
+
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return mainWindow;
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Window State Persistence
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const saveWindowState = (window: BrowserWindow) => {
|
|
51
|
+
if (!window.isMinimized() && !window.isMaximized()) {
|
|
52
|
+
const [x, y] = window.getPosition();
|
|
53
|
+
const [width, height] = window.getSize();
|
|
54
|
+
settings.set('windowState', { x, y, width, height });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const restoreWindowState = (window: BrowserWindow) => {
|
|
59
|
+
const state = settings.get('windowState');
|
|
60
|
+
if (state) {
|
|
61
|
+
window.setBounds({ x: state.x, y: state.y, width: state.width, height: state.height });
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
window.on('close', () => saveWindowState(window));
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Multi-Window Management
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
export class WindowManager {
|
|
72
|
+
private windows: Map<string, BrowserWindow> = new Map();
|
|
73
|
+
|
|
74
|
+
createWindow(id: string, options: BrowserWindowConstructorOptions) {
|
|
75
|
+
const window = new BrowserWindow(options);
|
|
76
|
+
this.windows.set(id, window);
|
|
77
|
+
window.on('closed', () => this.windows.delete(id));
|
|
78
|
+
return window;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getWindow(id: string) {
|
|
82
|
+
return this.windows.get(id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Window IPC Controller
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
|
91
|
+
export default class BrowserWindowsCtr extends ControllerModule {
|
|
92
|
+
static override readonly groupName = 'windows';
|
|
93
|
+
|
|
94
|
+
@IpcMethod()
|
|
95
|
+
minimizeWindow() {
|
|
96
|
+
BrowserWindow.getFocusedWindow()?.minimize();
|
|
97
|
+
return { success: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@IpcMethod()
|
|
101
|
+
maximizeWindow() {
|
|
102
|
+
const win = BrowserWindow.getFocusedWindow();
|
|
103
|
+
win?.isMaximized() ? win.restore() : win?.maximize();
|
|
104
|
+
return { success: true };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Renderer Service
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// src/services/electron/windowService.ts
|
|
113
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
114
|
+
|
|
115
|
+
const ipc = ensureElectronIpc();
|
|
116
|
+
|
|
117
|
+
export const windowService = {
|
|
118
|
+
minimize: () => ipc.windows.minimizeWindow(),
|
|
119
|
+
maximize: () => ipc.windows.maximizeWindow(),
|
|
120
|
+
close: () => ipc.windows.closeWindow(),
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Frameless Window
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const window = new BrowserWindow({
|
|
128
|
+
frame: false,
|
|
129
|
+
titleBarStyle: 'hidden',
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```css
|
|
134
|
+
.titlebar { -webkit-app-region: drag; }
|
|
135
|
+
.titlebar-button { -webkit-app-region: no-drag; }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Best Practices
|
|
139
|
+
|
|
140
|
+
1. Use `show: false` initially, show after content loads
|
|
141
|
+
2. Always set secure `webPreferences`
|
|
142
|
+
3. Handle `webContents.on('crashed')` for recovery
|
|
143
|
+
4. Clean up resources on `window.on('closed')`
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: drizzle
|
|
3
|
+
description: Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Drizzle ORM Schema Style Guide
|
|
7
|
+
|
|
8
|
+
## Configuration
|
|
9
|
+
|
|
10
|
+
- Config: `drizzle.config.ts`
|
|
11
|
+
- Schemas: `src/database/schemas/`
|
|
12
|
+
- Migrations: `src/database/migrations/`
|
|
13
|
+
- Dialect: `postgresql` with `strict: true`
|
|
14
|
+
|
|
15
|
+
## Helper Functions
|
|
16
|
+
|
|
17
|
+
Location: `src/database/schemas/_helpers.ts`
|
|
18
|
+
|
|
19
|
+
- `timestamptz(name)`: Timestamp with timezone
|
|
20
|
+
- `createdAt()`, `updatedAt()`, `accessedAt()`: Standard timestamp columns
|
|
21
|
+
- `timestamps`: Object with all three for easy spread
|
|
22
|
+
|
|
23
|
+
## Naming Conventions
|
|
24
|
+
|
|
25
|
+
- **Tables**: Plural snake_case (`users`, `session_groups`)
|
|
26
|
+
- **Columns**: snake_case (`user_id`, `created_at`)
|
|
27
|
+
|
|
28
|
+
## Column Definitions
|
|
29
|
+
|
|
30
|
+
### Primary Keys
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
id: text('id')
|
|
34
|
+
.primaryKey()
|
|
35
|
+
.$defaultFn(() => idGenerator('agents'))
|
|
36
|
+
.notNull(),
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
ID prefixes make entity types distinguishable. For internal tables, use `uuid`.
|
|
40
|
+
|
|
41
|
+
### Foreign Keys
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
userId: text('user_id')
|
|
45
|
+
.references(() => users.id, { onDelete: 'cascade' })
|
|
46
|
+
.notNull(),
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Timestamps
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
...timestamps, // Spread from _helpers.ts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Indexes
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// Return array (object style deprecated)
|
|
59
|
+
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Type Inference
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
export const insertAgentSchema = createInsertSchema(agents);
|
|
66
|
+
export type NewAgent = typeof agents.$inferInsert;
|
|
67
|
+
export type AgentItem = typeof agents.$inferSelect;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Example Pattern
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
export const agents = pgTable(
|
|
74
|
+
'agents',
|
|
75
|
+
{
|
|
76
|
+
id: text('id').primaryKey().$defaultFn(() => idGenerator('agents')).notNull(),
|
|
77
|
+
slug: varchar('slug', { length: 100 }).$defaultFn(() => randomSlug(4)).unique(),
|
|
78
|
+
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
|
79
|
+
clientId: text('client_id'),
|
|
80
|
+
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
|
81
|
+
...timestamps,
|
|
82
|
+
},
|
|
83
|
+
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Common Patterns
|
|
88
|
+
|
|
89
|
+
### Junction Tables (Many-to-Many)
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
export const agentsKnowledgeBases = pgTable(
|
|
93
|
+
'agents_knowledge_bases',
|
|
94
|
+
{
|
|
95
|
+
agentId: text('agent_id').references(() => agents.id, { onDelete: 'cascade' }).notNull(),
|
|
96
|
+
knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, { onDelete: 'cascade' }).notNull(),
|
|
97
|
+
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
|
98
|
+
enabled: boolean('enabled').default(true),
|
|
99
|
+
...timestamps,
|
|
100
|
+
},
|
|
101
|
+
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Database Migrations
|
|
106
|
+
|
|
107
|
+
See `references/db-migrations.md` for detailed migration guide.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Generate migrations
|
|
111
|
+
bun run db:generate
|
|
112
|
+
|
|
113
|
+
# After modifying SQL (e.g., adding IF NOT EXISTS)
|
|
114
|
+
bun run db:generate:client
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Migration Best Practices
|
|
118
|
+
|
|
119
|
+
```sql
|
|
120
|
+
-- ✅ Idempotent operations
|
|
121
|
+
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "avatar" text;
|
|
122
|
+
DROP TABLE IF EXISTS "old_table";
|
|
123
|
+
CREATE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
|
|
124
|
+
|
|
125
|
+
-- ❌ Non-idempotent
|
|
126
|
+
ALTER TABLE "users" ADD COLUMN "avatar" text;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Rename migration files meaningfully: `0046_meaningless.sql` → `0046_user_add_avatar.sql`
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Database Migrations Guide
|
|
2
|
+
|
|
3
|
+
## Step 1: Generate Migrations
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun run db:generate
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This generates:
|
|
10
|
+
|
|
11
|
+
- `packages/database/migrations/0046_meaningless_file_name.sql`
|
|
12
|
+
|
|
13
|
+
And updates:
|
|
14
|
+
|
|
15
|
+
- `packages/database/migrations/meta/_journal.json`
|
|
16
|
+
- `packages/database/src/core/migrations.json`
|
|
17
|
+
- `docs/development/database-schema.dbml`
|
|
18
|
+
|
|
19
|
+
## Step 2: Optimize Migration SQL Filename
|
|
20
|
+
|
|
21
|
+
Rename auto-generated filename to be meaningful:
|
|
22
|
+
|
|
23
|
+
`0046_meaningless_file_name.sql` → `0046_user_add_avatar_column.sql`
|
|
24
|
+
|
|
25
|
+
## Step 3: Use Idempotent Clauses (Defensive Programming)
|
|
26
|
+
|
|
27
|
+
Always use defensive clauses to make migrations idempotent:
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
-- ✅ Good: Idempotent operations
|
|
31
|
+
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "avatar" text;
|
|
32
|
+
DROP TABLE IF EXISTS "old_table";
|
|
33
|
+
CREATE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
|
|
34
|
+
ALTER TABLE "posts" DROP COLUMN IF EXISTS "deprecated_field";
|
|
35
|
+
|
|
36
|
+
-- ❌ Bad: Non-idempotent operations
|
|
37
|
+
ALTER TABLE "users" ADD COLUMN "avatar" text;
|
|
38
|
+
DROP TABLE "old_table";
|
|
39
|
+
CREATE INDEX "users_email_idx" ON "users" ("email");
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Important
|
|
43
|
+
|
|
44
|
+
After modifying migration SQL (e.g., adding `IF NOT EXISTS` clauses), run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bun run db:generate:client
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This updates the hash in `packages/database/src/core/migrations.json`.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hotkey
|
|
3
|
+
description: Guide for adding keyboard shortcuts. Use when implementing new hotkeys, registering shortcuts, or working with keyboard interactions. Triggers on hotkey implementation or keyboard shortcut tasks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adding Keyboard Shortcuts Guide
|
|
7
|
+
|
|
8
|
+
## Steps to Add a New Hotkey
|
|
9
|
+
|
|
10
|
+
### 1. Update Hotkey Constant
|
|
11
|
+
|
|
12
|
+
In `src/types/hotkey.ts`:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
export const HotkeyEnum = {
|
|
16
|
+
// existing...
|
|
17
|
+
ClearChat: 'clearChat', // Add new
|
|
18
|
+
} as const;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Register Default Hotkey
|
|
22
|
+
|
|
23
|
+
In `src/const/hotkeys.ts`:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { KeyMapEnum as Key, combineKeys } from '@lobehub/ui';
|
|
27
|
+
|
|
28
|
+
export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
|
|
29
|
+
{
|
|
30
|
+
group: HotkeyGroupEnum.Conversation,
|
|
31
|
+
id: HotkeyEnum.ClearChat,
|
|
32
|
+
keys: combineKeys([Key.Mod, Key.Shift, Key.Backspace]),
|
|
33
|
+
scopes: [HotkeyScopeEnum.Chat],
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Add i18n Translation
|
|
39
|
+
|
|
40
|
+
In `src/locales/default/hotkey.ts`:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const hotkey: HotkeyI18nTranslations = {
|
|
44
|
+
clearChat: {
|
|
45
|
+
desc: '清空当前会话的所有消息记录',
|
|
46
|
+
title: '清空聊天记录',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 4. Create and Register Hook
|
|
52
|
+
|
|
53
|
+
In `src/hooks/useHotkeys/chatScope.ts`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
export const useClearChatHotkey = () => {
|
|
57
|
+
const clearMessages = useChatStore((s) => s.clearMessages);
|
|
58
|
+
return useHotkeyById(HotkeyEnum.ClearChat, clearMessages);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const useRegisterChatHotkeys = () => {
|
|
62
|
+
useClearChatHotkey();
|
|
63
|
+
// ...other hotkeys
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 5. Add Tooltip (Optional)
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
const clearChatHotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.ClearChat));
|
|
71
|
+
|
|
72
|
+
<Tooltip hotkey={clearChatHotkey} title={t('clearChat.title', { ns: 'hotkey' })}>
|
|
73
|
+
<Button icon={<DeleteOutlined />} onClick={clearMessages} />
|
|
74
|
+
</Tooltip>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Best Practices
|
|
78
|
+
|
|
79
|
+
1. **Scope**: Choose global or chat scope based on functionality
|
|
80
|
+
2. **Grouping**: Place in appropriate group (System/Layout/Conversation)
|
|
81
|
+
3. **Conflict check**: Ensure no conflict with system/browser shortcuts
|
|
82
|
+
4. **Platform**: Use `Key.Mod` instead of hardcoded `Ctrl` or `Cmd`
|
|
83
|
+
5. **Clear description**: Provide title and description for users
|
|
84
|
+
|
|
85
|
+
## Troubleshooting
|
|
86
|
+
|
|
87
|
+
- **Not working**: Check scope and RegisterHotkeys hook
|
|
88
|
+
- **Not in settings**: Verify HOTKEYS_REGISTRATION config
|
|
89
|
+
- **Conflict**: HotkeyInput component shows warnings
|
|
90
|
+
- **Page-specific**: Ensure correct scope activation
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
name: i18n
|
|
3
|
+
description: Internationalization guide using react-i18next. Use when adding translations, creating i18n keys, or working with localized text in React components (.tsx files). Triggers on translation tasks, locale management, or i18n implementation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# LobeChat Internationalization Guide
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- Default language: Chinese (zh-CN), Framework: react-i18next
|
|
8
|
+
- Default language: Chinese (zh-CN)
|
|
9
|
+
- Framework: react-i18next
|
|
11
10
|
- **Only edit files in `src/locales/default/`** - Never edit JSON files in `locales/`
|
|
12
|
-
- Run `pnpm i18n` to generate
|
|
11
|
+
- Run `pnpm i18n` to generate translations (or manually translate zh-CN/en-US for dev preview)
|
|
13
12
|
|
|
14
13
|
## Key Naming Convention
|
|
15
14
|
|
|
@@ -19,37 +18,30 @@ alwaysApply: false
|
|
|
19
18
|
// ✅ Correct
|
|
20
19
|
export default {
|
|
21
20
|
'alert.cloud.action': '立即体验',
|
|
22
|
-
'clientDB.error.desc': '数据库初始化遇到问题',
|
|
23
21
|
'sync.actions.sync': '立即同步',
|
|
24
22
|
'sync.status.ready': '已连接',
|
|
25
23
|
};
|
|
26
24
|
|
|
27
|
-
// ❌ Avoid
|
|
25
|
+
// ❌ Avoid nested objects
|
|
28
26
|
export default {
|
|
29
27
|
alert: { cloud: { action: '...' } },
|
|
30
28
|
};
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
**
|
|
34
|
-
|
|
35
|
-
- `clientDB.modal.title` - Feature + context + property
|
|
36
|
-
- `sync.actions.sync` - Feature + group + action
|
|
37
|
-
- `sync.status.ready` - Feature + group + status
|
|
31
|
+
**Patterns:** `{feature}.{context}.{action|status}`
|
|
38
32
|
|
|
39
33
|
**Parameters:** Use `{{variableName}}` syntax
|
|
40
|
-
|
|
41
34
|
```typescript
|
|
42
35
|
'alert.cloud.desc': '我们提供 {{credit}} 额度积分',
|
|
43
36
|
```
|
|
44
37
|
|
|
45
|
-
**Avoid key conflicts:**
|
|
46
|
-
|
|
38
|
+
**Avoid key conflicts:**
|
|
47
39
|
```typescript
|
|
48
|
-
// ❌ Conflict
|
|
40
|
+
// ❌ Conflict
|
|
49
41
|
'clientDB.solve': '自助解决',
|
|
50
42
|
'clientDB.solve.backup.title': '数据备份',
|
|
51
43
|
|
|
52
|
-
// ✅ Solution
|
|
44
|
+
// ✅ Solution
|
|
53
45
|
'clientDB.solve.action': '自助解决',
|
|
54
46
|
'clientDB.solve.backup.title': '数据备份',
|
|
55
47
|
```
|
|
@@ -68,17 +60,16 @@ import { useTranslation } from 'react-i18next';
|
|
|
68
60
|
|
|
69
61
|
const { t } = useTranslation('common');
|
|
70
62
|
|
|
71
|
-
// Basic
|
|
72
63
|
t('newFeature.title')
|
|
73
|
-
// With parameters
|
|
74
64
|
t('alert.cloud.desc', { credit: '1000' })
|
|
65
|
+
|
|
75
66
|
// Multiple namespaces
|
|
76
67
|
const { t } = useTranslation(['common', 'chat']);
|
|
77
68
|
t('common:save')
|
|
78
69
|
```
|
|
79
70
|
|
|
80
|
-
##
|
|
81
|
-
|
|
82
|
-
auth, authError, changelog, chat, color, **common**, components, discover, editor, electron, error, file, home, hotkey, image, knowledgeBase, labs, marketAuth, memory, metadata, migration, modelProvider, models, oauth, onboarding, plugin, portal, providers, ragEval, **setting**, subscription, thread, tool, topic, welcome
|
|
71
|
+
## Common Namespaces
|
|
83
72
|
|
|
84
73
|
**Most used:** `common` (shared UI), `chat` (chat features), `setting` (settings)
|
|
74
|
+
|
|
75
|
+
Others: auth, changelog, components, discover, editor, electron, error, file, hotkey, knowledgeBase, memory, models, plugin, portal, providers, tool, topic
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear
|
|
3
|
+
description: Linear issue management guide. Use when working with Linear issues, creating issues, updating status, or adding comments. Triggers on Linear issue references (LOBE-xxx), issue tracking, or project management tasks. Requires Linear MCP tools to be available.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Linear Issue Management
|
|
7
|
+
|
|
8
|
+
Before using Linear workflows, search for `linear` MCP tools. If not found, treat as not installed.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. **Retrieve issue details** before starting: `mcp__linear-server__get_issue`
|
|
13
|
+
2. **Check for sub-issues**: Use `mcp__linear-server__list_issues` with `parentId` filter
|
|
14
|
+
3. **Update issue status** when completing: `mcp__linear-server__update_issue`
|
|
15
|
+
4. **Add completion comment** (REQUIRED): `mcp__linear-server__create_comment`
|
|
16
|
+
|
|
17
|
+
## Creating Issues
|
|
18
|
+
|
|
19
|
+
When creating issues with `mcp__linear-server__create_issue`, **MUST add the `claude code` label**.
|
|
20
|
+
|
|
21
|
+
## Completion Comment (REQUIRED)
|
|
22
|
+
|
|
23
|
+
Every completed issue MUST have a comment summarizing work done. This is critical for:
|
|
24
|
+
- Team visibility
|
|
25
|
+
- Code review context
|
|
26
|
+
- Future reference
|
|
27
|
+
|
|
28
|
+
## PR Association (REQUIRED)
|
|
29
|
+
|
|
30
|
+
When creating PRs for Linear issues, include magic keywords in PR body:
|
|
31
|
+
- `Fixes LOBE-123`
|
|
32
|
+
- `Closes LOBE-123`
|
|
33
|
+
- `Resolves LOBE-123`
|
|
34
|
+
|
|
35
|
+
## Per-Issue Completion Rule
|
|
36
|
+
|
|
37
|
+
When working on multiple issues, update EACH issue IMMEDIATELY after completing it:
|
|
38
|
+
|
|
39
|
+
1. Complete implementation
|
|
40
|
+
2. Run `bun run type-check`
|
|
41
|
+
3. Run related tests
|
|
42
|
+
4. Create PR if needed
|
|
43
|
+
5. Update status to **"In Review"** (NOT "Done")
|
|
44
|
+
6. Add completion comment
|
|
45
|
+
7. Move to next issue
|
|
46
|
+
|
|
47
|
+
**Note:** Status → "In Review" when PR created. "Done" only after PR merged.
|
|
48
|
+
|
|
49
|
+
**❌ Wrong:** Complete all → Update all statuses → Add all comments
|
|
50
|
+
|
|
51
|
+
**✅ Correct:** Complete A → Update A → Comment A → Complete B → ...
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: microcopy
|
|
3
|
+
description: UI copy and microcopy guidelines. Use when writing UI text, buttons, error messages, empty states, onboarding, or any user-facing copy. Triggers on i18n translation, UI text writing, or copy improvement tasks. Supports both Chinese and English.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# LobeHub UI Microcopy Guidelines
|
|
7
|
+
|
|
8
|
+
Brand: **Where Agents Collaborate** - Focus on collaborative agent system, not just "generation".
|
|
9
|
+
|
|
10
|
+
## Fixed Terminology
|
|
11
|
+
|
|
12
|
+
| Chinese | English |
|
|
13
|
+
|---------|---------|
|
|
14
|
+
| 空间 | Workspace |
|
|
15
|
+
| 助理 | Agent |
|
|
16
|
+
| 群组 | Group |
|
|
17
|
+
| 上下文 | Context |
|
|
18
|
+
| 记忆 | Memory |
|
|
19
|
+
| 连接器 | Integration |
|
|
20
|
+
| 技能 | Skill |
|
|
21
|
+
| 助理档案 | Agent Profile |
|
|
22
|
+
| 话题 | Topic |
|
|
23
|
+
| 文稿 | Page |
|
|
24
|
+
| 社区 | Community |
|
|
25
|
+
| 资源 | Resource |
|
|
26
|
+
| 库 | Library |
|
|
27
|
+
| 模型服务商 | Provider |
|
|
28
|
+
|
|
29
|
+
## Brand Principles
|
|
30
|
+
|
|
31
|
+
1. **Create**: One sentence → usable Agent; clear next step
|
|
32
|
+
2. **Collaborate**: Multi-agent; shared Context; controlled
|
|
33
|
+
3. **Evolve**: Remember with consent; explainable; replayable
|
|
34
|
+
|
|
35
|
+
## Writing Rules
|
|
36
|
+
|
|
37
|
+
1. **Clarity first**: Short sentences, strong verbs, minimal adjectives
|
|
38
|
+
2. **Layered**: Main line (simple) + optional detail (precise)
|
|
39
|
+
3. **Consistent verbs**: Create / Connect / Run / Pause / Retry / View details
|
|
40
|
+
4. **Actionable**: Every message tells next step; avoid generic "OK/Cancel"
|
|
41
|
+
|
|
42
|
+
## Human Warmth (Balanced)
|
|
43
|
+
|
|
44
|
+
Default: **80% information, 20% warmth**
|
|
45
|
+
Key moments: **70/30** (first-time, empty state, failures, long waits)
|
|
46
|
+
|
|
47
|
+
**Hard cap**: At most half sentence of warmth, followed by clear next step.
|
|
48
|
+
|
|
49
|
+
**Order**:
|
|
50
|
+
1. Acknowledge situation (no judgment)
|
|
51
|
+
2. Restore control (pause/replay/edit/undo/clear Memory)
|
|
52
|
+
3. Provide next action
|
|
53
|
+
|
|
54
|
+
**Avoid**: Preachy encouragement, grand narratives, over-anthropomorphizing
|
|
55
|
+
|
|
56
|
+
## Patterns
|
|
57
|
+
|
|
58
|
+
**Getting started**:
|
|
59
|
+
- "Starting with one sentence is enough. Describe your goal."
|
|
60
|
+
- "Not sure where to begin? Tell me the outcome."
|
|
61
|
+
|
|
62
|
+
**Long wait**:
|
|
63
|
+
- "Running… You can switch tasks—I'll notify you when done."
|
|
64
|
+
- "This may take a few minutes. To speed up: reduce Context / switch model."
|
|
65
|
+
|
|
66
|
+
**Failure**:
|
|
67
|
+
- "That didn't run through. Retry, or view details to fix."
|
|
68
|
+
- "Connection failed. Re-authorize in Settings, or try again later."
|
|
69
|
+
|
|
70
|
+
**Collaboration**:
|
|
71
|
+
- "Align everyone to the same Context."
|
|
72
|
+
- "Different opinions are fine. Write the goal first."
|
|
73
|
+
|
|
74
|
+
## Errors/Exceptions
|
|
75
|
+
|
|
76
|
+
Must include:
|
|
77
|
+
1. **What happened**
|
|
78
|
+
2. (Optional) **Why**
|
|
79
|
+
3. **What user can do next**
|
|
80
|
+
|
|
81
|
+
Provide: Retry / View details / Go to Settings / Contact support / Copy logs
|
|
82
|
+
|
|
83
|
+
Never blame user. Put error codes in "Details".
|