@lobehub/chat 0.118.7 → 0.118.9
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/.i18nrc.js +1 -0
- package/CHANGELOG.md +50 -0
- package/docs/Add-New-Locale.md +54 -0
- package/docs/Add-New-Locale.zh-CN.md +54 -0
- package/docs/Home.md +10 -8
- package/docs/_Sidebar.md +21 -19
- package/locales/de-DE/tool.json +1 -0
- package/locales/en-US/tool.json +1 -0
- package/locales/es-ES/tool.json +1 -0
- package/locales/fr-FR/tool.json +1 -0
- package/locales/it-IT/tool.json +1 -0
- package/locales/ja-JP/tool.json +1 -0
- package/locales/ko-KR/tool.json +1 -0
- package/locales/nl-NL/tool.json +1 -0
- package/locales/pl-PL/tool.json +1 -0
- package/locales/pt-BR/tool.json +1 -0
- package/locales/ru-RU/tool.json +1 -0
- package/locales/tr-TR/tool.json +1 -0
- package/locales/vi-VN/chat.json +77 -0
- package/locales/vi-VN/common.json +113 -0
- package/locales/vi-VN/empty.json +6 -0
- package/locales/vi-VN/error.json +53 -0
- package/locales/vi-VN/market.json +30 -0
- package/locales/vi-VN/migration.json +45 -0
- package/locales/vi-VN/plugin.json +166 -0
- package/locales/vi-VN/setting.json +306 -0
- package/locales/vi-VN/tool.json +9 -0
- package/locales/vi-VN/welcome.json +14 -0
- package/locales/zh-CN/tool.json +1 -0
- package/locales/zh-TW/tool.json +1 -0
- package/package.json +2 -1
- package/src/app/chat/(desktop)/features/ChatInput/index.tsx +18 -1
- package/src/locales/resources.ts +5 -0
- package/src/store/chat/slices/message/action.test.ts +71 -0
- package/src/store/chat/slices/message/action.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.118.
|
|
3
|
+
"version": "0.118.9",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dev": "next dev -p 3010",
|
|
33
33
|
"docs": "lobe-i18n md && npm run workflow:docs",
|
|
34
34
|
"i18n": "npm run workflow:i18n && lobe-i18n",
|
|
35
|
+
"i18n-md": "lobe-i18n md",
|
|
35
36
|
"lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
|
|
36
37
|
"lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
|
|
37
38
|
"lint:md": "remark . --quiet --frail --output",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActionIcon, ChatInputArea, ChatSendButton } from '@lobehub/ui';
|
|
2
2
|
import { Maximize2, Minimize2 } from 'lucide-react';
|
|
3
|
-
import { memo, useCallback, useMemo } from 'react';
|
|
3
|
+
import { memo, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import {
|
|
@@ -78,6 +78,23 @@ const ChatInputDesktopLayout = memo(() => {
|
|
|
78
78
|
[expand],
|
|
79
79
|
);
|
|
80
80
|
|
|
81
|
+
const hasValue = !!value;
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const fn = (e: BeforeUnloadEvent) => {
|
|
85
|
+
if (hasValue) {
|
|
86
|
+
// set returnValue to trigger alert modal
|
|
87
|
+
// Note: No matter what value is set, the browser will display the standard text
|
|
88
|
+
e.returnValue = '你有正在输入中的内容,确定要离开吗?';
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
window.addEventListener('beforeunload', fn);
|
|
93
|
+
return () => {
|
|
94
|
+
window.removeEventListener('beforeunload', fn);
|
|
95
|
+
};
|
|
96
|
+
}, [hasValue]);
|
|
97
|
+
|
|
81
98
|
return (
|
|
82
99
|
<>
|
|
83
100
|
{canUpload && <DragUpload />}
|
package/src/locales/resources.ts
CHANGED
|
@@ -12,6 +12,7 @@ export const locales = [
|
|
|
12
12
|
'tr-TR',
|
|
13
13
|
'zh-CN',
|
|
14
14
|
'zh-TW',
|
|
15
|
+
'vi-VN',
|
|
15
16
|
] as const;
|
|
16
17
|
|
|
17
18
|
export type DefaultResources = typeof resources;
|
|
@@ -96,6 +97,10 @@ export const localeOptions: LocaleOptions = [
|
|
|
96
97
|
label: 'Italiano',
|
|
97
98
|
value: 'it-IT',
|
|
98
99
|
},
|
|
100
|
+
{
|
|
101
|
+
label: 'Tiếng Việt',
|
|
102
|
+
value: 'vi-VN',
|
|
103
|
+
},
|
|
99
104
|
] as LocaleOptions;
|
|
100
105
|
|
|
101
106
|
export const supportLocales: string[] = [...locales, 'en', 'zh'];
|
|
@@ -666,4 +666,75 @@ describe('chatMessage actions', () => {
|
|
|
666
666
|
});
|
|
667
667
|
});
|
|
668
668
|
});
|
|
669
|
+
|
|
670
|
+
describe('toggleChatLoading', () => {
|
|
671
|
+
it('should set loading state and create an AbortController when loading is true', () => {
|
|
672
|
+
const { result } = renderHook(() => useChatStore());
|
|
673
|
+
const action = 'loading-action';
|
|
674
|
+
|
|
675
|
+
act(() => {
|
|
676
|
+
result.current.toggleChatLoading(true, 'message-id', action);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
const state = useChatStore.getState();
|
|
680
|
+
expect(state.abortController).toBeInstanceOf(AbortController);
|
|
681
|
+
expect(state.chatLoadingId).toEqual('message-id');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('should clear loading state and abort controller when loading is false', () => {
|
|
685
|
+
const { result } = renderHook(() => useChatStore());
|
|
686
|
+
const action = 'stop-loading-action';
|
|
687
|
+
|
|
688
|
+
// Set initial loading state
|
|
689
|
+
act(() => {
|
|
690
|
+
result.current.toggleChatLoading(true, 'message-id', 'start-loading-action');
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// Stop loading
|
|
694
|
+
act(() => {
|
|
695
|
+
result.current.toggleChatLoading(false, undefined, action);
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
const state = useChatStore.getState();
|
|
699
|
+
expect(state.abortController).toBeUndefined();
|
|
700
|
+
expect(state.chatLoadingId).toBeUndefined();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('should attach beforeunload event listener when loading starts', () => {
|
|
704
|
+
const { result } = renderHook(() => useChatStore());
|
|
705
|
+
const addEventListenerSpy = vi.spyOn(window, 'addEventListener');
|
|
706
|
+
|
|
707
|
+
act(() => {
|
|
708
|
+
result.current.toggleChatLoading(true, 'message-id', 'loading-action');
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('should remove beforeunload event listener when loading stops', () => {
|
|
715
|
+
const { result } = renderHook(() => useChatStore());
|
|
716
|
+
const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener');
|
|
717
|
+
|
|
718
|
+
// Start and then stop loading to trigger the removal of the event listener
|
|
719
|
+
act(() => {
|
|
720
|
+
result.current.toggleChatLoading(true, 'message-id', 'start-loading-action');
|
|
721
|
+
result.current.toggleChatLoading(false, undefined, 'stop-loading-action');
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('should not create a new AbortController if one already exists', () => {
|
|
728
|
+
const { result } = renderHook(() => useChatStore());
|
|
729
|
+
const abortController = new AbortController();
|
|
730
|
+
|
|
731
|
+
act(() => {
|
|
732
|
+
useChatStore.setState({ abortController });
|
|
733
|
+
result.current.toggleChatLoading(true, 'message-id', 'loading-action');
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const state = useChatStore.getState();
|
|
737
|
+
expect(state.abortController).toEqual(abortController);
|
|
738
|
+
});
|
|
739
|
+
});
|
|
669
740
|
});
|
|
@@ -75,6 +75,12 @@ export interface ChatMessageAction {
|
|
|
75
75
|
|
|
76
76
|
const getAgentConfig = () => agentSelectors.currentAgentConfig(useSessionStore.getState());
|
|
77
77
|
|
|
78
|
+
const preventLeavingFn = (e: BeforeUnloadEvent) => {
|
|
79
|
+
// set returnValue to trigger alert modal
|
|
80
|
+
// Note: No matter what value is set, the browser will display the standard text
|
|
81
|
+
e.returnValue = '你有正在生成中的请求,确定要离开吗?';
|
|
82
|
+
};
|
|
83
|
+
|
|
78
84
|
export const chatMessage: StateCreator<
|
|
79
85
|
ChatStore,
|
|
80
86
|
[['zustand/devtools', never]],
|
|
@@ -398,11 +404,16 @@ export const chatMessage: StateCreator<
|
|
|
398
404
|
},
|
|
399
405
|
toggleChatLoading: (loading, id, action) => {
|
|
400
406
|
if (loading) {
|
|
407
|
+
window.addEventListener('beforeunload', preventLeavingFn);
|
|
408
|
+
|
|
401
409
|
const abortController = new AbortController();
|
|
402
410
|
set({ abortController, chatLoadingId: id }, false, action);
|
|
411
|
+
|
|
403
412
|
return abortController;
|
|
404
413
|
} else {
|
|
405
414
|
set({ abortController: undefined, chatLoadingId: undefined }, false, action);
|
|
415
|
+
|
|
416
|
+
window.removeEventListener('beforeunload', preventLeavingFn);
|
|
406
417
|
}
|
|
407
418
|
},
|
|
408
419
|
});
|