@lobehub/lobehub 2.0.0-next.97 → 2.0.0-next.98
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/.console-log-whitelist.json +14 -0
- package/.github/workflows/check-console-log.yml +117 -0
- package/.github/workflows/desktop-pr-build.yml +4 -4
- package/.github/workflows/release-desktop-beta.yml +4 -4
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/test.yml +5 -5
- package/CHANGELOG.md +33 -0
- package/changelog/v1.json +12 -0
- package/docs/development/database-schema.dbml +1 -0
- package/e2e/package.json +1 -1
- package/locales/ar/file.json +9 -11
- package/locales/bg-BG/file.json +8 -10
- package/locales/de-DE/file.json +9 -11
- package/locales/en-US/file.json +12 -14
- package/locales/es-ES/file.json +7 -9
- package/locales/fa-IR/file.json +9 -11
- package/locales/fr-FR/file.json +6 -8
- package/locales/it-IT/file.json +8 -10
- package/locales/ja-JP/file.json +10 -12
- package/locales/ko-KR/file.json +8 -10
- package/locales/nl-NL/file.json +8 -10
- package/locales/pl-PL/file.json +7 -9
- package/locales/pt-BR/file.json +7 -9
- package/locales/ru-RU/file.json +9 -11
- package/locales/tr-TR/file.json +8 -10
- package/locales/vi-VN/file.json +9 -11
- package/locales/zh-CN/file.json +10 -12
- package/locales/zh-TW/file.json +10 -12
- package/package.json +3 -2
- package/packages/database/migrations/0047_add_slug_document.sql +6 -0
- package/packages/database/migrations/meta/0047_snapshot.json +7891 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/core/migrations.json +16 -7
- package/packages/database/src/models/document.ts +2 -2
- package/packages/database/src/schemas/file.ts +7 -1
- package/packages/model-bank/src/aiModels/qwen.ts +5 -3
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +21 -21
- package/packages/obervability-otel/package.json +2 -2
- package/scripts/checkConsoleLog.mts +148 -0
- package/scripts/prebuild.mts +5 -5
- package/src/app/[variants]/(main)/changelog/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +20 -16
- package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -3
- package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +3 -2
- package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +14 -4
- package/src/app/[variants]/desktopRouter.config.tsx +23 -0
- package/src/app/[variants]/mobileRouter.config.tsx +23 -0
- package/src/features/KnowledgeManager/DocumentExplorer/NoteEditorModal.tsx +0 -20
- package/src/features/KnowledgeManager/DocumentExplorer/index.tsx +3 -3
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/index.tsx +0 -20
- package/src/features/KnowledgeManager/Header/AddButton.tsx +0 -1
- package/src/features/KnowledgeManager/Header/NewNoteButton.tsx +1 -1
- package/src/features/KnowledgeManager/Header/TogglePanelButton.tsx +2 -2
- package/src/features/KnowledgeManager/Home/UploadEntries.tsx +2 -2
- package/src/features/KnowledgeManager/Home/index.tsx +4 -4
- package/src/features/User/UserPanel/useMenu.tsx +7 -3
- package/src/locales/default/file.ts +11 -13
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +1 -4
|
@@ -329,6 +329,13 @@
|
|
|
329
329
|
"when": 1763453175961,
|
|
330
330
|
"tag": "0046_add_parent_id",
|
|
331
331
|
"breakpoints": true
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"idx": 47,
|
|
335
|
+
"version": "7",
|
|
336
|
+
"when": 1763535087148,
|
|
337
|
+
"tag": "0047_add_slug_document",
|
|
338
|
+
"breakpoints": true
|
|
332
339
|
}
|
|
333
340
|
],
|
|
334
341
|
"version": "6"
|
|
@@ -778,15 +778,24 @@
|
|
|
778
778
|
{
|
|
779
779
|
"sql": [
|
|
780
780
|
"ALTER TABLE \"documents\" ALTER COLUMN \"id\" SET DATA TYPE varchar(255);",
|
|
781
|
-
"\nALTER TABLE \"documents\" ADD COLUMN \"parent_id\" varchar(255);",
|
|
782
|
-
"\nALTER TABLE \"files\" ADD COLUMN \"parent_id\" varchar(255);",
|
|
783
|
-
"\
|
|
784
|
-
"\
|
|
785
|
-
"\nCREATE INDEX \"documents_parent_id_idx\" ON \"documents\" USING btree (\"parent_id\");",
|
|
786
|
-
"\nCREATE INDEX \"files_parent_id_idx\" ON \"files\" USING btree (\"parent_id\");"
|
|
781
|
+
"\nALTER TABLE \"documents\" ADD COLUMN IF NOT EXISTS \"parent_id\" varchar(255);",
|
|
782
|
+
"\nALTER TABLE \"files\" ADD COLUMN IF NOT EXISTS \"parent_id\" varchar(255);",
|
|
783
|
+
"\nDO $$ BEGIN\n ALTER TABLE \"documents\" ADD CONSTRAINT \"documents_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;",
|
|
784
|
+
"\nDO $$ BEGIN\n ALTER TABLE \"files\" ADD CONSTRAINT \"files_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;",
|
|
785
|
+
"\nCREATE INDEX IF NOT EXISTS \"documents_parent_id_idx\" ON \"documents\" USING btree (\"parent_id\");",
|
|
786
|
+
"\nCREATE INDEX IF NOT EXISTS \"files_parent_id_idx\" ON \"files\" USING btree (\"parent_id\");"
|
|
787
787
|
],
|
|
788
788
|
"bps": true,
|
|
789
789
|
"folderMillis": 1763453175961,
|
|
790
|
-
"hash": "
|
|
790
|
+
"hash": "6c2081c8ac22772a8276052c9ae0852a4c03463cbe998df26f85683ab356b914"
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
"sql": [
|
|
794
|
+
"ALTER TABLE \"documents\" ADD COLUMN IF NOT EXISTS \"slug\" varchar(255);",
|
|
795
|
+
"\nDO $$ BEGIN\n CREATE UNIQUE INDEX IF NOT EXISTS \"documents_slug_user_id_unique\" ON \"documents\" (\"slug\",\"user_id\") WHERE \"slug\" IS NOT NULL;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;"
|
|
796
|
+
],
|
|
797
|
+
"bps": true,
|
|
798
|
+
"folderMillis": 1763535087148,
|
|
799
|
+
"hash": "57a82ce14d96ffa9f140ff63f00af994e91a74703f4d2378286e36be259f117b"
|
|
791
800
|
}
|
|
792
801
|
]
|
|
@@ -31,14 +31,14 @@ export class DocumentModel {
|
|
|
31
31
|
return this.db.delete(documents).where(eq(documents.userId, this.userId));
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
query = async () => {
|
|
34
|
+
query = async (): Promise<DocumentItem[]> => {
|
|
35
35
|
return this.db.query.documents.findMany({
|
|
36
36
|
orderBy: [desc(documents.updatedAt)],
|
|
37
37
|
where: eq(documents.userId, this.userId),
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
findById = async (id: string) => {
|
|
41
|
+
findById = async (id: string): Promise<DocumentItem | undefined> => {
|
|
42
42
|
return this.db.query.documents.findFirst({
|
|
43
43
|
where: and(eq(documents.id, id), eq(documents.userId, this.userId)),
|
|
44
44
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
|
+
import { isNotNull } from 'drizzle-orm';
|
|
2
3
|
import {
|
|
3
4
|
boolean,
|
|
4
5
|
index,
|
|
@@ -91,6 +92,8 @@ export const documents = pgTable(
|
|
|
91
92
|
|
|
92
93
|
editorData: jsonb('editor_data').$type<Record<string, any>>(),
|
|
93
94
|
|
|
95
|
+
slug: varchar('slug', { length: 255 }),
|
|
96
|
+
|
|
94
97
|
// Timestamps
|
|
95
98
|
...timestamps,
|
|
96
99
|
},
|
|
@@ -100,6 +103,9 @@ export const documents = pgTable(
|
|
|
100
103
|
index('documents_file_id_idx').on(table.fileId),
|
|
101
104
|
index('documents_parent_id_idx').on(table.parentId),
|
|
102
105
|
uniqueIndex('documents_client_id_user_id_unique').on(table.clientId, table.userId),
|
|
106
|
+
uniqueIndex('documents_slug_user_id_unique')
|
|
107
|
+
.on(table.slug, table.userId)
|
|
108
|
+
.where(isNotNull(table.slug)),
|
|
103
109
|
],
|
|
104
110
|
);
|
|
105
111
|
|
|
@@ -133,7 +139,7 @@ export const files = pgTable(
|
|
|
133
139
|
url: text('url').notNull(),
|
|
134
140
|
source: text('source').$type<FileSource>(),
|
|
135
141
|
|
|
136
|
-
// Parent
|
|
142
|
+
// Parent Folder or Document
|
|
137
143
|
// @ts-ignore
|
|
138
144
|
parentId: varchar('parent_id', { length: 255 }).references(() => documents.id, {
|
|
139
145
|
onDelete: 'set null',
|
|
@@ -1499,11 +1499,12 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
|
1499
1499
|
},
|
|
1500
1500
|
{
|
|
1501
1501
|
abilities: {
|
|
1502
|
-
vision: true,
|
|
1503
1502
|
reasoning: true,
|
|
1503
|
+
vision: true,
|
|
1504
1504
|
},
|
|
1505
1505
|
contextWindowTokens: 131_072,
|
|
1506
|
-
description:
|
|
1506
|
+
description:
|
|
1507
|
+
'Qwen3 VL 32B 思考模式(开源版),针对高难度强推理与长视频理解场景,提供顶尖的视觉+文本推理能力。',
|
|
1507
1508
|
displayName: 'Qwen3 VL 32B Thinking',
|
|
1508
1509
|
id: 'qwen3-vl-32b-thinking',
|
|
1509
1510
|
maxOutput: 32_768,
|
|
@@ -1525,7 +1526,8 @@ const qwenChatModels: AIChatModelCard[] = [
|
|
|
1525
1526
|
vision: true,
|
|
1526
1527
|
},
|
|
1527
1528
|
contextWindowTokens: 131_072,
|
|
1528
|
-
description:
|
|
1529
|
+
description:
|
|
1530
|
+
'Qwen3 VL 32B 非思考模式(Instruct),适用于非思考指令场景,保持强大的视觉理解能力。',
|
|
1529
1531
|
displayName: 'Qwen3 VL 32B Instruct',
|
|
1530
1532
|
id: 'qwen3-vl-32b-instruct',
|
|
1531
1533
|
maxOutput: 32_768,
|
|
@@ -138,10 +138,10 @@ export interface OpenAICompatibleFactoryOptions<T extends Record<string, any> =
|
|
|
138
138
|
useToolsCalling?: boolean;
|
|
139
139
|
};
|
|
140
140
|
models?:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
| ((params: { client: OpenAI }) => Promise<ChatModelCard[]>)
|
|
142
|
+
| {
|
|
143
|
+
transformModel?: (model: OpenAI.Model) => ChatModelCard;
|
|
144
|
+
};
|
|
145
145
|
provider: string;
|
|
146
146
|
responses?: {
|
|
147
147
|
handlePayload?: (
|
|
@@ -317,9 +317,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
317
317
|
const postPayload = chatCompletion?.handlePayload
|
|
318
318
|
? chatCompletion.handlePayload(processedPayload, this._options)
|
|
319
319
|
: ({
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
...processedPayload,
|
|
321
|
+
stream: processedPayload.stream ?? true,
|
|
322
|
+
} as OpenAI.ChatCompletionCreateParamsStreaming);
|
|
323
323
|
|
|
324
324
|
if ((postPayload as any).apiMode === 'responses') {
|
|
325
325
|
return this.handleResponseAPIMode(processedPayload, options);
|
|
@@ -385,13 +385,13 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
385
385
|
return StreamingResponse(
|
|
386
386
|
chatCompletion?.handleStream
|
|
387
387
|
? chatCompletion.handleStream(prod, {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
388
|
+
callbacks: streamOptions.callbacks,
|
|
389
|
+
inputStartAt,
|
|
390
|
+
})
|
|
391
391
|
: OpenAIStream(prod, {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
392
|
+
...streamOptions,
|
|
393
|
+
inputStartAt,
|
|
394
|
+
}),
|
|
395
395
|
{
|
|
396
396
|
headers: options?.headers,
|
|
397
397
|
},
|
|
@@ -415,9 +415,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
415
415
|
return StreamingResponse(
|
|
416
416
|
chatCompletion?.handleStream
|
|
417
417
|
? chatCompletion.handleStream(stream, {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
418
|
+
callbacks: streamOptions.callbacks,
|
|
419
|
+
inputStartAt,
|
|
420
|
+
})
|
|
421
421
|
: OpenAIStream(stream, { ...streamOptions, enableStreaming: false, inputStartAt }),
|
|
422
422
|
{
|
|
423
423
|
headers: options?.headers,
|
|
@@ -839,11 +839,11 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
839
839
|
...res,
|
|
840
840
|
...(reasoning || reasoning_effort
|
|
841
841
|
? {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
842
|
+
reasoning: {
|
|
843
|
+
...reasoning,
|
|
844
|
+
...(reasoning_effort && { effort: reasoning_effort }),
|
|
845
|
+
},
|
|
846
|
+
}
|
|
847
847
|
: {}),
|
|
848
848
|
input,
|
|
849
849
|
...(max_tokens && { max_output_tokens: max_tokens }),
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@opentelemetry/api": "^1.9.0",
|
|
12
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
12
|
+
"@opentelemetry/auto-instrumentations-node": "^0.67.0",
|
|
13
13
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
|
|
14
14
|
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
|
15
15
|
"@opentelemetry/instrumentation": "^0.208.0",
|
|
16
16
|
"@opentelemetry/instrumentation-http": "^0.208.0",
|
|
17
|
-
"@opentelemetry/instrumentation-pg": "^0.
|
|
17
|
+
"@opentelemetry/instrumentation-pg": "^0.61.0",
|
|
18
18
|
"@opentelemetry/resources": "^2.2.0",
|
|
19
19
|
"@opentelemetry/sdk-metrics": "^2.2.0",
|
|
20
20
|
"@opentelemetry/sdk-node": "^0.208.0",
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
interface WhitelistConfig {
|
|
7
|
+
files?: string[];
|
|
8
|
+
patterns?: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const WHITELIST_PATH = '.console-log-whitelist.json';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load whitelist configuration
|
|
15
|
+
*/
|
|
16
|
+
const loadWhitelist = (): WhitelistConfig => {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(WHITELIST_PATH, 'utf8');
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
} catch {
|
|
21
|
+
return { files: [], patterns: [] };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a file is whitelisted
|
|
27
|
+
*/
|
|
28
|
+
const isWhitelisted = (filePath: string, whitelist: WhitelistConfig): boolean => {
|
|
29
|
+
const normalizedPath = filePath.replaceAll('\\', '/');
|
|
30
|
+
|
|
31
|
+
// Check exact file matches
|
|
32
|
+
if (whitelist.files?.some((f) => normalizedPath.includes(f.replaceAll('\\', '/')))) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check pattern matches (simple glob-like patterns)
|
|
37
|
+
if (whitelist.patterns) {
|
|
38
|
+
for (const pattern of whitelist.patterns) {
|
|
39
|
+
// Escape dots and replace glob patterns
|
|
40
|
+
// Use a placeholder for ** to avoid conflicts with single *
|
|
41
|
+
let regexPattern = pattern
|
|
42
|
+
.replaceAll('.', '\\.')
|
|
43
|
+
.replaceAll('**', '\u0000DOUBLESTAR\u0000')
|
|
44
|
+
.replaceAll('*', '[^/]*')
|
|
45
|
+
.replaceAll('\u0000DOUBLESTAR\u0000', '.*');
|
|
46
|
+
|
|
47
|
+
// If pattern ends with /**, match everything under that directory
|
|
48
|
+
// If pattern ends with **, just match everything from that point
|
|
49
|
+
const regex = new RegExp(`^${regexPattern}`);
|
|
50
|
+
if (regex.test(normalizedPath)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Main check function
|
|
61
|
+
*/
|
|
62
|
+
const checkConsoleLogs = () => {
|
|
63
|
+
const whitelist = loadWhitelist();
|
|
64
|
+
|
|
65
|
+
console.log('🔍 Checking for console.log statements...\n');
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Search for console.log in TypeScript and JavaScript files
|
|
69
|
+
const output = execSync(
|
|
70
|
+
`git grep -n "console\\.log" -- "*.ts" "*.tsx" "*.js" "*.jsx" "*.mts" "*.cts" || true`,
|
|
71
|
+
{ encoding: 'utf8' },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!output.trim()) {
|
|
75
|
+
console.log('✅ No console.log statements found!');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const lines = output.trim().split('\n');
|
|
80
|
+
const violations: Array<{ content: string, file: string; line: string; }> = [];
|
|
81
|
+
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
// Parse git grep output: filename:lineNumber:content
|
|
84
|
+
const match = line.match(/^([^:]+):(\d+):(.+)$/);
|
|
85
|
+
if (!match) continue;
|
|
86
|
+
|
|
87
|
+
const [, filePath, lineNumber, content] = match;
|
|
88
|
+
|
|
89
|
+
// Skip if whitelisted
|
|
90
|
+
if (isWhitelisted(filePath, whitelist)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Skip comments
|
|
95
|
+
const trimmedContent = content.trim();
|
|
96
|
+
if (trimmedContent.startsWith('//') || trimmedContent.startsWith('*')) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
violations.push({
|
|
101
|
+
content: content.trim(),
|
|
102
|
+
file: filePath,
|
|
103
|
+
line: lineNumber,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (violations.length === 0) {
|
|
108
|
+
console.log('✅ No console.log violations found (all matches are whitelisted or in comments)!');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Report violations as warnings
|
|
113
|
+
console.log('⚠️ Found console.log statements in the following files:\n');
|
|
114
|
+
|
|
115
|
+
// Use GitHub Actions annotation format for better visibility in CI
|
|
116
|
+
const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
|
|
117
|
+
|
|
118
|
+
for (const violation of violations) {
|
|
119
|
+
if (isCI) {
|
|
120
|
+
// GitHub Actions warning annotation format
|
|
121
|
+
console.log(`::warning file=${violation.file},line=${violation.line}::console.log found: ${violation.content}`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log(` ${violation.file}:${violation.line}`);
|
|
124
|
+
console.log(` ${violation.content}\n`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(`\n💡 Total violations: ${violations.length}`);
|
|
129
|
+
console.log(
|
|
130
|
+
`\n📝 To whitelist files, add them to ${WHITELIST_PATH} in the following format:`,
|
|
131
|
+
);
|
|
132
|
+
console.log(`{
|
|
133
|
+
"files": ["path/to/file.ts"],
|
|
134
|
+
"patterns": ["scripts/**/*.mts", "**/*.test.ts"]
|
|
135
|
+
}\n`);
|
|
136
|
+
|
|
137
|
+
// Exit with 0 to not block CI, but warnings will still be visible
|
|
138
|
+
process.exit(0);
|
|
139
|
+
} catch (error: unknown) {
|
|
140
|
+
if (error instanceof Error && 'status' in error && error.status !== 0) {
|
|
141
|
+
console.error('❌ Error running git grep:', error.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
checkConsoleLogs();
|
package/scripts/prebuild.mts
CHANGED
|
@@ -10,11 +10,11 @@ dotenv.config();
|
|
|
10
10
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
11
11
|
const partialBuildPages = [
|
|
12
12
|
// no need for desktop
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
},
|
|
13
|
+
// {
|
|
14
|
+
// name: 'changelog',
|
|
15
|
+
// disabled: isDesktop,
|
|
16
|
+
// paths: ['src/app/[variants]/(main)/changelog'],
|
|
17
|
+
// },
|
|
18
18
|
{
|
|
19
19
|
name: 'auth',
|
|
20
20
|
disabled: isDesktop,
|
|
@@ -18,7 +18,7 @@ const Page = (props: { isMobile: boolean }) => {
|
|
|
18
18
|
const { isMobile } = props;
|
|
19
19
|
const { hideDocs } = useServerConfigStore(featureFlagsSelectors);
|
|
20
20
|
|
|
21
|
-
const { data } = useSWR('changelog-index', async () => {
|
|
21
|
+
const { data = [] } = useSWR('changelog-index', async () => {
|
|
22
22
|
const changelogService = new ChangelogService();
|
|
23
23
|
return await changelogService.getChangelogIndex();
|
|
24
24
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useResponsive, useTheme } from 'antd-style';
|
|
4
|
+
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
|
4
5
|
import { memo, useEffect, useRef } from 'react';
|
|
5
6
|
import { Flexbox } from 'react-layout-kit';
|
|
6
7
|
import { useSearchParams } from 'react-router-dom';
|
|
@@ -13,7 +14,6 @@ import SettingsContent from '../SettingsContent';
|
|
|
13
14
|
import { LayoutProps } from '../type';
|
|
14
15
|
import Header from './Header';
|
|
15
16
|
import SideBar from './SideBar';
|
|
16
|
-
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
|
17
17
|
|
|
18
18
|
const Layout = memo<LayoutProps>(() => {
|
|
19
19
|
const ref = useRef<HTMLDivElement | null>(null);
|
|
@@ -22,21 +22,23 @@ const Layout = memo<LayoutProps>(() => {
|
|
|
22
22
|
|
|
23
23
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
24
24
|
|
|
25
|
-
const [activeTabState, setActiveTabState] = useMergedState(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
const [activeTabState, setActiveTabState] = useMergedState(
|
|
26
|
+
{
|
|
27
|
+
active: (searchParams.get('active') as SettingsTabs)
|
|
28
|
+
? (searchParams.get('active') as SettingsTabs)
|
|
29
|
+
: SettingsTabs.Common,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
onChange: (obj: { active: SettingsTabs; provider?: string }) => {
|
|
33
|
+
if (obj.provider) {
|
|
34
|
+
setSearchParams({ active: obj.active, provider: obj.provider });
|
|
35
|
+
} else {
|
|
36
|
+
searchParams.delete('provider');
|
|
37
|
+
setSearchParams({ active: obj.active });
|
|
38
|
+
}
|
|
39
|
+
},
|
|
38
40
|
},
|
|
39
|
-
|
|
41
|
+
);
|
|
40
42
|
|
|
41
43
|
const setActiveTab = (tab: SettingsTabs) => {
|
|
42
44
|
if (tab === SettingsTabs.Provider) {
|
|
@@ -57,7 +59,9 @@ const Layout = memo<LayoutProps>(() => {
|
|
|
57
59
|
};
|
|
58
60
|
}, []);
|
|
59
61
|
|
|
60
|
-
const category =
|
|
62
|
+
const category = (
|
|
63
|
+
<CategoryContent activeTab={activeTabState.active} onMenuSelect={setActiveTab} />
|
|
64
|
+
);
|
|
61
65
|
|
|
62
66
|
return (
|
|
63
67
|
<Flexbox
|
|
@@ -33,9 +33,12 @@ const ProviderCard = memo<ProviderCardProps>(
|
|
|
33
33
|
return (
|
|
34
34
|
<Flexbox className={cx(styles.container)} gap={24}>
|
|
35
35
|
<Flexbox gap={12} padding={16} width={'100%'}>
|
|
36
|
-
<div
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
<div
|
|
37
|
+
onClick={() => {
|
|
38
|
+
onProviderSelect(id);
|
|
39
|
+
}}
|
|
40
|
+
style={{ cursor: 'pointer' }}
|
|
41
|
+
>
|
|
39
42
|
<Flexbox gap={12} width={'100%'}>
|
|
40
43
|
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
|
41
44
|
{source === 'builtin' ? (
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import { useSearchParams } from 'react-router-dom';
|
|
5
|
+
|
|
3
6
|
import { isCustomBranding } from '@/const/version';
|
|
4
7
|
|
|
5
8
|
import DesktopLayout from '../_layout/Desktop';
|
|
6
9
|
import MobileLayout from '../_layout/Mobile';
|
|
7
10
|
import ProviderDetailPage from '../detail';
|
|
8
11
|
import Footer from './Footer';
|
|
9
|
-
import { useMemo, useState } from 'react';
|
|
10
|
-
import { useSearchParams } from 'react-router-dom';
|
|
11
12
|
|
|
12
13
|
const Page = (props: { mobile?: boolean }) => {
|
|
13
14
|
const [SearchParams, setSearchParams] = useSearchParams();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dynamic from 'next/dynamic';
|
|
2
|
+
|
|
2
3
|
import Loading from '@/components/Loading/BrandTextLoading';
|
|
3
4
|
|
|
4
5
|
const NewAPI = dynamic(() => import('./newapi'), { loading: () => <Loading />, ssr: false });
|
|
@@ -7,17 +8,26 @@ const VertexAI = dynamic(() => import('./vertexai'), { loading: () => <Loading /
|
|
|
7
8
|
const GitHub = dynamic(() => import('./github'), { loading: () => <Loading />, ssr: false });
|
|
8
9
|
const Ollama = dynamic(() => import('./ollama'), { loading: () => <Loading />, ssr: false });
|
|
9
10
|
const ComfyUI = dynamic(() => import('./comfyui'), { loading: () => <Loading />, ssr: false });
|
|
10
|
-
const Cloudflare = dynamic(() => import('./cloudflare'), {
|
|
11
|
+
const Cloudflare = dynamic(() => import('./cloudflare'), {
|
|
12
|
+
loading: () => <Loading />,
|
|
13
|
+
ssr: false,
|
|
14
|
+
});
|
|
11
15
|
const Bedrock = dynamic(() => import('./bedrock'), { loading: () => <Loading />, ssr: false });
|
|
12
16
|
const AzureAI = dynamic(() => import('./azureai'), { loading: () => <Loading />, ssr: false });
|
|
13
17
|
const Azure = dynamic(() => import('./azure'), { loading: () => <Loading />, ssr: false });
|
|
14
|
-
const ProviderGrid = dynamic(() => import('../(list)/ProviderGrid'), {
|
|
15
|
-
|
|
18
|
+
const ProviderGrid = dynamic(() => import('../(list)/ProviderGrid'), {
|
|
19
|
+
loading: () => <Loading />,
|
|
20
|
+
ssr: false,
|
|
21
|
+
});
|
|
22
|
+
const DefaultPage = dynamic(() => import('./default/ProviderDetialPage'), {
|
|
23
|
+
loading: () => <Loading />,
|
|
24
|
+
ssr: false,
|
|
25
|
+
});
|
|
16
26
|
|
|
17
27
|
type ProviderDetailPageProps = {
|
|
18
28
|
id?: string | null;
|
|
19
29
|
onProviderSelect: (provider: string) => void;
|
|
20
|
-
}
|
|
30
|
+
};
|
|
21
31
|
|
|
22
32
|
const ProviderDetailPage = (props: ProviderDetailPageProps) => {
|
|
23
33
|
const { id, onProviderSelect } = props;
|
|
@@ -40,6 +40,16 @@ const ChatLayout = dynamic(() => import('./(main)/chat/_layout/Desktop'), {
|
|
|
40
40
|
ssr: false,
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
// Changelog components
|
|
44
|
+
const ChangelogPage = dynamic(() => import('./(main)/changelog/index').then((m) => m.DesktopPage), {
|
|
45
|
+
loading: () => <Loading />,
|
|
46
|
+
ssr: false,
|
|
47
|
+
});
|
|
48
|
+
const ChangelogLayout = dynamic(() => import('./(main)/changelog/_layout/Desktop'), {
|
|
49
|
+
loading: () => <Loading />,
|
|
50
|
+
ssr: false,
|
|
51
|
+
});
|
|
52
|
+
|
|
43
53
|
// Discover List components
|
|
44
54
|
const DesktopHomePage = dynamic(
|
|
45
55
|
() => import('./(main)/discover/(list)/(home)/index').then((m) => m.DesktopHomePage),
|
|
@@ -256,6 +266,7 @@ const KnowledgeErrorBoundary = createErrorBoundary('/knowledge');
|
|
|
256
266
|
const SettingsErrorBoundary = createErrorBoundary('/settings');
|
|
257
267
|
const ImageErrorBoundary = createErrorBoundary('/image');
|
|
258
268
|
const ProfileErrorBoundary = createErrorBoundary('/profile');
|
|
269
|
+
const ChangelogErrorBoundary = createErrorBoundary('/changelog');
|
|
259
270
|
const RootErrorBoundary = createErrorBoundary('/chat'); // Root level falls back to chat
|
|
260
271
|
|
|
261
272
|
// Root layout wrapper component
|
|
@@ -455,6 +466,18 @@ export const createDesktopRouter = (locale: Locales) =>
|
|
|
455
466
|
path: 'profile',
|
|
456
467
|
},
|
|
457
468
|
|
|
469
|
+
// changelog routes
|
|
470
|
+
{
|
|
471
|
+
children: [
|
|
472
|
+
{
|
|
473
|
+
element: <ChangelogPage />,
|
|
474
|
+
index: true,
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
element: <ChangelogLayout locale={locale} />,
|
|
478
|
+
errorElement: <ChangelogErrorBoundary />,
|
|
479
|
+
path: 'changelog',
|
|
480
|
+
},
|
|
458
481
|
// Default route - redirect to chat
|
|
459
482
|
{
|
|
460
483
|
index: true,
|
|
@@ -41,6 +41,15 @@ const ChatLayout = dynamic(() => import('./(main)/chat/_layout/Mobile'), {
|
|
|
41
41
|
ssr: false,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
// Changelog components
|
|
45
|
+
const ChangelogPage = dynamic(() => import('./(main)/changelog/index').then((m) => m.MobilePage), {
|
|
46
|
+
loading: () => <Loading />,
|
|
47
|
+
ssr: false,
|
|
48
|
+
});
|
|
49
|
+
const ChangelogLayout = dynamic(() => import('./(main)/changelog/_layout/Mobile'), {
|
|
50
|
+
loading: () => <Loading />,
|
|
51
|
+
ssr: false,
|
|
52
|
+
});
|
|
44
53
|
// Discover List components
|
|
45
54
|
const MobileHomePage = dynamic(
|
|
46
55
|
() => import('./(main)/discover/(list)/(home)/index').then((m) => m.MobileHomePage),
|
|
@@ -272,6 +281,7 @@ const createErrorBoundary = (resetPath: string) => {
|
|
|
272
281
|
// Create error boundaries for each route
|
|
273
282
|
const ChatErrorBoundary = createErrorBoundary('/chat');
|
|
274
283
|
const DiscoverErrorBoundary = createErrorBoundary('/discover');
|
|
284
|
+
const ChangelogErrorBoundary = createErrorBoundary('/changelog');
|
|
275
285
|
const KnowledgeErrorBoundary = createErrorBoundary('/knowledge');
|
|
276
286
|
const SettingsErrorBoundary = createErrorBoundary('/settings');
|
|
277
287
|
const ImageErrorBoundary = createErrorBoundary('/image');
|
|
@@ -499,6 +509,19 @@ export const createMobileRouter = (locale: Locales) =>
|
|
|
499
509
|
path: 'me',
|
|
500
510
|
},
|
|
501
511
|
|
|
512
|
+
// changelog routes
|
|
513
|
+
{
|
|
514
|
+
children: [
|
|
515
|
+
{
|
|
516
|
+
element: <ChangelogPage />,
|
|
517
|
+
index: true,
|
|
518
|
+
},
|
|
519
|
+
],
|
|
520
|
+
element: <ChangelogLayout locale={locale} />,
|
|
521
|
+
errorElement: <ChangelogErrorBoundary />,
|
|
522
|
+
path: 'changelog',
|
|
523
|
+
},
|
|
524
|
+
|
|
502
525
|
// Default route - redirect to chat
|
|
503
526
|
{
|
|
504
527
|
index: true,
|
|
@@ -143,40 +143,20 @@ const NoteEditorModal = memo<NoteEditorModalProps>(
|
|
|
143
143
|
const handleOpenChange = (isOpen: boolean) => {
|
|
144
144
|
// When modal opens, load document content if in edit mode
|
|
145
145
|
if (isOpen && documentId && editor) {
|
|
146
|
-
console.log('[NoteEditorModal] Loading content:', {
|
|
147
|
-
cachedEditorDataPreview: cachedEditorData
|
|
148
|
-
? JSON.stringify(cachedEditorData).slice(0, 100)
|
|
149
|
-
: null,
|
|
150
|
-
cachedEditorDataType: typeof cachedEditorData,
|
|
151
|
-
documentId,
|
|
152
|
-
documentTitle,
|
|
153
|
-
hasCachedEditorData: !!cachedEditorData,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
146
|
// If editorData is already cached (from list), use it directly
|
|
157
147
|
if (cachedEditorData) {
|
|
158
|
-
console.log('[NoteEditorModal] Using cached editorData', cachedEditorData);
|
|
159
148
|
setNoteTitle(documentTitle || '');
|
|
160
149
|
editor.setDocument('json', JSON.stringify(cachedEditorData));
|
|
161
150
|
return;
|
|
162
151
|
}
|
|
163
152
|
|
|
164
153
|
// Otherwise, fetch full content from API
|
|
165
|
-
console.log('[NoteEditorModal] Fetching from API');
|
|
166
154
|
documentService
|
|
167
155
|
.getDocumentById(documentId)
|
|
168
156
|
.then((doc) => {
|
|
169
157
|
if (doc && doc.content) {
|
|
170
158
|
setNoteTitle(doc.title || doc.filename || '');
|
|
171
159
|
|
|
172
|
-
console.log('[NoteEditorModal] Fetched doc.editorData:', {
|
|
173
|
-
editorDataPreview: doc.editorData
|
|
174
|
-
? JSON.stringify(doc.editorData).slice(0, 100)
|
|
175
|
-
: null,
|
|
176
|
-
editorDataType: typeof doc.editorData,
|
|
177
|
-
hasEditorData: !!doc.editorData,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
160
|
editor.setDocument('json', doc.editorData);
|
|
181
161
|
}
|
|
182
162
|
})
|