diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index 00d1290..e93f99f 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -275,8 +275,8 @@ export function ArtifactFilePreview({ if (language === "markdown") { return (
- diff --git a/frontend/src/components/workspace/messages/message-list-item.tsx b/frontend/src/components/workspace/messages/message-list-item.tsx index 8576a78..ae2718b 100644 --- a/frontend/src/components/workspace/messages/message-list-item.tsx +++ b/frontend/src/components/workspace/messages/message-list-item.tsx @@ -1,7 +1,7 @@ import type { Message } from "@langchain/langgraph-sdk"; import { FileIcon } from "lucide-react"; import { useParams } from "next/navigation"; -import { memo, useMemo } from "react"; +import { memo, useMemo, type ImgHTMLAttributes } from "react"; import rehypeKatex from "rehype-katex"; import { @@ -48,7 +48,7 @@ export function MessageListItem({ /> @@ -82,13 +82,13 @@ function MessageImage({ if (!src) return null; const imgClassName = cn("overflow-hidden rounded-lg", `max-w-[${maxWidth}]`); - + if (typeof src !== "string") { return {alt}; } - + const url = src.startsWith("/mnt/") ? resolveArtifactURL(src, threadId) : src; - + return ( {alt} @@ -108,12 +108,23 @@ function MessageContent_({ const rehypePlugins = useRehypeSplitWordsIntoSpans(isLoading); const isHuman = message.type === "human"; const { thread_id } = useParams<{ thread_id: string }>(); + const components = useMemo( + () => ({ + img: (props: ImgHTMLAttributes) => ( + + ), + }), + [thread_id], + ); const rawContent = extractContentFromMessage(message); const reasoningContent = extractReasoningContentFromMessage(message); const { contentToParse, uploadedFiles } = useMemo(() => { if (!isLoading && reasoningContent && !rawContent) { - return { contentToParse: reasoningContent, uploadedFiles: [] as UploadedFile[] }; + return { + contentToParse: reasoningContent, + uploadedFiles: [] as UploadedFile[], + }; } if (isHuman && rawContent) { const { files, cleanContent: contentWithoutFiles } = @@ -126,15 +137,17 @@ function MessageContent_({ }; }, [isLoading, rawContent, reasoningContent, isHuman]); - const filesList = uploadedFiles.length > 0 && thread_id ? ( - - ) : null; + const filesList = + uploadedFiles.length > 0 && thread_id ? ( + + ) : null; if (isHuman) { const messageResponse = contentToParse ? ( {contentToParse} @@ -159,15 +172,7 @@ function MessageContent_({ isLoading={isLoading} rehypePlugins={[...rehypePlugins, [rehypeKatex, { output: "html" }]]} className="my-3" - components={{ - img: (props) => ( - - ), - }} + components={components} /> ); @@ -176,14 +181,33 @@ function MessageContent_({ /** * Get file extension and check helpers */ -const getFileExt = (filename: string) => filename.split(".").pop()?.toLowerCase() ?? ""; +const getFileExt = (filename: string) => + filename.split(".").pop()?.toLowerCase() ?? ""; const FILE_TYPE_MAP: Record = { - json: "JSON", csv: "CSV", txt: "TXT", md: "Markdown", - py: "Python", js: "JavaScript", ts: "TypeScript", tsx: "TSX", jsx: "JSX", - html: "HTML", css: "CSS", xml: "XML", yaml: "YAML", yml: "YAML", - pdf: "PDF", png: "PNG", jpg: "JPG", jpeg: "JPEG", gif: "GIF", - svg: "SVG", zip: "ZIP", tar: "TAR", gz: "GZ", + json: "JSON", + csv: "CSV", + txt: "TXT", + md: "Markdown", + py: "Python", + js: "JavaScript", + ts: "TypeScript", + tsx: "TSX", + jsx: "JSX", + html: "HTML", + css: "CSS", + xml: "XML", + yaml: "YAML", + yml: "YAML", + pdf: "PDF", + png: "PNG", + jpg: "JPG", + jpeg: "JPEG", + gif: "GIF", + svg: "SVG", + zip: "ZIP", + tar: "TAR", + gz: "GZ", }; const IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"]; @@ -200,13 +224,23 @@ function isImageFile(filename: string): boolean { /** * Uploaded files list component */ -function UploadedFilesList({ files, threadId }: { files: UploadedFile[]; threadId: string }) { +function UploadedFilesList({ + files, + threadId, +}: { + files: UploadedFile[]; + threadId: string; +}) { if (files.length === 0) return null; return (
{files.map((file, index) => ( - + ))}
); @@ -215,7 +249,13 @@ function UploadedFilesList({ files, threadId }: { files: UploadedFile[]; threadI /** * Single uploaded file card component */ -function UploadedFileCard({ file, threadId }: { file: UploadedFile; threadId: string }) { +function UploadedFileCard({ + file, + threadId, +}: { + file: UploadedFile; + threadId: string; +}) { if (!threadId) return null; const isImage = isImageFile(file.filename); @@ -242,12 +282,18 @@ function UploadedFileCard({ file, threadId }: { file: UploadedFile; threadId: st
- + {file.filename}
- + {getFileTypeLabel(file.filename)} {file.size} diff --git a/frontend/src/components/workspace/messages/subtask-card.tsx b/frontend/src/components/workspace/messages/subtask-card.tsx index eb88eb1..b2aa74b 100644 --- a/frontend/src/components/workspace/messages/subtask-card.tsx +++ b/frontend/src/components/workspace/messages/subtask-card.tsx @@ -126,7 +126,7 @@ export function SubtaskCard({ {task.prompt && (