@lobehub/chat 1.119.2 → 1.120.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.
Files changed (56) hide show
  1. package/.vscode/settings.json +2 -3
  2. package/CHANGELOG.md +33 -0
  3. package/changelog/v1.json +12 -0
  4. package/package.json +1 -4
  5. package/packages/database/src/models/__tests__/generationBatch.test.ts +47 -1
  6. package/packages/database/src/models/generationBatch.ts +8 -1
  7. package/packages/model-bank/src/aiModels/aihubmix.ts +1 -1
  8. package/packages/model-bank/src/aiModels/google.ts +4 -4
  9. package/packages/model-bank/src/aiModels/openrouter.ts +2 -2
  10. package/packages/model-bank/src/aiModels/qwen.ts +3 -1
  11. package/packages/model-bank/src/aiModels/siliconcloud.ts +6 -0
  12. package/packages/model-bank/src/aiModels/vertexai.ts +2 -2
  13. package/packages/model-runtime/src/google/createImage.ts +52 -24
  14. package/packages/model-runtime/src/qwen/index.ts +1 -1
  15. package/packages/model-runtime/src/siliconcloud/index.ts +1 -1
  16. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +2 -16
  17. package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +1 -3
  18. package/src/app/[variants]/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx +1 -3
  19. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  20. package/src/config/featureFlags/schema.test.ts +1 -2
  21. package/src/config/featureFlags/schema.ts +0 -6
  22. package/src/config/featureFlags/utils/parser.test.ts +7 -7
  23. package/src/database/_deprecated/core/index.ts +0 -1
  24. package/src/database/_deprecated/core/model.ts +4 -38
  25. package/src/database/_deprecated/models/message.ts +1 -1
  26. package/src/layout/GlobalProvider/StoreInitialization.tsx +0 -3
  27. package/src/store/serverConfig/selectors.test.ts +0 -1
  28. package/src/store/user/initialState.ts +1 -4
  29. package/src/store/user/selectors.ts +0 -1
  30. package/src/store/user/store.ts +1 -4
  31. package/docs/self-hosting/advanced/webrtc.mdx +0 -86
  32. package/docs/self-hosting/advanced/webrtc.zh-CN.mdx +0 -80
  33. package/src/app/[variants]/(main)/settings/sync/features/Alert.tsx +0 -53
  34. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/Card.tsx +0 -42
  35. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/DeviceName.tsx +0 -62
  36. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/SystemIcon.tsx +0 -31
  37. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/index.tsx +0 -103
  38. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/ChannelNameInput.tsx +0 -45
  39. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.css +0 -238
  40. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.tsx +0 -79
  41. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/generateRandomRoomName.ts +0 -4
  42. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/index.tsx +0 -103
  43. package/src/app/[variants]/(main)/settings/sync/index.tsx +0 -17
  44. package/src/app/[variants]/(main)/settings/sync/page.tsx +0 -29
  45. package/src/database/_deprecated/core/sync.ts +0 -321
  46. package/src/features/SyncStatusInspector/DisableSync.tsx +0 -79
  47. package/src/features/SyncStatusInspector/EnableSync.tsx +0 -132
  48. package/src/features/SyncStatusInspector/EnableTag.tsx +0 -66
  49. package/src/features/SyncStatusInspector/index.tsx +0 -27
  50. package/src/hooks/useSyncData.ts +0 -50
  51. package/src/services/__tests__/sync.test.ts +0 -56
  52. package/src/services/sync.ts +0 -19
  53. package/src/store/user/slices/sync/action.test.ts +0 -164
  54. package/src/store/user/slices/sync/action.ts +0 -101
  55. package/src/store/user/slices/sync/initialState.ts +0 -13
  56. package/src/store/user/slices/sync/selectors.ts +0 -20
@@ -4,7 +4,6 @@ import { ZodObject } from 'zod';
4
4
  import { nanoid } from '@/utils/uuid';
5
5
 
6
6
  import { BrowserDB, BrowserDBSchema, browserDB } from './db';
7
- import { dataSync } from './sync';
8
7
  import { DBBaseFieldsSchema } from './types/db';
9
8
 
10
9
  export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchema[N]['table']> {
@@ -22,10 +21,6 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
22
21
  return this.db[this._tableName] as Dexie.Table;
23
22
  }
24
23
 
25
- get yMap() {
26
- return dataSync.getYMap(this._tableName);
27
- }
28
-
29
24
  // **************** Create *************** //
30
25
 
31
26
  /**
@@ -79,7 +74,6 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
79
74
  */
80
75
  createWithNewId?: boolean;
81
76
  idGenerator?: () => string;
82
- withSync?: boolean;
83
77
  } = {},
84
78
  ): Promise<{
85
79
  added: number;
@@ -88,7 +82,7 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
88
82
  skips: string[];
89
83
  success: boolean;
90
84
  }> {
91
- const { idGenerator = nanoid, createWithNewId = false, withSync = true } = options;
85
+ const { idGenerator = nanoid, createWithNewId = false } = options;
92
86
  const validatedData: any[] = [];
93
87
  const errors = [];
94
88
  const skips: string[] = [];
@@ -141,15 +135,6 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
141
135
  try {
142
136
  await this.table.bulkAdd(validatedData);
143
137
 
144
- if (withSync) {
145
- dataSync.transact(() => {
146
- const pools = validatedData.map(async (item) => {
147
- await this.updateYMapItem(item.id);
148
- });
149
- Promise.all(pools);
150
- });
151
- }
152
-
153
138
  return {
154
139
  added: validatedData.length,
155
140
  ids: validatedData.map((item) => item.id),
@@ -174,28 +159,16 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
174
159
  // **************** Delete *************** //
175
160
 
176
161
  protected async _deleteWithSync(id: string) {
177
- const result = await this.table.delete(id);
178
- // sync delete data to yjs data map
179
- this.yMap?.delete(id);
180
- return result;
162
+ return await this.table.delete(id);
181
163
  }
182
164
 
183
165
  protected async _bulkDeleteWithSync(keys: string[]) {
184
166
  await this.table.bulkDelete(keys);
185
167
  // sync delete data to yjs data map
186
-
187
- dataSync.transact(() => {
188
- keys.forEach((id) => {
189
- this.yMap?.delete(id);
190
- });
191
- });
192
168
  }
193
169
 
194
170
  protected async _clearWithSync() {
195
- const result = await this.table.clear();
196
- // sync clear data to yjs data map
197
- this.yMap?.clear();
198
- return result;
171
+ return await this.table.clear();
199
172
  }
200
173
 
201
174
  // **************** Update *************** //
@@ -235,18 +208,11 @@ export class BaseModel<N extends keyof BrowserDBSchema = any, T = BrowserDBSchem
235
208
 
236
209
  protected async _bulkPutWithSync(items: T[]) {
237
210
  await this.table.bulkPut(items);
238
-
239
- await dataSync.transact(() => {
240
- items.forEach((items) => {
241
- this.updateYMapItem((items as any).id);
242
- });
243
- });
244
211
  }
245
212
 
246
213
  // **************** Helper *************** //
247
214
 
248
215
  private updateYMapItem = async (id: string) => {
249
- const newData = await this.table.get(id);
250
- this.yMap?.set(id, newData);
216
+ await this.table.get(id);
251
217
  };
252
218
  }
@@ -118,7 +118,7 @@ class _MessageModel extends BaseModel {
118
118
  async batchCreate(messages: ChatMessage[]) {
119
119
  const data: DB_Message[] = messages.map((m) => this.mapChatMessageToDBMessage(m));
120
120
 
121
- return this._batchAdd(data, { withSync: true });
121
+ return this._batchAdd(data);
122
122
  }
123
123
 
124
124
  async duplicateMessages(messages: ChatMessage[]): Promise<ChatMessage[]> {
@@ -7,7 +7,6 @@ import { createStoreUpdater } from 'zustand-utils';
7
7
 
8
8
  import { enableNextAuth } from '@/const/auth';
9
9
  import { useIsMobile } from '@/hooks/useIsMobile';
10
- import { useEnabledDataSync } from '@/hooks/useSyncData';
11
10
  import { useAgentStore } from '@/store/agent';
12
11
  import { useAiInfraStore } from '@/store/aiInfra';
13
12
  import { useGlobalStore } from '@/store/global';
@@ -70,8 +69,6 @@ const StoreInitialization = memo(() => {
70
69
  },
71
70
  });
72
71
 
73
- useEnabledDataSync();
74
-
75
72
  const useStoreUpdater = createStoreUpdater(useGlobalStore);
76
73
 
77
74
  const mobile = useIsMobile();
@@ -17,7 +17,6 @@ describe('featureFlagsSelectors', () => {
17
17
  const result = featureFlagsSelectors(store.getState());
18
18
 
19
19
  expect(result).toEqual({
20
- enableWebrtc: false,
21
20
  isAgentEditable: false,
22
21
  showApiKeyManage: false,
23
22
  enablePlugins: true,
@@ -3,17 +3,14 @@ import { CommonState, initialCommonState } from './slices/common/initialState';
3
3
  import { ModelListState, initialModelListState } from './slices/modelList/initialState';
4
4
  import { UserPreferenceState, initialPreferenceState } from './slices/preference/initialState';
5
5
  import { UserSettingsState, initialSettingsState } from './slices/settings/initialState';
6
- import { UserSyncState, initialSyncState } from './slices/sync/initialState';
7
6
 
8
- export type UserState = UserSyncState &
9
- UserSettingsState &
7
+ export type UserState = UserSettingsState &
10
8
  UserPreferenceState &
11
9
  UserAuthState &
12
10
  ModelListState &
13
11
  CommonState;
14
12
 
15
13
  export const initialState: UserState = {
16
- ...initialSyncState,
17
14
  ...initialSettingsState,
18
15
  ...initialPreferenceState,
19
16
  ...initialAuthState,
@@ -10,4 +10,3 @@ export {
10
10
  systemAgentSelectors,
11
11
  userGeneralSettingsSelectors,
12
12
  } from './slices/settings/selectors';
13
- export { syncSettingsSelectors } from './slices/sync/selectors';
@@ -10,12 +10,10 @@ import { type CommonAction, createCommonSlice } from './slices/common/action';
10
10
  import { type ModelListAction, createModelListSlice } from './slices/modelList/action';
11
11
  import { type PreferenceAction, createPreferenceSlice } from './slices/preference/action';
12
12
  import { type UserSettingsAction, createSettingsSlice } from './slices/settings/action';
13
- import { type SyncAction, createSyncSlice } from './slices/sync/action';
14
13
 
15
14
  // =============== 聚合 createStoreFn ============ //
16
15
 
17
- export type UserStore = SyncAction &
18
- UserState &
16
+ export type UserStore = UserState &
19
17
  UserSettingsAction &
20
18
  PreferenceAction &
21
19
  ModelListAction &
@@ -24,7 +22,6 @@ export type UserStore = SyncAction &
24
22
 
25
23
  const createStore: StateCreator<UserStore, [['zustand/devtools', never]]> = (...parameters) => ({
26
24
  ...initialState,
27
- ...createSyncSlice(...parameters),
28
25
  ...createSettingsSlice(...parameters),
29
26
  ...createPreferenceSlice(...parameters),
30
27
  ...createAuthSlice(...parameters),
@@ -1,86 +0,0 @@
1
- ---
2
- title: LobeChat WebRTC Sync - Real-Time Data Sharing
3
- description: >-
4
- Explore LobeChat's WebRTC sync for real-time data sharing and privacy without servers.
5
-
6
- tags:
7
- - WebRTC
8
- - LobeChat
9
- - Data Synchronization
10
- - Real-Time Communication
11
- - Peer-to-Peer
12
- ---
13
-
14
- # LobeChat WebRTC Sync
15
-
16
- ## Introduction to WebRTC
17
-
18
- WebRTC (Web Real-Time Communication) is a technology that enables peer-to-peer communication between browsers. In LobeChat, we experimentally implemented real-time data synchronization between devices based on WebRTC and YJS, without relying on traditional server databases. This solution offers high privacy, zero conflicts, and provides a real-time session synchronization experience.
19
-
20
- ## Configuring WebRTC for Synchronization
21
-
22
- To use the WebRTC synchronization feature in LobeChat, you need to complete the following steps:
23
-
24
- <Steps>
25
- ### Deploy Signaling Server
26
-
27
- Deploy a WebRTC signaling server with one click using the Zeabur platform:
28
-
29
- [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/MY0JZG?referralCode=arvinxx)
30
-
31
- Alternatively, you can view the [source code](https://github.com/lobehub/y-webrtc-signaling) and deploy it on your own.
32
-
33
- After deployment, you will receive a URL, for example: `https://my-signaling-server.zeabur.app`.
34
-
35
- ### Enable WebRTC Sync in the Deployment Instance
36
-
37
- The WebRTC sync feature in LobeChat is hidden by default and needs to be enabled by adding the environment variable `FEATURE_FLAGS=+webrtc_sync`.
38
-
39
- ### Configure WebRTC Sync Settings in LobeChat
40
-
41
- 1. Open LobeChat settings -> Data Sync
42
- 2. Enter the signaling server address in the WebRTC sync section;
43
- 3. Set the sync channel name and password
44
-
45
- <Image alt={'LobeChat Data Sync Settings Page'} height={356} inStep src={'https://github.com/lobehub/lobe-chat/assets/28616219/bf86bf1e-87fb-4015-8587-15ff28bb9c24'} />
46
-
47
- ### Repeat the Above Configuration on Devices that Need to Sync
48
-
49
- Ensure all devices use the same signaling server, channel name, and password. Once configured, the devices should automatically start syncing data.
50
- </Steps>
51
-
52
- ## Limitations and Known Issues
53
-
54
- Although WebRTC has the advantages of no database and flexibility, after extensive community testing, the following limitations and known issues have been identified:
55
-
56
- ### Requirement for Devices to be Online Simultaneously
57
-
58
- WebRTC requires devices to be online simultaneously to synchronize, meaning changes cannot be made on one device while offline and then synced later on another device.
59
-
60
- This limitation is due to the communication nature of WebRTC. In a pure frontend, serverless scenario, data synchronization between two devices can only be achieved through peer-to-peer communication. When one device is online and the other is offline, it is impossible to determine where the data should come from. Only when both devices are online can data communication occur. This mode is more like an online chat room where everyone needs to be online to see each other's data and achieve synchronization.
61
-
62
- Therefore, in certain situations, WebRTC's pure peer-to-peer approach may not fully meet users' needs (e.g., one device is a work computer, and the other is a home computer), and there are also some issues with data synchronization.
63
-
64
- ### Network Issues Leading to Sync Failures
65
-
66
- Due to the implementation mechanism of WebRTC, its peer-to-peer communication has strict network requirements. Many of our users have reported:
67
-
68
- - Syncing between PCs is possible, but syncing between a mobile device with a SIM card and a PC is not, although syncing is possible when using the same WIFI as the PC;
69
- - Syncing fails when switching networks.
70
-
71
- ### Stability and Performance Issues
72
-
73
- - Some users have reported ICE connection failures on the Firefox browser: [WebRTC Data Sync Feedback](https://github.com/lobehub/lobe-chat/issues/1683#issuecomment-2094745907)
74
- - For extremely long text or large amounts of conversation records, the synchronization process may slow down or become unstable: [When the model outputs a very long conversation, the end of the conversation will contain synchronization-related content tags, leading to sync failures](https://github.com/lobehub/lobe-chat/issues/1962)
75
-
76
- ## Our Recommendations
77
-
78
- Considering the above reasons, we recommend users treat the WebRTC sync feature as experimental and regularly back up important data.
79
-
80
- We have already released a more stable and user-friendly server database synchronization solution ([deployment guide](/docs/self-hosting/advanced/server-database)). We recommend users prioritize using the server database synchronization solution.
81
-
82
- <Callout type={'warning'}>
83
- Please note that we have officially announced the archiving of this sync feature in [PR
84
- 3182](https://github.com/lobehub/lobe-chat/pull/3182), and the above issues will no longer be
85
- considered for fixes.
86
- </Callout>
@@ -1,80 +0,0 @@
1
- ---
2
- title: LobeChat WebRTC 同步配置指南
3
- description: 在 LobeChat 中实现基于 WebRTC 和 YJS 的设备间实时数据同步。了解如何配置 WebRTC 并开启同步功能,以及使用局限性和已知问题。
4
- tags:
5
- - YJS
6
- - 信令服务器
7
- ---
8
-
9
- # LobeChat WebRTC 同步
10
-
11
- ## WebRTC 简介
12
-
13
- WebRTC (Web Real-Time Communication) 是一项实现浏览器之间点对点通信的技术。在 LobeChat 中,我们实验性地基于 WebRTC 和 YJS 实现了设备间的实时数据同步,无需依赖传统的服务器数据库。这种方案具有高度隐私性、零冲突性,并能提供实时会话同步体验。
14
-
15
- ## 配置 WebRTC 并实现同步
16
-
17
- 要使用 LobeChat 的 WebRTC 同步功能,需要完成以下步骤:
18
-
19
- <Steps>
20
- ### 部署信令服务器
21
-
22
- 使用 Zeabur 平台一键部署 WebRTC 信令服务器:
23
-
24
- [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/MY0JZG?referralCode=arvinxx)
25
-
26
- 或者查看 [源码](https://github.com/lobehub/y-webrtc-signaling) 自行部署。
27
-
28
- 部署完成后,可以得到一个 URL,例如:`https://my-signaling-server.zeabur.app`。
29
-
30
- ### 在部署实例中开启 WebRTC 同步
31
-
32
- LobeChat 默认隐藏了 WebRTC 同步功能,需要通过添加环境变量 `FEATURE_FLAGS=+webrtc_sync` 来开启 WebRTC 同步特性。
33
-
34
- ### 配置 LobeChat 的 WebRTC 同步设置
35
-
36
- 1. 打开 LobeChat 设置 -> 数据同步
37
- 2. 在 WebRTC 同步中填写信令服务器地址;
38
- 3. 设置同步频道名称和密码
39
-
40
- <Image alt={'LobeChat 数据同步设置页'} height={356} inStep src={'https://github.com/lobehub/lobe-chat/assets/28616219/bf86bf1e-87fb-4015-8587-15ff28bb9c24'} />
41
-
42
- ### 在需要同步的设备上重复以上配置
43
-
44
- 确保所有设备使用相同的信令服务器、频道名称和密码,完成配置后,设备间应该可以开始自动同步数据。
45
- </Steps>
46
-
47
- ## 使用局限性和已知问题
48
-
49
- 虽然 WebRTC 具有无数据库、比较灵活的特性,但目前该功能经过大范围社区测试,存在以下局限性和已知问题:
50
-
51
- ### 设备同时在线要求
52
-
53
- WebRTC 要求设备同时在线才能进行同步,这意味着无法在一台设备离线时在另一台设备上进行更改并稍后同步。
54
-
55
- 这是 WebRTC 本身的通信特性有关系,由于在纯前端、无服务端的情况下,两个设备的数据同步只能通过点对点通信的形式达成。当一个设备在线,一个设备离线的情况下,我们无从感知数据到底应该从哪来,只有当两台设备都在线的时候,双发数据才能通信。其实这种模式更像是一个在线聊天室,大家都在线时才能看到对方的数据,然后达成同步。
56
-
57
- 因此 WebRTC 这种纯点对点的方式在某些情况下并无法完全满足用户的诉求(例如一个是公司电脑,一个是家里电脑),同时也存在一些数据同步层面的问题。
58
-
59
- ### 网络问题可能导致同步失败
60
-
61
- 由于 WebRTC 的实现机制,其点对点通信对于网络要求非常苛刻,我们的很多用户反馈:
62
-
63
- - 在 PC 上可以互相同步、 手机 sim 卡无法和 PC 同步、但是换成和 PC 一样的 WIFI 可以和 PC 同步;
64
- - 任何切换网络都无法同步;
65
-
66
- ### 稳定性与性能问题
67
-
68
- - 部分用户报告在 Firefox 浏览器上遇到 ICE 连接失败的问题:[WebRTC Data Sync Feedback](https://github.com/lobehub/lobe-chat/issues/1683#issuecomment-2094745907)
69
- - 对于超长文本或大量对话记录,同步过程可能变慢或不稳定:[当模型输出超长对话时,对话末尾会出现同步相关的内容标签,导致同步失败](https://github.com/lobehub/lobe-chat/issues/1962)
70
-
71
- ## 我们的建议
72
-
73
- 鉴于以上原因,我们建议用户将 WebRTC 同步功能视为实验性功能,并定期备份重要数据。
74
-
75
- 目前我们已经发布了更稳定、更用户友好的服务端数据库同步方案([部署指南](/zh/docs/self-hosting/advanced/server-database)),我们建议用户优先考虑使用服务端数据库同步方案。
76
-
77
- <Callout type={'warning'}>
78
- 请注意,我们已经在 [PR 3182](https://github.com/lobehub/lobe-chat/pull/3182)
79
- 中正式宣布归档该同步特性,上述问题将不再考虑进行修复。
80
- </Callout>
@@ -1,53 +0,0 @@
1
- 'use client';
2
-
3
- import { Alert } from '@lobehub/ui';
4
- import Link from 'next/link';
5
- import { memo } from 'react';
6
- import { Trans } from 'react-i18next';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import { MAX_WIDTH } from '@/const/layoutTokens';
10
- import { WEBRTC_SYNC_DOCUMENTS } from '@/const/url';
11
- import { useUserStore } from '@/store/user';
12
- import { preferenceSelectors } from '@/store/user/selectors';
13
-
14
- interface ExperimentAlertProps {
15
- mobile?: boolean;
16
- }
17
- const ExperimentAlert = memo<ExperimentAlertProps>(({ mobile }) => {
18
- const [hideSyncAlert, updatePreference] = useUserStore((s) => [
19
- preferenceSelectors.hideSyncAlert(s),
20
- s.updatePreference,
21
- ]);
22
-
23
- return (
24
- !hideSyncAlert && (
25
- <Flexbox style={{ maxWidth: MAX_WIDTH }} width={'100%'}>
26
- <Alert
27
- banner={mobile}
28
- closable
29
- message={
30
- <Trans i18nKey="sync.warning.tip" ns={'setting'}>
31
- 经过较长一段时间测试,WebRTC 同步可能无法稳定满足通用的数据同步诉求。请自行
32
- <Link
33
- aria-label={'Webrtc Sync deployment'}
34
- href={WEBRTC_SYNC_DOCUMENTS}
35
- style={{ color: 'inherit', textDecoration: 'underline' }}
36
- target="_blank"
37
- >
38
- 部署信令服务器
39
- </Link>
40
- 后使用。
41
- </Trans>
42
- }
43
- onClose={() => {
44
- updatePreference({ hideSyncAlert: true });
45
- }}
46
- type={'warning'}
47
- />
48
- </Flexbox>
49
- )
50
- );
51
- });
52
-
53
- export default ExperimentAlert;
@@ -1,42 +0,0 @@
1
- import { createStyles } from 'antd-style';
2
- import { ReactNode, memo } from 'react';
3
- import { Center, Flexbox } from 'react-layout-kit';
4
-
5
- const useStyles = createStyles(({ css, token, responsive }) => ({
6
- container: css`
7
- border-radius: ${token.borderRadius}px;
8
- background: ${token.colorFillTertiary};
9
-
10
- .${responsive.mobile} {
11
- width: 100%;
12
- }
13
- `,
14
- icon: css`
15
- width: 24px;
16
- height: 24px;
17
- `,
18
- title: css`
19
- font-size: 16px;
20
- `,
21
- }));
22
-
23
- const Card = memo<{ icon: ReactNode; title: string }>(({ title, icon }) => {
24
- const { styles } = useStyles();
25
-
26
- return (
27
- <Flexbox
28
- align={'center'}
29
- className={styles.container}
30
- flex={1}
31
- gap={12}
32
- horizontal
33
- paddingBlock={12}
34
- paddingInline={20}
35
- >
36
- <Center className={styles.icon}>{icon}</Center>
37
- <div className={styles.title}>{title}</div>
38
- </Flexbox>
39
- );
40
- });
41
-
42
- export default Card;
@@ -1,62 +0,0 @@
1
- 'use client';
2
-
3
- import { EditableText, Text } from '@lobehub/ui';
4
- import { memo, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import { useUserStore } from '@/store/user';
9
- import { syncSettingsSelectors } from '@/store/user/selectors';
10
-
11
- const DeviceName = memo(() => {
12
- const { t } = useTranslation('setting');
13
-
14
- const [deviceName, setSettings] = useUserStore((s) => [
15
- syncSettingsSelectors.deviceName(s),
16
- s.setSettings,
17
- ]);
18
-
19
- const [editing, setEditing] = useState(false);
20
-
21
- const updateDeviceName = (deviceName: string) => {
22
- setSettings({ sync: { deviceName } });
23
- setEditing(false);
24
- };
25
-
26
- return (
27
- <Flexbox
28
- align={'center'}
29
- flex={1}
30
- height={40}
31
- horizontal
32
- style={{ fontSize: 20, fontWeight: 'bold', lineHeight: 1, minWidth: 240, paddingLeft: 8 }}
33
- >
34
- {!deviceName && !editing && (
35
- <Flexbox
36
- onClick={() => {
37
- setEditing(true);
38
- }}
39
- style={{ cursor: 'pointer' }}
40
- >
41
- <Text type={'secondary'}>{t('sync.device.deviceName.hint')}</Text>
42
- </Flexbox>
43
- )}
44
- <EditableText
45
- editing={editing}
46
- inputProps={{
47
- placeholder: t('sync.device.deviceName.placeholder'),
48
- }}
49
- onBlur={(e) => updateDeviceName(e.target.value)}
50
- onChange={(e) => {
51
- updateDeviceName(e);
52
- }}
53
- onEditingChange={setEditing}
54
- size={'large'}
55
- value={deviceName}
56
- variant={'filled'}
57
- />
58
- </Flexbox>
59
- );
60
- });
61
-
62
- export default DeviceName;
@@ -1,31 +0,0 @@
1
- import {
2
- SiAndroid,
3
- SiApple,
4
- SiBlackberry,
5
- SiGooglechrome,
6
- SiLinux,
7
- SiWindows11,
8
- } from '@icons-pack/react-simple-icons';
9
- import { memo } from 'react';
10
-
11
- const SystemIcon = memo<{ title?: string }>(({ title }) => {
12
- if (!title) return;
13
-
14
- if (['Mac OS', 'iOS', 'iPadOS'].includes(title)) return <SiApple size={24} />;
15
-
16
- // Remove Microsoft brands in @icons-pack/react-simple-icons v10
17
- // https://github.com/simple-icons/simple-icons/pull/10019
18
- if (['Windows'].includes(title)) return <SiWindows11 size={24} />;
19
-
20
- if (title === 'Android') return <SiAndroid size={24} />;
21
-
22
- if (['BlackBerry'].includes(title)) return <SiBlackberry size={24} />;
23
-
24
- if (title === 'Linux') return <SiLinux size={24} />;
25
-
26
- if (title === 'Chrome OS') return <SiGooglechrome size={24} />;
27
-
28
- return null;
29
- });
30
-
31
- export default SystemIcon;
@@ -1,103 +0,0 @@
1
- 'use client';
2
-
3
- import { Text } from '@lobehub/ui';
4
- import { createStyles } from 'antd-style';
5
- import { rgba } from 'polished';
6
- import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Flexbox } from 'react-layout-kit';
9
-
10
- import { BrowserIcon } from '@/components/BrowserIcon';
11
- import { MAX_WIDTH } from '@/const/layoutTokens';
12
-
13
- import Card from './Card';
14
- import DeviceName from './DeviceName';
15
- import SystemIcon from './SystemIcon';
16
-
17
- const useStyles = createStyles(({ css, cx, responsive, isDarkMode, token, stylish }) => ({
18
- container: css`
19
- position: relative;
20
- width: 100%;
21
- border-radius: ${token.borderRadiusLG}px;
22
- `,
23
- content: css`
24
- z-index: 2;
25
- padding: 8px;
26
- border-radius: ${token.borderRadiusLG - 1}px;
27
- background: ${rgba(token.colorBgContainer, isDarkMode ? 0.7 : 1)};
28
- `,
29
- glow: cx(
30
- stylish.gradientAnimation,
31
- css`
32
- pointer-events: none;
33
- opacity: 0.5;
34
- background-image: linear-gradient(
35
- -45deg,
36
- ${isDarkMode ? token.geekblue4 : token.geekblue},
37
- ${isDarkMode ? token.cyan4 : token.cyan}
38
- );
39
- animation-duration: 10s;
40
- `,
41
- ),
42
- wrapper: css`
43
- ${responsive.mobile} {
44
- padding-block: 8px;
45
- padding-inline: 4px;
46
- }
47
- `,
48
- }));
49
-
50
- interface DeviceCardProps {
51
- browser?: string;
52
- os?: string;
53
- }
54
-
55
- const DeviceCard = memo<DeviceCardProps>(({ browser, os }) => {
56
- const { styles } = useStyles();
57
- const { t } = useTranslation('setting');
58
-
59
- return (
60
- <Flexbox
61
- className={styles.wrapper}
62
- style={{ maxWidth: MAX_WIDTH, position: 'relative' }}
63
- width={'100%'}
64
- >
65
- <Flexbox className={styles.container} padding={4}>
66
- <Flexbox horizontal paddingBlock={8} paddingInline={12}>
67
- <div>
68
- <Text strong style={{ fontSize: 18 }}>
69
- {t('sync.device.title')}
70
- </Text>
71
- </div>
72
- </Flexbox>
73
- <Flexbox
74
- align={'center'}
75
- className={styles.content}
76
- flex={1}
77
- gap={16}
78
- horizontal
79
- justify={'space-between'}
80
- padding={12}
81
- wrap={'wrap'}
82
- >
83
- <DeviceName />
84
- <Flexbox flex={1} gap={12} horizontal>
85
- <Card icon={<SystemIcon title={os} />} title={os || t('sync.device.unknownOS')} />
86
- <Card
87
- icon={browser && <BrowserIcon browser={browser} size={24} />}
88
- title={browser || t('sync.device.unknownBrowser')}
89
- />
90
- </Flexbox>
91
- </Flexbox>
92
- <Flexbox
93
- className={styles.glow}
94
- height={'100%'}
95
- style={{ left: 0, position: 'absolute', top: 0 }}
96
- width={'100%'}
97
- />
98
- </Flexbox>
99
- </Flexbox>
100
- );
101
- });
102
-
103
- export default DeviceCard;