diff --git a/client/src/components/Artifacts/Artifacts.tsx b/client/src/components/Artifacts/Artifacts.tsx index ffb60e4cc3..d0b52e44ac 100644 --- a/client/src/components/Artifacts/Artifacts.tsx +++ b/client/src/components/Artifacts/Artifacts.tsx @@ -9,6 +9,7 @@ export default function Artifacts() { const { isVisible, activeTab, + isSubmitting, setActiveTab, currentIndex, cycleArtifact, @@ -85,6 +86,7 @@ export default function Artifacts() { content={`\`\`\`${getFileExtension(currentArtifact.type)}\n${ currentArtifact.content ?? '' }\`\`\``} + isSubmitting={isSubmitting} /> diff --git a/client/src/components/Artifacts/Code.tsx b/client/src/components/Artifacts/Code.tsx index 5b94bf8580..de92c4c0da 100644 --- a/client/src/components/Artifacts/Code.tsx +++ b/client/src/components/Artifacts/Code.tsx @@ -1,4 +1,4 @@ -import React, { memo, useState } from 'react'; +import React, { memo, useEffect, useRef, useState } from 'react'; import rehypeKatex from 'rehype-katex'; import ReactMarkdown from 'react-markdown'; import rehypeHighlight from 'rehype-highlight'; @@ -29,35 +29,73 @@ export const code: React.ElementType = memo(({ inline, className, children }: TC return {children}; }); -export const CodeMarkdown = memo(({ content = '' }: { content: string }) => { - const currentContent = content; - const rehypePlugins = [ - [rehypeKatex, { output: 'mathml' }], - [ - rehypeHighlight, - { - detect: true, - ignoreMissing: true, - subset: langSubset, - }, - ], - ]; +export const CodeMarkdown = memo( + ({ content = '', isSubmitting }: { content: string; isSubmitting: boolean }) => { + const scrollRef = useRef(null); + const [userScrolled, setUserScrolled] = useState(false); + const currentContent = content; + const rehypePlugins = [ + [rehypeKatex, { output: 'mathml' }], + [ + rehypeHighlight, + { + detect: true, + ignoreMissing: true, + subset: langSubset, + }, + ], + ]; - return ( - { + const scrollContainer = scrollRef.current; + if (!scrollContainer) { + return; } - > - {currentContent} - - ); -}); + + const handleScroll = () => { + const { scrollTop, scrollHeight, clientHeight } = scrollContainer; + const isNearBottom = scrollHeight - scrollTop - clientHeight < 50; + + if (!isNearBottom) { + setUserScrolled(true); + } else { + setUserScrolled(false); + } + }; + + scrollContainer.addEventListener('scroll', handleScroll); + + return () => { + scrollContainer.removeEventListener('scroll', handleScroll); + }; + }, []); + + useEffect(() => { + const scrollContainer = scrollRef.current; + if (!scrollContainer || !isSubmitting || userScrolled) { + return; + } + + scrollContainer.scrollTop = scrollContainer.scrollHeight; + }, [content, isSubmitting, userScrolled]); + + return ( +
+ + {currentContent} + +
+ ); + }, +); export const CopyCodeButton: React.FC<{ content: string }> = ({ content }) => { const localize = useLocalize();