@lobehub/lobehub 2.0.0-next.221 → 2.0.0-next.223
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 +58 -0
- package/changelog/v1.json +18 -0
- package/e2e/package.json +1 -1
- package/e2e/src/mocks/index.ts +2 -2
- package/e2e/src/steps/{discover → community}/detail-pages.steps.ts +8 -8
- package/e2e/src/steps/{discover → community}/interactions.steps.ts +4 -4
- 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/correctOIDCUrl.test.ts +80 -19
- package/packages/utils/src/server/correctOIDCUrl.ts +89 -24
- package/packages/utils/src/server/index.ts +1 -0
- 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)/oidc/callback/desktop/route.ts +7 -36
- 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 +9 -6
- 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
- /package/e2e/src/features/{discover → community}/detail-pages.feature +0 -0
- /package/e2e/src/features/{discover → community}/interactions.feature +0 -0
- /package/e2e/src/features/{discover → community}/smoke.feature +0 -0
- /package/e2e/src/mocks/{discover → community}/data.ts +0 -0
- /package/e2e/src/mocks/{discover → community}/handlers.ts +0 -0
- /package/e2e/src/mocks/{discover → community}/index.ts +0 -0
- /package/e2e/src/mocks/{discover → community}/types.ts +0 -0
- /package/e2e/src/steps/{discover → community}/smoke.steps.ts +0 -0
|
@@ -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
|
+
};
|