🚀 feat: Artifact Editing & Downloads (#5428)

* refactor: expand container

* chore: bump @codesandbox/sandpack-react to latest

* WIP: first pass, show editor

* feat: implement ArtifactCodeEditor and ArtifactTabs components for enhanced artifact management

* refactor: fileKey

* refactor: auto scrolling code editor and add messageId to artifact

* feat: first pass, editing artifact

* feat: first pass, robust artifact replacement

* fix: robust artifact replacement & re-render when expected

* feat: Download Artifacts

* refactor: improve artifact editing UX

* fix: layout shift of new download button

* fix: enhance missing output checks and logging in StreamRunManager
This commit is contained in:
Danny Avila 2025-01-23 18:19:04 -05:00 committed by GitHub
parent 87383fec27
commit ed57bb4711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1156 additions and 237 deletions

View file

@ -8,9 +8,14 @@ import ReactMarkdown from 'react-markdown';
import rehypeHighlight from 'rehype-highlight';
import remarkDirective from 'remark-directive';
import type { Pluggable } from 'unified';
import {
useToastContext,
ArtifactProvider,
CodeBlockProvider,
useCodeBlockContext,
} from '~/Providers';
import { Artifact, artifactPlugin } from '~/components/Artifacts/Artifact';
import { langSubset, preprocessLaTeX, handleDoubleClick } from '~/utils';
import { useToastContext, CodeBlockProvider, useCodeBlockContext } from '~/Providers';
import CodeBlock from '~/components/Messages/Content/CodeBlock';
import { useFileDownload } from '~/data-provider';
import useLocalize from '~/hooks/useLocalize';
@ -194,27 +199,29 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
: [supersub, remarkGfm, [remarkMath, { singleDollarTextMath: true }]];
return (
<CodeBlockProvider>
<ReactMarkdown
/** @ts-ignore */
remarkPlugins={remarkPlugins}
/* @ts-ignore */
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
{
code,
a,
p,
artifact: Artifact,
} as {
[nodeType: string]: React.ElementType;
<ArtifactProvider>
<CodeBlockProvider>
<ReactMarkdown
/** @ts-ignore */
remarkPlugins={remarkPlugins}
/* @ts-ignore */
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
{
code,
a,
p,
artifact: Artifact,
} as {
[nodeType: string]: React.ElementType;
}
}
}
>
{isLatestMessage && showCursor === true ? currentContent + cursor : currentContent}
</ReactMarkdown>
</CodeBlockProvider>
>
{isLatestMessage && showCursor === true ? currentContent + cursor : currentContent}
</ReactMarkdown>
</CodeBlockProvider>
</ArtifactProvider>
);
});

View file

@ -7,7 +7,7 @@ import ReactMarkdown from 'react-markdown';
import rehypeHighlight from 'rehype-highlight';
import type { PluggableList } from 'unified';
import { code, codeNoExecution, a, p } from './Markdown';
import { CodeBlockProvider } from '~/Providers';
import { CodeBlockProvider, ArtifactProvider } from '~/Providers';
import { langSubset } from '~/utils';
const MarkdownLite = memo(
@ -25,30 +25,32 @@ const MarkdownLite = memo(
];
return (
<CodeBlockProvider>
<ReactMarkdown
remarkPlugins={[
<ArtifactProvider>
<CodeBlockProvider>
<ReactMarkdown
remarkPlugins={[
/** @ts-ignore */
supersub,
remarkGfm,
[remarkMath, { singleDollarTextMath: true }],
]}
/** @ts-ignore */
supersub,
remarkGfm,
[remarkMath, { singleDollarTextMath: true }],
]}
/** @ts-ignore */
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
{
code: codeExecution ? code : codeNoExecution,
a,
p,
} as {
[nodeType: string]: React.ElementType;
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
{
code: codeExecution ? code : codeNoExecution,
a,
p,
} as {
[nodeType: string]: React.ElementType;
}
}
}
>
{content}
</ReactMarkdown>
</CodeBlockProvider>
>
{content}
</ReactMarkdown>
</CodeBlockProvider>
</ArtifactProvider>
);
},
);