chore: revert markdown to original component + new artifacts rendering

This commit is contained in:
Danny Avila 2024-08-22 18:15:27 -04:00
parent 801b0de49b
commit 85df66265d

View file

@ -1,10 +1,9 @@
import React, { memo, RefObject, useMemo, useRef } from 'react';
import React, { memo, useMemo } from 'react';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import supersub from 'remark-supersub';
import rehypeKatex from 'rehype-katex';
import { useRecoilValue } from 'recoil';
import { visit } from 'unist-util-visit';
import ReactMarkdown from 'react-markdown';
import rehypeHighlight from 'rehype-highlight';
import remarkDirective from 'remark-directive';
@ -14,7 +13,6 @@ import { CodeBlockArtifact, CodeMarkdown } from '~/components/Artifacts/Code';
import { artifact, artifactPlugin } from '~/components/Artifacts/Artifact';
import CodeBlock from '~/components/Messages/Content/CodeBlock';
import { useFileDownload } from '~/data-provider';
import { filenameMap } from '~/utils/artifacts';
import useLocalize from '~/hooks/useLocalize';
import { useToastContext } from '~/Providers';
import store from '~/store';
@ -23,14 +21,9 @@ type TCodeProps = {
inline: boolean;
className?: string;
children: React.ReactNode;
isLatestMessage: boolean;
showCursor?: boolean;
artifactId: string;
codeBlocksRef: RefObject<number | null>;
};
export const code: React.ElementType = memo(({ inline, className, children, ...props }: TCodeProps) => {
const codeArtifacts = useRecoilValue(store.codeArtifacts);
export const code: React.ElementType = memo(({ inline, className, children }: TCodeProps) => {
const match = /language-(\w+)/.exec(className ?? '');
const lang = match && match[1];
@ -40,104 +33,83 @@ export const code: React.ElementType = memo(({ inline, className, children, ...p
{children}
</code>
);
} else {
return <CodeBlock lang={lang ?? 'text'} codeChildren={children} />;
}
const codeString = Array.isArray(children) ? children.join('') : children;
console.log('code lang, children, props', lang, children, props);
const isNonArtifact = filenameMap[lang ?? ''] === undefined;
if (codeArtifacts && typeof codeString === 'string' && isNonArtifact) {
return <CodeMarkdown content={`\`\`\`${lang}\n${codeString}\n\`\`\``} {...props}/>;
} else if (codeArtifacts && typeof codeString === 'string') {
return <CodeBlockArtifact lang={lang ?? 'text'} codeString={codeString} {...props}/>;
}
return <CodeBlock lang={lang ?? 'text'} codeChildren={children} />;
});
export const a: React.ElementType = memo(({ href, children }: { href: string; children: React.ReactNode }) => {
const user = useRecoilValue(store.user);
const { showToast } = useToastContext();
const localize = useLocalize();
export const a: React.ElementType = memo(
({ href, children }: { href: string; children: React.ReactNode }) => {
const user = useRecoilValue(store.user);
const { showToast } = useToastContext();
const localize = useLocalize();
const { file_id, filename, filepath } = useMemo(() => {
const pattern = new RegExp(`(?:files|outputs)/${user?.id}/([^\\s]+)`);
const match = href.match(pattern);
if (match && match[0]) {
const path = match[0];
const parts = path.split('/');
const name = parts.pop();
const file_id = parts.pop();
return { file_id, filename: name, filepath: path };
const { file_id, filename, filepath } = useMemo(() => {
const pattern = new RegExp(`(?:files|outputs)/${user?.id}/([^\\s]+)`);
const match = href.match(pattern);
if (match && match[0]) {
const path = match[0];
const parts = path.split('/');
const name = parts.pop();
const file_id = parts.pop();
return { file_id, filename: name, filepath: path };
}
return { file_id: '', filename: '', filepath: '' };
}, [user?.id, href]);
const { refetch: downloadFile } = useFileDownload(user?.id ?? '', file_id);
const props: { target?: string; onClick?: React.MouseEventHandler } = { target: '_new' };
if (!file_id || !filename) {
return (
<a href={href} {...props}>
{children}
</a>
);
}
return { file_id: '', filename: '', filepath: '' };
}, [user?.id, href]);
const { refetch: downloadFile } = useFileDownload(user?.id ?? '', file_id);
const props: { target?: string; onClick?: React.MouseEventHandler } = { target: '_new' };
const handleDownload = async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
try {
const stream = await downloadFile();
if (stream.data == null || stream.data === '') {
console.error('Error downloading file: No data found');
showToast({
status: 'error',
message: localize('com_ui_download_error'),
});
return;
}
const link = document.createElement('a');
link.href = stream.data;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(stream.data);
} catch (error) {
console.error('Error downloading file:', error);
}
};
props.onClick = handleDownload;
props.target = '_blank';
if (!file_id || !filename) {
return (
<a href={href} {...props}>
<a
href={filepath.startsWith('files/') ? `/api/${filepath}` : `/api/files/${filepath}`}
{...props}
>
{children}
</a>
);
}
const handleDownload = async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
try {
const stream = await downloadFile();
if (!stream.data) {
console.error('Error downloading file: No data found');
showToast({
status: 'error',
message: localize('com_ui_download_error'),
});
return;
}
const link = document.createElement('a');
link.href = stream.data;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(stream.data);
} catch (error) {
console.error('Error downloading file:', error);
}
};
props.onClick = handleDownload;
props.target = '_blank';
return (
<a
href={filepath.startsWith('files/') ? `/api/${filepath}` : `/api/files/${filepath}`}
{...props}
>
{children}
</a>
);
});
},
);
export const p: React.ElementType = memo(({ children }: { children: React.ReactNode }) => {
return <p className="mb-2 whitespace-pre-wrap">{children}</p>;
});
export const div: React.ElementType = memo(({ node, ...props }) => {
if (props.className === 'artifact') {
return (
<div className="artifact">
<h3>{props['data-identifier']}</h3>
<p>Type: {props['data-type']}</p>
{props.children}
</div>
);
}
return <div {...props} />;
});
const cursor = ' ';
type TContentProps = {
@ -150,7 +122,6 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
const artifactIdRef = useRef<string | null>(null);
const codeBlocksRef = useRef<number | null>(null);
const LaTeXParsing = useRecoilValue<boolean>(store.LaTeXParsing);
const codeArtifacts = useRecoilValue<boolean>(store.codeArtifacts);
const isInitializing = content === '';
@ -160,25 +131,7 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
currentContent = LaTeXParsing ? preprocessLaTeX(currentContent) : currentContent;
}
if (artifactIdRef.current === null) {
artifactIdRef.current = new Date().toISOString();
}
const codePlugin: Pluggable = () => {
return (tree) => {
visit(tree, { tagName: 'code' }, (node) => {
node.properties = {
...node.properties,
isLatestMessage,
showCursor,
artifactId: artifactIdRef.current,
codeBlocksRef: codeBlocksRef.current,
};
});
};
};
const rehypePlugins: PluggableList = codeArtifacts ? [[rehypeKatex, { output: 'mathml' }], [codePlugin], [rehypeRaw]] : [
const rehypePlugins = [
[rehypeKatex, { output: 'mathml' }],
[
rehypeHighlight,
@ -203,10 +156,14 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
return (
<ReactMarkdown
remarkPlugins={[
/* @ts-ignore */
remarkDirective,
supersub, remarkGfm, [remarkMath, { singleDollarTextMath: true }],
supersub,
remarkGfm,
[remarkMath, { singleDollarTextMath: true }],
artifactPlugin,
]}
/* @ts-ignore */
rehypePlugins={rehypePlugins}
// linkTarget="_new"
components={
@ -215,6 +172,8 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
a,
p,
artifact,
} as {
[nodeType: string]: React.ElementType;
}
}
>