@lobehub/lobehub 2.0.0-next.33 → 2.0.0-next.35
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/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/model-bank/src/aiModels/google.ts +1 -1
- package/src/app/[variants]/(main)/chat/ChatRouter.tsx +83 -0
- package/src/app/[variants]/(main)/chat/_layout/ChatLayout.tsx +22 -0
- package/src/app/[variants]/(main)/chat/_layout/Desktop/SessionPanel.tsx +12 -7
- package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/_layout/FeatureFlagsProvider.tsx +24 -0
- package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +3 -2
- package/src/app/[variants]/(main)/chat/_layout/type.ts +0 -1
- package/src/app/[variants]/(main)/chat/components/ConversationArea.tsx +29 -0
- package/src/app/[variants]/(main)/chat/components/MainChatPage.tsx +25 -0
- package/src/app/[variants]/(main)/chat/components/PortalPanel.tsx +28 -0
- package/src/app/[variants]/(main)/chat/components/SessionPanel.tsx +33 -0
- package/src/app/[variants]/(main)/chat/{settings/page.tsx → components/SettingsPage.tsx} +35 -3
- package/src/app/[variants]/(main)/chat/components/TopicSidebar.tsx +30 -0
- package/src/app/[variants]/(main)/chat/components/WorkspaceLayout.tsx +73 -0
- package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/{layout.ts → layout.tsx} +0 -1
- package/src/app/[variants]/(main)/chat/page.tsx +12 -0
- package/src/app/[variants]/(main)/settings/provider/ProviderMenu/List.tsx +97 -7
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/DisabledModels.tsx +144 -8
- package/src/features/Portal/GroupThread/Body/index.tsx +1 -1
- package/src/hooks/useHotkeys/chatScope.ts +1 -1
- package/src/locales/default/modelProvider.ts +15 -1
- package/src/server/services/mcp/deps/checkers/ManualInstallationChecker.test.ts +162 -0
- package/src/server/services/mcp/deps/checkers/NpmInstallationChecker.test.ts +374 -0
- package/src/server/services/mcp/deps/checkers/PythonInstallationChecker.test.ts +368 -0
- package/src/store/global/initialState.ts +4 -0
- package/src/store/global/selectors/systemStatus.ts +6 -0
- package/src/app/[variants]/(main)/chat/(workspace)/layout.ts +0 -11
- package/src/app/[variants]/(main)/chat/(workspace)/page.tsx +0 -53
- package/src/app/[variants]/(main)/chat/@session/default.tsx +0 -31
- package/src/app/[variants]/(main)/chat/settings/layout.tsx +0 -21
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/default.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatHydration/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/ClassicChat.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/GroupChat.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/MessageFromUrl.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/useSendMenuItems.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/MentionedUsers/MentionedUserItem.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/MentionedUsers/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/ActionBar.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/Files/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/InputArea/Container.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/InputArea/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/Send.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/useSend.ts +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/useSend.ts +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/OrchestratorThinking.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/Thread.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/ThreadItem.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/Content.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/AddButton.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/useTemplateMatching.ts +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatMinimap/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ThreadHydration.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ZenModeToast/Toast.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ZenModeToast/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/AgentSettings/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/AgentTeamSettings/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/ChangelogModal.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/SettingButton.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/ShareButton/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/TelemetryNotification.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/HeaderAction.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Main.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/HistoryLimitTags.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/KnowledgeTag.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/MemberCountTag.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/SearchTags.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/Portal.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/TopicPanel.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/ChatHeader/ChatHeaderTitle.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/ChatHeader/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/TopicModal.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/type.ts +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/_layout/Desktop.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/_layout/Mobile.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/default.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/error.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/features/Body.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/loading.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/_layout/Desktop.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/_layout/Mobile.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/default.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/AgentConfig/SystemRole.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/AgentConfig/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/ConfigLayout.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/ConfigSwitcher.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupMember.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupMemberItem.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupRole.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/style.ts +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/SkeletonList.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/Header.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ByTimeMode/GroupItem.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ByTimeMode/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/FlatMode/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/SearchResult/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadItem/Content.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadItem/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadList/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/DefaultContent.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicSearchBar/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionHydration.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/CollapseGroup/Actions.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/CollapseGroup/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/DefaultMode.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Inbox/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/AddButton.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/Item/Actions.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/Item/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/ListItem/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/CreateGroupModal.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/RenameGroupModal.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/SearchMode.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionSearchBar.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session → session}/features/SkeletonList.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/PanelBody.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/SessionHeader.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/index.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Mobile/SessionHeader.tsx +0 -0
- /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Mobile/index.tsx +0 -0
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ActionIcon, ScrollShadow, Text } from '@lobehub/ui';
|
|
3
|
+
import { ActionIcon, Dropdown, Icon, ScrollShadow, Text } from '@lobehub/ui';
|
|
4
|
+
import type { ItemType } from 'antd/es/menu/interface';
|
|
4
5
|
import isEqual from 'fast-deep-equal';
|
|
5
|
-
import { ArrowDownUpIcon } from 'lucide-react';
|
|
6
|
-
import { useState } from 'react';
|
|
6
|
+
import { ArrowDownUpIcon, LucideCheck } from 'lucide-react';
|
|
7
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
7
8
|
import { useTranslation } from 'react-i18next';
|
|
8
9
|
import { Flexbox } from 'react-layout-kit';
|
|
9
10
|
|
|
10
11
|
import { aiProviderSelectors } from '@/store/aiInfra';
|
|
11
12
|
import { useAiInfraStore } from '@/store/aiInfra/store';
|
|
13
|
+
import { useGlobalStore } from '@/store/global';
|
|
14
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
12
15
|
|
|
13
16
|
import All from './All';
|
|
14
17
|
import ProviderItem from './Item';
|
|
15
18
|
import SortProviderModal from './SortProviderModal';
|
|
16
19
|
|
|
20
|
+
// Sort type enumeration
|
|
21
|
+
enum SortType {
|
|
22
|
+
Alphabetical = 'alphabetical',
|
|
23
|
+
AlphabeticalDesc = 'alphabeticalDesc',
|
|
24
|
+
Default = 'default',
|
|
25
|
+
}
|
|
26
|
+
|
|
17
27
|
const ProviderList = (props: {
|
|
18
28
|
mobile?: boolean;
|
|
19
29
|
onProviderSelect: (providerKey: string) => void;
|
|
@@ -21,6 +31,19 @@ const ProviderList = (props: {
|
|
|
21
31
|
const { onProviderSelect, mobile } = props;
|
|
22
32
|
const { t } = useTranslation('modelProvider');
|
|
23
33
|
const [open, setOpen] = useState(false);
|
|
34
|
+
|
|
35
|
+
const [sortType, updateSystemStatus] = useGlobalStore((s) => [
|
|
36
|
+
systemStatusSelectors.disabledModelProvidersSortType(s),
|
|
37
|
+
s.updateSystemStatus,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const updateSortType = useCallback(
|
|
41
|
+
(newSortType: SortType) => {
|
|
42
|
+
updateSystemStatus({ disabledModelProvidersSortType: newSortType });
|
|
43
|
+
},
|
|
44
|
+
[updateSystemStatus],
|
|
45
|
+
);
|
|
46
|
+
|
|
24
47
|
const enabledModelProviderList = useAiInfraStore(
|
|
25
48
|
aiProviderSelectors.enabledAiProviderList,
|
|
26
49
|
isEqual,
|
|
@@ -30,6 +53,34 @@ const ProviderList = (props: {
|
|
|
30
53
|
aiProviderSelectors.disabledAiProviderList,
|
|
31
54
|
isEqual,
|
|
32
55
|
);
|
|
56
|
+
|
|
57
|
+
// Sort model providers based on sort type
|
|
58
|
+
const sortedDisabledProviders = useMemo(() => {
|
|
59
|
+
const providers = [...disabledModelProviderList];
|
|
60
|
+
switch (sortType) {
|
|
61
|
+
case SortType.Alphabetical: {
|
|
62
|
+
return providers.sort((a, b) => {
|
|
63
|
+
const cmpDisplay = (a.name || a.id).localeCompare(b.name || b.id);
|
|
64
|
+
if (cmpDisplay !== 0) return cmpDisplay;
|
|
65
|
+
return a.id.localeCompare(b.id);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
case SortType.AlphabeticalDesc: {
|
|
69
|
+
return providers.sort((a, b) => {
|
|
70
|
+
const cmpDisplay = (b.name || a.id).localeCompare(a.name || b.id);
|
|
71
|
+
if (cmpDisplay !== 0) return cmpDisplay;
|
|
72
|
+
return b.id.localeCompare(a.id);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
case SortType.Default: {
|
|
76
|
+
return providers;
|
|
77
|
+
}
|
|
78
|
+
default: {
|
|
79
|
+
return providers;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, [disabledModelProviderList, sortType]);
|
|
83
|
+
|
|
33
84
|
return (
|
|
34
85
|
<ScrollShadow gap={4} height={'100%'} paddingInline={12} size={4} style={{ paddingBottom: 32 }}>
|
|
35
86
|
{!mobile && <All onClick={onProviderSelect} />}
|
|
@@ -63,10 +114,49 @@ const ProviderList = (props: {
|
|
|
63
114
|
{enabledModelProviderList.map((item) => (
|
|
64
115
|
<ProviderItem {...item} key={item.id} onClick={onProviderSelect} />
|
|
65
116
|
))}
|
|
66
|
-
<
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
117
|
+
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
|
118
|
+
<Text style={{ fontSize: 12, marginTop: 8 }} type={'secondary'}>
|
|
119
|
+
{t('menu.list.disabled')}
|
|
120
|
+
</Text>
|
|
121
|
+
{disabledModelProviderList.length > 1 && (
|
|
122
|
+
<Dropdown
|
|
123
|
+
menu={{
|
|
124
|
+
items: [
|
|
125
|
+
{
|
|
126
|
+
icon: sortType === SortType.Default ? <Icon icon={LucideCheck} /> : <div />,
|
|
127
|
+
key: 'default',
|
|
128
|
+
label: t('menu.list.disabledActions.sortDefault'),
|
|
129
|
+
onClick: () => updateSortType(SortType.Default),
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: 'divider',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
icon: sortType === SortType.Alphabetical ? <Icon icon={LucideCheck} /> : <div />,
|
|
136
|
+
key: 'alphabetical',
|
|
137
|
+
label: t('menu.list.disabledActions.sortAlphabetical'),
|
|
138
|
+
onClick: () => updateSortType(SortType.Alphabetical),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
icon:
|
|
142
|
+
sortType === SortType.AlphabeticalDesc ? <Icon icon={LucideCheck} /> : <div />,
|
|
143
|
+
key: 'alphabeticalDesc',
|
|
144
|
+
label: t('menu.list.disabledActions.sortAlphabeticalDesc'),
|
|
145
|
+
onClick: () => updateSortType(SortType.AlphabeticalDesc),
|
|
146
|
+
},
|
|
147
|
+
] as ItemType[],
|
|
148
|
+
}}
|
|
149
|
+
trigger={['click']}
|
|
150
|
+
>
|
|
151
|
+
<ActionIcon
|
|
152
|
+
icon={ArrowDownUpIcon}
|
|
153
|
+
size={'small'}
|
|
154
|
+
title={t('menu.list.disabledActions.sort')}
|
|
155
|
+
/>
|
|
156
|
+
</Dropdown>
|
|
157
|
+
)}
|
|
158
|
+
</Flexbox>
|
|
159
|
+
{sortedDisabledProviders.map((item) => (
|
|
70
160
|
<ProviderItem {...item} key={item.id} onClick={onProviderSelect} />
|
|
71
161
|
))}
|
|
72
162
|
</ScrollShadow>
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { Button, Text } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Button, Dropdown, Icon, Text } from '@lobehub/ui';
|
|
2
|
+
import type { ItemType } from 'antd/es/menu/interface';
|
|
2
3
|
import isEqual from 'fast-deep-equal';
|
|
3
|
-
import { ChevronDown } from 'lucide-react';
|
|
4
|
-
import { memo, useMemo, useState } from 'react';
|
|
4
|
+
import { ArrowDownUpIcon, ChevronDown, LucideCheck } from 'lucide-react';
|
|
5
|
+
import { memo, useCallback, useMemo, useState } from 'react';
|
|
5
6
|
import { useTranslation } from 'react-i18next';
|
|
6
7
|
import { Flexbox } from 'react-layout-kit';
|
|
7
8
|
|
|
8
9
|
import { useAiInfraStore } from '@/store/aiInfra';
|
|
9
10
|
import { aiModelSelectors } from '@/store/aiInfra/selectors';
|
|
11
|
+
import { useGlobalStore } from '@/store/global';
|
|
12
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
10
13
|
|
|
11
14
|
import ModelItem from './ModelItem';
|
|
12
15
|
|
|
@@ -14,10 +17,32 @@ interface DisabledModelsProps {
|
|
|
14
17
|
activeTab: string;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
// Sort type enumeration
|
|
21
|
+
enum SortType {
|
|
22
|
+
Alphabetical = 'alphabetical',
|
|
23
|
+
AlphabeticalDesc = 'alphabeticalDesc',
|
|
24
|
+
Default = 'default',
|
|
25
|
+
ReleasedAt = 'releasedAt',
|
|
26
|
+
ReleasedAtDesc = 'releasedAtDesc',
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
const DisabledModels = memo<DisabledModelsProps>(({ activeTab }) => {
|
|
18
30
|
const { t } = useTranslation('modelProvider');
|
|
19
31
|
|
|
20
32
|
const [showMore, setShowMore] = useState(false);
|
|
33
|
+
|
|
34
|
+
const [sortType, updateSystemStatus] = useGlobalStore((s) => [
|
|
35
|
+
systemStatusSelectors.disabledModelsSortType(s),
|
|
36
|
+
s.updateSystemStatus,
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const updateSortType = useCallback(
|
|
40
|
+
(newSortType: SortType) => {
|
|
41
|
+
updateSystemStatus({ disabledModelsSortType: newSortType });
|
|
42
|
+
},
|
|
43
|
+
[updateSystemStatus],
|
|
44
|
+
);
|
|
45
|
+
|
|
21
46
|
const disabledModels = useAiInfraStore(aiModelSelectors.disabledAiProviderModelList, isEqual);
|
|
22
47
|
|
|
23
48
|
// Filter models based on active tab
|
|
@@ -26,18 +51,129 @@ const DisabledModels = memo<DisabledModelsProps>(({ activeTab }) => {
|
|
|
26
51
|
return disabledModels.filter((model) => model.type === activeTab);
|
|
27
52
|
}, [disabledModels, activeTab]);
|
|
28
53
|
|
|
29
|
-
|
|
54
|
+
// Sort models based on sort type
|
|
55
|
+
const sortedDisabledModels = useMemo(() => {
|
|
56
|
+
const models = [...filteredDisabledModels];
|
|
57
|
+
switch (sortType) {
|
|
58
|
+
case SortType.Alphabetical: {
|
|
59
|
+
return models.sort((a, b) => {
|
|
60
|
+
const cmpDisplay = (a.displayName || a.id).localeCompare(b.displayName || b.id);
|
|
61
|
+
if (cmpDisplay !== 0) return cmpDisplay;
|
|
62
|
+
return a.id.localeCompare(b.id);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
case SortType.AlphabeticalDesc: {
|
|
66
|
+
return models.sort((a, b) => {
|
|
67
|
+
const cmpDisplay = (b.displayName || b.id).localeCompare(a.displayName || a.id);
|
|
68
|
+
if (cmpDisplay !== 0) return cmpDisplay;
|
|
69
|
+
return b.id.localeCompare(a.id);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
case SortType.ReleasedAt: {
|
|
73
|
+
return models.sort((a, b) => {
|
|
74
|
+
const aHasDate = !!a.releasedAt;
|
|
75
|
+
const bHasDate = !!b.releasedAt;
|
|
76
|
+
|
|
77
|
+
if (aHasDate && !bHasDate) return -1;
|
|
78
|
+
if (!aHasDate && bHasDate) return 1;
|
|
79
|
+
if (!aHasDate && !bHasDate) return 0;
|
|
80
|
+
|
|
81
|
+
return a.releasedAt!.localeCompare(b.releasedAt!);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
case SortType.ReleasedAtDesc: {
|
|
85
|
+
return models.sort((a, b) => {
|
|
86
|
+
const aHasDate = !!a.releasedAt;
|
|
87
|
+
const bHasDate = !!b.releasedAt;
|
|
88
|
+
|
|
89
|
+
if (aHasDate && !bHasDate) return -1;
|
|
90
|
+
if (!aHasDate && bHasDate) return 1;
|
|
91
|
+
if (!aHasDate && !bHasDate) return 0;
|
|
92
|
+
|
|
93
|
+
return b.releasedAt!.localeCompare(a.releasedAt!);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
case SortType.Default: {
|
|
97
|
+
return models;
|
|
98
|
+
}
|
|
99
|
+
default: {
|
|
100
|
+
return models;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}, [filteredDisabledModels, sortType]);
|
|
104
|
+
|
|
105
|
+
const displayModels = showMore ? sortedDisabledModels : sortedDisabledModels.slice(0, 10);
|
|
30
106
|
|
|
31
107
|
return (
|
|
32
108
|
filteredDisabledModels.length > 0 && (
|
|
33
109
|
<Flexbox>
|
|
34
|
-
<
|
|
35
|
-
{
|
|
36
|
-
|
|
110
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
111
|
+
<Text style={{ fontSize: 12, marginTop: 8 }} type={'secondary'}>
|
|
112
|
+
{t('providerModels.list.disabled')}
|
|
113
|
+
</Text>
|
|
114
|
+
{filteredDisabledModels.length > 1 && (
|
|
115
|
+
<Dropdown
|
|
116
|
+
menu={{
|
|
117
|
+
items: [
|
|
118
|
+
{
|
|
119
|
+
icon: sortType === SortType.Default ? <Icon icon={LucideCheck} /> : <div />,
|
|
120
|
+
key: 'default',
|
|
121
|
+
label: t('providerModels.list.disabledActions.sortDefault'),
|
|
122
|
+
onClick: () => updateSortType(SortType.Default),
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
type: 'divider',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
icon:
|
|
129
|
+
sortType === SortType.Alphabetical ? <Icon icon={LucideCheck} /> : <div />,
|
|
130
|
+
key: 'alphabetical',
|
|
131
|
+
label: t('providerModels.list.disabledActions.sortAlphabetical'),
|
|
132
|
+
onClick: () => updateSortType(SortType.Alphabetical),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
icon:
|
|
136
|
+
sortType === SortType.AlphabeticalDesc ? (
|
|
137
|
+
<Icon icon={LucideCheck} />
|
|
138
|
+
) : (
|
|
139
|
+
<div />
|
|
140
|
+
),
|
|
141
|
+
key: 'alphabeticalDesc',
|
|
142
|
+
label: t('providerModels.list.disabledActions.sortAlphabeticalDesc'),
|
|
143
|
+
onClick: () => updateSortType(SortType.AlphabeticalDesc),
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: 'divider',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
icon: sortType === SortType.ReleasedAt ? <Icon icon={LucideCheck} /> : <div />,
|
|
150
|
+
key: 'releasedAt',
|
|
151
|
+
label: t('providerModels.list.disabledActions.sortReleasedAt'),
|
|
152
|
+
onClick: () => updateSortType(SortType.ReleasedAt),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
icon:
|
|
156
|
+
sortType === SortType.ReleasedAtDesc ? <Icon icon={LucideCheck} /> : <div />,
|
|
157
|
+
key: 'releasedAtDesc',
|
|
158
|
+
label: t('providerModels.list.disabledActions.sortReleasedAtDesc'),
|
|
159
|
+
onClick: () => updateSortType(SortType.ReleasedAtDesc),
|
|
160
|
+
},
|
|
161
|
+
] as ItemType[],
|
|
162
|
+
}}
|
|
163
|
+
trigger={['click']}
|
|
164
|
+
>
|
|
165
|
+
<ActionIcon
|
|
166
|
+
icon={ArrowDownUpIcon}
|
|
167
|
+
size={'small'}
|
|
168
|
+
title={t('providerModels.list.disabledActions.sort')}
|
|
169
|
+
/>
|
|
170
|
+
</Dropdown>
|
|
171
|
+
)}
|
|
172
|
+
</Flexbox>
|
|
37
173
|
{displayModels.map((item) => (
|
|
38
174
|
<ModelItem {...item} key={item.id} />
|
|
39
175
|
))}
|
|
40
|
-
{!showMore &&
|
|
176
|
+
{!showMore && sortedDisabledModels.length > 10 && (
|
|
41
177
|
<Button
|
|
42
178
|
block
|
|
43
179
|
icon={ChevronDown}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { memo } from 'react';
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
|
-
import ChatInput from '@/app/[variants]/(main)/chat/
|
|
6
|
+
import ChatInput from '@/app/[variants]/(main)/chat/components/conversation/features/ChatInput';
|
|
7
7
|
import { useChatGroupStore } from '@/store/chatGroup';
|
|
8
8
|
|
|
9
9
|
import ThreadChatList from './ThreadChatList';
|
|
@@ -2,7 +2,7 @@ import isEqual from 'fast-deep-equal';
|
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import { useHotkeysContext } from 'react-hotkeys-hook';
|
|
4
4
|
|
|
5
|
-
import { useSend } from '@/app/[variants]/(main)/chat/
|
|
5
|
+
import { useSend } from '@/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend';
|
|
6
6
|
import { useClearCurrentMessages } from '@/features/ChatInput/ActionBar/Clear';
|
|
7
7
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
8
8
|
import { useActionSWR } from '@/libs/swr';
|
|
@@ -202,6 +202,12 @@ export default {
|
|
|
202
202
|
all: '全部',
|
|
203
203
|
list: {
|
|
204
204
|
disabled: '未启用',
|
|
205
|
+
disabledActions: {
|
|
206
|
+
sort: '排序方式',
|
|
207
|
+
sortAlphabetical: '按字母排序',
|
|
208
|
+
sortAlphabeticalDesc: '按字母倒序排序',
|
|
209
|
+
sortDefault: '默认排序',
|
|
210
|
+
},
|
|
205
211
|
enabled: '已启用',
|
|
206
212
|
},
|
|
207
213
|
notFound: '未找到搜索结果',
|
|
@@ -399,7 +405,15 @@ export default {
|
|
|
399
405
|
list: {
|
|
400
406
|
addNew: '添加模型',
|
|
401
407
|
disabled: '未启用',
|
|
402
|
-
disabledActions: {
|
|
408
|
+
disabledActions: {
|
|
409
|
+
showMore: '显示全部',
|
|
410
|
+
sort: '排序方式',
|
|
411
|
+
sortAlphabetical: '按字母排序',
|
|
412
|
+
sortAlphabeticalDesc: '按字母倒序排序',
|
|
413
|
+
sortDefault: '默认排序',
|
|
414
|
+
sortReleasedAt: '按最早发布时间排序',
|
|
415
|
+
sortReleasedAtDesc: '按最新发布时间排序',
|
|
416
|
+
},
|
|
403
417
|
empty: {
|
|
404
418
|
desc: '请创建自定义模型或拉取模型后开始使用吧',
|
|
405
419
|
title: '暂无可用模型',
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { ManualInstallationChecker } from './ManualInstallationChecker';
|
|
4
|
+
|
|
5
|
+
describe('ManualInstallationChecker', () => {
|
|
6
|
+
let checker: ManualInstallationChecker;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
checker = new ManualInstallationChecker();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('checkPackageInstalled', () => {
|
|
13
|
+
it('should always return not installed for manual packages', async () => {
|
|
14
|
+
const result = await checker.checkPackageInstalled({ packageName: 'manual-package' });
|
|
15
|
+
|
|
16
|
+
expect(result).toEqual({
|
|
17
|
+
installed: false,
|
|
18
|
+
packageName: 'manual-package',
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should handle packageName with repository URL', async () => {
|
|
23
|
+
const result = await checker.checkPackageInstalled({
|
|
24
|
+
packageName: 'my-custom-tool',
|
|
25
|
+
repositoryUrlToClone: 'https://github.com/user/repo.git',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result.installed).toBe(false);
|
|
29
|
+
expect(result.packageName).toBe('my-custom-tool');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should use repositoryUrlToClone as packageName when packageName is not provided', async () => {
|
|
33
|
+
const result = await checker.checkPackageInstalled({
|
|
34
|
+
repositoryUrlToClone: 'https://github.com/user/custom-tool.git',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
installed: false,
|
|
39
|
+
packageName: 'https://github.com/user/custom-tool.git',
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return empty packageName when neither packageName nor repositoryUrlToClone provided', async () => {
|
|
44
|
+
const result = await checker.checkPackageInstalled({});
|
|
45
|
+
|
|
46
|
+
expect(result).toEqual({
|
|
47
|
+
installed: false,
|
|
48
|
+
packageName: '',
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should handle undefined packageName', async () => {
|
|
53
|
+
const result = await checker.checkPackageInstalled({ packageName: undefined });
|
|
54
|
+
|
|
55
|
+
expect(result).toEqual({
|
|
56
|
+
installed: false,
|
|
57
|
+
packageName: '',
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should handle empty string packageName', async () => {
|
|
62
|
+
const result = await checker.checkPackageInstalled({ packageName: '' });
|
|
63
|
+
|
|
64
|
+
expect(result).toEqual({
|
|
65
|
+
installed: false,
|
|
66
|
+
packageName: '',
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should handle empty string repositoryUrlToClone as fallback', async () => {
|
|
71
|
+
const result = await checker.checkPackageInstalled({
|
|
72
|
+
packageName: '',
|
|
73
|
+
repositoryUrlToClone: '',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(result).toEqual({
|
|
77
|
+
installed: false,
|
|
78
|
+
packageName: '',
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should prioritize packageName over repositoryUrlToClone', async () => {
|
|
83
|
+
const result = await checker.checkPackageInstalled({
|
|
84
|
+
packageName: 'my-tool',
|
|
85
|
+
repositoryUrlToClone: 'https://github.com/user/repo.git',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.packageName).toBe('my-tool');
|
|
89
|
+
expect(result.installed).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should handle complex package names', async () => {
|
|
93
|
+
const result = await checker.checkPackageInstalled({
|
|
94
|
+
packageName: '@scope/package-name-with-special.chars_123',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(result).toEqual({
|
|
98
|
+
installed: false,
|
|
99
|
+
packageName: '@scope/package-name-with-special.chars_123',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should handle SSH repository URLs', async () => {
|
|
104
|
+
const result = await checker.checkPackageInstalled({
|
|
105
|
+
repositoryUrlToClone: 'git@github.com:user/repo.git',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(result).toEqual({
|
|
109
|
+
installed: false,
|
|
110
|
+
packageName: 'git@github.com:user/repo.git',
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should handle local file paths', async () => {
|
|
115
|
+
const result = await checker.checkPackageInstalled({
|
|
116
|
+
packageName: '/usr/local/custom-tool',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(result).toEqual({
|
|
120
|
+
installed: false,
|
|
121
|
+
packageName: '/usr/local/custom-tool',
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle Windows-style paths', async () => {
|
|
126
|
+
const result = await checker.checkPackageInstalled({
|
|
127
|
+
packageName: 'C:\\Program Files\\CustomTool',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(result).toEqual({
|
|
131
|
+
installed: false,
|
|
132
|
+
packageName: 'C:\\Program Files\\CustomTool',
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle package names with unicode characters', async () => {
|
|
137
|
+
const result = await checker.checkPackageInstalled({
|
|
138
|
+
packageName: 'my-tool-日本語',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(result).toEqual({
|
|
142
|
+
installed: false,
|
|
143
|
+
packageName: 'my-tool-日本語',
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should always return false for installed regardless of input', async () => {
|
|
148
|
+
const testCases = [
|
|
149
|
+
{ packageName: 'package1' },
|
|
150
|
+
{ packageName: 'package2', repositoryUrlToClone: 'https://example.com/repo.git' },
|
|
151
|
+
{ repositoryUrlToClone: 'https://example.com/repo2.git' },
|
|
152
|
+
{},
|
|
153
|
+
{ packageName: '' },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
for (const testCase of testCases) {
|
|
157
|
+
const result = await checker.checkPackageInstalled(testCase);
|
|
158
|
+
expect(result.installed).toBe(false);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|