diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index e5f12c3..bf40ea3 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -24,7 +24,6 @@ "react-text-truncate": "^0.19.0", "react-textarea-autosize": "^8.5.3", "react-use": "^17.5.1", - "react-virtuoso": "^4.7.13", "rewire": "^7.0.0", "typescript": "^4.9.5", "web-vitals": "^2.1.4" @@ -17517,18 +17516,6 @@ "react-dom": "*" } }, - "node_modules/react-virtuoso": { - "version": "4.7.13", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.7.13.tgz", - "integrity": "sha512-rabPhipwJ8rdA6TDk1vdVqVoU6eOkWukqoC1pNQVBCsvjBvIeJMi9nO079s0L7EsRzAxFFQNahX+8vuuY4F1Qg==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16 || >=17 || >= 18", - "react-dom": ">=16 || >=17 || >= 18" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 3b9733b..09530aa 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -19,7 +19,6 @@ "react-text-truncate": "^0.19.0", "react-textarea-autosize": "^8.5.3", "react-use": "^17.5.1", - "react-virtuoso": "^4.7.13", "rewire": "^7.0.0", "typescript": "^4.9.5", "web-vitals": "^2.1.4" diff --git a/webview-ui/src/components/ChatView.tsx b/webview-ui/src/components/ChatView.tsx index 646e115..ed0cad1 100644 --- a/webview-ui/src/components/ChatView.tsx +++ b/webview-ui/src/components/ChatView.tsx @@ -11,7 +11,6 @@ import { getSyntaxHighlighterStyleFromTheme } from "../utilities/getSyntaxHighli import { vscode } from "../utilities/vscode" import ChatRow from "./ChatRow" import TaskHeader from "./TaskHeader" -import { Virtuoso, type VirtuosoHandle } from "react-virtuoso" import Announcement from "./Announcement" interface ChatViewProps { @@ -43,8 +42,7 @@ const ChatView = ({ messages, isHidden, vscodeThemeName, showAnnouncement, hideA const [syntaxHighlighterStyle, setSyntaxHighlighterStyle] = useState(vsDarkPlus) - const virtuosoRef = useRef(null) - + const chatContainerRef = useRef(null) const [expandedRows, setExpandedRows] = useState>({}) const toggleRowExpansion = (ts: number) => { @@ -314,16 +312,23 @@ const ChatView = ({ messages, isHidden, vscodeThemeName, showAnnouncement, hideA }, [modifiedMessages]) useEffect(() => { - // We use a setTimeout to ensure new content is rendered before scrolling to the bottom. virtuoso's followOutput would scroll to the bottom before the new content could render. const timer = setTimeout(() => { - // TODO: we can use virtuoso's isAtBottom to prevent scrolling if user is scrolled up, and show a 'scroll to bottom' button for better UX - // NOTE: scroll to bottom may not work if you use margin, see virtuoso's troubleshooting - virtuosoRef.current?.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: "smooth" }) - }, 50) + scrollToBottom() + }, 0) return () => clearTimeout(timer) }, [visibleMessages]) + const scrollToBottom = (instant: boolean = false) => { + if (chatContainerRef.current) { + const scrollOptions: ScrollToOptions = { + top: chatContainerRef.current.scrollHeight, + behavior: instant ? "auto" : "smooth", + } + chatContainerRef.current.scrollTo(scrollOptions) + } + } + const placeholderText = useMemo(() => { if (messages.at(-1)?.ask === "command_output") { return "Type input to command stdin..." @@ -371,23 +376,14 @@ const ChatView = ({ messages, isHidden, vscodeThemeName, showAnnouncement, hideA )} - { - // const lastMessage = modifiedMessages.at(-1) - // if (lastMessage && shouldShowChatRow(lastMessage)) { - // return "smooth" - // } - // return false - // }} - increaseViewportBy={{ top: 0, bottom: Number.MAX_SAFE_INTEGER }} // hack to make sure the last message is always rendered to get truly perfect scroll to bottom animation when new messages are added (Number.MAX_SAFE_INTEGER is safe for arithmetic operations, which is all virtuoso uses this value for in src/sizeRangeSystem.ts) - data={visibleMessages} // messages is the raw format returned by extension, modifiedMessages is the manipulated structure that combines certain messages of related type, and visibleMessages is the filtered structure that removes messages that should not be rendered - itemContent={(index, message) => ( + }}> + {visibleMessages.map((message, index) => ( - )} - /> + ))} +
setInputValue(e.target.value)} onKeyDown={handleKeyDown} - onHeightChange={() => - //virtuosoRef.current?.scrollToIndex({ index: "LAST", align: "end", behavior: "auto" }) - virtuosoRef.current?.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: "auto" }) - } + onHeightChange={() => scrollToBottom(true)} placeholder={placeholderText} maxRows={10} autoFocus={true}