refactor: first pass rewrite

This commit is contained in:
Danny Avila 2024-08-22 18:51:57 -04:00
parent 85df66265d
commit fc6eb9f77f
5 changed files with 106 additions and 55 deletions

View file

@ -1,34 +1,72 @@
import { useEffect, useCallback } from 'react';
import { visit } from 'unist-util-visit'; import { visit } from 'unist-util-visit';
import type { Plugin } from 'unified'; import { useSetRecoilState } from 'recoil';
import type { Root } from 'mdast'; import { artifactsState, artifactIdsState } from '~/store/artifacts';
import type { Pluggable } from 'unified';
export function artifactPlugin() { export const artifactPlugin: Pluggable = () => {
return (tree) => { return (tree) => {
visit( visit(tree, ['textDirective', 'leafDirective', 'containerDirective'], (node) => {
tree, node.data = {
['textDirective', 'leafDirective', 'containerDirective'], hName: node.name,
(node) => { hProperties: node.attributes,
node.data = { ...node.data,
hName: node.name, };
hProperties: node.attributes, return node;
...node.data, });
};
return node;
},
);
}; };
} };
export function Artifact({ node, ...props }) {
const setArtifacts = useSetRecoilState(artifactsState);
const setArtifactIds = useSetRecoilState(artifactIdsState);
const updateArtifact = useCallback(() => {
const content =
props.children && typeof props.children === 'string'
? props.children
: props.children?.props?.children || '';
const title = props.title || 'Untitled Artifact';
const type = props.type || 'unknown';
const identifier = props.identifier || 'no-identifier';
const artifactKey = `${identifier}_${type}_${title}`.replace(/\s+/g, '_').toLowerCase();
setArtifacts((prevArtifacts) => {
if (prevArtifacts[artifactKey] && prevArtifacts[artifactKey].content === content) {
return prevArtifacts;
}
return {
...prevArtifacts,
[artifactKey]: {
id: artifactKey,
identifier,
title,
type,
content,
},
};
});
setArtifactIds((prevIds) => {
if (!prevIds.includes(artifactKey)) {
return [...prevIds, artifactKey];
}
return prevIds;
});
}, [props, setArtifacts, setArtifactIds]);
useEffect(() => {
updateArtifact();
}, [updateArtifact]);
export function artifact({ node, ...props }) {
// if (props.className === 'artifact') {
console.dir(props, { depth: null });
return ( return (
<div className="artifact"> <div className="artifact">
<h3>{props.dataIdentifier}</h3> <b>{props.title || 'Untitled Artifact'}</b>
<p>Type: {props.dataType}</p> <p>Type: {props.type || 'unknown'}</p>
<p>Identifier: {props.identifier || 'No identifier'}</p>
{props.children} {props.children}
</div> </div>
); );
// } }
// return <div {...props} />;
}

View file

@ -2,21 +2,19 @@ import React, { useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import * as Tabs from '@radix-ui/react-tabs'; import * as Tabs from '@radix-ui/react-tabs';
import { Sandpack } from '@codesandbox/sandpack-react'; import { Sandpack } from '@codesandbox/sandpack-react';
import { import { SandpackPreview, SandpackProvider } from '@codesandbox/sandpack-react/unstyled';
SandpackPreview,
SandpackProvider,
} from '@codesandbox/sandpack-react/unstyled';
import { mapCodeFiles, sharedOptions, sharedFiles, sharedProps } from '~/utils/artifacts'; import { mapCodeFiles, sharedOptions, sharedFiles, sharedProps } from '~/utils/artifacts';
import store from '~/store'; import store from '~/store';
export function CodeViewer({ showEditor = false }: { showEditor?: boolean }) { export function CodeViewer({ showEditor = false }: { showEditor?: boolean }) {
const codeBlockIds = useRecoilValue(store.codeBlockIdsState); const artifactIds = useRecoilValue(store.artifactIdsState);
const codeBlocks = useRecoilValue(store.codeBlocksState); const artifacts = useRecoilValue(store.artifactsState);
const files = useMemo(() => mapCodeFiles(codeBlockIds, codeBlocks), [codeBlockIds, codeBlocks]); // const files = useMemo(() => mapCodeFiles(artifactIds, artifacts), [artifactIds, artifacts]);
console.log('CODE FILES & blocks', files, codeBlocks); // console.log('CODE FILES & blocks', files, artifacts);
if ((Object.keys(files)).length === 0) { const files = {};
if (Object.keys(files).length === 0) {
return null; return null;
} }
@ -55,32 +53,34 @@ export function CodeViewer({ showEditor = false }: { showEditor?: boolean }) {
export default function Artifacts() { export default function Artifacts() {
const [activeTab, setActiveTab] = useState('code'); const [activeTab, setActiveTab] = useState('code');
const codeBlockIds = useRecoilValue(store.codeBlockIdsState); const artifactIds = useRecoilValue(store.artifactIdsState);
const codeBlocks = useRecoilValue(store.codeBlocksState); const artifacts = useRecoilValue(store.artifactsState);
const files = useMemo(() => mapCodeFiles(codeBlockIds, codeBlocks), [codeBlockIds, codeBlocks]); // const files = useMemo(() => mapCodeFiles(artifactIds, artifacts), [artifactIds, artifacts]);
const firstFileContent = Object.values(files)[0] || ''; // const firstFileContent = Object.values(files)[0] || '';
const firstFileContent = '';
return ( return (
<div className="flex h-full flex-col text-text-primary text-xl w-full rounded-xl py-2"> <div className="flex h-full w-full flex-col rounded-xl py-2 text-xl text-text-primary">
<div className="bg-gray-800 rounded-lg shadow-lg overflow-hidden flex flex-col h-full"> <div className="flex h-full flex-col overflow-hidden rounded-lg bg-gray-800 shadow-lg">
<Tabs.Root value={activeTab} onValueChange={setActiveTab} className="flex flex-col h-full"> <Tabs.Root value={activeTab} onValueChange={setActiveTab} className="flex h-full flex-col">
<Tabs.List className="flex bg-gray-700"> <Tabs.List className="flex bg-gray-700">
<Tabs.Trigger <Tabs.Trigger
value="code" value="code"
className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white focus:outline-none focus:text-white" className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white focus:text-white focus:outline-none"
> >
Code Code
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger <Tabs.Trigger
value="preview" value="preview"
className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white focus:outline-none focus:text-white" className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white focus:text-white focus:outline-none"
> >
Preview Preview
</Tabs.Trigger> </Tabs.Trigger>
</Tabs.List> </Tabs.List>
<Tabs.Content value="code" className="flex-grow overflow-auto"> <Tabs.Content value="code" className="flex-grow overflow-auto">
<pre className="h-full bg-gray-900 p-4 rounded text-sm overflow-auto"> <pre className="h-full overflow-auto rounded bg-gray-900 p-4 text-sm">
<code>{firstFileContent}</code> <code>{firstFileContent}</code>
</pre> </pre>
</Tabs.Content> </Tabs.Content>

View file

@ -10,7 +10,7 @@ import remarkDirective from 'remark-directive';
import type { PluggableList, Pluggable } from 'unified'; import type { PluggableList, Pluggable } from 'unified';
import { langSubset, preprocessLaTeX, handleDoubleClick } from '~/utils'; import { langSubset, preprocessLaTeX, handleDoubleClick } from '~/utils';
import { CodeBlockArtifact, CodeMarkdown } from '~/components/Artifacts/Code'; import { CodeBlockArtifact, CodeMarkdown } from '~/components/Artifacts/Code';
import { artifact, artifactPlugin } from '~/components/Artifacts/Artifact'; import { Artifact as artifact, artifactPlugin } from '~/components/Artifacts/Artifact';
import CodeBlock from '~/components/Messages/Content/CodeBlock'; import CodeBlock from '~/components/Messages/Content/CodeBlock';
import { useFileDownload } from '~/data-provider'; import { useFileDownload } from '~/data-provider';
import useLocalize from '~/hooks/useLocalize'; import useLocalize from '~/hooks/useLocalize';
@ -156,11 +156,13 @@ const Markdown = memo(({ content = '', showCursor, isLatestMessage }: TContentPr
return ( return (
<ReactMarkdown <ReactMarkdown
remarkPlugins={[ remarkPlugins={[
/* @ts-ignore */
remarkDirective, remarkDirective,
/* @ts-ignore */
supersub, supersub,
remarkGfm, remarkGfm,
/* @ts-ignore */
[remarkMath, { singleDollarTextMath: true }], [remarkMath, { singleDollarTextMath: true }],
/* @ts-ignore */
artifactPlugin, artifactPlugin,
]} ]}
/* @ts-ignore */ /* @ts-ignore */

View file

@ -24,7 +24,7 @@ export default function Presentation({
const { data: startupConfig } = useGetStartupConfig(); const { data: startupConfig } = useGetStartupConfig();
const codeArtifacts = useRecoilValue(store.codeArtifacts); const codeArtifacts = useRecoilValue(store.codeArtifacts);
const hideSidePanel = useRecoilValue(store.hideSidePanel); const hideSidePanel = useRecoilValue(store.hideSidePanel);
const codeBlockIds = useRecoilValue(store.codeBlockIdsState); const codeBlockIds = useRecoilValue(store.artifactIdsState);
const interfaceConfig = useMemo( const interfaceConfig = useMemo(
() => startupConfig?.interface ?? defaultInterface, () => startupConfig?.interface ?? defaultInterface,
[startupConfig], [startupConfig],

View file

@ -1,27 +1,38 @@
// client/src/store/artifacts.ts
import { atom } from 'recoil'; import { atom } from 'recoil';
import type { CodeBlock } from '~/common'; import { logger } from '~/utils';
export interface Artifact {
identifier?: string;
title: string;
type: string;
content: string;
}
export const codeBlocksState = atom<Record<string, CodeBlock>>({ export const artifactsState = atom<Record<string, Artifact>>({
key: 'codeBlocksState', key: 'artifactsState',
default: {}, default: {},
effects: [ effects: [
({ onSet, node }) => { ({ onSet, node }) => {
onSet(async (newValue) => { onSet(async (newValue) => {
console.log('Recoil Effect: Setting codeBlocksState', { key: node.key, newValue }); logger.log('artifacts', 'Recoil Effect: Setting artifactsState', {
key: node.key,
newValue,
});
}); });
}, },
] as const, ] as const,
}); });
export const codeBlockIdsState = atom<string[]>({ export const artifactIdsState = atom<string[]>({
key: 'codeBlockIdsState', key: 'artifactIdsState',
default: [], default: [],
effects: [ effects: [
({ onSet, node }) => { ({ onSet, node }) => {
onSet(async (newValue) => { onSet(async (newValue) => {
console.log('Recoil Effect: Setting codeBlockIdsState', { key: node.key, newValue }); logger.log('artifacts', 'Recoil Effect: Setting artifactIdsState', {
key: node.key,
newValue,
});
}); });
}, },
] as const, ] as const,
}); });