@lobehub/lobehub 2.0.0-next.344 → 2.0.0-next.346
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/.cursor/rules/i18n.mdc +1 -1
- package/.cursor/rules/modal-imperative.mdc +162 -0
- package/.cursor/rules/rules-index.mdc +1 -0
- package/.env.example +0 -14
- package/.eslintrc.js +8 -1
- package/CHANGELOG.md +66 -0
- package/CLAUDE.md +4 -2
- package/Dockerfile +3 -13
- package/README.md +3 -5
- package/README.zh-CN.md +3 -5
- package/changelog/v1.json +20 -0
- package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
- package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
- package/e2e/src/support/webServer.ts +2 -0
- package/locales/ar/error.json +0 -4
- package/locales/bg-BG/error.json +0 -4
- package/locales/de-DE/error.json +0 -4
- package/locales/en-US/error.json +0 -4
- package/locales/es-ES/error.json +0 -4
- package/locales/fa-IR/error.json +0 -4
- package/locales/fr-FR/error.json +0 -4
- package/locales/it-IT/error.json +0 -4
- package/locales/ja-JP/error.json +0 -4
- package/locales/ko-KR/error.json +0 -4
- package/locales/nl-NL/error.json +0 -4
- package/locales/pl-PL/error.json +0 -4
- package/locales/pt-BR/error.json +0 -4
- package/locales/ru-RU/error.json +0 -4
- package/locales/tr-TR/error.json +0 -4
- package/locales/vi-VN/error.json +0 -4
- package/locales/zh-CN/error.json +0 -4
- package/locales/zh-TW/error.json +0 -4
- package/package.json +12 -12
- package/packages/builtin-agents/package.json +2 -0
- package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
- package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
- package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
- package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
- package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
- package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
- package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
- package/packages/file-loaders/package.json +1 -1
- package/packages/file-loaders/src/loadFile.ts +10 -15
- package/packages/file-loaders/src/loaders/index.ts +68 -19
- package/packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap +1 -1
- package/packages/file-loaders/test/__snapshots__/loaders.test.ts.snap +1 -1
- package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
- package/packages/model-bank/src/modelProviders/fal.ts +0 -1
- package/packages/types/src/fetch.ts +1 -2
- package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
- package/packages/utils/src/server/auth.ts +1 -9
- package/pnpm-workspace.yaml +1 -0
- package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
- package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
- package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
- package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
- package/scripts/countEnWord.ts +1 -1
- package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
- package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
- package/scripts/prebuild.mts +10 -8
- package/scripts/serverLauncher/startServer.js +23 -5
- package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
- package/src/app/(backend)/middleware/auth/index.ts +0 -15
- package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
- package/src/app/(backend)/middleware/auth/utils.ts +2 -17
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
- package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
- package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
- package/src/app/[variants]/(main)/resource/features/store/action.ts +2 -2
- package/src/app/[variants]/(main)/resource/features/store/initialState.ts +2 -2
- package/src/app/[variants]/(main)/resource/store/action.ts +2 -2
- package/src/app/[variants]/(main)/resource/store/initialState.ts +2 -2
- package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
- package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
- package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
- package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
- package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
- package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
- package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
- package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
- package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
- package/src/app/robots.tsx +1 -1
- package/src/envs/auth.ts +2 -27
- package/src/envs/llm.ts +2 -2
- package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
- package/src/features/ChatMiniMap/utils.ts +1 -1
- package/src/features/CommandMenu/SearchResults.tsx +1 -1
- package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
- package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
- package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
- package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
- package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +5 -8
- package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
- package/src/features/IntegrationDetailModal/index.tsx +21 -283
- package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
- package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
- package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
- package/src/features/ProfileEditor/AgentTool.tsx +14 -20
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +0 -8
- package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
- package/src/features/ResourceManager/index.tsx +1 -1
- package/src/features/ShareModal/SharePdf/PdfPreview.tsx +4 -4
- package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
- package/src/features/SkillStore/Search/index.tsx +1 -1
- package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
- package/src/features/SkillStore/index.tsx +15 -33
- package/src/features/User/UserPanel/PanelContent.tsx +0 -8
- package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
- package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
- package/src/features/User/__tests__/useMenu.test.tsx +2 -43
- package/src/layout/AuthProvider/index.tsx +0 -5
- package/src/libs/next/config/define-config.ts +20 -15
- package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
- package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
- package/src/libs/next/proxy/define-config.ts +4 -53
- package/src/libs/next-auth/adapter/index.ts +1 -2
- package/src/libs/oidc-provider/provider.test.ts +5 -316
- package/src/libs/pdfjs/pdf.worker.ts +1 -0
- package/src/libs/pdfjs/worker.ts +12 -0
- package/src/libs/trpc/lambda/context.test.ts +0 -13
- package/src/libs/trpc/lambda/context.ts +3 -22
- package/src/libs/trpc/middleware/userAuth.ts +2 -4
- package/src/libs/trusted-client/getSessionUser.ts +2 -17
- package/src/locales/default/error.ts +0 -6
- package/src/locales/default/index.ts +0 -2
- package/src/proxy.ts +0 -1
- package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
- package/src/server/routers/lambda/user.ts +6 -63
- package/src/server/services/changelog/index.test.ts +3 -2
- package/src/server/services/changelog/index.ts +1 -1
- package/src/server/services/user/index.ts +0 -83
- package/src/services/chat/index.ts +1 -2
- package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
- package/src/store/user/slices/auth/action.test.ts +1 -81
- package/src/store/user/slices/auth/action.ts +3 -28
- package/src/store/user/slices/auth/initialState.ts +1 -18
- package/src/store/user/slices/auth/selectors.test.ts +2 -127
- package/src/store/user/slices/auth/selectors.ts +1 -21
- package/src/utils/errorResponse.ts +1 -4
- package/src/utils/markdownToTxt.ts +20 -0
- package/locales/ar/clerk.json +0 -545
- package/locales/bg-BG/clerk.json +0 -545
- package/locales/de-DE/clerk.json +0 -545
- package/locales/en-US/clerk.json +0 -545
- package/locales/es-ES/clerk.json +0 -545
- package/locales/fa-IR/clerk.json +0 -545
- package/locales/fr-FR/clerk.json +0 -545
- package/locales/it-IT/clerk.json +0 -545
- package/locales/ja-JP/clerk.json +0 -545
- package/locales/ko-KR/clerk.json +0 -545
- package/locales/nl-NL/clerk.json +0 -545
- package/locales/pl-PL/clerk.json +0 -545
- package/locales/pt-BR/clerk.json +0 -545
- package/locales/ru-RU/clerk.json +0 -545
- package/locales/tr-TR/clerk.json +0 -545
- package/locales/vi-VN/clerk.json +0 -545
- package/locales/zh-CN/clerk.json +0 -545
- package/locales/zh-TW/clerk.json +0 -545
- package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
- package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
- package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
- package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
- package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
- package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
- package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
- package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
- package/src/libs/clerk-auth/index.test.ts +0 -216
- package/src/libs/clerk-auth/index.ts +0 -80
- package/src/locales/default/clerk.ts +0 -677
- package/src/server/services/user/index.test.ts +0 -220
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
dataSelectors,
|
|
7
|
+
messageStateSelectors,
|
|
8
|
+
useConversationStore,
|
|
9
|
+
virtuaListSelectors,
|
|
10
|
+
} from '../../../store';
|
|
11
|
+
import BackBottom from '../BackBottom';
|
|
12
|
+
import { AT_BOTTOM_THRESHOLD, OPEN_DEV_INSPECTOR } from './DebugInspector';
|
|
13
|
+
|
|
14
|
+
const AutoScroll = memo(() => {
|
|
15
|
+
const atBottom = useConversationStore(virtuaListSelectors.atBottom);
|
|
16
|
+
const isScrolling = useConversationStore(virtuaListSelectors.isScrolling);
|
|
17
|
+
const isGenerating = useConversationStore(messageStateSelectors.isAIGenerating);
|
|
18
|
+
const scrollToBottom = useConversationStore((s) => s.scrollToBottom);
|
|
19
|
+
const dbMessages = useConversationStore(dataSelectors.dbMessages);
|
|
20
|
+
|
|
21
|
+
const shouldAutoScroll = atBottom && isGenerating && !isScrolling;
|
|
22
|
+
|
|
23
|
+
// 获取最后一条消息的 content 长度,用于监听流式输出
|
|
24
|
+
const lastMessage = dbMessages.at(-1);
|
|
25
|
+
const lastMessageContentLength =
|
|
26
|
+
typeof lastMessage?.content === 'string' ? lastMessage.content.length : 0;
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (shouldAutoScroll) {
|
|
30
|
+
scrollToBottom(false);
|
|
31
|
+
}
|
|
32
|
+
}, [shouldAutoScroll, scrollToBottom, dbMessages.length, lastMessageContentLength]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div style={{ position: 'relative', width: '100%' }}>
|
|
36
|
+
{OPEN_DEV_INSPECTOR && (
|
|
37
|
+
<>
|
|
38
|
+
{/* Threshold 区域顶部边界线 */}
|
|
39
|
+
<div
|
|
40
|
+
style={{
|
|
41
|
+
background: atBottom ? '#22c55e' : '#ef4444',
|
|
42
|
+
height: 2,
|
|
43
|
+
left: 0,
|
|
44
|
+
opacity: 0.5,
|
|
45
|
+
pointerEvents: 'none',
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
right: 0,
|
|
48
|
+
top: -AT_BOTTOM_THRESHOLD,
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
{/* Threshold 区域 mask - 显示在指示线上方 */}
|
|
53
|
+
<div
|
|
54
|
+
style={{
|
|
55
|
+
background: atBottom
|
|
56
|
+
? 'linear-gradient(to top, rgba(34, 197, 94, 0.15), transparent)'
|
|
57
|
+
: 'linear-gradient(to top, rgba(239, 68, 68, 0.1), transparent)',
|
|
58
|
+
height: AT_BOTTOM_THRESHOLD,
|
|
59
|
+
left: 0,
|
|
60
|
+
pointerEvents: 'none',
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
right: 0,
|
|
63
|
+
top: -AT_BOTTOM_THRESHOLD,
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
{/* AutoScroll 位置指示线(底部) */}
|
|
68
|
+
<div
|
|
69
|
+
style={{
|
|
70
|
+
background: atBottom ? '#22c55e' : '#ef4444',
|
|
71
|
+
height: 2,
|
|
72
|
+
position: 'relative',
|
|
73
|
+
width: '100%',
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
</>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
<BackBottom onScrollToBottom={() => scrollToBottom(true)} visible={!atBottom} />
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
AutoScroll.displayName = 'ConversationAutoScroll';
|
|
85
|
+
|
|
86
|
+
export default AutoScroll;
|
|
@@ -7,6 +7,10 @@ import { VList, type VListHandle } from 'virtua';
|
|
|
7
7
|
import WideScreenContainer from '../../../WideScreenContainer';
|
|
8
8
|
import { useConversationStore, virtuaListSelectors } from '../../store';
|
|
9
9
|
import AutoScroll from './AutoScroll';
|
|
10
|
+
import DebugInspector, {
|
|
11
|
+
AT_BOTTOM_THRESHOLD,
|
|
12
|
+
OPEN_DEV_INSPECTOR,
|
|
13
|
+
} from './AutoScroll/DebugInspector';
|
|
10
14
|
|
|
11
15
|
interface VirtualizedListProps {
|
|
12
16
|
dataSource: string[];
|
|
@@ -23,15 +27,11 @@ const VirtualizedList = memo<VirtualizedListProps>(({ dataSource, itemContent })
|
|
|
23
27
|
const prevDataLengthRef = useRef(dataSource.length);
|
|
24
28
|
const scrollEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
25
29
|
|
|
26
|
-
const atBottomThreshold = 200;
|
|
27
|
-
|
|
28
30
|
// Store actions
|
|
29
31
|
const registerVirtuaScrollMethods = useConversationStore((s) => s.registerVirtuaScrollMethods);
|
|
30
32
|
const setScrollState = useConversationStore((s) => s.setScrollState);
|
|
31
33
|
const resetVisibleItems = useConversationStore((s) => s.resetVisibleItems);
|
|
32
|
-
const scrollToBottom = useConversationStore((s) => s.scrollToBottom);
|
|
33
34
|
const setActiveIndex = useConversationStore((s) => s.setActiveIndex);
|
|
34
|
-
const atBottom = useConversationStore(virtuaListSelectors.atBottom);
|
|
35
35
|
const activeIndex = useConversationStore(virtuaListSelectors.activeIndex);
|
|
36
36
|
|
|
37
37
|
// Check if at bottom based on scroll position
|
|
@@ -43,8 +43,8 @@ const VirtualizedList = memo<VirtualizedListProps>(({ dataSource, itemContent })
|
|
|
43
43
|
const scrollSize = ref.scrollSize;
|
|
44
44
|
const viewportSize = ref.viewportSize;
|
|
45
45
|
|
|
46
|
-
return scrollSize - scrollOffset - viewportSize <=
|
|
47
|
-
}, [
|
|
46
|
+
return scrollSize - scrollOffset - viewportSize <= AT_BOTTOM_THRESHOLD;
|
|
47
|
+
}, [AT_BOTTOM_THRESHOLD]);
|
|
48
48
|
|
|
49
49
|
// Handle scroll events
|
|
50
50
|
const handleScroll = useCallback(() => {
|
|
@@ -131,6 +131,8 @@ const VirtualizedList = memo<VirtualizedListProps>(({ dataSource, itemContent })
|
|
|
131
131
|
|
|
132
132
|
return (
|
|
133
133
|
<>
|
|
134
|
+
{/* Debug Inspector - 放在 VList 外面,不会被虚拟列表回收 */}
|
|
135
|
+
{OPEN_DEV_INSPECTOR && <DebugInspector />}
|
|
134
136
|
<VList
|
|
135
137
|
bufferSize={typeof window !== 'undefined' ? window.innerHeight : 0}
|
|
136
138
|
data={dataSource}
|
|
@@ -142,12 +144,14 @@ const VirtualizedList = memo<VirtualizedListProps>(({ dataSource, itemContent })
|
|
|
142
144
|
{(messageId, index): ReactElement => {
|
|
143
145
|
const isAgentCouncil = messageId.includes('agentCouncil');
|
|
144
146
|
const content = itemContent(index, messageId);
|
|
147
|
+
const isLast = index === dataSource.length - 1;
|
|
145
148
|
|
|
146
149
|
if (isAgentCouncil) {
|
|
147
150
|
// AgentCouncil needs full width for horizontal scroll
|
|
148
151
|
return (
|
|
149
152
|
<div key={messageId} style={{ position: 'relative', width: '100%' }}>
|
|
150
153
|
{content}
|
|
154
|
+
{isLast && <AutoScroll />}
|
|
151
155
|
</div>
|
|
152
156
|
);
|
|
153
157
|
}
|
|
@@ -155,21 +159,11 @@ const VirtualizedList = memo<VirtualizedListProps>(({ dataSource, itemContent })
|
|
|
155
159
|
return (
|
|
156
160
|
<WideScreenContainer key={messageId} style={{ position: 'relative' }}>
|
|
157
161
|
{content}
|
|
162
|
+
{isLast && <AutoScroll />}
|
|
158
163
|
</WideScreenContainer>
|
|
159
164
|
);
|
|
160
165
|
}}
|
|
161
166
|
</VList>
|
|
162
|
-
<WideScreenContainer
|
|
163
|
-
onChange={() => {
|
|
164
|
-
if (!atBottom) return;
|
|
165
|
-
setTimeout(() => scrollToBottom(true), 100);
|
|
166
|
-
}}
|
|
167
|
-
style={{
|
|
168
|
-
position: 'relative',
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
<AutoScroll />
|
|
172
|
-
</WideScreenContainer>
|
|
173
167
|
</>
|
|
174
168
|
);
|
|
175
169
|
}, isEqual);
|
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
import { ScrollShadow } from '@lobehub/ui';
|
|
2
|
-
import { type PropsWithChildren, type RefObject, memo, useEffect
|
|
2
|
+
import { type PropsWithChildren, type RefObject, memo, useEffect } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
4
|
+
import { useAutoScroll } from '@/hooks/useAutoScroll';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
interface AutoScrollShadowProps extends PropsWithChildren {
|
|
7
|
+
/**
|
|
8
|
+
* Content string to track for auto-scrolling
|
|
9
|
+
*/
|
|
10
|
+
content?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the content is currently streaming/generating
|
|
13
|
+
*/
|
|
14
|
+
streaming?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const AutoScrollShadow = memo<AutoScrollShadowProps>(({ children, content, streaming }) => {
|
|
18
|
+
const { ref, handleScroll, resetScrollLock } = useAutoScroll<HTMLDivElement>({
|
|
19
|
+
deps: [content],
|
|
20
|
+
enabled: streaming,
|
|
21
|
+
});
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
// Reset scroll lock when content is cleared (new stream starts)
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!content) {
|
|
26
|
+
resetScrollLock();
|
|
17
27
|
}
|
|
18
|
-
}, []);
|
|
28
|
+
}, [content, resetScrollLock]);
|
|
19
29
|
|
|
20
30
|
return (
|
|
21
31
|
<ScrollShadow
|
|
22
32
|
height={'max(33vh, 480px)'}
|
|
23
33
|
hideScrollBar
|
|
24
|
-
|
|
34
|
+
onScroll={handleScroll}
|
|
35
|
+
ref={ref as RefObject<HTMLDivElement>}
|
|
25
36
|
size={16}
|
|
26
37
|
>
|
|
27
38
|
{children}
|
|
@@ -92,7 +92,7 @@ const CouncilMember = memo<CouncilMemberProps>(({ item, index }) => {
|
|
|
92
92
|
showTitle
|
|
93
93
|
time={createdAt}
|
|
94
94
|
>
|
|
95
|
-
<AutoScrollShadow>
|
|
95
|
+
<AutoScrollShadow content={content} streaming={generating}>
|
|
96
96
|
<MessageContent {...item} />
|
|
97
97
|
</AutoScrollShadow>
|
|
98
98
|
</ChatItem>
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Flexbox } from '@lobehub/ui';
|
|
4
|
-
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
|
5
4
|
import { Fragment, memo, useCallback, useState } from 'react';
|
|
6
5
|
import { Document, Page, pdfjs } from 'react-pdf';
|
|
7
|
-
import 'react-pdf/dist/
|
|
8
|
-
import 'react-pdf/dist/
|
|
6
|
+
import 'react-pdf/dist/Page/AnnotationLayer.css';
|
|
7
|
+
import 'react-pdf/dist/Page/TextLayer.css';
|
|
9
8
|
|
|
10
9
|
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
10
|
+
import '@/libs/pdfjs/worker';
|
|
11
11
|
import { lambdaQuery } from '@/libs/trpc/client';
|
|
12
12
|
|
|
13
13
|
import HighlightLayer from './HighlightLayer';
|
|
14
14
|
import { styles } from './style';
|
|
15
15
|
import useResizeObserver from './useResizeObserver';
|
|
16
16
|
|
|
17
|
-
// 如果海外的地址: https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs
|
|
18
|
-
pdfjs.GlobalWorkerOptions.workerSrc = `https://registry.npmmirror.com/pdfjs-dist/${pdfjs.version}/files/build/pdf.worker.min.mjs`;
|
|
19
|
-
|
|
20
17
|
const options = {
|
|
21
18
|
cMapUrl: `https://registry.npmmirror.com/pdfjs-dist/${pdfjs.version}/files/cmaps/`,
|
|
22
19
|
standardFontDataUrl: `https://registry.npmmirror.com/pdfjs-dist/${pdfjs.version}/files/standard_fonts/`,
|
|
@@ -46,8 +43,8 @@ const PDFViewer = memo<PDFViewerProps>(({ url, fileId }) => {
|
|
|
46
43
|
|
|
47
44
|
useResizeObserver(containerRef, onResize);
|
|
48
45
|
|
|
49
|
-
const onDocumentLoadSuccess = (
|
|
50
|
-
setNumPages(
|
|
46
|
+
const onDocumentLoadSuccess = (document: unknown) => {
|
|
47
|
+
setNumPages((document as { numPages: number }).numPages);
|
|
51
48
|
setIsLoaded(true);
|
|
52
49
|
};
|
|
53
50
|
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type KlavisServerType,
|
|
5
|
+
type LobehubSkillProviderType,
|
|
6
|
+
getKlavisServerByServerIdentifier,
|
|
7
|
+
getLobehubSkillProviderById,
|
|
8
|
+
} from '@lobechat/const';
|
|
9
|
+
import { Flexbox, Icon, Image, Tag, Text, Typography, useModalContext } from '@lobehub/ui';
|
|
10
|
+
import { Button, Divider } from 'antd';
|
|
11
|
+
import { createStyles, cssVar } from 'antd-style';
|
|
12
|
+
import type { Klavis } from 'klavis';
|
|
13
|
+
import { ExternalLink, Loader2, SquareArrowOutUpRight } from 'lucide-react';
|
|
14
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
15
|
+
import { useTranslation } from 'react-i18next';
|
|
16
|
+
|
|
17
|
+
import { useSkillConnect } from '@/features/SkillStore/LobeHubList/useSkillConnect';
|
|
18
|
+
import { useToolStore } from '@/store/tool';
|
|
19
|
+
import { klavisStoreSelectors, lobehubSkillStoreSelectors } from '@/store/tool/selectors';
|
|
20
|
+
import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
|
|
21
|
+
import { LobehubSkillStatus } from '@/store/tool/slices/lobehubSkillStore/types';
|
|
22
|
+
|
|
23
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
24
|
+
authorLink: css`
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
gap: 4px;
|
|
29
|
+
align-items: center;
|
|
30
|
+
|
|
31
|
+
color: ${token.colorPrimary};
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
text-decoration: underline;
|
|
35
|
+
}
|
|
36
|
+
`,
|
|
37
|
+
detailItem: css`
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
gap: 4px;
|
|
41
|
+
`,
|
|
42
|
+
detailLabel: css`
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
color: ${token.colorTextTertiary};
|
|
45
|
+
`,
|
|
46
|
+
header: css`
|
|
47
|
+
display: flex;
|
|
48
|
+
gap: 16px;
|
|
49
|
+
align-items: center;
|
|
50
|
+
|
|
51
|
+
padding: 16px;
|
|
52
|
+
border-radius: 12px;
|
|
53
|
+
|
|
54
|
+
background: ${token.colorFillTertiary};
|
|
55
|
+
`,
|
|
56
|
+
icon: css`
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
align-items: center;
|
|
60
|
+
justify-content: center;
|
|
61
|
+
|
|
62
|
+
width: 56px;
|
|
63
|
+
height: 56px;
|
|
64
|
+
border-radius: 12px;
|
|
65
|
+
|
|
66
|
+
background: ${token.colorBgContainer};
|
|
67
|
+
`,
|
|
68
|
+
introduction: css`
|
|
69
|
+
font-size: 14px;
|
|
70
|
+
line-height: 1.8;
|
|
71
|
+
color: ${token.colorText};
|
|
72
|
+
`,
|
|
73
|
+
sectionTitle: css`
|
|
74
|
+
font-size: 14px;
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
color: ${token.colorText};
|
|
77
|
+
`,
|
|
78
|
+
title: css`
|
|
79
|
+
font-size: 18px;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
color: ${token.colorText};
|
|
82
|
+
`,
|
|
83
|
+
toolTag: css`
|
|
84
|
+
font-family: ${token.fontFamilyCode};
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
`,
|
|
87
|
+
toolsContainer: css`
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-wrap: wrap;
|
|
90
|
+
gap: 8px;
|
|
91
|
+
`,
|
|
92
|
+
trustWarning: css`
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
line-height: 1.6;
|
|
95
|
+
color: ${token.colorTextTertiary};
|
|
96
|
+
`,
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
export type IntegrationType = 'klavis' | 'lobehub';
|
|
100
|
+
|
|
101
|
+
export interface IntegrationDetailContentProps {
|
|
102
|
+
identifier: string;
|
|
103
|
+
serverName?: Klavis.McpServerName;
|
|
104
|
+
type: IntegrationType;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const IntegrationDetailContent = ({
|
|
108
|
+
type,
|
|
109
|
+
identifier,
|
|
110
|
+
serverName,
|
|
111
|
+
}: IntegrationDetailContentProps) => {
|
|
112
|
+
const { styles } = useStyles();
|
|
113
|
+
const { t } = useTranslation(['plugin', 'setting']);
|
|
114
|
+
const { close } = useModalContext();
|
|
115
|
+
|
|
116
|
+
const {
|
|
117
|
+
handleConnect,
|
|
118
|
+
isConnecting,
|
|
119
|
+
isConnected: hookIsConnected,
|
|
120
|
+
} = useSkillConnect({
|
|
121
|
+
identifier,
|
|
122
|
+
serverName,
|
|
123
|
+
type,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const hasTriggeredConnectRef = useRef(false);
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (hasTriggeredConnectRef.current && hookIsConnected) {
|
|
130
|
+
close();
|
|
131
|
+
}
|
|
132
|
+
}, [hookIsConnected, close]);
|
|
133
|
+
|
|
134
|
+
const handleConnectWithTracking = async () => {
|
|
135
|
+
hasTriggeredConnectRef.current = true;
|
|
136
|
+
await handleConnect();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const config = useMemo((): KlavisServerType | LobehubSkillProviderType | undefined => {
|
|
140
|
+
if (type === 'klavis') {
|
|
141
|
+
return getKlavisServerByServerIdentifier(identifier);
|
|
142
|
+
}
|
|
143
|
+
return getLobehubSkillProviderById(identifier);
|
|
144
|
+
}, [type, identifier]);
|
|
145
|
+
|
|
146
|
+
const klavisServers = useToolStore(klavisStoreSelectors.getServers);
|
|
147
|
+
const lobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers);
|
|
148
|
+
|
|
149
|
+
const serverState = useMemo(() => {
|
|
150
|
+
if (type === 'klavis') {
|
|
151
|
+
return klavisServers.find((s) => s.identifier === identifier);
|
|
152
|
+
}
|
|
153
|
+
return lobehubSkillServers.find((s) => s.identifier === identifier);
|
|
154
|
+
}, [type, identifier, klavisServers, lobehubSkillServers]);
|
|
155
|
+
|
|
156
|
+
const isConnected = useMemo(() => {
|
|
157
|
+
if (!serverState) return false;
|
|
158
|
+
if (type === 'klavis') {
|
|
159
|
+
return serverState.status === KlavisServerStatus.CONNECTED;
|
|
160
|
+
}
|
|
161
|
+
return serverState.status === LobehubSkillStatus.CONNECTED;
|
|
162
|
+
}, [type, serverState]);
|
|
163
|
+
|
|
164
|
+
const tools = useMemo(() => {
|
|
165
|
+
return serverState?.tools?.map((tool) => tool.name) || [];
|
|
166
|
+
}, [serverState]);
|
|
167
|
+
|
|
168
|
+
if (!config) return null;
|
|
169
|
+
|
|
170
|
+
const { author, authorUrl, description, icon, introduction, label } = config;
|
|
171
|
+
|
|
172
|
+
const i18nIdentifier =
|
|
173
|
+
type === 'klavis'
|
|
174
|
+
? (config as KlavisServerType).identifier
|
|
175
|
+
: (config as LobehubSkillProviderType).id;
|
|
176
|
+
const i18nPrefix = type === 'klavis' ? 'tools.klavis.servers' : 'tools.lobehubSkill.providers';
|
|
177
|
+
|
|
178
|
+
const localizedDescription = t(`${i18nPrefix}.${i18nIdentifier}.description`, {
|
|
179
|
+
defaultValue: description,
|
|
180
|
+
ns: 'setting',
|
|
181
|
+
});
|
|
182
|
+
const localizedIntroduction = t(`${i18nPrefix}.${i18nIdentifier}.introduction`, {
|
|
183
|
+
defaultValue: introduction,
|
|
184
|
+
ns: 'setting',
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const renderIcon = () => {
|
|
188
|
+
if (typeof icon === 'string') {
|
|
189
|
+
return <Image alt={label} height={36} src={icon} width={36} />;
|
|
190
|
+
}
|
|
191
|
+
return <Icon fill={cssVar.colorText} icon={icon} size={36} />;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const handleAuthorClick = () => {
|
|
195
|
+
if (authorUrl) {
|
|
196
|
+
window.open(authorUrl, '_blank', 'noopener,noreferrer');
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const renderConnectButton = () => {
|
|
201
|
+
if (isConnected) return null;
|
|
202
|
+
|
|
203
|
+
if (isConnecting) {
|
|
204
|
+
return (
|
|
205
|
+
<Button disabled icon={<Icon icon={Loader2} spin />} type="default">
|
|
206
|
+
{t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
|
|
207
|
+
</Button>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<Button
|
|
213
|
+
icon={<Icon icon={SquareArrowOutUpRight} />}
|
|
214
|
+
onClick={handleConnectWithTracking}
|
|
215
|
+
type="primary"
|
|
216
|
+
>
|
|
217
|
+
{t('tools.klavis.connect', { defaultValue: 'Connect', ns: 'setting' })}
|
|
218
|
+
</Button>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<Flexbox gap={20}>
|
|
224
|
+
{/* Header */}
|
|
225
|
+
<Flexbox
|
|
226
|
+
align="center"
|
|
227
|
+
className={styles.header}
|
|
228
|
+
horizontal
|
|
229
|
+
justify="space-between"
|
|
230
|
+
style={{ flexWrap: 'nowrap' }}
|
|
231
|
+
>
|
|
232
|
+
<Flexbox align="center" gap={16} horizontal>
|
|
233
|
+
<div className={styles.icon}>{renderIcon()}</div>
|
|
234
|
+
<Flexbox gap={4}>
|
|
235
|
+
<span className={styles.title}>{label}</span>
|
|
236
|
+
<Text style={{ fontSize: 14 }} type="secondary">
|
|
237
|
+
{localizedDescription}
|
|
238
|
+
</Text>
|
|
239
|
+
</Flexbox>
|
|
240
|
+
</Flexbox>
|
|
241
|
+
{renderConnectButton()}
|
|
242
|
+
</Flexbox>
|
|
243
|
+
|
|
244
|
+
{/* Introduction */}
|
|
245
|
+
<Typography className={styles.introduction}>{localizedIntroduction}</Typography>
|
|
246
|
+
|
|
247
|
+
{/* Developed by */}
|
|
248
|
+
<Flexbox gap={8}>
|
|
249
|
+
<Flexbox align="center" gap={4} horizontal>
|
|
250
|
+
<span className={styles.sectionTitle}>{t('integrationDetail.developedBy')}</span>
|
|
251
|
+
<span
|
|
252
|
+
className={styles.authorLink}
|
|
253
|
+
onClick={handleAuthorClick}
|
|
254
|
+
style={{ cursor: authorUrl ? 'pointer' : 'default' }}
|
|
255
|
+
>
|
|
256
|
+
{author}
|
|
257
|
+
{authorUrl && <Icon icon={ExternalLink} size={12} />}
|
|
258
|
+
</span>
|
|
259
|
+
</Flexbox>
|
|
260
|
+
<Text className={styles.trustWarning} type="secondary">
|
|
261
|
+
{t('integrationDetail.trustWarning')}
|
|
262
|
+
</Text>
|
|
263
|
+
</Flexbox>
|
|
264
|
+
|
|
265
|
+
{/* Tools */}
|
|
266
|
+
{tools.length > 0 && (
|
|
267
|
+
<>
|
|
268
|
+
<Divider style={{ margin: 0 }} />
|
|
269
|
+
<Flexbox gap={12}>
|
|
270
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
271
|
+
<span className={styles.sectionTitle}>{t('integrationDetail.tools')}</span>
|
|
272
|
+
<Tag>{tools.length}</Tag>
|
|
273
|
+
</Flexbox>
|
|
274
|
+
<div className={styles.toolsContainer}>
|
|
275
|
+
{tools.map((tool) => (
|
|
276
|
+
<Tag className={styles.toolTag} key={tool}>
|
|
277
|
+
{tool}
|
|
278
|
+
</Tag>
|
|
279
|
+
))}
|
|
280
|
+
</div>
|
|
281
|
+
</Flexbox>
|
|
282
|
+
</>
|
|
283
|
+
)}
|
|
284
|
+
|
|
285
|
+
{/* Details */}
|
|
286
|
+
<Divider style={{ margin: 0 }} />
|
|
287
|
+
<Flexbox gap={12}>
|
|
288
|
+
<span className={styles.sectionTitle}>{t('integrationDetail.details')}</span>
|
|
289
|
+
<Flexbox gap={16} horizontal>
|
|
290
|
+
<div className={styles.detailItem}>
|
|
291
|
+
<span className={styles.detailLabel}>{t('integrationDetail.author')}</span>
|
|
292
|
+
<span
|
|
293
|
+
className={styles.authorLink}
|
|
294
|
+
onClick={handleAuthorClick}
|
|
295
|
+
style={{ cursor: authorUrl ? 'pointer' : 'default' }}
|
|
296
|
+
>
|
|
297
|
+
{author}
|
|
298
|
+
{authorUrl && <Icon icon={ExternalLink} size={12} />}
|
|
299
|
+
</span>
|
|
300
|
+
</div>
|
|
301
|
+
</Flexbox>
|
|
302
|
+
</Flexbox>
|
|
303
|
+
</Flexbox>
|
|
304
|
+
);
|
|
305
|
+
};
|