@lobehub/chat 1.77.6 → 1.77.8
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/.github/scripts/pr-comment.js +80 -0
- package/.github/scripts/pr-release-body.js +59 -0
- package/.github/workflows/release-desktop.yml +331 -0
- package/.github/workflows/test.yml +1 -1
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +21 -0
- package/next.config.ts +16 -11
- package/package.json +92 -89
- package/packages/electron-client-ipc/README.md +48 -0
- package/packages/electron-client-ipc/package.json +7 -0
- package/packages/electron-client-ipc/src/events/devtools.ts +6 -0
- package/packages/electron-client-ipc/src/events/index.ts +13 -0
- package/packages/electron-client-ipc/src/index.ts +2 -0
- package/packages/electron-client-ipc/src/types/dispatch.ts +10 -0
- package/packages/electron-client-ipc/src/types/index.ts +1 -0
- package/packages/electron-server-ipc/README.md +1 -1
- package/packages/web-crawler/src/crawImpl/search1api.ts +20 -17
- package/pnpm-workspace.yaml +1 -0
- package/scripts/setup-test-postgres-db.sh +21 -0
- package/src/app/desktop/devtools/page.tsx +89 -0
- package/src/app/desktop/layout.tsx +31 -0
- package/src/app/layout.tsx +11 -0
- package/src/app/not-found.tsx +1 -0
- package/src/const/desktop.ts +1 -0
- package/src/const/version.ts +2 -0
- package/src/database/client/db.ts +3 -10
- package/src/database/models/__tests__/message.test.ts +97 -26
- package/src/database/models/__tests__/session.test.ts +2 -0
- package/src/database/models/drizzleMigration.ts +15 -0
- package/src/database/models/message.ts +10 -5
- package/src/database/models/user.ts +3 -0
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +13 -3
- package/src/features/DevPanel/features/FloatPanel.tsx +23 -6
- package/src/features/User/UserPanel/index.tsx +10 -6
- package/src/libs/trpc/middleware/userAuth.ts +10 -0
- package/src/server/routers/tools/__tests__/search.test.ts +1 -0
- package/src/server/translation.test.ts +72 -52
- package/src/server/translation.ts +2 -11
- package/src/services/electron/devtools.ts +9 -0
- package/src/styles/electron.ts +14 -0
- package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/index.tsx +3 -8
- package/src/tools/web-browsing/Render/Search/SearchResult/ShowMore.tsx +2 -4
- package/src/types/electron.ts +11 -0
- package/src/utils/electron/dispatch.ts +10 -0
- package/tsconfig.json +6 -6
- package/vitest.config.ts +3 -1
- package/vitest.server.config.ts +7 -3
@@ -1,12 +1,9 @@
|
|
1
1
|
// @vitest-environment node
|
2
2
|
import { cookies } from 'next/headers';
|
3
|
-
import
|
4
|
-
import * as path from 'node:path';
|
5
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
6
4
|
|
7
|
-
import { DEFAULT_LANG
|
5
|
+
import { DEFAULT_LANG } from '@/const/locale';
|
8
6
|
import { normalizeLocale } from '@/locales/resources';
|
9
|
-
import * as env from '@/utils/env';
|
10
7
|
|
11
8
|
import { getLocale, translation } from './translation';
|
12
9
|
|
@@ -15,15 +12,6 @@ vi.mock('next/headers', () => ({
|
|
15
12
|
cookies: vi.fn(),
|
16
13
|
}));
|
17
14
|
|
18
|
-
vi.mock('node:fs', () => ({
|
19
|
-
existsSync: vi.fn(),
|
20
|
-
readFileSync: vi.fn(),
|
21
|
-
}));
|
22
|
-
|
23
|
-
vi.mock('node:path', () => ({
|
24
|
-
join: vi.fn(),
|
25
|
-
}));
|
26
|
-
|
27
15
|
vi.mock('@/const/locale', () => ({
|
28
16
|
DEFAULT_LANG: 'en-US',
|
29
17
|
LOBE_LOCALE_COOKIE: 'LOBE_LOCALE',
|
@@ -37,6 +25,28 @@ vi.mock('@/utils/env', () => ({
|
|
37
25
|
isDev: false,
|
38
26
|
}));
|
39
27
|
|
28
|
+
// 模拟动态导入结果
|
29
|
+
const mockTranslations = {
|
30
|
+
key1: 'Value 1',
|
31
|
+
key2: 'Value 2 with {{param}}',
|
32
|
+
nested: { key: 'Nested value' },
|
33
|
+
};
|
34
|
+
|
35
|
+
const mockDefaultTranslations = {
|
36
|
+
key1: '默认值 1',
|
37
|
+
key2: '默认值 2 带 {{param}}',
|
38
|
+
nested: { key: '默认嵌套值' },
|
39
|
+
};
|
40
|
+
|
41
|
+
// 重写导入函数
|
42
|
+
vi.mock('@/../locales/en-US/common.json', async () => {
|
43
|
+
return mockTranslations;
|
44
|
+
});
|
45
|
+
|
46
|
+
vi.mock('@/locales/default/common', async () => {
|
47
|
+
return mockDefaultTranslations;
|
48
|
+
});
|
49
|
+
|
40
50
|
describe('getLocale', () => {
|
41
51
|
const mockCookieStore = {
|
42
52
|
get: vi.fn(),
|
@@ -61,17 +71,12 @@ describe('getLocale', () => {
|
|
61
71
|
});
|
62
72
|
|
63
73
|
describe('translation', () => {
|
64
|
-
const mockTranslations = {
|
65
|
-
key1: 'Value 1',
|
66
|
-
key2: 'Value 2 with {{param}}',
|
67
|
-
nested: { key: 'Nested value' },
|
68
|
-
};
|
69
|
-
|
70
74
|
beforeEach(() => {
|
71
75
|
vi.clearAllMocks();
|
72
|
-
|
73
|
-
(
|
74
|
-
|
76
|
+
// 重置 import 模拟
|
77
|
+
vi.doMock('@/../locales/en-US/common.json', async () => {
|
78
|
+
return mockTranslations;
|
79
|
+
});
|
75
80
|
});
|
76
81
|
|
77
82
|
it('should return correct translation object', async () => {
|
@@ -88,43 +93,58 @@ describe('translation', () => {
|
|
88
93
|
expect(t('nested.key')).toBe('Nested value');
|
89
94
|
});
|
90
95
|
|
91
|
-
it('should
|
96
|
+
it('should handle multiple parameters in translation string', async () => {
|
97
|
+
// 模拟多参数翻译
|
98
|
+
vi.doMock('@/../locales/en-US/common.json', async () => ({
|
99
|
+
multiParam: 'Hello {{name}}, you have {{count}} messages',
|
100
|
+
}));
|
101
|
+
|
92
102
|
const { t } = await translation('common', 'en-US');
|
93
|
-
expect(t('
|
103
|
+
expect(t('multiParam', { name: 'John', count: '5' })).toBe('Hello John, you have 5 messages');
|
94
104
|
});
|
95
105
|
|
96
|
-
it('should
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
);
|
106
|
+
it('should handle different namespaces', async () => {
|
107
|
+
// 模拟另一个命名空间
|
108
|
+
vi.doMock('@/../locales/en-US/chat.json', async () => ({
|
109
|
+
welcome: 'Welcome to the chat',
|
110
|
+
}));
|
111
|
+
|
112
|
+
const { t } = await translation('chat', 'en-US');
|
113
|
+
expect(t('welcome')).toBe('Welcome to the chat');
|
103
114
|
});
|
104
115
|
|
105
|
-
it('should
|
106
|
-
|
107
|
-
(
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
116
|
+
it('should handle deep nested objects in translations', async () => {
|
117
|
+
// 模拟深层嵌套对象
|
118
|
+
vi.doMock('@/../locales/en-US/common.json', async () => ({
|
119
|
+
very: {
|
120
|
+
deeply: {
|
121
|
+
nested: {
|
122
|
+
key: 'Found the nested value',
|
123
|
+
},
|
124
|
+
},
|
125
|
+
},
|
126
|
+
}));
|
127
|
+
|
128
|
+
const { t } = await translation('common', 'en-US');
|
129
|
+
expect(t('very.deeply.nested.key')).toBe('Found the nested value');
|
113
130
|
});
|
114
131
|
|
115
|
-
it('should handle
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
});
|
132
|
+
it('should handle empty parameters object', async () => {
|
133
|
+
vi.doMock('@/../locales/en-US/common.json', async () => ({
|
134
|
+
simpleText: 'Just a simple text',
|
135
|
+
}));
|
120
136
|
|
121
|
-
const
|
122
|
-
expect(
|
123
|
-
|
124
|
-
'Error while reading translation file',
|
125
|
-
expect.any(Error),
|
126
|
-
);
|
137
|
+
const { t } = await translation('common', 'en-US');
|
138
|
+
expect(t('simpleText', {})).toBe('Just a simple text');
|
139
|
+
});
|
127
140
|
|
128
|
-
|
141
|
+
it('should handle missing parameters in translation string', async () => {
|
142
|
+
vi.doMock('@/../locales/en-US/common.json', async () => ({
|
143
|
+
withParam: 'Text with {{param}}',
|
144
|
+
}));
|
145
|
+
|
146
|
+
const { t } = await translation('common', 'en-US');
|
147
|
+
// 当缺少参数时应保留占位符
|
148
|
+
expect(t('withParam')).toBe('Text with {{param}}');
|
129
149
|
});
|
130
150
|
});
|
@@ -1,8 +1,6 @@
|
|
1
1
|
'use server';
|
2
2
|
|
3
3
|
import { get } from 'lodash-es';
|
4
|
-
import { existsSync, readFileSync } from 'node:fs';
|
5
|
-
import { join } from 'node:path';
|
6
4
|
|
7
5
|
import { DEFAULT_LANG } from '@/const/locale';
|
8
6
|
import { Locales, NS, normalizeLocale } from '@/locales/resources';
|
@@ -17,15 +15,8 @@ export const translation = async (ns: NS = 'common', hl: string) => {
|
|
17
15
|
let i18ns = {};
|
18
16
|
const lng = await getLocale(hl);
|
19
17
|
try {
|
20
|
-
|
21
|
-
|
22
|
-
if (!isExist)
|
23
|
-
filepath = join(
|
24
|
-
process.cwd(),
|
25
|
-
`locales/${normalizeLocale(isDev ? 'zh-CN' : DEFAULT_LANG)}/${ns}.json`,
|
26
|
-
);
|
27
|
-
const file = readFileSync(filepath, 'utf8');
|
28
|
-
i18ns = JSON.parse(file);
|
18
|
+
if (isDev && lng === 'zh-CN') i18ns = await import(`@/locales/default/${ns}`);
|
19
|
+
i18ns = await import(`@/../locales/${normalizeLocale(lng)}/${ns}.json`);
|
29
20
|
} catch (e) {
|
30
21
|
console.error('Error while reading translation file', e);
|
31
22
|
}
|
@@ -16,25 +16,22 @@ const useStyles = createStyles(({ css, token }) => {
|
|
16
16
|
flex: 1;
|
17
17
|
|
18
18
|
padding: 8px;
|
19
|
+
border-radius: 8px;
|
19
20
|
|
20
21
|
color: initial;
|
21
22
|
|
22
|
-
border-radius: 8px;
|
23
|
-
|
24
23
|
&:hover {
|
25
24
|
background: ${token.colorFillTertiary};
|
26
25
|
}
|
27
26
|
`,
|
28
27
|
desc: css`
|
29
28
|
overflow: hidden;
|
30
|
-
|
31
29
|
display: -webkit-box;
|
32
30
|
-webkit-box-orient: vertical;
|
31
|
+
-webkit-line-clamp: 2;
|
33
32
|
|
34
33
|
color: ${token.colorTextTertiary};
|
35
34
|
text-overflow: ellipsis;
|
36
|
-
|
37
|
-
-webkit-line-clamp: 2;
|
38
35
|
`,
|
39
36
|
displayLink: css`
|
40
37
|
color: ${token.colorTextQuaternary};
|
@@ -45,14 +42,12 @@ const useStyles = createStyles(({ css, token }) => {
|
|
45
42
|
`,
|
46
43
|
url: css`
|
47
44
|
overflow: hidden;
|
48
|
-
{ /* stylelint-disable-line */ }
|
49
45
|
display: -webkit-box;
|
50
46
|
-webkit-box-orient: vertical;
|
47
|
+
-webkit-line-clamp: 1;
|
51
48
|
|
52
49
|
color: ${token.colorTextDescription};
|
53
50
|
text-overflow: ellipsis;
|
54
|
-
|
55
|
-
-webkit-line-clamp: 1;
|
56
51
|
`,
|
57
52
|
};
|
58
53
|
});
|
@@ -14,12 +14,12 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
14
14
|
|
15
15
|
height: 100%;
|
16
16
|
padding: 8px;
|
17
|
+
border-radius: 8px;
|
17
18
|
|
18
19
|
font-size: 12px;
|
19
20
|
color: initial;
|
20
21
|
|
21
22
|
background: ${token.colorFillQuaternary};
|
22
|
-
border-radius: 8px;
|
23
23
|
|
24
24
|
&:hover {
|
25
25
|
background: ${token.colorFillTertiary};
|
@@ -27,13 +27,11 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
27
27
|
`,
|
28
28
|
title: css`
|
29
29
|
overflow: hidden;
|
30
|
-
{ /* stylelint-disable-line */ }
|
31
30
|
display: -webkit-box;
|
32
31
|
-webkit-box-orient: vertical;
|
32
|
+
-webkit-line-clamp: 2;
|
33
33
|
|
34
34
|
text-overflow: ellipsis;
|
35
|
-
|
36
|
-
-webkit-line-clamp: 2;
|
37
35
|
`,
|
38
36
|
}));
|
39
37
|
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { DispatchInvoke } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* client 端请求 sketch 端 event 数据的方法
|
5
|
+
*/
|
6
|
+
export const dispatch: DispatchInvoke = async (event, ...data) => {
|
7
|
+
if (!window.electronAPI) throw new Error('electronAPI not found');
|
8
|
+
|
9
|
+
return window.electronAPI.invoke(event, ...data);
|
10
|
+
};
|
package/tsconfig.json
CHANGED
@@ -27,16 +27,16 @@
|
|
27
27
|
}
|
28
28
|
]
|
29
29
|
},
|
30
|
-
"exclude": ["node_modules", "public/sw.js"],
|
30
|
+
"exclude": ["node_modules", "public/sw.js", "apps/desktop"],
|
31
31
|
"include": [
|
32
|
+
"**/*.d.ts",
|
33
|
+
"**/*.ts",
|
34
|
+
"**/*.tsx",
|
35
|
+
".next/types/**/*.ts",
|
32
36
|
"next-env.d.ts",
|
33
|
-
"vitest.config.ts",
|
34
37
|
"src",
|
35
38
|
"tests",
|
36
|
-
"
|
37
|
-
"**/*.d.ts",
|
38
|
-
"**/*.tsx",
|
39
|
-
".next/types/**/*.ts"
|
39
|
+
"vitest.config.ts"
|
40
40
|
],
|
41
41
|
"ts-node": {
|
42
42
|
"compilerOptions": {
|
package/vitest.config.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { resolve } from 'node:path';
|
2
|
-
import { defineConfig } from 'vitest/config';
|
2
|
+
import { coverageConfigDefaults, defineConfig } from 'vitest/config';
|
3
3
|
|
4
4
|
export default defineConfig({
|
5
5
|
optimizeDeps: {
|
@@ -14,6 +14,8 @@ export default defineConfig({
|
|
14
14
|
coverage: {
|
15
15
|
all: false,
|
16
16
|
exclude: [
|
17
|
+
// https://github.com/lobehub/lobe-chat/pull/7265
|
18
|
+
...coverageConfigDefaults.exclude,
|
17
19
|
'__mocks__/**',
|
18
20
|
// just ignore the migration code
|
19
21
|
// we will use pglite in the future
|
package/vitest.server.config.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { resolve } from 'node:path';
|
2
|
-
import { defineConfig } from 'vitest/config';
|
2
|
+
import { coverageConfigDefaults, defineConfig } from 'vitest/config';
|
3
3
|
|
4
4
|
export default defineConfig({
|
5
5
|
test: {
|
@@ -8,8 +8,12 @@ export default defineConfig({
|
|
8
8
|
},
|
9
9
|
coverage: {
|
10
10
|
all: false,
|
11
|
-
exclude: [
|
12
|
-
|
11
|
+
exclude: [
|
12
|
+
// https://github.com/lobehub/lobe-chat/pull/7265
|
13
|
+
...coverageConfigDefaults.exclude,
|
14
|
+
'src/database/server/core/dbForTest.ts',
|
15
|
+
],
|
16
|
+
include: ['src/database/models/**/*.ts', 'src/database/server/**/*.ts'],
|
13
17
|
provider: 'v8',
|
14
18
|
reporter: ['text', 'json', 'lcov', 'text-summary'],
|
15
19
|
reportsDirectory: './coverage/server',
|