@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.
Files changed (130) hide show
  1. package/.github/workflows/claude-auto-testing.yml +6 -3
  2. package/.github/workflows/claude-dedupe-issues.yml +8 -1
  3. package/.github/workflows/claude-issue-triage.yml +8 -14
  4. package/.github/workflows/claude-translate-comments.yml +6 -3
  5. package/.github/workflows/claude-translator.yml +12 -14
  6. package/.github/workflows/claude.yml +10 -20
  7. package/.github/workflows/test.yml +26 -0
  8. package/CHANGELOG.md +58 -0
  9. package/changelog/v1.json +18 -0
  10. package/e2e/package.json +1 -1
  11. package/e2e/src/mocks/index.ts +2 -2
  12. package/e2e/src/steps/{discover → community}/detail-pages.steps.ts +8 -8
  13. package/e2e/src/steps/{discover → community}/interactions.steps.ts +4 -4
  14. package/locales/zh-CN/components.json +1 -0
  15. package/package.json +3 -3
  16. package/packages/const/src/index.ts +0 -1
  17. package/packages/memory-user-memory/package.json +1 -0
  18. package/packages/memory-user-memory/src/extractors/context.test.ts +3 -2
  19. package/packages/memory-user-memory/src/extractors/experience.test.ts +3 -2
  20. package/packages/memory-user-memory/src/extractors/identity.test.ts +23 -6
  21. package/packages/memory-user-memory/src/extractors/preference.test.ts +3 -2
  22. package/packages/memory-user-memory/vitest.config.ts +4 -0
  23. package/packages/model-runtime/src/providers/replicate/index.ts +1 -1
  24. package/packages/ssrf-safe-fetch/index.test.ts +2 -2
  25. package/packages/ssrf-safe-fetch/package.json +3 -2
  26. package/packages/types/src/serverConfig.ts +2 -0
  27. package/packages/utils/package.json +1 -1
  28. package/packages/utils/src/client/xor-obfuscation.test.ts +32 -32
  29. package/packages/utils/src/client/xor-obfuscation.ts +3 -4
  30. package/packages/utils/src/imageToBase64.ts +1 -1
  31. package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
  32. package/packages/utils/src/server/auth.ts +1 -1
  33. package/packages/utils/src/server/correctOIDCUrl.test.ts +80 -19
  34. package/packages/utils/src/server/correctOIDCUrl.ts +89 -24
  35. package/packages/utils/src/server/index.ts +1 -0
  36. package/packages/utils/src/server/xor.test.ts +9 -7
  37. package/packages/utils/src/server/xor.ts +1 -1
  38. package/packages/web-crawler/package.json +1 -1
  39. package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +1 -1
  40. package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
  41. package/scripts/prebuild.mts +58 -1
  42. package/src/app/(backend)/api/auth/[...all]/route.ts +2 -1
  43. package/src/app/(backend)/middleware/auth/index.ts +3 -3
  44. package/src/app/(backend)/middleware/auth/utils.test.ts +1 -1
  45. package/src/app/(backend)/middleware/auth/utils.ts +1 -1
  46. package/src/app/(backend)/oidc/callback/desktop/route.ts +7 -36
  47. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
  48. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +1 -1
  49. package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
  50. package/src/app/(backend)/webapi/proxy/route.ts +1 -1
  51. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
  52. package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
  53. package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
  54. package/src/app/[variants]/(auth)/signin/useSignIn.ts +2 -2
  55. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
  56. package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +12 -6
  57. package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
  58. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -1
  59. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
  60. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +1 -1
  61. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +1 -1
  62. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -1
  63. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +1 -1
  64. package/src/components/ModelSelect/index.tsx +103 -72
  65. package/src/envs/auth.ts +30 -9
  66. package/src/features/Conversation/Messages/AssistantGroup/components/EditState.tsx +15 -32
  67. package/src/features/Conversation/Messages/AssistantGroup/index.tsx +9 -7
  68. package/src/features/EditorModal/EditorCanvas.tsx +31 -50
  69. package/src/features/EditorModal/TextareCanvas.tsx +3 -1
  70. package/src/features/EditorModal/index.tsx +14 -4
  71. package/src/features/ModelSwitchPanel/components/Footer.tsx +42 -0
  72. package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +103 -0
  73. package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +24 -0
  74. package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +180 -0
  75. package/src/features/ModelSwitchPanel/components/List/index.tsx +99 -0
  76. package/src/features/ModelSwitchPanel/components/PanelContent.tsx +77 -0
  77. package/src/features/ModelSwitchPanel/components/Toolbar.tsx +54 -0
  78. package/src/features/ModelSwitchPanel/const.ts +29 -0
  79. package/src/features/ModelSwitchPanel/hooks/useBuildVirtualItems.ts +122 -0
  80. package/src/features/ModelSwitchPanel/hooks/useCurrentModelName.ts +18 -0
  81. package/src/features/ModelSwitchPanel/hooks/useDelayedRender.ts +18 -0
  82. package/src/features/ModelSwitchPanel/hooks/useModelAndProvider.ts +14 -0
  83. package/src/features/ModelSwitchPanel/hooks/usePanelHandlers.ts +33 -0
  84. package/src/features/ModelSwitchPanel/hooks/usePanelSize.ts +33 -0
  85. package/src/features/ModelSwitchPanel/hooks/usePanelState.ts +20 -0
  86. package/src/features/ModelSwitchPanel/index.tsx +25 -706
  87. package/src/features/ModelSwitchPanel/styles.ts +58 -0
  88. package/src/features/ModelSwitchPanel/types.ts +73 -0
  89. package/src/features/ModelSwitchPanel/utils.ts +24 -0
  90. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  91. package/src/features/User/__tests__/PanelContent.test.tsx +1 -1
  92. package/src/features/User/__tests__/UserAvatar.test.tsx +1 -1
  93. package/src/features/User/__tests__/useMenu.test.tsx +1 -1
  94. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -1
  95. package/src/libs/better-auth/auth-client.ts +7 -3
  96. package/src/libs/better-auth/define-config.ts +2 -2
  97. package/src/libs/next/proxy/define-config.ts +9 -6
  98. package/src/libs/oidc-provider/provider.test.ts +1 -1
  99. package/src/libs/trpc/async/context.ts +1 -1
  100. package/src/libs/trpc/lambda/context.ts +7 -8
  101. package/src/libs/trpc/middleware/userAuth.ts +1 -1
  102. package/src/libs/trusted-client/getSessionUser.ts +1 -1
  103. package/src/locales/default/components.ts +1 -0
  104. package/src/server/globalConfig/index.ts +2 -0
  105. package/src/server/routers/async/caller.ts +1 -1
  106. package/src/server/routers/lambda/__tests__/user.test.ts +2 -2
  107. package/src/server/routers/lambda/user.ts +2 -1
  108. package/src/services/_auth.ts +3 -3
  109. package/src/services/chat/index.ts +1 -1
  110. package/src/services/chat/mecha/contextEngineering.ts +1 -1
  111. package/src/store/global/initialState.ts +10 -0
  112. package/src/store/global/selectors/systemStatus.ts +5 -0
  113. package/src/store/serverConfig/selectors.ts +5 -1
  114. package/src/store/tool/slices/mcpStore/action.ts +74 -75
  115. package/src/store/user/slices/auth/action.test.ts +1 -1
  116. package/src/store/user/slices/auth/action.ts +1 -1
  117. package/src/store/user/slices/auth/initialState.ts +1 -1
  118. package/src/store/user/slices/auth/selectors.test.ts +1 -1
  119. package/src/store/user/slices/auth/selectors.ts +1 -1
  120. package/src/store/user/slices/common/action.ts +1 -1
  121. package/src/store/userMemory/slices/context/action.ts +6 -6
  122. package/packages/const/src/auth.ts +0 -14
  123. /package/e2e/src/features/{discover → community}/detail-pages.feature +0 -0
  124. /package/e2e/src/features/{discover → community}/interactions.feature +0 -0
  125. /package/e2e/src/features/{discover → community}/smoke.feature +0 -0
  126. /package/e2e/src/mocks/{discover → community}/data.ts +0 -0
  127. /package/e2e/src/mocks/{discover → community}/handlers.ts +0 -0
  128. /package/e2e/src/mocks/{discover → community}/index.ts +0 -0
  129. /package/e2e/src/mocks/{discover → community}/types.ts +0 -0
  130. /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
+ };