refactor: upgrade gpt-vis (#1883)

This commit is contained in:
hustcc
2024-08-28 13:37:45 +08:00
committed by GitHub
parent 131bc7b89b
commit 1cb7e35295
12 changed files with 518 additions and 260 deletions

View File

@@ -6,7 +6,7 @@ import { githubLightTheme } from '@uiw/react-json-view/githubLight';
import { Alert, Spin } from 'antd';
import classNames from 'classnames';
import React, { useContext, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import { GPTVis } from '@antv/gpt-vis';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
@@ -41,30 +41,36 @@ const VisResponse: React.FC<{ data: VisResponseProps }> = ({ data }) => {
return (
<div className="flex flex-1 flex-col">
<Alert
className={classNames('mb-4', {
'bg-[#fafafa] border-[transparent]': !type,
className={classNames("mb-4", {
"bg-[#fafafa] border-[transparent]": !type,
})}
message={data.name}
type={type}
{...(type && { showIcon: true })}
{...(type === 'warning' && { icon: <Spin indicator={<LoadingOutlined spin />} /> })}
{...(type === "warning" && {
icon: <Spin indicator={<LoadingOutlined spin />} />,
})}
/>
{data.result && (
<JsonView
style={{ ...theme, width: '100%', padding: 10 }}
style={{ ...theme, width: "100%", padding: 10 }}
className={classNames({
'bg-[#fafafa]': mode === 'light',
"bg-[#fafafa]": mode === "light",
})}
value={JSON.parse(data.result || '{}')}
value={JSON.parse(data.result || "{}")}
enableClipboard={false}
displayDataTypes={false}
objectSortKeys={false}
/>
)}
{data.err_msg && (
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
<GPTVis
components={markdownComponents}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
>
{data.err_msg}
</ReactMarkdown>
</GPTVis>
)}
</div>
);

View File

@@ -1,7 +1,6 @@
import ModelIcon from '@/new-components/chat/content/ModelIcon';
import { LinkOutlined, SwapRightOutlined } from '@ant-design/icons';
import { Popover, Space } from 'antd';
import ReactMarkdown from 'react-markdown';
import { SwapRightOutlined } from '@ant-design/icons';
import { GPTVis } from '@antv/gpt-vis';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import markdownComponents from './config';
@@ -24,7 +23,11 @@ function AgentMessages({ data }: Props) {
{data.map((item, index) => (
<div key={index} className="rounded">
<div className="flex items-center mb-3 text-sm">
{item.model ? <ModelIcon model={item.model} /> : <div className="rounded-full w-6 h-6 bg-gray-100" />}
{item.model ? (
<ModelIcon model={item.model} />
) : (
<div className="rounded-full w-6 h-6 bg-gray-100" />
)}
<div className="ml-2 opacity-70">
{item.sender}
<SwapRightOutlined className="mx-2 text-base" />
@@ -32,11 +35,17 @@ function AgentMessages({ data }: Props) {
</div>
</div>
<div className="whitespace-normal text-sm mb-3">
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
<GPTVis
components={markdownComponents}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
>
{item.markdown}
</ReactMarkdown>
</GPTVis>
</div>
{item.resource && item.resource !== 'null' && <ReferencesContent references={item.resource} />}
{item.resource && item.resource !== "null" && (
<ReferencesContent references={item.resource} />
)}
</div>
))}
</>

View File

@@ -1,6 +1,6 @@
import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons';
import { Collapse } from 'antd';
import ReactMarkdown from 'react-markdown';
import { GPTVis } from '@antv/gpt-vis';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
@@ -32,7 +32,7 @@ function AgentPlans({ data }: Props) {
<span>
{item.name} - {item.agent}
</span>
{item.status === 'complete' ? (
{item.status === "complete" ? (
<CheckOutlined className="!text-green-500 ml-2" />
) : (
<ClockCircleOutlined className="!text-gray-500 ml-2" />
@@ -40,9 +40,13 @@ function AgentPlans({ data }: Props) {
</div>
),
children: (
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
<GPTVis
components={markdownComponents}
rehypePlugins={[rehypeRaw]}
remarkPlugins={[remarkGfm]}
>
{item.markdown}
</ReactMarkdown>
</GPTVis>
),
};
})}

View File

@@ -1,146 +1,241 @@
import { AutoChart, BackEndChartType, getChartType } from '@/components/chart';
import { LinkOutlined, ReadOutlined, SyncOutlined } from '@ant-design/icons';
import { Datum } from '@antv/ava';
import { Image, Table, Tabs, TabsProps, Tag } from 'antd';
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import { LinkOutlined, ReadOutlined, SyncOutlined } from "@ant-design/icons";
import { GPTVis, withDefaultChartCode } from "@antv/gpt-vis";
import { Table, Image, Tag, Tabs, TabsProps } from "antd";
import { AutoChart, BackEndChartType, getChartType } from "@/components/chart";
import { Datum } from "@antv/ava";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import AgentMessages from "./agent-messages";
import AgentPlans from "./agent-plans";
import { CodePreview } from "./code-preview";
import ReferencesContent from "./ReferencesContent";
import VisChart from "./vis-chart";
import VisCode from "./vis-code";
import VisConvertError from "./vis-convert-error";
import VisDashboard from "./vis-dashboard";
import VisPlugin from "./vis-plugin";
import VisAppLink from "./VisAppLink";
import VisChatLink from "./VisChatLink";
import VisResponse from "./VisResponse";
import { formatSql } from "@/utils";
import AgentMessages from './agent-messages';
import AgentPlans from './agent-plans';
import { CodePreview } from './code-preview';
import ReferencesContent from './ReferencesContent';
import VisChart from './vis-chart';
import VisCode from './vis-code';
import VisConvertError from './vis-convert-error';
import VisDashboard from './vis-dashboard';
import VisPlugin from './vis-plugin';
import VisAppLink from './VisAppLink';
import VisChatLink from './VisChatLink';
import VisResponse from './VisResponse';
import { formatSql } from '@/utils';
type MarkdownComponent = Parameters<typeof GPTVis>["0"]["components"];
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
const customeTags: (keyof JSX.IntrinsicElements)[] = ['custom-view', 'chart-view', 'references', 'summary'];
const customeTags: (keyof JSX.IntrinsicElements)[] = [
"custom-view",
"chart-view",
"references",
"summary",
];
function matchCustomeTagValues(context: string) {
const matchValues = customeTags.reduce<string[]>((acc, tagName) => {
const tagReg = new RegExp(`<${tagName}[^>]*\/?>`, 'gi');
const tagReg = new RegExp(`<${tagName}[^>]*\/?>`, "gi");
context = context.replace(tagReg, (matchVal) => {
acc.push(matchVal);
return '';
return "";
});
return acc;
}, []);
return { context, matchValues };
}
const codeComponents = {
/**
* @description
* Custom code block rendering, which can be used to render custom components in the code block.
* Is it defined in gpt-vis, and the default rendering contains `vis-chart`.
*/
code: withDefaultChartCode({
languageRenderers: {
"agent-plans": ({
node,
className,
children,
style,
}) => {
const content = String(children);
/**
* @description
* In some cases, tags are nested within code syntax,
* so it is necessary to extract the tags present in the code block and render them separately.
*/
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof AgentPlans
>[0]["data"];
return <AgentPlans data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"agent-messages": ({
node,
className,
children,
style,
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof AgentMessages
>[0]["data"];
return <AgentMessages data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-convert-error": ({
node,
className,
children,
style,
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisConvertError
>[0]["data"];
return <VisConvertError data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-dashboard": ({
node,
className,
children,
style,
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisDashboard
>[0]["data"];
return <VisDashboard data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-chart": ({ node, className, children, style }) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisChart
>[0]["data"];
return <VisChart data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-plugin": ({
node,
className,
children,
style,
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisPlugin
>[0]["data"];
return <VisPlugin data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-code": ({ node, className, children, style, ...props }) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisCode
>[0]["data"];
return <VisCode data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-app-link": ({
node,
className,
children,
style,
...props
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisAppLink
>[0]["data"];
return <VisAppLink data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
"vis-api-response": ({
node,
className,
children,
style,
...props
}) => {
const content = String(children);
const lang = className?.replace("language-", "") || "javascript";
try {
const data = JSON.parse(content) as Parameters<
typeof VisResponse
>[0]["data"];
return <VisResponse data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
},
},
defaultRenderer({ node, className, children, style, ...props }) {
const content = String(children);
const lang = className?.replace("language-", "") || "";
const { context, matchValues } = matchCustomeTagValues(content);
console.log(111, { node, className, children, style, ...props }, lang);
return (
<>
{lang ? (
<CodePreview code={context} language={lang || "javascript"} />
) : (
<code
{...props}
style={style}
className="p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm"
>
{children}
</code>
)}
<GPTVis
components={markdownComponents}
rehypePlugins={[rehypeRaw]}
remarkPlugins={[remarkGfm]}
>
{matchValues.join("\n")}
</GPTVis>
</>
);
},
}),
};
const basicComponents: MarkdownComponent = {
code({ inline, node, className, children, style, ...props }) {
const content = String(children);
/**
* @description
* In some cases, tags are nested within code syntax,
* so it is necessary to extract the tags present in the code block and render them separately.
*/
const { context, matchValues } = matchCustomeTagValues(content);
const lang = className?.replace('language-', '') || 'javascript';
if (lang === 'agent-plans') {
try {
const data = JSON.parse(content) as Parameters<typeof AgentPlans>[0]['data'];
return <AgentPlans data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'agent-messages') {
try {
const data = JSON.parse(content) as Parameters<typeof AgentMessages>[0]['data'];
return <AgentMessages data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-convert-error') {
try {
const data = JSON.parse(content) as Parameters<typeof VisConvertError>[0]['data'];
return <VisConvertError data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-dashboard') {
try {
const data = JSON.parse(content) as Parameters<typeof VisDashboard>[0]['data'];
return <VisDashboard data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-chart') {
try {
const data = JSON.parse(content) as Parameters<typeof VisChart>[0]['data'];
return <VisChart data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-plugin') {
try {
const data = JSON.parse(content) as Parameters<typeof VisPlugin>[0]['data'];
return <VisPlugin data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-code') {
try {
const data = JSON.parse(content) as Parameters<typeof VisCode>[0]['data'];
return <VisCode data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-app-link') {
try {
const data = JSON.parse(content) as Parameters<typeof VisAppLink>[0]['data'];
return <VisAppLink data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-api-response') {
try {
const data = JSON.parse(content) as Parameters<typeof VisResponse>[0]['data'];
return <VisResponse data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
return (
<>
{!inline ? (
<CodePreview code={context} language={lang} />
) : (
<code {...props} style={style} className="p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm">
{children}
</code>
)}
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
{matchValues.join('\n')}
</ReactMarkdown>
</>
);
},
...codeComponents,
ul({ children }) {
return <ul className="py-1">{children}</ul>;
},
@@ -148,22 +243,46 @@ const basicComponents: MarkdownComponent = {
return <ol className="py-1">{children}</ol>;
},
li({ children, ordered }) {
return <li className={`text-sm leading-7 ml-5 pl-2 text-gray-600 dark:text-gray-300 ${ordered ? 'list-decimal' : 'list-disc'}`}>{children}</li>;
return (
<li
className={`text-sm leading-7 ml-5 pl-2 text-gray-600 dark:text-gray-300 ${
ordered ? "list-decimal" : "list-disc"
}`}
>
{children}
</li>
);
},
table({ children }) {
return <table className="my-2 rounded-tl-md rounded-tr-md bg-white dark:bg-gray-800 text-sm rounded-lg overflow-hidden">{children}</table>;
return (
<table className="my-2 rounded-tl-md rounded-tr-md bg-white dark:bg-gray-800 text-sm rounded-lg overflow-hidden">
{children}
</table>
);
},
thead({ children }) {
return <thead className="bg-[#fafafa] dark:bg-black font-semibold">{children}</thead>;
return (
<thead className="bg-[#fafafa] dark:bg-black font-semibold">
{children}
</thead>
);
},
th({ children }) {
return <th className="!text-left p-4">{children}</th>;
},
td({ children }) {
return <td className="p-4 border-t border-[#f0f0f0] dark:border-gray-700">{children}</td>;
return (
<td className="p-4 border-t border-[#f0f0f0] dark:border-gray-700">
{children}
</td>
);
},
h1({ children }) {
return <h3 className="text-2xl font-bold my-4 border-b border-slate-300 pb-4">{children}</h3>;
return (
<h3 className="text-2xl font-bold my-4 border-b border-slate-300 pb-4">
{children}
</h3>
);
},
h2({ children }) {
return <h3 className="text-xl font-bold my-3">{children}</h3>;
@@ -209,8 +328,8 @@ const basicComponents: MarkdownComponent = {
);
},
button({ children, className, ...restProps }) {
if (className === 'chat-link') {
const msg = (restProps as any)?.['data-msg'];
if (className === "chat-link") {
const msg = (restProps as any)?.["data-msg"];
return <VisChatLink msg={msg}>{children}</VisChatLink>;
}
return (
@@ -223,32 +342,32 @@ const basicComponents: MarkdownComponent = {
const returnSqlVal = (val: string) => {
const punctuationMap: any = {
'': ',',
'。': '.',
'': '?',
'': '!',
'': ':',
'': ';',
'“': '"',
'”': '"',
'': "'",
'': "'",
'': '(',
'': ')',
'【': '[',
'】': ']',
'《': '<',
'》': '>',
'—': '-',
'、': ',',
'…': '...',
"": ",",
"。": ".",
"": "?",
"": "!",
"": ":",
"": ";",
"“": '"',
"”": '"',
"": "'",
"": "'",
"": "(",
"": ")",
"【": "[",
"】": "]",
"《": "<",
"》": ">",
"—": "-",
"、": ",",
"…": "...",
};
const regex = new RegExp(Object.keys(punctuationMap).join('|'), 'g');
const regex = new RegExp(Object.keys(punctuationMap).join("|"), "g");
return val.replace(regex, (match) => punctuationMap[match]);
};
const extraComponents: MarkdownComponent = {
'chart-view': function ({ content, children }) {
"chart-view": function ({ content, children }) {
let data: {
data: Datum[];
type: BackEndChartType;
@@ -259,11 +378,12 @@ const extraComponents: MarkdownComponent = {
} catch (e) {
console.log(e, content);
data = {
type: 'response_table',
sql: '',
type: "response_table",
sql: "",
data: [],
};
}
console.log(111, data);
const columns = data?.data?.[0]
? Object.keys(data?.data?.[0])?.map((item) => {
@@ -276,25 +396,46 @@ const extraComponents: MarkdownComponent = {
: [];
const ChartItem = {
key: 'chart',
label: 'Chart',
children: <AutoChart data={data?.data} chartType={getChartType(data?.type)} />,
key: "chart",
label: "Chart",
children: (
<AutoChart data={data?.data} chartType={getChartType(data?.type)} />
),
};
const SqlItem = {
key: 'sql',
label: 'SQL',
children: <CodePreview code={formatSql(returnSqlVal(data?.sql), 'mysql') as string} language={'sql'} />,
key: "sql",
label: "SQL",
children: (
<CodePreview
code={formatSql(returnSqlVal(data?.sql), "mysql") as string}
language={"sql"}
/>
),
};
const DataItem = {
key: 'data',
label: 'Data',
children: <Table dataSource={data?.data} columns={columns} scroll={{x:true}} virtual={true} />,
key: "data",
label: "Data",
children: (
<Table
dataSource={data?.data}
columns={columns}
scroll={{ x: true }}
virtual={true}
/>
),
};
const TabItems: TabsProps['items'] = data?.type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];
const TabItems: TabsProps["items"] =
data?.type === "response_table"
? [DataItem, SqlItem]
: [ChartItem, SqlItem, DataItem];
return (
<div>
<Tabs defaultActiveKey={data?.type === 'response_table' ? 'data' : 'chart'} items={TabItems} size="small" />
<Tabs
defaultActiveKey={data?.type === "response_table" ? "data" : "chart"}
items={TabItems}
size="small"
/>
{children}
</div>
);

View File

@@ -1,6 +1,6 @@
import { PropsWithChildren, ReactNode, memo, useContext, useMemo } from 'react';
import { CheckOutlined, ClockCircleOutlined, CloseOutlined, CodeOutlined, LoadingOutlined, RobotOutlined, UserOutlined } from '@ant-design/icons';
import ReactMarkdown from 'react-markdown';
import { GPTVis } from '@antv/gpt-vis';
import { IChatDialogueMessageSchema } from '@/types/chat';
import rehypeRaw from 'rehype-raw';
import classNames from 'classnames';
@@ -22,7 +22,7 @@ interface Props {
onLinkClick?: () => void;
}
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
type MarkdownComponent = Parameters<typeof GPTVis>["0"]["components"];
type DBGPTView = {
name: string;
@@ -112,15 +112,23 @@ function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithC
const { bgClass, icon } = pluginViewStatusMapper[status] ?? {};
return (
<div className="bg-white dark:bg-[#212121] rounded-lg overflow-hidden my-2 flex flex-col lg:max-w-[80%]">
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
<div
className={classNames(
"flex px-4 md:px-6 py-2 items-center text-white text-sm",
bgClass
)}
>
{name}
{icon}
</div>
{result ? (
<div className="px-4 md:px-6 py-4 text-sm">
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{result ?? ''}
</ReactMarkdown>
<GPTVis
components={markdownComponents}
rehypePlugins={[rehypeRaw]}
>
{result ?? ""}
</GPTVis>
</div>
) : (
<div className="px-4 md:px-6 py-4 text-sm">{err_msg}</div>
@@ -136,32 +144,48 @@ function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithC
return (
<div
className={classNames('relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words', {
'bg-white dark:bg-[#232734]': isRobot,
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
})}
className={classNames(
"relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words",
{
"bg-white dark:bg-[#232734]": isRobot,
"lg:w-full xl:w-full pl-0": [
"chat_with_db_execute",
"chat_dashboard",
].includes(scene),
}
)}
>
<div className="mr-2 flex flex-shrink-0 items-center justify-center h-7 w-7 rounded-full text-lg sm:mr-4">
{isRobot ? renderModelIcon(model_name) || <RobotOutlined /> : <UserOutlined />}
{isRobot ? (
renderModelIcon(model_name) || <RobotOutlined />
) : (
<UserOutlined />
)}
</div>
<div className="flex-1 overflow-hidden items-center text-md leading-8 pb-2">
{/* User Input */}
{!isRobot && typeof context === 'string' && context}
{!isRobot && typeof context === "string" && context}
{/* Render Report */}
{isRobot && isChartChat && typeof context === 'object' && (
{isRobot && isChartChat && typeof context === "object" && (
<div>
{`[${context.template_name}]: `}
<span className="text-theme-primary cursor-pointer" onClick={onLinkClick}>
<span
className="text-theme-primary cursor-pointer"
onClick={onLinkClick}
>
<CodeOutlined className="mr-1" />
{context.template_introduce || 'More Details'}
{context.template_introduce || "More Details"}
</span>
</div>
)}
{/* Markdown */}
{isRobot && typeof context === 'string' && (
<ReactMarkdown components={{ ...markdownComponents, ...extraMarkdownComponents }} rehypePlugins={[rehypeRaw]}>
{isRobot && typeof context === "string" && (
<GPTVis
components={{ ...markdownComponents, ...extraMarkdownComponents }}
rehypePlugins={[rehypeRaw]}
>
{formatMarkdownVal(value)}
</ReactMarkdown>
</GPTVis>
)}
{!!relations?.length && (
<div className="flex flex-wrap mt-2">

View File

@@ -1,4 +1,4 @@
import ReactMarkdown from 'react-markdown';
import { GPTVis } from '@antv/gpt-vis';
import remarkGfm from 'remark-gfm';
import markdownComponents from './config';
import { CodePreview } from './code-preview';
@@ -29,9 +29,12 @@ function VisCode({ data }: Props) {
{data.code.map((item, index) => (
<div
key={index}
className={classNames('px-4 py-2 text-[#121417] dark:text-white cursor-pointer', {
'bg-white dark:bg-theme-dark-container': index === show,
})}
className={classNames(
"px-4 py-2 text-[#121417] dark:text-white cursor-pointer",
{
"bg-white dark:bg-theme-dark-container": index === show,
}
)}
onClick={() => {
setShow(index);
}}
@@ -53,13 +56,18 @@ function VisCode({ data }: Props) {
<div>
<div className="flex">
<div className="bg-white dark:bg-theme-dark-container px-4 py-2 text-[#121417] dark:text-white">
{t('Terminal')} {data.exit_success ? <CheckOutlined className="text-green-600" /> : <CloseOutlined className="text-red-600" />}
{t("Terminal")}{" "}
{data.exit_success ? (
<CheckOutlined className="text-green-600" />
) : (
<CloseOutlined className="text-red-600" />
)}
</div>
</div>
<div className="p-4 max-h-72 overflow-y-auto whitespace-normal bg-white dark:dark:bg-theme-dark">
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]}>
<GPTVis components={markdownComponents} remarkPlugins={[remarkGfm]}>
{data.log}
</ReactMarkdown>
</GPTVis>
</div>
</div>
</div>

View File

@@ -1,7 +1,7 @@
import { CheckOutlined, ClockCircleOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import { ReactNode } from 'react';
import ReactMarkdown from 'react-markdown';
import { GPTVis } from '@antv/gpt-vis';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
@@ -47,15 +47,24 @@ function VisPlugin({ data }: Props) {
return (
<div className="bg-theme-light dark:bg-theme-dark-container rounded overflow-hidden my-2 flex flex-col">
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
<div
className={classNames(
"flex px-4 md:px-6 py-2 items-center text-white text-sm",
bgClass
)}
>
{data.name}
{icon}
</div>
{data.result ? (
<div className="px-4 md:px-6 py-4 text-sm whitespace-normal">
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
{data.result ?? ''}
</ReactMarkdown>
<GPTVis
components={markdownComponents}
rehypePlugins={[rehypeRaw]}
remarkPlugins={[remarkGfm]}
>
{data.result ?? ""}
</GPTVis>
</div>
) : (
<div className="px-4 md:px-6 py-4 text-sm">{data.err_msg}</div>