mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-20 17:34:10 +01:00
refactor: first pass rewrite
This commit is contained in:
parent
85df66265d
commit
fc6eb9f77f
5 changed files with 106 additions and 55 deletions
|
|
@ -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} />;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue