mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
|
|
import React, { useMemo, useState } from 'react';
|
||
|
|
import { useRecoilValue } from 'recoil';
|
||
|
|
import { CodeInProgress } from './CodeProgress';
|
||
|
|
import { imageExtRegex } from 'librechat-data-provider';
|
||
|
|
import type { TFile, TAttachment, TAttachmentMetadata } from 'librechat-data-provider';
|
||
|
|
import ProgressText from '~/components/Chat/Messages/Content/ProgressText';
|
||
|
|
import FinishedIcon from '~/components/Chat/Messages/Content/FinishedIcon';
|
||
|
|
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||
|
|
import Image from '~/components/Chat/Messages/Content/Image';
|
||
|
|
import LogContent from './LogContent';
|
||
|
|
import { useProgress } from '~/hooks';
|
||
|
|
import store from '~/store';
|
||
|
|
|
||
|
|
interface ParsedArgs {
|
||
|
|
lang: string;
|
||
|
|
code: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useParseArgs(args: string): ParsedArgs {
|
||
|
|
return useMemo(() => {
|
||
|
|
const langMatch = args.match(/"lang"\s*:\s*"(\w+)"/);
|
||
|
|
const codeMatch = args.match(/"code"\s*:\s*"(.+?)(?="\s*,\s*"args"|$)/s);
|
||
|
|
|
||
|
|
let code = '';
|
||
|
|
if (codeMatch) {
|
||
|
|
code = codeMatch[1];
|
||
|
|
if (code.endsWith('"}')) {
|
||
|
|
code = code.slice(0, -2);
|
||
|
|
}
|
||
|
|
code = code.replace(/\\n/g, '\n').replace(/\\/g, '');
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
lang: langMatch ? langMatch[1] : '',
|
||
|
|
code,
|
||
|
|
};
|
||
|
|
}, [args]);
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function ExecuteCode({
|
||
|
|
initialProgress = 0.1,
|
||
|
|
args,
|
||
|
|
output = '',
|
||
|
|
isSubmitting,
|
||
|
|
attachments,
|
||
|
|
}: {
|
||
|
|
initialProgress: number;
|
||
|
|
args: string;
|
||
|
|
output?: string;
|
||
|
|
isSubmitting: boolean;
|
||
|
|
attachments?: TAttachment[];
|
||
|
|
}) {
|
||
|
|
const showAnalysisCode = useRecoilValue(store.showCode);
|
||
|
|
const [showCode, setShowCode] = useState(showAnalysisCode);
|
||
|
|
|
||
|
|
const { lang, code } = useParseArgs(args);
|
||
|
|
const progress = useProgress(initialProgress);
|
||
|
|
|
||
|
|
const radius = 56.08695652173913;
|
||
|
|
const circumference = 2 * Math.PI * radius;
|
||
|
|
const offset = circumference - progress * circumference;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<div className="my-2.5 flex items-center gap-2.5">
|
||
|
|
<div className="relative h-5 w-5 shrink-0">
|
||
|
|
{progress < 1 ? (
|
||
|
|
<CodeInProgress
|
||
|
|
offset={offset}
|
||
|
|
radius={radius}
|
||
|
|
progress={progress}
|
||
|
|
isSubmitting={isSubmitting}
|
||
|
|
circumference={circumference}
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<FinishedIcon />
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
<ProgressText
|
||
|
|
progress={progress}
|
||
|
|
onClick={() => setShowCode((prev) => !prev)}
|
||
|
|
inProgressText="Analyzing"
|
||
|
|
finishedText="Finished analyzing"
|
||
|
|
hasInput={!!code.length}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
{showCode && (
|
||
|
|
<div className="code-analyze-block mb-3 mt-0.5 overflow-hidden rounded-xl bg-black">
|
||
|
|
<MarkdownLite content={code ? `\`\`\`${lang}\n${code}\n\`\`\`` : ''} />
|
||
|
|
{output.length > 0 && (
|
||
|
|
<div className="bg-gray-700 p-4 text-xs">
|
||
|
|
<div
|
||
|
|
className="prose flex flex-col-reverse text-white"
|
||
|
|
style={{
|
||
|
|
color: 'white',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<pre className="shrink-0">
|
||
|
|
<LogContent output={output} attachments={attachments} />
|
||
|
|
</pre>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
{attachments?.map((attachment, index) => {
|
||
|
|
const { width, height, filepath } = attachment as TFile & TAttachmentMetadata;
|
||
|
|
const isImage =
|
||
|
|
imageExtRegex.test(attachment.filename) &&
|
||
|
|
width != null &&
|
||
|
|
height != null &&
|
||
|
|
filepath != null;
|
||
|
|
if (isImage) {
|
||
|
|
return (
|
||
|
|
<Image
|
||
|
|
key={index}
|
||
|
|
altText={attachment.filename}
|
||
|
|
imagePath={filepath}
|
||
|
|
height={height}
|
||
|
|
width={width}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
})}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|