@lobehub/chat 0.155.4 → 0.155.6
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 +42 -0
- package/README.md +2 -2
- package/README.zh-CN.md +8 -8
- package/package.json +1 -1
- package/src/app/(main)/(mobile)/me/features/Header.tsx +2 -9
- package/src/app/(main)/(mobile)/me/loading.tsx +1 -12
- package/src/app/(main)/(mobile)/me/page.tsx +2 -5
- package/src/app/(main)/@nav/_layout/Mobile.tsx +1 -1
- package/src/app/(main)/_layout/Mobile.tsx +5 -1
- package/src/app/(main)/chat/(workspace)/@conversation/default.tsx +23 -0
- package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/index.tsx +2 -1
- package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.test.tsx +6 -6
- package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.tsx +2 -1
- package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/index.tsx +6 -2
- package/src/app/(main)/chat/{(mobile)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Mobile}/index.tsx +9 -4
- package/src/app/(main)/chat/(workspace)/@topic/default.tsx +19 -0
- package/src/app/(main)/chat/{(desktop)/features/SideBar/SystemRole/index.tsx → (workspace)/@topic/features/SystemRole/SystemRoleContent.tsx} +2 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx +18 -0
- package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Header.tsx +2 -0
- package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/index.tsx +26 -28
- package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/TopicSearchBar/index.tsx +4 -2
- package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/index.tsx +3 -4
- package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/HeaderAction.tsx +2 -0
- package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/Main.tsx +3 -1
- package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/Tags.tsx +1 -1
- package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/index.tsx +1 -2
- package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/HotKeys.tsx +2 -0
- package/src/app/(main)/chat/{(desktop)/features/SideBar/index.tsx → (workspace)/_layout/Desktop/TopicPanel.tsx} +23 -20
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx +35 -0
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx +35 -0
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +26 -0
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx +21 -0
- package/src/app/(main)/chat/(workspace)/_layout/type.ts +7 -0
- package/src/app/(main)/chat/{features → (workspace)/features}/PluginTag/index.tsx +2 -0
- package/src/app/(main)/chat/{features → (workspace)/features}/SettingButton.tsx +3 -1
- package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/index.tsx +5 -6
- package/src/app/(main)/chat/{features/TelemetryNotification/index.tsx → (workspace)/features/TelemetryNotification.tsx} +2 -0
- package/src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx +27 -0
- package/src/app/(main)/chat/(workspace)/layout.ts +11 -0
- package/src/app/(main)/chat/(workspace)/page.tsx +19 -0
- package/src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx +22 -0
- package/src/app/(main)/chat/{_layout → @session/_layout}/Desktop/SessionHeader.tsx +2 -0
- package/src/app/(main)/chat/@session/_layout/Desktop/index.tsx +15 -0
- package/src/app/(main)/chat/{(mobile)/features → @session/_layout/Mobile}/SessionHeader.tsx +7 -20
- package/src/app/(main)/chat/@session/_layout/Mobile/index.tsx +19 -0
- package/src/app/(main)/chat/@session/default.tsx +23 -0
- package/src/app/(main)/chat/{components/SessionHydration/index.tsx → @session/features/SessionHydration.tsx} +1 -0
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/DefaultMode.tsx +3 -3
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Inbox/index.tsx +2 -2
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/Item/index.tsx +2 -1
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/index.tsx +3 -4
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/ListItem/index.tsx +12 -10
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/SearchMode.tsx +4 -2
- package/src/app/(main)/chat/{features → @session/features}/SessionListContent/index.tsx +2 -0
- package/src/app/(main)/chat/{features/SessionSearchBar/index.tsx → @session/features/SessionSearchBar.tsx} +3 -5
- package/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx +79 -0
- package/src/app/(main)/chat/_layout/Desktop/index.tsx +11 -7
- package/src/app/(main)/chat/_layout/Mobile.tsx +52 -0
- package/src/app/(main)/chat/_layout/type.ts +1 -0
- package/src/app/(main)/chat/error.tsx +5 -0
- package/src/app/(main)/chat/features/Migration/index.tsx +3 -8
- package/src/app/(main)/chat/not-found.tsx +3 -0
- package/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx +3 -4
- package/src/app/(main)/chat/settings/features/HeaderContent.tsx +2 -2
- package/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx +2 -2
- package/src/app/(main)/market/@detail/features/Header.tsx +2 -2
- package/src/components/Cell/Divider.tsx +2 -2
- package/src/components/Cell/index.tsx +2 -2
- package/src/components/ModelTag/ModelIcon.tsx +2 -2
- package/src/components/StoreHydration/ChatHydration/index.tsx +2 -2
- package/src/const/session.ts +2 -0
- package/src/const/url.ts +5 -1
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +3 -2
- package/src/features/ChatInput/STT/browser.tsx +2 -1
- package/src/features/ChatInput/STT/openai.tsx +2 -1
- package/src/features/ChatInput/useChatInput.ts +2 -1
- package/src/features/ChatInput/useSend.ts +2 -1
- package/src/features/Conversation/Actions/customAction.ts +9 -0
- package/src/features/Conversation/Extras/Assistant.tsx +2 -1
- package/src/features/Conversation/Extras/User.tsx +2 -1
- package/src/features/Conversation/components/AutoScroll.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/index.tsx +22 -12
- package/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +1 -1
- package/src/features/Conversation/components/InboxWelcome/index.tsx +2 -2
- package/src/features/Conversation/components/SkeletonList.tsx +21 -8
- package/src/features/Conversation/components/VirtualizedList/index.tsx +19 -17
- package/src/features/Conversation/index.tsx +12 -31
- package/src/features/PluginStore/InstalledPluginList.tsx +28 -21
- package/src/features/PluginStore/OnlineList.tsx +4 -10
- package/src/features/PluginStore/PluginItem/Action.tsx +3 -2
- package/src/features/PluginStore/PluginItem/index.tsx +2 -0
- package/src/features/PluginStore/index.tsx +4 -2
- package/src/features/User/UserAvatar.tsx +2 -1
- package/src/features/User/UserPanel/useMenu.tsx +1 -1
- package/src/layout/GlobalProvider/AppTheme.tsx +7 -17
- package/src/libs/agent-runtime/openrouter/index.ts +4 -1
- package/src/store/chat/slices/message/action.test.ts +52 -2
- package/src/store/chat/slices/message/action.ts +40 -13
- package/src/store/chat/slices/message/initialState.ts +13 -1
- package/src/store/chat/slices/message/selectors.test.ts +1 -1
- package/src/store/chat/slices/message/selectors.ts +10 -1
- package/src/store/chat/slices/plugin/action.test.ts +10 -2
- package/src/store/chat/slices/plugin/action.ts +6 -2
- package/src/store/global/action.ts +2 -0
- package/src/store/global/initialState.ts +2 -0
- package/src/styles/global.ts +12 -9
- package/src/styles/mobileHeader.ts +1 -1
- package/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx +0 -27
- package/src/app/(main)/(mobile)/me/features/style.ts +0 -29
- package/src/app/(main)/chat/(desktop)/features/Conversation.tsx +0 -19
- package/src/app/(main)/chat/(desktop)/index.tsx +0 -22
- package/src/app/(main)/chat/(mobile)/features/SessionList.tsx +0 -17
- package/src/app/(main)/chat/(mobile)/features/TopicList.tsx +0 -29
- package/src/app/(main)/chat/(mobile)/index.tsx +0 -26
- package/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx +0 -56
- package/src/app/(main)/chat/(mobile)/mobile/page.tsx +0 -26
- package/src/app/(main)/chat/_layout/Desktop/SessionList.tsx +0 -39
- package/src/app/(main)/chat/_layout/Mobile/index.tsx +0 -9
- package/src/app/(main)/chat/page.tsx +0 -25
- package/src/features/FolderPanel/index.tsx +0 -60
- package/src/utils/screen.ts +0 -14
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/DragUpload.tsx +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/LocalFiles.tsx +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/SendMore.tsx +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Header/index.tsx +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/__tests__/useAutoFocus.test.ts +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/useAutoFocus.ts +0 -0
- /package/src/app/(main)/chat/{(mobile)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Mobile}/Files.tsx +0 -0
- /package/src/app/(main)/chat/{(desktop)/features/SideBar → (workspace)/@topic/features}/SystemRole/style.ts +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/DefaultContent.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/SkeletonList.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/TopicContent.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/TopicItem.tsx +0 -0
- /package/src/app/(main)/chat/{(mobile)/mobile → (workspace)/_layout/Mobile}/ChatHeader/ChatHeaderTitle.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/PluginTag/PluginStatus.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/Preview.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/ShareModal.tsx +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/style.ts +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/type.ts +0 -0
- /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/useScreenshot.ts +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/CollapseGroup/Actions.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/CollapseGroup/index.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/AddButton.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/Item/Actions.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/ConfigGroupModal/index.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/CreateGroupModal.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/RenameGroupModal.tsx +0 -0
- /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/SkeletonList.tsx +0 -0
|
@@ -365,6 +365,31 @@ describe('chatMessage actions', () => {
|
|
|
365
365
|
});
|
|
366
366
|
});
|
|
367
367
|
|
|
368
|
+
describe('toggleMessageEditing action', () => {
|
|
369
|
+
it('should add message id to messageEditingIds when editing is true', () => {
|
|
370
|
+
const { result } = renderHook(() => useChatStore());
|
|
371
|
+
const messageId = 'message-id';
|
|
372
|
+
|
|
373
|
+
act(() => {
|
|
374
|
+
result.current.toggleMessageEditing(messageId, true);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
expect(result.current.messageEditingIds).toContain(messageId);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should remove message id from messageEditingIds when editing is false', () => {
|
|
381
|
+
const { result } = renderHook(() => useChatStore());
|
|
382
|
+
const messageId = 'abc';
|
|
383
|
+
|
|
384
|
+
act(() => {
|
|
385
|
+
result.current.toggleMessageEditing(messageId, true);
|
|
386
|
+
result.current.toggleMessageEditing(messageId, false);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
expect(result.current.messageEditingIds).not.toContain(messageId);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
368
393
|
describe('internal_resendMessage action', () => {
|
|
369
394
|
it('should resend a message by id and refresh messages', async () => {
|
|
370
395
|
const { result } = renderHook(() => useChatStore());
|
|
@@ -701,7 +726,7 @@ describe('chatMessage actions', () => {
|
|
|
701
726
|
|
|
702
727
|
const state = useChatStore.getState();
|
|
703
728
|
expect(state.abortController).toBeInstanceOf(AbortController);
|
|
704
|
-
expect(state.
|
|
729
|
+
expect(state.chatLoadingIds).toEqual(['message-id']);
|
|
705
730
|
});
|
|
706
731
|
|
|
707
732
|
it('should clear loading state and abort controller when loading is false', () => {
|
|
@@ -720,7 +745,7 @@ describe('chatMessage actions', () => {
|
|
|
720
745
|
|
|
721
746
|
const state = useChatStore.getState();
|
|
722
747
|
expect(state.abortController).toBeUndefined();
|
|
723
|
-
expect(state.
|
|
748
|
+
expect(state.chatLoadingIds).toEqual([]);
|
|
724
749
|
});
|
|
725
750
|
|
|
726
751
|
it('should attach beforeunload event listener when loading starts', () => {
|
|
@@ -760,4 +785,29 @@ describe('chatMessage actions', () => {
|
|
|
760
785
|
expect(state.abortController).toEqual(abortController);
|
|
761
786
|
});
|
|
762
787
|
});
|
|
788
|
+
|
|
789
|
+
describe('internal_toggleMessageLoading action', () => {
|
|
790
|
+
it('should add message id to messageLoadingIds when loading is true', () => {
|
|
791
|
+
const { result } = renderHook(() => useChatStore());
|
|
792
|
+
const messageId = 'message-id';
|
|
793
|
+
|
|
794
|
+
act(() => {
|
|
795
|
+
result.current.internal_toggleMessageLoading(true, messageId);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
expect(result.current.messageLoadingIds).toContain(messageId);
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('should remove message id from messageLoadingIds when loading is false', () => {
|
|
802
|
+
const { result } = renderHook(() => useChatStore());
|
|
803
|
+
const messageId = 'ddd-id';
|
|
804
|
+
|
|
805
|
+
act(() => {
|
|
806
|
+
result.current.internal_toggleMessageLoading(true, messageId);
|
|
807
|
+
result.current.internal_toggleMessageLoading(false, messageId);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
expect(result.current.messageLoadingIds).not.toContain(messageId);
|
|
811
|
+
});
|
|
812
|
+
});
|
|
763
813
|
});
|
|
@@ -72,7 +72,7 @@ export interface ChatMessageAction {
|
|
|
72
72
|
stopGenerateMessage: () => void;
|
|
73
73
|
copyMessage: (id: string, content: string) => Promise<void>;
|
|
74
74
|
refreshMessages: () => Promise<void>;
|
|
75
|
-
|
|
75
|
+
toggleMessageEditing: (id: string, editing: boolean) => void;
|
|
76
76
|
// ========= ↓ Internal Method ↓ ========== //
|
|
77
77
|
// ========================================== //
|
|
78
78
|
// ========================================== //
|
|
@@ -137,6 +137,18 @@ const preventLeavingFn = (e: BeforeUnloadEvent) => {
|
|
|
137
137
|
e.returnValue = '你有正在生成中的请求,确定要离开吗?';
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
const toggleBooleanList = (ids: string[], id: string, loading: boolean) => {
|
|
141
|
+
return produce(ids, (draft) => {
|
|
142
|
+
if (loading) {
|
|
143
|
+
draft.push(id);
|
|
144
|
+
} else {
|
|
145
|
+
const index = draft.indexOf(id);
|
|
146
|
+
|
|
147
|
+
if (index >= 0) draft.splice(index, 1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
140
152
|
export const chatMessage: StateCreator<
|
|
141
153
|
ChatStore,
|
|
142
154
|
[['zustand/devtools', never]],
|
|
@@ -244,7 +256,13 @@ export const chatMessage: StateCreator<
|
|
|
244
256
|
|
|
245
257
|
get().internal_traceMessage(id, { eventType: TraceEventType.CopyMessage });
|
|
246
258
|
},
|
|
247
|
-
|
|
259
|
+
toggleMessageEditing: (id, editing) => {
|
|
260
|
+
set(
|
|
261
|
+
{ messageEditingIds: toggleBooleanList(get().messageEditingIds, id, editing) },
|
|
262
|
+
false,
|
|
263
|
+
'toggleMessageEditing',
|
|
264
|
+
);
|
|
265
|
+
},
|
|
248
266
|
stopGenerateMessage: () => {
|
|
249
267
|
const { abortController, internal_toggleChatLoading } = get();
|
|
250
268
|
if (!abortController) return;
|
|
@@ -518,11 +536,28 @@ export const chatMessage: StateCreator<
|
|
|
518
536
|
window.addEventListener('beforeunload', preventLeavingFn);
|
|
519
537
|
|
|
520
538
|
const abortController = new AbortController();
|
|
521
|
-
set(
|
|
539
|
+
set(
|
|
540
|
+
{
|
|
541
|
+
abortController,
|
|
542
|
+
chatLoadingIds: toggleBooleanList(get().messageLoadingIds, id!, loading),
|
|
543
|
+
},
|
|
544
|
+
false,
|
|
545
|
+
action,
|
|
546
|
+
);
|
|
522
547
|
|
|
523
548
|
return abortController;
|
|
524
549
|
} else {
|
|
525
|
-
|
|
550
|
+
if (!id) {
|
|
551
|
+
set({ abortController: undefined, chatLoadingIds: [] }, false, action);
|
|
552
|
+
} else
|
|
553
|
+
set(
|
|
554
|
+
{
|
|
555
|
+
abortController: undefined,
|
|
556
|
+
chatLoadingIds: toggleBooleanList(get().messageLoadingIds, id, loading),
|
|
557
|
+
},
|
|
558
|
+
false,
|
|
559
|
+
action,
|
|
560
|
+
);
|
|
526
561
|
|
|
527
562
|
window.removeEventListener('beforeunload', preventLeavingFn);
|
|
528
563
|
}
|
|
@@ -530,15 +565,7 @@ export const chatMessage: StateCreator<
|
|
|
530
565
|
internal_toggleMessageLoading: (loading, id) => {
|
|
531
566
|
set(
|
|
532
567
|
{
|
|
533
|
-
messageLoadingIds:
|
|
534
|
-
if (loading) {
|
|
535
|
-
draft.push(id);
|
|
536
|
-
} else {
|
|
537
|
-
const index = draft.indexOf(id);
|
|
538
|
-
|
|
539
|
-
if (index >= 0) draft.splice(index, 1);
|
|
540
|
-
}
|
|
541
|
-
}),
|
|
568
|
+
messageLoadingIds: toggleBooleanList(get().messageLoadingIds, id, loading),
|
|
542
569
|
},
|
|
543
570
|
false,
|
|
544
571
|
'internal_toggleMessageLoading',
|
|
@@ -7,8 +7,18 @@ export interface ChatMessageState {
|
|
|
7
7
|
* @description 当前正在编辑或查看的会话
|
|
8
8
|
*/
|
|
9
9
|
activeId: string;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* is the AI message is generating
|
|
12
|
+
*/
|
|
13
|
+
chatLoadingIds: string[];
|
|
11
14
|
inputMessage: string;
|
|
15
|
+
/**
|
|
16
|
+
* is the message is editing
|
|
17
|
+
*/
|
|
18
|
+
messageEditingIds: string[];
|
|
19
|
+
/**
|
|
20
|
+
* is the message is creating or updating in the service
|
|
21
|
+
*/
|
|
12
22
|
messageLoadingIds: string[];
|
|
13
23
|
messages: ChatMessage[];
|
|
14
24
|
/**
|
|
@@ -19,7 +29,9 @@ export interface ChatMessageState {
|
|
|
19
29
|
|
|
20
30
|
export const initialMessageState: ChatMessageState = {
|
|
21
31
|
activeId: 'inbox',
|
|
32
|
+
chatLoadingIds: [],
|
|
22
33
|
inputMessage: '',
|
|
34
|
+
messageEditingIds: [],
|
|
23
35
|
messageLoadingIds: [],
|
|
24
36
|
messages: [],
|
|
25
37
|
messagesInit: false,
|
|
@@ -107,7 +107,7 @@ describe('chatSelectors', () => {
|
|
|
107
107
|
it('should return the properties of a function message', () => {
|
|
108
108
|
const state = merge(initialStore, {
|
|
109
109
|
messages: mockMessages,
|
|
110
|
-
|
|
110
|
+
chatLoadingIds: ['msg3'], // Assuming this id represents a loading state
|
|
111
111
|
});
|
|
112
112
|
const props = chatSelectors.getFunctionMessageProps(mockMessages[2])(state);
|
|
113
113
|
expect(props).toEqual({
|
|
@@ -123,7 +123,7 @@ const getFunctionMessageProps =
|
|
|
123
123
|
command: plugin,
|
|
124
124
|
content,
|
|
125
125
|
id: plugin?.identifier,
|
|
126
|
-
loading:
|
|
126
|
+
loading: s.chatLoadingIds.includes(id),
|
|
127
127
|
type: plugin?.type as LobePluginType,
|
|
128
128
|
});
|
|
129
129
|
|
|
@@ -134,6 +134,11 @@ const latestMessage = (s: ChatStore) => currentChats(s).at(-1);
|
|
|
134
134
|
|
|
135
135
|
const currentChatLoadingState = (s: ChatStore) => !s.messagesInit;
|
|
136
136
|
|
|
137
|
+
const isMessageEditing = (id: string) => (s: ChatStore) => s.messageEditingIds.includes(id);
|
|
138
|
+
const isMessageLoading = (id: string) => (s: ChatStore) => s.messageLoadingIds.includes(id);
|
|
139
|
+
const isMessageGenerating = (id: string) => (s: ChatStore) => s.chatLoadingIds.includes(id);
|
|
140
|
+
const isAIGenerating = (s: ChatStore) => s.chatLoadingIds.length > 0;
|
|
141
|
+
|
|
137
142
|
export const chatSelectors = {
|
|
138
143
|
chatsMessageString,
|
|
139
144
|
currentChatIDsWithGuideMessage,
|
|
@@ -145,6 +150,10 @@ export const chatSelectors = {
|
|
|
145
150
|
getFunctionMessageProps,
|
|
146
151
|
getMessageById,
|
|
147
152
|
getTraceIdByMessageId,
|
|
153
|
+
isAIGenerating,
|
|
154
|
+
isMessageEditing,
|
|
155
|
+
isMessageGenerating,
|
|
156
|
+
isMessageLoading,
|
|
148
157
|
latestMessage,
|
|
149
158
|
showInboxWelcome,
|
|
150
159
|
};
|
|
@@ -131,7 +131,11 @@ describe('ChatPluginAction', () => {
|
|
|
131
131
|
});
|
|
132
132
|
expect(storeState.refreshMessages).toHaveBeenCalled();
|
|
133
133
|
expect(storeState.triggerAIMessage).toHaveBeenCalled();
|
|
134
|
-
expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
|
|
134
|
+
expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
|
|
135
|
+
false,
|
|
136
|
+
'message-id',
|
|
137
|
+
'plugin/fetchPlugin/end',
|
|
138
|
+
);
|
|
135
139
|
});
|
|
136
140
|
|
|
137
141
|
it('should handle errors when the plugin API call fails', async () => {
|
|
@@ -159,7 +163,11 @@ describe('ChatPluginAction', () => {
|
|
|
159
163
|
expect(chatService.runPluginApi).toHaveBeenCalledWith(pluginPayload, { trace: {} });
|
|
160
164
|
expect(messageService.updateMessageError).toHaveBeenCalledWith(messageId, error);
|
|
161
165
|
expect(storeState.refreshMessages).toHaveBeenCalled();
|
|
162
|
-
expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
|
|
166
|
+
expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
|
|
167
|
+
false,
|
|
168
|
+
'message-id',
|
|
169
|
+
'plugin/fetchPlugin/end',
|
|
170
|
+
);
|
|
163
171
|
expect(storeState.triggerAIMessage).not.toHaveBeenCalled(); // 确保在错误情况下不调用此方法
|
|
164
172
|
});
|
|
165
173
|
});
|
|
@@ -135,7 +135,11 @@ export const chatPlugin: StateCreator<
|
|
|
135
135
|
let data: string;
|
|
136
136
|
|
|
137
137
|
try {
|
|
138
|
-
const abortController = internal_toggleChatLoading(
|
|
138
|
+
const abortController = internal_toggleChatLoading(
|
|
139
|
+
true,
|
|
140
|
+
id,
|
|
141
|
+
n('fetchPlugin/start') as string,
|
|
142
|
+
);
|
|
139
143
|
|
|
140
144
|
const message = chatSelectors.getMessageById(id)(get());
|
|
141
145
|
|
|
@@ -162,7 +166,7 @@ export const chatPlugin: StateCreator<
|
|
|
162
166
|
data = '';
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
internal_toggleChatLoading(false);
|
|
169
|
+
internal_toggleChatLoading(false, id, n('fetchPlugin/end') as string);
|
|
166
170
|
// 如果报错则结束了
|
|
167
171
|
if (!data) return;
|
|
168
172
|
|
|
@@ -97,6 +97,8 @@ export const globalActionSlice: StateCreator<
|
|
|
97
97
|
onSuccess: (preference) => {
|
|
98
98
|
const nextPreference = merge(get().preference, preference);
|
|
99
99
|
|
|
100
|
+
set({ isPreferenceInit: true });
|
|
101
|
+
|
|
100
102
|
if (isEqual(get().preference, nextPreference)) return;
|
|
101
103
|
|
|
102
104
|
set({ preference: nextPreference }, false, n('initPreference'));
|
|
@@ -50,6 +50,7 @@ export interface GlobalPreferenceState {
|
|
|
50
50
|
export interface GlobalCommonState {
|
|
51
51
|
hasNewVersion?: boolean;
|
|
52
52
|
isMobile?: boolean;
|
|
53
|
+
isPreferenceInit?: boolean;
|
|
53
54
|
latestVersion?: string;
|
|
54
55
|
router?: AppRouterInstance;
|
|
55
56
|
sidebarKey: SidebarTabKey;
|
|
@@ -59,6 +60,7 @@ export type GlobalState = GlobalCommonState & GlobalPreferenceState;
|
|
|
59
60
|
|
|
60
61
|
export const initialState: GlobalState = {
|
|
61
62
|
isMobile: false,
|
|
63
|
+
isPreferenceInit: false,
|
|
62
64
|
preference: {
|
|
63
65
|
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
64
66
|
inputHeight: 200,
|
package/src/styles/global.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { Theme, css } from 'antd-style';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// fix ios input keyboard
|
|
4
|
+
// overflow: hidden;
|
|
5
|
+
// ref: https://zhuanlan.zhihu.com/p/113855026
|
|
6
|
+
export default ({ token }: { prefixCls: string; token: Theme }) => css`
|
|
4
7
|
html,
|
|
5
8
|
body,
|
|
6
|
-
#__next
|
|
7
|
-
.${prefixCls}-app {
|
|
9
|
+
#__next {
|
|
8
10
|
position: relative;
|
|
11
|
+
|
|
9
12
|
overscroll-behavior: none;
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
height: 100%;
|
|
15
|
+
min-height: 100dvh;
|
|
16
|
+
max-height: 100dvh;
|
|
17
|
+
|
|
18
|
+
background: ${token.colorBgLayout};
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
* {
|
|
15
22
|
scrollbar-color: ${token.colorFill} transparent;
|
|
16
23
|
scrollbar-width: thin;
|
|
17
24
|
}
|
|
18
|
-
|
|
19
|
-
p {
|
|
20
|
-
margin-bottom: 0;
|
|
21
|
-
}
|
|
22
25
|
`;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { PropsWithChildren, memo } from 'react';
|
|
4
|
-
import { Flexbox } from 'react-layout-kit';
|
|
5
|
-
|
|
6
|
-
import UserAvatar from '@/features/User/UserAvatar';
|
|
7
|
-
|
|
8
|
-
import { useStyles } from './style';
|
|
9
|
-
|
|
10
|
-
export const AVATAR_SIZE = 80;
|
|
11
|
-
|
|
12
|
-
const AvatarBanner = memo<PropsWithChildren>(({ children }) => {
|
|
13
|
-
const { styles } = useStyles();
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<>
|
|
17
|
-
<Flexbox align={'center'} className={styles.bannerBox} justify={'center'}>
|
|
18
|
-
<div className={styles.bannerImg}>
|
|
19
|
-
<UserAvatar shape={'square'} size={AVATAR_SIZE} />
|
|
20
|
-
</div>
|
|
21
|
-
</Flexbox>
|
|
22
|
-
<Flexbox className={styles.info}>{children}</Flexbox>
|
|
23
|
-
</>
|
|
24
|
-
);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export default AvatarBanner;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { createStyles } from 'antd-style';
|
|
2
|
-
|
|
3
|
-
export const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
|
4
|
-
bannerBox: css`
|
|
5
|
-
position: relative;
|
|
6
|
-
|
|
7
|
-
overflow: hidden;
|
|
8
|
-
flex: none;
|
|
9
|
-
|
|
10
|
-
width: 100%;
|
|
11
|
-
height: 100px;
|
|
12
|
-
|
|
13
|
-
background: ${token.colorFill};
|
|
14
|
-
`,
|
|
15
|
-
bannerImg: css`
|
|
16
|
-
position: absolute;
|
|
17
|
-
scale: 8;
|
|
18
|
-
filter: blur(6px) saturate(2);
|
|
19
|
-
`,
|
|
20
|
-
info: css`
|
|
21
|
-
position: relative;
|
|
22
|
-
|
|
23
|
-
margin-top: ${-token.borderRadiusLG}px;
|
|
24
|
-
|
|
25
|
-
background: ${isDarkMode ? token.colorBgLayout : token.colorBgContainer};
|
|
26
|
-
border-top-left-radius: ${token.borderRadiusLG}px;
|
|
27
|
-
border-top-right-radius: ${token.borderRadiusLG}px;
|
|
28
|
-
`,
|
|
29
|
-
}));
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
|
|
3
|
-
import RawConversation from '@/features/Conversation';
|
|
4
|
-
|
|
5
|
-
import TelemetryNotification from '../../features/TelemetryNotification';
|
|
6
|
-
import ChatInput from './ChatInput';
|
|
7
|
-
import HotKeys from './HotKeys';
|
|
8
|
-
|
|
9
|
-
const Conversation = memo(() => {
|
|
10
|
-
return (
|
|
11
|
-
<>
|
|
12
|
-
<RawConversation chatInput={<ChatInput />} />
|
|
13
|
-
<HotKeys />
|
|
14
|
-
<TelemetryNotification />
|
|
15
|
-
</>
|
|
16
|
-
);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
export default Conversation;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { memo } from 'react';
|
|
4
|
-
import { Flexbox } from 'react-layout-kit';
|
|
5
|
-
|
|
6
|
-
import ClientResponsiveContent from '@/components/client/ClientResponsiveContent';
|
|
7
|
-
|
|
8
|
-
import ChatHeader from './features/ChatHeader';
|
|
9
|
-
import Conversation from './features/Conversation';
|
|
10
|
-
import SideBar from './features/SideBar';
|
|
11
|
-
|
|
12
|
-
const Desktop = memo(() => (
|
|
13
|
-
<>
|
|
14
|
-
<ChatHeader />
|
|
15
|
-
<Flexbox flex={1} height={'calc(100% - 64px)'} horizontal>
|
|
16
|
-
<Conversation />
|
|
17
|
-
<SideBar />
|
|
18
|
-
</Flexbox>
|
|
19
|
-
</>
|
|
20
|
-
));
|
|
21
|
-
|
|
22
|
-
export default ClientResponsiveContent({ Desktop, Mobile: () => import('../(mobile)') });
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
|
|
3
|
-
import SessionListContent from '../../features/SessionListContent';
|
|
4
|
-
import SessionSearchBar from '../../features/SessionSearchBar';
|
|
5
|
-
|
|
6
|
-
const Sessions = memo(() => {
|
|
7
|
-
return (
|
|
8
|
-
<>
|
|
9
|
-
<div style={{ padding: '8px 16px' }}>
|
|
10
|
-
<SessionSearchBar mobile />
|
|
11
|
-
</div>
|
|
12
|
-
<SessionListContent />
|
|
13
|
-
</>
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export default Sessions;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Modal } from '@lobehub/ui';
|
|
2
|
-
import { memo } from 'react';
|
|
3
|
-
import { useTranslation } from 'react-i18next';
|
|
4
|
-
|
|
5
|
-
import { useGlobalStore } from '@/store/global';
|
|
6
|
-
|
|
7
|
-
import TopicListContent from '../../features/TopicListContent';
|
|
8
|
-
|
|
9
|
-
const Topics = memo(() => {
|
|
10
|
-
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
11
|
-
s.preference.mobileShowTopic,
|
|
12
|
-
s.toggleMobileTopic,
|
|
13
|
-
]);
|
|
14
|
-
|
|
15
|
-
const { t } = useTranslation('chat');
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<Modal
|
|
19
|
-
allowFullscreen
|
|
20
|
-
onCancel={() => toggleConfig(false)}
|
|
21
|
-
open={showAgentSettings}
|
|
22
|
-
title={t('topic.title')}
|
|
23
|
-
>
|
|
24
|
-
<TopicListContent mobile />
|
|
25
|
-
</Modal>
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
export default Topics;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useRouter } from 'next/navigation';
|
|
4
|
-
import { memo, useEffect } from 'react';
|
|
5
|
-
|
|
6
|
-
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
|
7
|
-
|
|
8
|
-
import SessionHeader from './features/SessionHeader';
|
|
9
|
-
import SessionList from './features/SessionList';
|
|
10
|
-
|
|
11
|
-
const ChatMobilePage = memo(() => {
|
|
12
|
-
const router = useRouter();
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
router.prefetch('/chat/mobile');
|
|
16
|
-
router.prefetch('/settings');
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<MobileContentLayout header={<SessionHeader />} withNav>
|
|
21
|
-
<SessionList />
|
|
22
|
-
</MobileContentLayout>
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export default ChatMobilePage;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { MobileNavBar } from '@lobehub/ui';
|
|
2
|
-
import { useRouter } from 'next/navigation';
|
|
3
|
-
import { memo, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
6
|
-
|
|
7
|
-
import SettingButton from '../../../features/SettingButton';
|
|
8
|
-
import ShareButton from '../../../features/ShareButton';
|
|
9
|
-
import ChatHeaderTitle from './ChatHeaderTitle';
|
|
10
|
-
|
|
11
|
-
const MobileHeader = memo(() => {
|
|
12
|
-
const router = useRouter();
|
|
13
|
-
const [open, setOpen] = useState(false);
|
|
14
|
-
|
|
15
|
-
const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors);
|
|
16
|
-
|
|
17
|
-
// const items: MenuProps['items'] = [
|
|
18
|
-
// {
|
|
19
|
-
// icon: <Icon icon={Share2} />,
|
|
20
|
-
// key: 'share',
|
|
21
|
-
// label: t('share', { ns: 'common' }),
|
|
22
|
-
// onClick: () => setOpen(true),
|
|
23
|
-
// },
|
|
24
|
-
// !isInbox && {
|
|
25
|
-
// icon: <Icon icon={Settings} />,
|
|
26
|
-
// key: 'settings',
|
|
27
|
-
// label: t('header.session', { ns: 'setting' }),
|
|
28
|
-
// onClick: () => router.push(pathString('/chat/settings', { hash: location.hash })),
|
|
29
|
-
// },
|
|
30
|
-
// ].filter(Boolean) as MenuProps['items'];
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<MobileNavBar
|
|
34
|
-
center={<ChatHeaderTitle />}
|
|
35
|
-
onBackClick={() => router.push('/chat')}
|
|
36
|
-
right={
|
|
37
|
-
<>
|
|
38
|
-
<ShareButton mobile open={open} setOpen={setOpen} />
|
|
39
|
-
{isAgentEditable && <SettingButton mobile />}
|
|
40
|
-
{/*<Dropdown*/}
|
|
41
|
-
{/* menu={{*/}
|
|
42
|
-
{/* items,*/}
|
|
43
|
-
{/* }}*/}
|
|
44
|
-
{/* trigger={['click']}*/}
|
|
45
|
-
{/*>*/}
|
|
46
|
-
{/* <ActionIcon icon={MoreHorizontal} />*/}
|
|
47
|
-
{/*</Dropdown>*/}
|
|
48
|
-
</>
|
|
49
|
-
}
|
|
50
|
-
showBackButton
|
|
51
|
-
style={{ width: '100%' }}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export default MobileHeader;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import dynamic from 'next/dynamic';
|
|
4
|
-
import { memo } from 'react';
|
|
5
|
-
|
|
6
|
-
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
|
7
|
-
import Conversation from '@/features/Conversation';
|
|
8
|
-
|
|
9
|
-
import SessionHydration from '../../components/SessionHydration';
|
|
10
|
-
import TelemetryNotification from '../../features/TelemetryNotification';
|
|
11
|
-
import ChatInput from '../features/ChatInput';
|
|
12
|
-
import ChatHeader from './ChatHeader';
|
|
13
|
-
|
|
14
|
-
const TopicList = dynamic(() => import('../features/TopicList'));
|
|
15
|
-
|
|
16
|
-
const Chat = memo(() => {
|
|
17
|
-
return (
|
|
18
|
-
<MobileContentLayout header={<ChatHeader />}>
|
|
19
|
-
<Conversation chatInput={<ChatInput />} mobile />
|
|
20
|
-
<TopicList />
|
|
21
|
-
<TelemetryNotification mobile />
|
|
22
|
-
<SessionHydration />
|
|
23
|
-
</MobileContentLayout>
|
|
24
|
-
);
|
|
25
|
-
});
|
|
26
|
-
export default Chat;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { DraggablePanelBody } from '@lobehub/ui';
|
|
4
|
-
import { createStyles } from 'antd-style';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
-
|
|
7
|
-
import FolderPanel from '@/features/FolderPanel';
|
|
8
|
-
|
|
9
|
-
import SessionListContent from '../../features/SessionListContent';
|
|
10
|
-
import Header from './SessionHeader';
|
|
11
|
-
|
|
12
|
-
const useStyles = createStyles(({ stylish, css, cx }) =>
|
|
13
|
-
cx(
|
|
14
|
-
stylish.noScrollbar,
|
|
15
|
-
css`
|
|
16
|
-
display: flex;
|
|
17
|
-
flex-direction: column;
|
|
18
|
-
gap: 2px;
|
|
19
|
-
padding: 8px 8px 0;
|
|
20
|
-
`,
|
|
21
|
-
),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const Sessions = memo(() => {
|
|
25
|
-
const { styles } = useStyles();
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<FolderPanel>
|
|
29
|
-
<Header />
|
|
30
|
-
<DraggablePanelBody className={styles}>
|
|
31
|
-
<SessionListContent />
|
|
32
|
-
</DraggablePanelBody>
|
|
33
|
-
</FolderPanel>
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
Sessions.displayName = 'SessionsList';
|
|
38
|
-
|
|
39
|
-
export default Sessions;
|