adds attachment support
This commit is contained in:
103
web/src/components/chat/chat-attachment-list.tsx
Normal file
103
web/src/components/chat/chat-attachment-list.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { FileText, Image as ImageIcon, X } from "lucide-preact";
|
||||
import type { ChatAttachment } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Props = {
|
||||
attachments: ChatAttachment[];
|
||||
tone?: "composer" | "user" | "assistant";
|
||||
onRemove?: (id: string) => void;
|
||||
};
|
||||
|
||||
function getTextPreview(value: string) {
|
||||
const normalized = value.replace(/\r/g, "").trim();
|
||||
if (!normalized) return "(empty file)";
|
||||
return normalized.length <= 280 ? normalized : `${normalized.slice(0, 280).trimEnd()}...`;
|
||||
}
|
||||
|
||||
function getSurfaceClasses(tone: Props["tone"]) {
|
||||
if (tone === "user") {
|
||||
return "border-white/12 bg-black/16 text-fuchsia-50";
|
||||
}
|
||||
if (tone === "assistant") {
|
||||
return "border-violet-300/16 bg-violet-400/8 text-violet-50";
|
||||
}
|
||||
return "border-violet-300/18 bg-background/40 text-violet-50";
|
||||
}
|
||||
|
||||
export function ChatAttachmentList({ attachments, tone = "composer", onRemove }: Props) {
|
||||
if (!attachments.length) return null;
|
||||
|
||||
const surfaceClasses = getSurfaceClasses(tone);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{attachments.map((attachment) => {
|
||||
const isImage = attachment.kind === "image";
|
||||
return (
|
||||
<div key={attachment.id} className={cn("overflow-hidden rounded-xl border", surfaceClasses)}>
|
||||
{isImage ? (
|
||||
<div className="grid gap-0 md:grid-cols-[minmax(0,220px)_minmax(0,1fr)]">
|
||||
<div className="border-b border-white/10 bg-black/10 md:border-b-0 md:border-r">
|
||||
<img src={attachment.dataUrl} alt={attachment.filename} className="block max-h-56 w-full object-cover" />
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col gap-2 p-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="mt-0.5 rounded-md border border-white/12 bg-white/5 p-1.5">
|
||||
<ImageIcon className="h-3.5 w-3.5" />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">{attachment.filename}</p>
|
||||
<p className="text-xs text-muted-foreground">{attachment.mimeType}</p>
|
||||
</div>
|
||||
{onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md border border-white/10 p-1 text-muted-foreground transition hover:bg-white/8 hover:text-foreground"
|
||||
onClick={() => onRemove(attachment.id)}
|
||||
aria-label={`Remove ${attachment.filename}`}
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="mt-0.5 rounded-md border border-white/12 bg-white/5 p-1.5">
|
||||
<FileText className="h-3.5 w-3.5" />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">{attachment.filename}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{attachment.mimeType}
|
||||
{attachment.truncated ? " · truncated" : ""}
|
||||
</p>
|
||||
</div>
|
||||
{onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md border border-white/10 p-1 text-muted-foreground transition hover:bg-white/8 hover:text-foreground"
|
||||
onClick={() => onRemove(attachment.id)}
|
||||
aria-label={`Remove ${attachment.filename}`}
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<pre className="mt-2 overflow-x-auto rounded-lg border border-white/8 bg-black/16 p-3 text-xs leading-5 text-inherit whitespace-pre-wrap">
|
||||
{getTextPreview(attachment.text)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user