feat: Add auto scrolling to CodeMarkdown (artifacts)

This commit is contained in:
Danny Avila 2024-08-24 23:26:57 -04:00
parent efbed07569
commit 7b20679463
2 changed files with 68 additions and 28 deletions

View file

@ -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}
/>
</Tabs.Content>
<Tabs.Content value="preview" className="flex-grow overflow-auto bg-white">

View file

@ -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 <code className={`hljs language-${lang} !whitespace-pre`}>{children}</code>;
});
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<HTMLDivElement>(null);
const [userScrolled, setUserScrolled] = useState(false);
const currentContent = content;
const rehypePlugins = [
[rehypeKatex, { output: 'mathml' }],
[
rehypeHighlight,
{
detect: true,
ignoreMissing: true,
subset: langSubset,
},
],
];
return (
<ReactMarkdown
/* @ts-ignore */
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
{ code } as {
[key: string]: React.ElementType;
}
useEffect(() => {
const scrollContainer = scrollRef.current;
if (!scrollContainer) {
return;
}
>
{currentContent}
</ReactMarkdown>
);
});
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 (
<div ref={scrollRef} className="max-h-full overflow-y-auto">
<ReactMarkdown
/* @ts-ignore */
rehypePlugins={rehypePlugins}
components={
{ code } as {
[key: string]: React.ElementType;
}
}
>
{currentContent}
</ReactMarkdown>
</div>
);
},
);
export const CopyCodeButton: React.FC<{ content: string }> = ({ content }) => {
const localize = useLocalize();