From ed97b30d51d9ba40571fa984a56fe533d1dccac8 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Tue, 2 Jun 2026 21:19:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(chat):=20=E8=81=8A=E5=A4=A9=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E6=9D=A1=E7=BE=8E=E5=8C=96=20+=20Markdown=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=20=E2=80=94=20OverlayScrollbars/CodeHighlighter/?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=A4=8D=E5=88=B6/=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lock | 6 + package.json | 2 + .../workbench/components/chat/ChatPanel.tsx | 20 +--- .../components/chat/ChatScrollArea.tsx | 58 +++++++++ .../chat/parts/CodeBlockWithCopy.tsx | 57 +++++++++ .../components/chat/parts/TextPart.tsx | 15 ++- .../components/chat/use-chat-scroll.ts | 37 ++++-- src/web/styles.css | 46 +++++-- .../chat/CodeBlockWithCopy.test.tsx | 99 +++++++++++++++ tests/web/hooks/use-chat-scroll.test.ts | 113 +++++++++++++----- 10 files changed, 385 insertions(+), 68 deletions(-) create mode 100644 src/web/consoles/workbench/components/chat/ChatScrollArea.tsx create mode 100644 src/web/consoles/workbench/components/chat/parts/CodeBlockWithCopy.tsx create mode 100644 tests/web/components/chat/CodeBlockWithCopy.test.tsx diff --git a/bun.lock b/bun.lock index 45b159c..4f617f5 100644 --- a/bun.lock +++ b/bun.lock @@ -19,6 +19,8 @@ "antd": "^6.4.3", "drizzle-orm": "^0.45.2", "es-toolkit": "^1.47.0", + "overlayscrollbars": "^2.16.0", + "overlayscrollbars-react": "^0.5.6", "pino": "^10.3.1", "pino-pretty": "^13.1.3", "pino-roll": "^4.0.0", @@ -1259,6 +1261,10 @@ "optionator": ["optionator@0.9.4", "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "overlayscrollbars": ["overlayscrollbars@2.16.0", "", {}, "sha512-N03oje/q7j93D0aLZtoCdsDSYLmhheSsv8H7oSLE7HhdV9P/bmCURtLV/KbPye7P/bpfyt/obSfDpGUYoJ0OWg=="], + + "overlayscrollbars-react": ["overlayscrollbars-react@0.5.6", "", { "peerDependencies": { "overlayscrollbars": "^2.0.0", "react": ">=16.8.0" } }, "sha512-E5To04bL5brn9GVCZ36SnfGanxa2I2MDkWoa4Cjo5wol7l+diAgi4DBc983V7l2nOk/OLJ6Feg4kySspQEGDBw=="], + "own-keys": ["own-keys@1.0.1", "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], "p-limit": ["p-limit@3.1.0", "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], diff --git a/package.json b/package.json index 4e1d231..385aee4 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,8 @@ "antd": "^6.4.3", "drizzle-orm": "^0.45.2", "es-toolkit": "^1.47.0", + "overlayscrollbars": "^2.16.0", + "overlayscrollbars-react": "^0.5.6", "pino": "^10.3.1", "pino-pretty": "^13.1.3", "pino-roll": "^4.0.0", diff --git a/src/web/consoles/workbench/components/chat/ChatPanel.tsx b/src/web/consoles/workbench/components/chat/ChatPanel.tsx index bdba0f5..79a4ee7 100644 --- a/src/web/consoles/workbench/components/chat/ChatPanel.tsx +++ b/src/web/consoles/workbench/components/chat/ChatPanel.tsx @@ -1,5 +1,5 @@ import { useChat } from "@ai-sdk/react"; -import { ArrowDownOutlined, CopyOutlined, EditOutlined, RedoOutlined, RobotOutlined } from "@ant-design/icons"; +import { CopyOutlined, EditOutlined, RedoOutlined, RobotOutlined } from "@ant-design/icons"; import { Sender } from "@ant-design/x"; import { useQueryClient } from "@tanstack/react-query"; import { DefaultChatTransport, type UIMessage } from "ai"; @@ -14,10 +14,10 @@ import { } from "../../../../hooks/use-conversations"; import { useLogger } from "../../../../hooks/use-logger"; import { useModelList } from "../../../../hooks/use-models"; +import { ChatScrollArea } from "./ChatScrollArea"; import { ReasoningPart } from "./parts/ReasoningPart"; import { TextPart } from "./parts/TextPart"; import { ToolPart } from "./parts/ToolPart"; -import { useChatScroll } from "./use-chat-scroll"; interface ChatPanelProps { conversationId: null | string; @@ -35,7 +35,6 @@ export function ChatPanel({ conversationId, onConversationCreated, projectId }: const [loadingHistory, setLoadingHistory] = useState(false); const [selectedModelId, setSelectedModelId] = useState(null); const fetchRef = useRef(fetchMessages); - const scrollRef = useRef(null); const skipHistoryLoadRef = useRef(null); const { data: modelsData } = useModelList({ pageSize: 200 }); @@ -56,8 +55,6 @@ export function ChatPanel({ conversationId, onConversationCreated, projectId }: const isLoading = status === "submitted" || status === "streaming"; - const { isAtBottom, scrollToBottom } = useChatScroll({ messages, scrollRef, status }); - useEffect(() => { if (!conversationId) { setMessages([]); @@ -348,7 +345,7 @@ export function ChatPanel({ conversationId, onConversationCreated, projectId }: ) : ( -
+ {messages.map((msg, idx) => ( )} -
- )} - {!isAtBottom && ( -