Skip to content

Commit 25b2473

Browse files
committed
CR: cn utility, refactor accepted files, nextImage
1 parent 1fbef48 commit 25b2473

File tree

4 files changed

+37
-28
lines changed

4 files changed

+37
-28
lines changed

src/components/thread/ContentBlocksPreview.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import type { Base64ContentBlock } from "@langchain/core/messages";
33
import { MultimodalPreview } from "../ui/MultimodalPreview";
4+
import { cn } from "@/lib/utils";
45

56
interface ContentBlocksPreviewProps {
67
blocks: Base64ContentBlock[];
@@ -9,15 +10,19 @@ interface ContentBlocksPreviewProps {
910
className?: string;
1011
}
1112

13+
/**
14+
* Renders a preview of content blocks with optional remove functionality.
15+
* Uses cn utility for robust class merging.
16+
*/
1217
export const ContentBlocksPreview: React.FC<ContentBlocksPreviewProps> = ({
1318
blocks,
1419
onRemove,
1520
size = "md",
16-
className = "",
21+
className,
1722
}) => {
1823
if (!blocks.length) return null;
1924
return (
20-
<div className={`flex flex-wrap gap-2 p-3.5 pb-0 ${className}`}>
25+
<div className={cn("flex flex-wrap gap-2 p-3.5 pb-0", className)}>
2126
{blocks.map((block, idx) => (
2227
<MultimodalPreview
2328
key={idx}

src/components/thread/messages/human.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,7 @@ export function HumanMessage({
138138
</div>
139139
)}
140140
{/* Render text if present, otherwise fallback to file/image name */}
141-
{contentString &&
142-
contentString !== "Other" &&
143-
contentString !== "Multimodal message" ? (
141+
{contentString ? (
144142
<p className="bg-muted ml-auto w-fit rounded-3xl px-4 py-2 text-right whitespace-pre-wrap">
145143
{contentString}
146144
</p>

src/components/ui/MultimodalPreview.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from "react";
22
import { File, Image as ImageIcon, X as XIcon } from "lucide-react";
33
import type { Base64ContentBlock } from "@langchain/core/messages";
4-
4+
import { cn } from "@/lib/utils";
5+
import Image from "next/image";
56
export interface MultimodalPreviewProps {
67
block: Base64ContentBlock;
78
removable?: boolean;
@@ -14,7 +15,7 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
1415
block,
1516
removable = false,
1617
onRemove,
17-
className = "",
18+
className,
1819
size = "md",
1920
}) => {
2021
// Sizing
@@ -38,13 +39,13 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
3839
if (size === "sm") imgClass = "rounded-md object-cover h-10 w-10 text-base";
3940
if (size === "lg") imgClass = "rounded-md object-cover h-24 w-24 text-xl";
4041
return (
41-
<div
42-
className={`relative inline-block${className ? ` ${className}` : ""}`}
43-
>
44-
<img
42+
<div className={cn("relative inline-block", className)}>
43+
<Image
4544
src={url}
4645
alt={String(block.metadata?.name || "uploaded image")}
4746
className={imgClass}
47+
width={size === "sm" ? 16 : size === "md" ? 32 : 48}
48+
height={size === "sm" ? 16 : size === "md" ? 32 : 48}
4849
/>
4950
{removable && (
5051
<button
@@ -68,20 +69,24 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
6869
) {
6970
const filename =
7071
block.metadata?.filename || block.metadata?.name || "PDF file";
71-
const fileClass = `relative flex items-center gap-2 rounded-md border bg-gray-100 px-3 py-2${className ? ` ${className}` : ""}`;
7272
return (
73-
<div className={fileClass}>
73+
<div
74+
className={cn(
75+
"relative flex items-center gap-2 rounded-md border bg-gray-100 px-3 py-2",
76+
className
77+
)}
78+
>
7479
<File
75-
className={
76-
"flex-shrink-0 text-teal-700 " +
77-
(size === "sm" ? "h-5 w-5" : "h-7 w-7")
78-
}
80+
className={cn(
81+
"flex-shrink-0 text-teal-700",
82+
size === "sm" ? "h-5 w-5" : "h-7 w-7"
83+
)}
7984
/>
8085
<span
81-
className={
82-
"truncate text-sm text-gray-800 " +
83-
(size === "sm" ? "max-w-[80px]" : "max-w-[160px]")
84-
}
86+
className={cn(
87+
"truncate text-sm text-gray-800",
88+
size === "sm" ? "max-w-[80px]" : "max-w-[160px]"
89+
)}
8590
>
8691
{String(filename)}
8792
</span>
@@ -100,9 +105,13 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
100105
}
101106

102107
// Fallback for unknown types
103-
const fallbackClass = `flex items-center gap-2 rounded-md border bg-gray-100 px-3 py-2 text-gray-500${className ? ` ${className}` : ""}`;
104108
return (
105-
<div className={fallbackClass}>
109+
<div
110+
className={cn(
111+
"flex items-center gap-2 rounded-md border bg-gray-100 px-3 py-2 text-gray-500",
112+
className
113+
)}
114+
>
106115
<File className="h-5 w-5 flex-shrink-0" />
107116
<span className="truncate text-xs">Unsupported file type</span>
108117
{removable && (

src/hooks/use-file-upload.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ import { toast } from "sonner";
33
import type { Base64ContentBlock } from "@langchain/core/messages";
44
import { fileToContentBlock } from "@/lib/multimodal-utils";
55

6-
export const SUPPORTED_IMAGE_TYPES = [
6+
export const SUPPORTED_FILE_TYPES = [
77
"image/jpeg",
88
"image/png",
99
"image/gif",
1010
"image/webp",
11-
];
12-
export const SUPPORTED_FILE_TYPES = [
13-
...SUPPORTED_IMAGE_TYPES,
1411
"application/pdf",
1512
];
1613

@@ -26,7 +23,7 @@ export function useFileUpload({
2623
const dropRef = useRef<HTMLDivElement>(null);
2724

2825
const isDuplicate = (file: File, blocks: Base64ContentBlock[]) => {
29-
if (SUPPORTED_IMAGE_TYPES.includes(file.type)) {
26+
if (SUPPORTED_FILE_TYPES.includes(file.type)) {
3027
return blocks.some(
3128
(b) =>
3229
b.type === "image" &&

0 commit comments

Comments
 (0)