@lobehub/lobehub 2.0.0-next.221 → 2.0.0-next.222
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/.github/workflows/claude-auto-testing.yml +6 -3
- package/.github/workflows/claude-dedupe-issues.yml +8 -1
- package/.github/workflows/claude-issue-triage.yml +8 -14
- package/.github/workflows/claude-translate-comments.yml +6 -3
- package/.github/workflows/claude-translator.yml +12 -14
- package/.github/workflows/claude.yml +10 -20
- package/.github/workflows/test.yml +26 -0
- package/CHANGELOG.md +33 -0
- package/changelog/v1.json +9 -0
- package/locales/zh-CN/components.json +1 -0
- package/package.json +3 -3
- package/packages/const/src/index.ts +0 -1
- package/packages/memory-user-memory/package.json +1 -0
- package/packages/memory-user-memory/src/extractors/context.test.ts +3 -2
- package/packages/memory-user-memory/src/extractors/experience.test.ts +3 -2
- package/packages/memory-user-memory/src/extractors/identity.test.ts +23 -6
- package/packages/memory-user-memory/src/extractors/preference.test.ts +3 -2
- package/packages/memory-user-memory/vitest.config.ts +4 -0
- package/packages/model-runtime/src/providers/replicate/index.ts +1 -1
- package/packages/ssrf-safe-fetch/index.test.ts +2 -2
- package/packages/ssrf-safe-fetch/package.json +3 -2
- package/packages/types/src/serverConfig.ts +2 -0
- package/packages/utils/package.json +1 -1
- package/packages/utils/src/client/xor-obfuscation.test.ts +32 -32
- package/packages/utils/src/client/xor-obfuscation.ts +3 -4
- package/packages/utils/src/imageToBase64.ts +1 -1
- package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
- package/packages/utils/src/server/auth.ts +1 -1
- package/packages/utils/src/server/xor.test.ts +9 -7
- package/packages/utils/src/server/xor.ts +1 -1
- package/packages/web-crawler/package.json +1 -1
- package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +1 -1
- package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
- package/scripts/prebuild.mts +58 -1
- package/src/app/(backend)/api/auth/[...all]/route.ts +2 -1
- package/src/app/(backend)/middleware/auth/index.ts +3 -3
- package/src/app/(backend)/middleware/auth/utils.test.ts +1 -1
- package/src/app/(backend)/middleware/auth/utils.ts +1 -1
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +1 -1
- package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
- package/src/app/(backend)/webapi/proxy/route.ts +1 -1
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/signin/useSignIn.ts +2 -2
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +12 -6
- package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
- package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -1
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -1
- package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +1 -1
- package/src/components/ModelSelect/index.tsx +103 -72
- package/src/envs/auth.ts +30 -9
- package/src/features/Conversation/Messages/AssistantGroup/components/EditState.tsx +15 -32
- package/src/features/Conversation/Messages/AssistantGroup/index.tsx +9 -7
- package/src/features/EditorModal/EditorCanvas.tsx +31 -50
- package/src/features/EditorModal/TextareCanvas.tsx +3 -1
- package/src/features/EditorModal/index.tsx +14 -4
- package/src/features/ModelSwitchPanel/components/Footer.tsx +42 -0
- package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +103 -0
- package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +24 -0
- package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +180 -0
- package/src/features/ModelSwitchPanel/components/List/index.tsx +99 -0
- package/src/features/ModelSwitchPanel/components/PanelContent.tsx +77 -0
- package/src/features/ModelSwitchPanel/components/Toolbar.tsx +54 -0
- package/src/features/ModelSwitchPanel/const.ts +29 -0
- package/src/features/ModelSwitchPanel/hooks/useBuildVirtualItems.ts +122 -0
- package/src/features/ModelSwitchPanel/hooks/useCurrentModelName.ts +18 -0
- package/src/features/ModelSwitchPanel/hooks/useDelayedRender.ts +18 -0
- package/src/features/ModelSwitchPanel/hooks/useModelAndProvider.ts +14 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelHandlers.ts +33 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelSize.ts +33 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelState.ts +20 -0
- package/src/features/ModelSwitchPanel/index.tsx +25 -706
- package/src/features/ModelSwitchPanel/styles.ts +58 -0
- package/src/features/ModelSwitchPanel/types.ts +73 -0
- package/src/features/ModelSwitchPanel/utils.ts +24 -0
- package/src/features/User/UserPanel/PanelContent.tsx +1 -1
- package/src/features/User/__tests__/PanelContent.test.tsx +1 -1
- package/src/features/User/__tests__/UserAvatar.test.tsx +1 -1
- package/src/features/User/__tests__/useMenu.test.tsx +1 -1
- package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -1
- package/src/libs/better-auth/auth-client.ts +7 -3
- package/src/libs/better-auth/define-config.ts +2 -2
- package/src/libs/next/proxy/define-config.ts +1 -2
- package/src/libs/oidc-provider/provider.test.ts +1 -1
- package/src/libs/trpc/async/context.ts +1 -1
- package/src/libs/trpc/lambda/context.ts +7 -8
- package/src/libs/trpc/middleware/userAuth.ts +1 -1
- package/src/libs/trusted-client/getSessionUser.ts +1 -1
- package/src/locales/default/components.ts +1 -0
- package/src/server/globalConfig/index.ts +2 -0
- package/src/server/routers/async/caller.ts +1 -1
- package/src/server/routers/lambda/__tests__/user.test.ts +2 -2
- package/src/server/routers/lambda/user.ts +2 -1
- package/src/services/_auth.ts +3 -3
- package/src/services/chat/index.ts +1 -1
- package/src/services/chat/mecha/contextEngineering.ts +1 -1
- package/src/store/global/initialState.ts +10 -0
- package/src/store/global/selectors/systemStatus.ts +5 -0
- package/src/store/serverConfig/selectors.ts +5 -1
- package/src/store/tool/slices/mcpStore/action.ts +74 -75
- package/src/store/user/slices/auth/action.test.ts +1 -1
- package/src/store/user/slices/auth/action.ts +1 -1
- package/src/store/user/slices/auth/initialState.ts +1 -1
- package/src/store/user/slices/auth/selectors.test.ts +1 -1
- package/src/store/user/slices/auth/selectors.ts +1 -1
- package/src/store/user/slices/common/action.ts +1 -1
- package/src/store/userMemory/slices/context/action.ts +6 -6
- package/packages/const/src/auth.ts +0 -14
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Flexbox, Icon, SearchBar, Segmented } from '@lobehub/ui';
|
|
2
|
+
import { ProviderIcon } from '@lobehub/ui/icons';
|
|
3
|
+
import { Brain } from 'lucide-react';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import { styles } from '../styles';
|
|
8
|
+
import type { GroupMode } from '../types';
|
|
9
|
+
|
|
10
|
+
interface ToolbarProps {
|
|
11
|
+
groupMode: GroupMode;
|
|
12
|
+
onGroupModeChange: (mode: GroupMode) => void;
|
|
13
|
+
onSearchKeywordChange: (keyword: string) => void;
|
|
14
|
+
searchKeyword: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Toolbar = memo<ToolbarProps>(
|
|
18
|
+
({ groupMode, onGroupModeChange, searchKeyword, onSearchKeywordChange }) => {
|
|
19
|
+
const { t } = useTranslation('components');
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Flexbox className={styles.toolbar} gap={4} horizontal paddingBlock={8} paddingInline={8}>
|
|
23
|
+
<SearchBar
|
|
24
|
+
allowClear
|
|
25
|
+
onChange={(e) => onSearchKeywordChange(e.target.value)}
|
|
26
|
+
placeholder={t('ModelSwitchPanel.searchPlaceholder')}
|
|
27
|
+
size="small"
|
|
28
|
+
style={{ flex: 1 }}
|
|
29
|
+
value={searchKeyword}
|
|
30
|
+
variant="borderless"
|
|
31
|
+
/>
|
|
32
|
+
<Segmented
|
|
33
|
+
onChange={(value) => onGroupModeChange(value as GroupMode)}
|
|
34
|
+
options={[
|
|
35
|
+
{
|
|
36
|
+
icon: <Icon icon={Brain} />,
|
|
37
|
+
title: t('ModelSwitchPanel.byModel'),
|
|
38
|
+
value: 'byModel',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
icon: <Icon icon={ProviderIcon} />,
|
|
42
|
+
title: t('ModelSwitchPanel.byProvider'),
|
|
43
|
+
value: 'byProvider',
|
|
44
|
+
},
|
|
45
|
+
]}
|
|
46
|
+
size="small"
|
|
47
|
+
value={groupMode}
|
|
48
|
+
/>
|
|
49
|
+
</Flexbox>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
Toolbar.displayName = 'Toolbar';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const STORAGE_KEY = 'MODEL_SWITCH_PANEL_WIDTH';
|
|
2
|
+
export const STORAGE_KEY_MODE = 'MODEL_SWITCH_PANEL_MODE';
|
|
3
|
+
export const DEFAULT_WIDTH = 430;
|
|
4
|
+
export const MIN_WIDTH = 280;
|
|
5
|
+
export const MAX_WIDTH = 600;
|
|
6
|
+
export const MAX_PANEL_HEIGHT = 460;
|
|
7
|
+
export const TOOLBAR_HEIGHT = 40;
|
|
8
|
+
export const FOOTER_HEIGHT = 48;
|
|
9
|
+
|
|
10
|
+
export const INITIAL_RENDER_COUNT = 15;
|
|
11
|
+
export const RENDER_ALL_DELAY_MS = 500;
|
|
12
|
+
|
|
13
|
+
export const ITEM_HEIGHT = {
|
|
14
|
+
'empty-model': 32,
|
|
15
|
+
'group-header': 32,
|
|
16
|
+
'model-item': 32,
|
|
17
|
+
'no-provider': 32,
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
export const ENABLE_RESIZING = {
|
|
21
|
+
bottom: false,
|
|
22
|
+
bottomLeft: false,
|
|
23
|
+
bottomRight: false,
|
|
24
|
+
left: false,
|
|
25
|
+
right: true,
|
|
26
|
+
top: false,
|
|
27
|
+
topLeft: false,
|
|
28
|
+
topRight: false,
|
|
29
|
+
} as const;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { EnabledProviderWithModels } from '@/types/aiProvider';
|
|
4
|
+
|
|
5
|
+
import type { GroupMode, ModelWithProviders, VirtualItem } from '../types';
|
|
6
|
+
|
|
7
|
+
export const useBuildVirtualItems = (
|
|
8
|
+
enabledList: EnabledProviderWithModels[],
|
|
9
|
+
groupMode: GroupMode,
|
|
10
|
+
searchKeyword: string = '',
|
|
11
|
+
): VirtualItem[] => {
|
|
12
|
+
return useMemo(() => {
|
|
13
|
+
if (enabledList.length === 0) {
|
|
14
|
+
return [{ type: 'no-provider' }] as VirtualItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Filter function for search
|
|
18
|
+
const matchesSearch = (text: string): boolean => {
|
|
19
|
+
if (!searchKeyword.trim()) return true;
|
|
20
|
+
const keyword = searchKeyword.toLowerCase().trim();
|
|
21
|
+
return text.toLowerCase().includes(keyword);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Sort providers: lobehub first, then others
|
|
25
|
+
const sortedProviders = [...enabledList].sort((a, b) => {
|
|
26
|
+
const aIsLobehub = a.id === 'lobehub';
|
|
27
|
+
const bIsLobehub = b.id === 'lobehub';
|
|
28
|
+
if (aIsLobehub && !bIsLobehub) return -1;
|
|
29
|
+
if (!aIsLobehub && bIsLobehub) return 1;
|
|
30
|
+
return 0;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (groupMode === 'byModel') {
|
|
34
|
+
// Group models by display name
|
|
35
|
+
const modelMap = new Map<string, ModelWithProviders>();
|
|
36
|
+
|
|
37
|
+
for (const providerItem of sortedProviders) {
|
|
38
|
+
for (const modelItem of providerItem.children) {
|
|
39
|
+
const displayName = modelItem.displayName || modelItem.id;
|
|
40
|
+
|
|
41
|
+
// Filter by search keyword
|
|
42
|
+
if (!matchesSearch(displayName) && !matchesSearch(providerItem.name)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!modelMap.has(displayName)) {
|
|
47
|
+
modelMap.set(displayName, {
|
|
48
|
+
displayName,
|
|
49
|
+
model: modelItem,
|
|
50
|
+
providers: [],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const entry = modelMap.get(displayName)!;
|
|
55
|
+
entry.providers.push({
|
|
56
|
+
id: providerItem.id,
|
|
57
|
+
logo: providerItem.logo,
|
|
58
|
+
name: providerItem.name,
|
|
59
|
+
source: providerItem.source,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Sort providers within each model: lobehub first
|
|
65
|
+
const modelArray = Array.from(modelMap.values());
|
|
66
|
+
for (const model of modelArray) {
|
|
67
|
+
model.providers.sort((a, b) => {
|
|
68
|
+
const aIsLobehub = a.id === 'lobehub';
|
|
69
|
+
const bIsLobehub = b.id === 'lobehub';
|
|
70
|
+
if (aIsLobehub && !bIsLobehub) return -1;
|
|
71
|
+
if (!aIsLobehub && bIsLobehub) return 1;
|
|
72
|
+
return 0;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Convert to array and sort by display name
|
|
77
|
+
return modelArray
|
|
78
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName))
|
|
79
|
+
.map((data) => ({
|
|
80
|
+
data,
|
|
81
|
+
type:
|
|
82
|
+
data.providers.length === 1
|
|
83
|
+
? ('model-item-single' as const)
|
|
84
|
+
: ('model-item-multiple' as const),
|
|
85
|
+
}));
|
|
86
|
+
} else {
|
|
87
|
+
// Group by provider (original structure)
|
|
88
|
+
const items: VirtualItem[] = [];
|
|
89
|
+
|
|
90
|
+
for (const providerItem of sortedProviders) {
|
|
91
|
+
// Filter models by search keyword
|
|
92
|
+
const filteredModels = providerItem.children.filter(
|
|
93
|
+
(modelItem) =>
|
|
94
|
+
matchesSearch(modelItem.displayName || modelItem.id) ||
|
|
95
|
+
matchesSearch(providerItem.name),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Only add provider group header if there are matching models or if search is empty
|
|
99
|
+
if (filteredModels.length > 0 || !searchKeyword.trim()) {
|
|
100
|
+
// Add provider group header
|
|
101
|
+
items.push({ provider: providerItem, type: 'group-header' });
|
|
102
|
+
|
|
103
|
+
if (filteredModels.length === 0) {
|
|
104
|
+
// Add empty model placeholder
|
|
105
|
+
items.push({ provider: providerItem, type: 'empty-model' });
|
|
106
|
+
} else {
|
|
107
|
+
// Add each filtered model item
|
|
108
|
+
for (const modelItem of filteredModels) {
|
|
109
|
+
items.push({
|
|
110
|
+
model: modelItem,
|
|
111
|
+
provider: providerItem,
|
|
112
|
+
type: 'provider-model-item',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return items;
|
|
120
|
+
}
|
|
121
|
+
}, [enabledList, groupMode, searchKeyword]);
|
|
122
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { EnabledProviderWithModels } from '@/types/aiProvider';
|
|
4
|
+
|
|
5
|
+
export const useCurrentModelName = (
|
|
6
|
+
enabledList: EnabledProviderWithModels[],
|
|
7
|
+
model: string,
|
|
8
|
+
): string => {
|
|
9
|
+
return useMemo(() => {
|
|
10
|
+
for (const providerItem of enabledList) {
|
|
11
|
+
const modelItem = providerItem.children.find((m) => m.id === model);
|
|
12
|
+
if (modelItem) {
|
|
13
|
+
return modelItem.displayName || modelItem.id;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return model;
|
|
17
|
+
}, [enabledList, model]);
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { RENDER_ALL_DELAY_MS } from '../const';
|
|
4
|
+
|
|
5
|
+
export const useDelayedRender = (isOpen: boolean) => {
|
|
6
|
+
const [renderAll, setRenderAll] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (isOpen && !renderAll) {
|
|
10
|
+
const timer = setTimeout(() => {
|
|
11
|
+
setRenderAll(true);
|
|
12
|
+
}, RENDER_ALL_DELAY_MS);
|
|
13
|
+
return () => clearTimeout(timer);
|
|
14
|
+
}
|
|
15
|
+
}, [isOpen, renderAll]);
|
|
16
|
+
|
|
17
|
+
return renderAll;
|
|
18
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useAgentStore } from '@/store/agent';
|
|
2
|
+
import { agentSelectors } from '@/store/agent/selectors';
|
|
3
|
+
|
|
4
|
+
export const useModelAndProvider = (modelProp?: string, providerProp?: string) => {
|
|
5
|
+
const [storeModel, storeProvider] = useAgentStore((s) => [
|
|
6
|
+
agentSelectors.currentAgentModel(s),
|
|
7
|
+
agentSelectors.currentAgentModelProvider(s),
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
const model = modelProp ?? storeModel;
|
|
11
|
+
const provider = providerProp ?? storeProvider;
|
|
12
|
+
|
|
13
|
+
return { model, provider };
|
|
14
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useAgentStore } from '@/store/agent';
|
|
4
|
+
|
|
5
|
+
interface UsePanelHandlersProps {
|
|
6
|
+
onModelChange?: (params: { model: string; provider: string }) => Promise<void>;
|
|
7
|
+
onOpenChange?: (open: boolean) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const usePanelHandlers = ({
|
|
11
|
+
onModelChange: onModelChangeProp,
|
|
12
|
+
onOpenChange,
|
|
13
|
+
}: UsePanelHandlersProps) => {
|
|
14
|
+
const updateAgentConfig = useAgentStore((s) => s.updateAgentConfig);
|
|
15
|
+
|
|
16
|
+
const handleModelChange = useCallback(
|
|
17
|
+
async (modelId: string, providerId: string) => {
|
|
18
|
+
const params = { model: modelId, provider: providerId };
|
|
19
|
+
if (onModelChangeProp) {
|
|
20
|
+
onModelChangeProp(params);
|
|
21
|
+
} else {
|
|
22
|
+
updateAgentConfig(params);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
[onModelChangeProp, updateAgentConfig],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const handleClose = useCallback(() => {
|
|
29
|
+
onOpenChange?.(false);
|
|
30
|
+
}, [onOpenChange]);
|
|
31
|
+
|
|
32
|
+
return { handleClose, handleModelChange };
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useGlobalStore } from '@/store/global';
|
|
4
|
+
import { systemStatusSelectors } from '@/store/global/selectors/systemStatus';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
FOOTER_HEIGHT,
|
|
8
|
+
ITEM_HEIGHT,
|
|
9
|
+
MAX_PANEL_HEIGHT,
|
|
10
|
+
TOOLBAR_HEIGHT,
|
|
11
|
+
} from '../const';
|
|
12
|
+
|
|
13
|
+
export const usePanelSize = (enabledListLength: number) => {
|
|
14
|
+
const panelWidth = useGlobalStore(systemStatusSelectors.modelSwitchPanelWidth);
|
|
15
|
+
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
|
16
|
+
|
|
17
|
+
const panelHeight = useMemo(
|
|
18
|
+
() =>
|
|
19
|
+
enabledListLength === 0
|
|
20
|
+
? TOOLBAR_HEIGHT + ITEM_HEIGHT['no-provider'] + FOOTER_HEIGHT
|
|
21
|
+
: MAX_PANEL_HEIGHT,
|
|
22
|
+
[enabledListLength],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const handlePanelWidthChange = useCallback(
|
|
26
|
+
(width: number) => {
|
|
27
|
+
updateSystemStatus({ modelSwitchPanelWidth: width });
|
|
28
|
+
},
|
|
29
|
+
[updateSystemStatus],
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return { handlePanelWidthChange, panelHeight, panelWidth };
|
|
33
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useGlobalStore } from '@/store/global';
|
|
4
|
+
import { systemStatusSelectors } from '@/store/global/selectors/systemStatus';
|
|
5
|
+
|
|
6
|
+
import type { GroupMode } from '../types';
|
|
7
|
+
|
|
8
|
+
export const usePanelState = () => {
|
|
9
|
+
const groupMode = useGlobalStore(systemStatusSelectors.modelSwitchPanelGroupMode) as GroupMode;
|
|
10
|
+
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
|
11
|
+
|
|
12
|
+
const handleGroupModeChange = useCallback(
|
|
13
|
+
(mode: GroupMode) => {
|
|
14
|
+
updateSystemStatus({ modelSwitchPanelGroupMode: mode });
|
|
15
|
+
},
|
|
16
|
+
[updateSystemStatus],
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return { groupMode, handleGroupModeChange };
|
|
20
|
+
};
|