"use client";
// Small presentational components for chat attachments. Kept in a
// separate file so ChatTab.tsx stays focused on state + send/receive
// orchestration. Both variants share the file-icon + name + size
// layout; the only difference is the trailing action (remove for
// pending, download for completed).
import type { ChatAttachment } from "./types";
function formatSize(bytes: number | undefined): string {
if (bytes == null) return "";
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
/** Inline pill for a file that the user has picked but not yet sent.
* Renders above the textarea; clicking × pops it from the pending
* list without uploading. */
export function PendingAttachmentPill({
file,
onRemove,
}: {
file: File;
onRemove: () => void;
}) {
return (
{file.name}{formatSize(file.size)}
);
}
/** Chip rendered inside a message bubble for a sent/received file.
* Clicking triggers the download via the passed onDownload callback
* so the parent controls workspace-scoped URL resolution. */
export function AttachmentChip({
attachment,
onDownload,
tone,
}: {
attachment: ChatAttachment;
onDownload: (a: ChatAttachment) => void;
tone: "user" | "agent";
}) {
const toneClasses =
tone === "user"
? "border-blue-400/30 bg-accent-strong/20 hover:bg-accent-strong/30 text-blue-100"
: "border-line/50 bg-surface-card/40 hover:bg-surface-card/50 text-ink";
return (
);
}
function FileGlyph({ className }: { className?: string }) {
return (
);
}
function DownloadGlyph({ className }: { className?: string }) {
return (
);
}