@lobehub/chat 0.140.0 → 0.141.0
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/CHANGELOG.md +50 -0
- package/locales/ar/common.json +34 -6
- package/locales/ar/setting.json +36 -0
- package/locales/de-DE/common.json +34 -6
- package/locales/de-DE/setting.json +36 -0
- package/locales/en-US/common.json +34 -6
- package/locales/en-US/setting.json +36 -0
- package/locales/es-ES/common.json +34 -6
- package/locales/es-ES/setting.json +36 -0
- package/locales/fr-FR/common.json +34 -6
- package/locales/fr-FR/setting.json +36 -0
- package/locales/it-IT/common.json +34 -6
- package/locales/it-IT/setting.json +38 -0
- package/locales/ja-JP/common.json +34 -6
- package/locales/ja-JP/setting.json +38 -0
- package/locales/ko-KR/common.json +34 -6
- package/locales/ko-KR/setting.json +36 -0
- package/locales/nl-NL/common.json +34 -6
- package/locales/nl-NL/setting.json +38 -0
- package/locales/pl-PL/common.json +34 -6
- package/locales/pl-PL/setting.json +36 -0
- package/locales/pt-BR/common.json +34 -6
- package/locales/pt-BR/setting.json +36 -0
- package/locales/ru-RU/common.json +34 -6
- package/locales/ru-RU/setting.json +36 -0
- package/locales/tr-TR/common.json +34 -6
- package/locales/tr-TR/setting.json +36 -0
- package/locales/vi-VN/common.json +34 -6
- package/locales/vi-VN/setting.json +36 -0
- package/locales/zh-CN/common.json +34 -6
- package/locales/zh-CN/setting.json +36 -0
- package/locales/zh-TW/common.json +34 -6
- package/locales/zh-TW/setting.json +36 -0
- package/package.json +10 -5
- package/src/app/chat/(desktop)/features/SessionHeader.tsx +5 -1
- package/src/app/chat/(mobile)/features/SessionHeader.tsx +9 -4
- package/src/app/chat/features/SessionListContent/List/SkeletonList.tsx +0 -1
- package/src/app/settings/(desktop)/features/Header.tsx +11 -1
- package/src/app/settings/(mobile)/features/Header/index.tsx +12 -1
- package/src/app/settings/features/SettingList/index.tsx +2 -1
- package/src/app/settings/sync/Alert.tsx +39 -0
- package/src/app/settings/sync/DeviceInfo/Card.tsx +41 -0
- package/src/app/settings/sync/DeviceInfo/DeviceName.tsx +66 -0
- package/src/app/settings/sync/DeviceInfo/index.tsx +117 -0
- package/src/app/settings/sync/PageTitle.tsx +11 -0
- package/src/app/settings/sync/WebRTC/ChannelNameInput.tsx +46 -0
- package/src/app/settings/sync/WebRTC/index.tsx +97 -0
- package/src/app/settings/sync/components/SyncSwitch/index.css +237 -0
- package/src/app/settings/sync/components/SyncSwitch/index.tsx +79 -0
- package/src/app/settings/sync/components/SystemIcon.tsx +16 -0
- package/src/app/settings/sync/layout.tsx +9 -0
- package/src/app/settings/sync/page.tsx +23 -0
- package/src/app/settings/sync/util.ts +4 -0
- package/src/components/BrowserIcon/components/Brave.tsx +56 -0
- package/src/components/BrowserIcon/components/Chrome.tsx +14 -0
- package/src/components/BrowserIcon/components/Chromium.tsx +14 -0
- package/src/components/BrowserIcon/components/Edge.tsx +36 -0
- package/src/components/BrowserIcon/components/Firefox.tsx +38 -0
- package/src/components/BrowserIcon/components/Opera.tsx +19 -0
- package/src/components/BrowserIcon/components/Safari.tsx +23 -0
- package/src/components/BrowserIcon/components/Samsung.tsx +21 -0
- package/src/components/BrowserIcon/index.tsx +50 -0
- package/src/components/BrowserIcon/types.ts +8 -0
- package/src/config/modelProviders/moonshot.ts +8 -0
- package/src/const/settings.ts +6 -0
- package/src/database/core/__tests__/model.test.ts +2 -2
- package/src/database/core/db.ts +1 -1
- package/src/database/core/index.ts +1 -0
- package/src/database/core/model.ts +83 -5
- package/src/database/core/sync.ts +328 -0
- package/src/database/models/__tests__/message.test.ts +0 -1
- package/src/database/models/__tests__/plugin.test.ts +5 -2
- package/src/database/models/file.ts +1 -1
- package/src/database/models/message.ts +49 -30
- package/src/database/models/plugin.ts +6 -5
- package/src/database/models/session.ts +15 -16
- package/src/database/models/sessionGroup.ts +14 -8
- package/src/database/models/topic.ts +14 -21
- package/src/features/SyncStatusInspector/DisableSync.tsx +79 -0
- package/src/features/SyncStatusInspector/EnableSync.tsx +136 -0
- package/src/features/SyncStatusInspector/EnableTag.tsx +66 -0
- package/src/features/SyncStatusInspector/index.tsx +27 -0
- package/src/hooks/useSyncData.ts +48 -0
- package/src/layout/GlobalLayout/StoreHydration.tsx +5 -0
- package/src/locales/default/common.ts +27 -5
- package/src/locales/default/setting.ts +37 -1
- package/src/services/chat.ts +6 -2
- package/src/services/config.ts +1 -1
- package/src/services/global.ts +15 -0
- package/src/store/chat/slices/topic/action.test.ts +1 -1
- package/src/store/chat/slices/topic/action.ts +21 -10
- package/src/store/global/slices/common/action.ts +71 -1
- package/src/store/global/slices/common/initialState.ts +9 -0
- package/src/store/global/slices/common/selectors.ts +1 -0
- package/src/store/global/slices/preference/initialState.ts +2 -1
- package/src/store/global/slices/preference/selectors.ts +3 -0
- package/src/store/global/slices/settings/selectors/index.ts +1 -0
- package/src/store/global/slices/settings/selectors/sync.ts +14 -0
- package/src/types/settings/index.ts +3 -0
- package/src/types/settings/sync.ts +10 -0
- package/src/types/sync.ts +41 -0
- package/src/utils/platform.ts +9 -3
- package/src/utils/responsive.ts +21 -0
|
@@ -10,17 +10,14 @@ export default {
|
|
|
10
10
|
},
|
|
11
11
|
about: '关于',
|
|
12
12
|
advanceSettings: '高级设置',
|
|
13
|
-
|
|
14
|
-
agentModel: '模型',
|
|
15
|
-
agentProfile: '助手信息',
|
|
13
|
+
|
|
16
14
|
appInitializing: 'LobeChat 启动中,请耐心等待...',
|
|
17
|
-
|
|
15
|
+
|
|
18
16
|
autoGenerate: '自动补全',
|
|
19
17
|
autoGenerateTooltip: '基于提示词自动补全助手描述',
|
|
20
18
|
cancel: '取消',
|
|
21
19
|
changelog: '更新日志',
|
|
22
20
|
close: '关闭',
|
|
23
|
-
confirmRemoveSessionItemAlert: '即将删除该助手,删除后该将无法找回,请确认你的操作',
|
|
24
21
|
copy: '复制',
|
|
25
22
|
copyFail: '复制失败',
|
|
26
23
|
copySuccess: '复制成功',
|
|
@@ -128,6 +125,31 @@ export default {
|
|
|
128
125
|
setting: '设置',
|
|
129
126
|
share: '分享',
|
|
130
127
|
stop: '停止',
|
|
128
|
+
sync: {
|
|
129
|
+
actions: { settings: '同步设置', sync: '立即同步' },
|
|
130
|
+
awareness: {
|
|
131
|
+
current: '当前设备',
|
|
132
|
+
},
|
|
133
|
+
channel: '频道',
|
|
134
|
+
disabled: {
|
|
135
|
+
actions: { enable: '开启云端同步', settings: '配置同步参数' },
|
|
136
|
+
desc: '当前会话数据仅存储于此浏览器中。如果你需要在多个设备间同步数据,请配置并开启云端同步。',
|
|
137
|
+
title: '数据同步未开启',
|
|
138
|
+
},
|
|
139
|
+
enabled: {
|
|
140
|
+
title: '数据同步',
|
|
141
|
+
},
|
|
142
|
+
status: {
|
|
143
|
+
connecting: '连接中',
|
|
144
|
+
disabled: '同步未开启',
|
|
145
|
+
ready: '已连接',
|
|
146
|
+
synced: '已同步',
|
|
147
|
+
syncing: '同步中',
|
|
148
|
+
unconnected: '连接失败',
|
|
149
|
+
},
|
|
150
|
+
title: '同步状态',
|
|
151
|
+
unconnected: { tip: '信令服务器连接失败,将无法建立点对点通信频道,请检查网络后重试' },
|
|
152
|
+
},
|
|
131
153
|
tab: {
|
|
132
154
|
chat: '会话',
|
|
133
155
|
market: '发现',
|
|
@@ -442,12 +442,48 @@ export default {
|
|
|
442
442
|
placeholder: '请输入助手的标识符,需要是唯一的,比如 web-development',
|
|
443
443
|
tooltips: '分享到助手市场',
|
|
444
444
|
},
|
|
445
|
-
|
|
445
|
+
sync: {
|
|
446
|
+
device: {
|
|
447
|
+
deviceName: {
|
|
448
|
+
hint: '添加名称以便于识别',
|
|
449
|
+
placeholder: '请输入设备名称',
|
|
450
|
+
title: '设备名称',
|
|
451
|
+
},
|
|
452
|
+
title: '设备信息',
|
|
453
|
+
unknownBrowser: '未知浏览器',
|
|
454
|
+
unknownOS: '未知系统',
|
|
455
|
+
},
|
|
456
|
+
warning: {
|
|
457
|
+
message: '本功能目前仍为实验性功能,可能存在预期外或不稳定的情况,如遇到问题请及时提交反馈。',
|
|
458
|
+
},
|
|
459
|
+
webrtc: {
|
|
460
|
+
channelName: {
|
|
461
|
+
desc: 'WebRTC 将使用此名创建同步频道,确保频道名称唯一',
|
|
462
|
+
placeholder: '请输入同步频道名称',
|
|
463
|
+
shuffle: '随机生成',
|
|
464
|
+
title: '同步频道名称',
|
|
465
|
+
},
|
|
466
|
+
channelPassword: {
|
|
467
|
+
desc: '添加密码确保频道私密性,只有密码正确时,设备才可加入频道',
|
|
468
|
+
placeholder: '请输入同步频道密码',
|
|
469
|
+
title: '同步频道密码',
|
|
470
|
+
},
|
|
471
|
+
desc: '实时、点对点的数据通信,需设备同时在线才可同步',
|
|
472
|
+
enabled: {
|
|
473
|
+
invalid: '请填写同步频道名称后再开启',
|
|
474
|
+
// desc: 'WebRTC 将使用此名创建同步频道,确保频道名称唯一',
|
|
475
|
+
title: '开启同步',
|
|
476
|
+
},
|
|
477
|
+
title: 'WebRTC 同步',
|
|
478
|
+
},
|
|
479
|
+
},
|
|
446
480
|
tab: {
|
|
447
481
|
about: '关于',
|
|
448
482
|
agent: '默认助手',
|
|
449
483
|
common: '通用设置',
|
|
484
|
+
experiment: '实验',
|
|
450
485
|
llm: '语言模型',
|
|
486
|
+
sync: '云端同步',
|
|
451
487
|
tts: '语音服务',
|
|
452
488
|
},
|
|
453
489
|
tools: {
|
package/src/services/chat.ts
CHANGED
|
@@ -7,7 +7,11 @@ import { TracePayload, TraceTagMap } from '@/const/trace';
|
|
|
7
7
|
import { ModelProvider } from '@/libs/agent-runtime';
|
|
8
8
|
import { filesSelectors, useFileStore } from '@/store/file';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
commonSelectors,
|
|
12
|
+
modelProviderSelectors,
|
|
13
|
+
preferenceSelectors,
|
|
14
|
+
} from '@/store/global/selectors';
|
|
11
15
|
import { useSessionStore } from '@/store/session';
|
|
12
16
|
import { agentSelectors } from '@/store/session/selectors';
|
|
13
17
|
import { useToolStore } from '@/store/tool';
|
|
@@ -310,7 +314,7 @@ class ChatService {
|
|
|
310
314
|
...trace,
|
|
311
315
|
enabled: true,
|
|
312
316
|
tags: [tag, ...(trace?.tags || []), ...tags].filter(Boolean) as string[],
|
|
313
|
-
userId: useGlobalStore.getState()
|
|
317
|
+
userId: commonSelectors.userId(useGlobalStore.getState()),
|
|
314
318
|
};
|
|
315
319
|
}
|
|
316
320
|
}
|
package/src/services/config.ts
CHANGED
|
@@ -42,7 +42,7 @@ class ConfigService {
|
|
|
42
42
|
return topicService.batchCreateTopics(topics);
|
|
43
43
|
};
|
|
44
44
|
importSessionGroups = async (sessionGroups: SessionGroupItem[]) => {
|
|
45
|
-
return sessionService.batchCreateSessionGroups(sessionGroups);
|
|
45
|
+
return sessionService.batchCreateSessionGroups(sessionGroups || []);
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
importConfigState = async (config: ConfigFile): Promise<ImportResults | undefined> => {
|
package/src/services/global.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { dataSync } from '@/database/core';
|
|
1
2
|
import { GlobalServerConfig } from '@/types/settings';
|
|
3
|
+
import { StartDataSyncParams } from '@/types/sync';
|
|
2
4
|
|
|
3
5
|
import { API_ENDPOINTS } from './_url';
|
|
4
6
|
|
|
@@ -20,6 +22,19 @@ class GlobalService {
|
|
|
20
22
|
|
|
21
23
|
return res.json();
|
|
22
24
|
};
|
|
25
|
+
|
|
26
|
+
enabledSync = async (params: StartDataSyncParams) => {
|
|
27
|
+
if (typeof window === 'undefined') return false;
|
|
28
|
+
|
|
29
|
+
await dataSync.startDataSync(params);
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
disableSync = async () => {
|
|
34
|
+
await dataSync.disconnect();
|
|
35
|
+
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
export const globalService = new GlobalService();
|
|
@@ -148,7 +148,7 @@ describe('topic action', () => {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
// Check if mutate has been called with the active session ID
|
|
151
|
-
expect(mutate).toHaveBeenCalledWith(activeId);
|
|
151
|
+
expect(mutate).toHaveBeenCalledWith(['SWR_USE_FETCH_TOPIC', activeId]);
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
it('should handle errors during refreshing topics', async () => {
|
|
@@ -9,6 +9,7 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
9
9
|
import { chainSummaryTitle } from '@/chains/summaryTitle';
|
|
10
10
|
import { LOADING_FLAT } from '@/const/message';
|
|
11
11
|
import { TraceNameMap } from '@/const/trace';
|
|
12
|
+
import { useClientDataSWR } from '@/libs/swr';
|
|
12
13
|
import { chatService } from '@/services/chat';
|
|
13
14
|
import { messageService } from '@/services/message';
|
|
14
15
|
import { topicService } from '@/services/topic';
|
|
@@ -22,6 +23,9 @@ import { topicSelectors } from './selectors';
|
|
|
22
23
|
|
|
23
24
|
const n = setNamespace('topic');
|
|
24
25
|
|
|
26
|
+
const SWR_USE_FETCH_TOPIC = 'SWR_USE_FETCH_TOPIC';
|
|
27
|
+
const SWR_USE_SEARCH_TOPIC = 'SWR_USE_SEARCH_TOPIC';
|
|
28
|
+
|
|
25
29
|
export interface ChatTopicAction {
|
|
26
30
|
favoriteTopic: (id: string, favState: boolean) => Promise<void>;
|
|
27
31
|
openNewTopicOrSaveTopic: () => Promise<void>;
|
|
@@ -141,18 +145,25 @@ export const chatTopic: StateCreator<
|
|
|
141
145
|
},
|
|
142
146
|
// query
|
|
143
147
|
useFetchTopics: (sessionId) =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
useClientDataSWR<ChatTopic[]>(
|
|
149
|
+
[SWR_USE_FETCH_TOPIC, sessionId],
|
|
150
|
+
async ([, sessionId]: [string, string]) => topicService.getTopics({ sessionId }),
|
|
151
|
+
{
|
|
152
|
+
onSuccess: (topics) => {
|
|
153
|
+
set({ topics, topicsInit: true }, false, n('useFetchTopics(success)', { sessionId }));
|
|
154
|
+
},
|
|
147
155
|
},
|
|
148
|
-
|
|
149
|
-
}),
|
|
156
|
+
),
|
|
150
157
|
useSearchTopics: (keywords) =>
|
|
151
|
-
useSWR<ChatTopic[]>(
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
useSWR<ChatTopic[]>(
|
|
159
|
+
[SWR_USE_SEARCH_TOPIC, keywords],
|
|
160
|
+
([, keywords]: [string, string]) => topicService.searchTopics(keywords),
|
|
161
|
+
{
|
|
162
|
+
onSuccess: (data) => {
|
|
163
|
+
set({ searchTopics: data }, false, n('useSearchTopics(success)', { keywords }));
|
|
164
|
+
},
|
|
154
165
|
},
|
|
155
|
-
|
|
166
|
+
),
|
|
156
167
|
switchTopic: async (id) => {
|
|
157
168
|
set({ activeTopicId: id }, false, n('toggleTopic'));
|
|
158
169
|
|
|
@@ -213,6 +224,6 @@ export const chatTopic: StateCreator<
|
|
|
213
224
|
set({ topicLoadingId: id }, false, n('updateTopicLoading'));
|
|
214
225
|
},
|
|
215
226
|
refreshTopic: async () => {
|
|
216
|
-
await mutate(get().activeId);
|
|
227
|
+
await mutate([SWR_USE_FETCH_TOPIC, get().activeId]);
|
|
217
228
|
},
|
|
218
229
|
});
|
|
@@ -11,12 +11,15 @@ import { messageService } from '@/services/message';
|
|
|
11
11
|
import { UserConfig, userService } from '@/services/user';
|
|
12
12
|
import type { GlobalStore } from '@/store/global';
|
|
13
13
|
import type { GlobalServerConfig, GlobalSettings } from '@/types/settings';
|
|
14
|
+
import { OnSyncEvent, PeerSyncStatus } from '@/types/sync';
|
|
14
15
|
import { merge } from '@/utils/merge';
|
|
16
|
+
import { browserInfo } from '@/utils/platform';
|
|
15
17
|
import { setNamespace } from '@/utils/storeDebug';
|
|
16
18
|
import { switchLang } from '@/utils/switchLang';
|
|
17
19
|
|
|
18
20
|
import { preferenceSelectors } from '../preference/selectors';
|
|
19
|
-
import { settingsSelectors } from '../settings/selectors';
|
|
21
|
+
import { settingsSelectors, syncSettingsSelectors } from '../settings/selectors';
|
|
22
|
+
import { commonSelectors } from './selectors';
|
|
20
23
|
|
|
21
24
|
const n = setNamespace('common');
|
|
22
25
|
|
|
@@ -24,11 +27,18 @@ const n = setNamespace('common');
|
|
|
24
27
|
* 设置操作
|
|
25
28
|
*/
|
|
26
29
|
export interface CommonAction {
|
|
30
|
+
refreshConnection: (onEvent: OnSyncEvent) => Promise<void>;
|
|
27
31
|
refreshUserConfig: () => Promise<void>;
|
|
28
32
|
switchBackToChat: (sessionId?: string) => void;
|
|
33
|
+
triggerEnableSync: (userId: string, onEvent: OnSyncEvent) => Promise<boolean>;
|
|
29
34
|
updateAvatar: (avatar: string) => Promise<void>;
|
|
30
35
|
useCheckLatestVersion: () => SWRResponse<string>;
|
|
31
36
|
useCheckTrace: (shouldFetch: boolean) => SWRResponse;
|
|
37
|
+
useEnabledSync: (
|
|
38
|
+
userEnableSync: boolean,
|
|
39
|
+
userId: string | undefined,
|
|
40
|
+
onEvent: OnSyncEvent,
|
|
41
|
+
) => SWRResponse;
|
|
32
42
|
useFetchServerConfig: () => SWRResponse;
|
|
33
43
|
useFetchUserConfig: (initServer: boolean) => SWRResponse<UserConfig | undefined>;
|
|
34
44
|
}
|
|
@@ -41,13 +51,53 @@ export const createCommonSlice: StateCreator<
|
|
|
41
51
|
[],
|
|
42
52
|
CommonAction
|
|
43
53
|
> = (set, get) => ({
|
|
54
|
+
refreshConnection: async (onEvent) => {
|
|
55
|
+
const userId = commonSelectors.userId(get());
|
|
56
|
+
|
|
57
|
+
if (!userId) return;
|
|
58
|
+
|
|
59
|
+
await get().triggerEnableSync(userId, onEvent);
|
|
60
|
+
},
|
|
61
|
+
|
|
44
62
|
refreshUserConfig: async () => {
|
|
45
63
|
await mutate([USER_CONFIG_FETCH_KEY, true]);
|
|
46
64
|
},
|
|
65
|
+
|
|
47
66
|
switchBackToChat: (sessionId) => {
|
|
48
67
|
get().router?.push(SESSION_CHAT_URL(sessionId || INBOX_SESSION_ID, get().isMobile));
|
|
49
68
|
},
|
|
69
|
+
triggerEnableSync: async (userId: string, onEvent: OnSyncEvent) => {
|
|
70
|
+
// double-check the sync ability
|
|
71
|
+
// if there is no channelName, don't start sync
|
|
72
|
+
const sync = syncSettingsSelectors.webrtcConfig(get());
|
|
73
|
+
if (!sync.channelName) return false;
|
|
74
|
+
|
|
75
|
+
const name = syncSettingsSelectors.deviceName(get());
|
|
50
76
|
|
|
77
|
+
const defaultUserName = `My ${browserInfo.browser} (${browserInfo.os})`;
|
|
78
|
+
|
|
79
|
+
set({ syncStatus: PeerSyncStatus.Connecting });
|
|
80
|
+
return globalService.enabledSync({
|
|
81
|
+
channel: {
|
|
82
|
+
name: sync.channelName,
|
|
83
|
+
password: sync.channelPassword,
|
|
84
|
+
},
|
|
85
|
+
onAwarenessChange(state) {
|
|
86
|
+
set({ syncAwareness: state });
|
|
87
|
+
},
|
|
88
|
+
onSyncEvent: onEvent,
|
|
89
|
+
onSyncStatusChange: (status) => {
|
|
90
|
+
set({ syncStatus: status });
|
|
91
|
+
},
|
|
92
|
+
signaling: sync.signaling,
|
|
93
|
+
user: {
|
|
94
|
+
id: userId,
|
|
95
|
+
// if user don't set the name, use default name
|
|
96
|
+
name: name || defaultUserName,
|
|
97
|
+
...browserInfo,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
},
|
|
51
101
|
updateAvatar: async (avatar) => {
|
|
52
102
|
await userService.updateAvatar(avatar);
|
|
53
103
|
await get().refreshUserConfig();
|
|
@@ -78,6 +128,26 @@ export const createCommonSlice: StateCreator<
|
|
|
78
128
|
revalidateOnFocus: false,
|
|
79
129
|
},
|
|
80
130
|
),
|
|
131
|
+
|
|
132
|
+
useEnabledSync: (userEnableSync, userId, onEvent) =>
|
|
133
|
+
useSWR<boolean>(
|
|
134
|
+
['enableSync', userEnableSync, userId],
|
|
135
|
+
async () => {
|
|
136
|
+
// if user don't enable sync or no userId ,don't start sync
|
|
137
|
+
if (!userId) return false;
|
|
138
|
+
|
|
139
|
+
// if user don't enable sync, stop sync
|
|
140
|
+
if (!userEnableSync) return globalService.disableSync();
|
|
141
|
+
|
|
142
|
+
return get().triggerEnableSync(userId, onEvent);
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
onSuccess: (syncEnabled) => {
|
|
146
|
+
set({ syncEnabled });
|
|
147
|
+
},
|
|
148
|
+
revalidateOnFocus: false,
|
|
149
|
+
},
|
|
150
|
+
),
|
|
81
151
|
useFetchServerConfig: () =>
|
|
82
152
|
useSWR<GlobalServerConfig>('fetchGlobalConfig', globalService.getGlobalConfig, {
|
|
83
153
|
onSuccess: (data) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
|
|
2
2
|
|
|
3
|
+
import { PeerSyncStatus, SyncAwarenessState } from '@/types/sync';
|
|
4
|
+
|
|
3
5
|
export enum SidebarTabKey {
|
|
4
6
|
Chat = 'chat',
|
|
5
7
|
Market = 'market',
|
|
@@ -11,6 +13,7 @@ export enum SettingsTabs {
|
|
|
11
13
|
Agent = 'agent',
|
|
12
14
|
Common = 'common',
|
|
13
15
|
LLM = 'llm',
|
|
16
|
+
Sync = 'sync',
|
|
14
17
|
TTS = 'tts',
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -25,9 +28,15 @@ export interface GlobalCommonState {
|
|
|
25
28
|
latestVersion?: string;
|
|
26
29
|
router?: AppRouterInstance;
|
|
27
30
|
sidebarKey: SidebarTabKey;
|
|
31
|
+
syncAwareness: SyncAwarenessState[];
|
|
32
|
+
syncEnabled: boolean;
|
|
33
|
+
syncStatus: PeerSyncStatus;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export const initialCommonState: GlobalCommonState = {
|
|
31
37
|
isMobile: false,
|
|
32
38
|
sidebarKey: SidebarTabKey.Chat,
|
|
39
|
+
syncAwareness: [],
|
|
40
|
+
syncEnabled: false,
|
|
41
|
+
syncStatus: PeerSyncStatus.Disabled,
|
|
33
42
|
};
|
|
@@ -4,4 +4,5 @@ export const commonSelectors = {
|
|
|
4
4
|
enabledOAuthSSO: (s: GlobalStore) => s.serverConfig.enabledOAuthSSO,
|
|
5
5
|
enabledTelemetryChat: (s: GlobalStore) => s.serverConfig.telemetry.langfuse || false,
|
|
6
6
|
userAvatar: (s: GlobalStore) => s.avatar || '',
|
|
7
|
+
userId: (s: GlobalStore) => s.userId,
|
|
7
8
|
};
|
|
@@ -9,10 +9,11 @@ export interface GlobalPreference {
|
|
|
9
9
|
// which sessionGroup should expand
|
|
10
10
|
expandSessionGroupKeys: SessionGroupId[];
|
|
11
11
|
guide?: Guide;
|
|
12
|
+
hideSyncAlert?: boolean;
|
|
12
13
|
inputHeight: number;
|
|
13
14
|
mobileShowTopic?: boolean;
|
|
14
|
-
sessionsWidth: number;
|
|
15
15
|
|
|
16
|
+
sessionsWidth: number;
|
|
16
17
|
showChatSideBar?: boolean;
|
|
17
18
|
showSessionPanel?: boolean;
|
|
18
19
|
showSystemRole?: boolean;
|
|
@@ -6,7 +6,10 @@ const useCmdEnterToSend = (s: GlobalStore): boolean => s.preference.useCmdEnterT
|
|
|
6
6
|
|
|
7
7
|
const userAllowTrace = (s: GlobalStore) => s.preference.telemetry;
|
|
8
8
|
|
|
9
|
+
const hideSyncAlert = (s: GlobalStore) => s.preference.hideSyncAlert;
|
|
10
|
+
|
|
9
11
|
export const preferenceSelectors = {
|
|
12
|
+
hideSyncAlert,
|
|
10
13
|
sessionGroupKeys,
|
|
11
14
|
useCmdEnterToSend,
|
|
12
15
|
userAllowTrace,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { GlobalStore } from '../../../store';
|
|
2
|
+
import { currentSettings } from './settings';
|
|
3
|
+
|
|
4
|
+
const webrtcConfig = (s: GlobalStore) => currentSettings(s).sync.webrtc;
|
|
5
|
+
const webrtcChannelName = (s: GlobalStore) => webrtcConfig(s).channelName;
|
|
6
|
+
const enableWebRTC = (s: GlobalStore) => webrtcConfig(s).enabled;
|
|
7
|
+
const deviceName = (s: GlobalStore) => currentSettings(s).sync.deviceName;
|
|
8
|
+
|
|
9
|
+
export const syncSettingsSelectors = {
|
|
10
|
+
deviceName,
|
|
11
|
+
enableWebRTC,
|
|
12
|
+
webrtcChannelName,
|
|
13
|
+
webrtcConfig,
|
|
14
|
+
};
|
|
@@ -4,12 +4,14 @@ import type { LobeAgentSession } from '@/types/session';
|
|
|
4
4
|
|
|
5
5
|
import { GlobalBaseSettings } from './base';
|
|
6
6
|
import { GlobalLLMConfig } from './modelProvider';
|
|
7
|
+
import { GlobalSyncSettings } from './sync';
|
|
7
8
|
import { GlobalTTSConfig } from './tts';
|
|
8
9
|
|
|
9
10
|
export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
|
|
10
11
|
|
|
11
12
|
export * from './base';
|
|
12
13
|
export * from './modelProvider';
|
|
14
|
+
export * from './sync';
|
|
13
15
|
export * from './tts';
|
|
14
16
|
|
|
15
17
|
export interface GlobalTool {
|
|
@@ -34,6 +36,7 @@ export interface GlobalServerConfig {
|
|
|
34
36
|
export interface GlobalSettings extends GlobalBaseSettings {
|
|
35
37
|
defaultAgent: GlobalDefaultAgent;
|
|
36
38
|
languageModel: GlobalLLMConfig;
|
|
39
|
+
sync: GlobalSyncSettings;
|
|
37
40
|
tool: GlobalTool;
|
|
38
41
|
tts: GlobalTTSConfig;
|
|
39
42
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LobeDBSchemaMap } from '@/database/core/db';
|
|
2
|
+
|
|
3
|
+
export type OnSyncEvent = (tableKey: keyof LobeDBSchemaMap) => void;
|
|
4
|
+
export type OnSyncStatusChange = (status: PeerSyncStatus) => void;
|
|
5
|
+
export type OnAwarenessChange = (state: SyncAwarenessState[]) => void;
|
|
6
|
+
|
|
7
|
+
// export type PeerSyncStatus = 'syncing' | 'synced' | 'ready' | 'unconnected';
|
|
8
|
+
|
|
9
|
+
export enum PeerSyncStatus {
|
|
10
|
+
Connecting = 'connecting',
|
|
11
|
+
Disabled = 'disabled',
|
|
12
|
+
Ready = 'ready',
|
|
13
|
+
Synced = 'synced',
|
|
14
|
+
Syncing = 'syncing',
|
|
15
|
+
Unconnected = 'unconnected',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StartDataSyncParams {
|
|
19
|
+
channel: {
|
|
20
|
+
name: string;
|
|
21
|
+
password?: string;
|
|
22
|
+
};
|
|
23
|
+
onAwarenessChange: OnAwarenessChange;
|
|
24
|
+
onSyncEvent: OnSyncEvent;
|
|
25
|
+
onSyncStatusChange: OnSyncStatusChange;
|
|
26
|
+
signaling?: string;
|
|
27
|
+
user: SyncUserInfo;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SyncUserInfo {
|
|
31
|
+
browser?: string;
|
|
32
|
+
id: string;
|
|
33
|
+
isMobile: boolean;
|
|
34
|
+
name?: string;
|
|
35
|
+
os?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SyncAwarenessState extends SyncUserInfo {
|
|
39
|
+
clientID: number;
|
|
40
|
+
current: boolean;
|
|
41
|
+
}
|
package/src/utils/platform.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import UAParser from 'ua-parser-js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const getParser = () => {
|
|
4
4
|
if (typeof window === 'undefined') return new UAParser('Node');
|
|
5
5
|
|
|
6
6
|
let ua = navigator.userAgent;
|
|
@@ -8,11 +8,17 @@ const getPaser = () => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export const getPlatform = () => {
|
|
11
|
-
return
|
|
11
|
+
return getParser().getOS().name;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export const getBrowser = () => {
|
|
15
|
-
return
|
|
15
|
+
return getParser().getResult().browser.name;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const browserInfo = {
|
|
19
|
+
browser: getBrowser(),
|
|
20
|
+
isMobile: getParser().getDevice().type === 'mobile',
|
|
21
|
+
os: getParser().getOS().name,
|
|
16
22
|
};
|
|
17
23
|
|
|
18
24
|
export const isMacOS = () => getPlatform() === 'Mac OS';
|
package/src/utils/responsive.ts
CHANGED
|
@@ -17,3 +17,24 @@ export const isMobileDevice = () => {
|
|
|
17
17
|
|
|
18
18
|
return device.type === 'mobile';
|
|
19
19
|
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* check mobile device in server
|
|
23
|
+
*/
|
|
24
|
+
export const gerServerDeviceInfo = () => {
|
|
25
|
+
if (typeof process === 'undefined') {
|
|
26
|
+
throw new Error('[Server method] you are importing a server-only module outside of server');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { get } = headers();
|
|
30
|
+
const ua = get('user-agent');
|
|
31
|
+
|
|
32
|
+
// console.debug(ua);
|
|
33
|
+
const parser = new UAParser(ua || '');
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
browser: parser.getBrowser().name,
|
|
37
|
+
isMobile: isMobileDevice(),
|
|
38
|
+
os: parser.getOS().name,
|
|
39
|
+
};
|
|
40
|
+
};
|