fix: show all uploaded files with format and size in left panel

Replace the single-file pill with a full file list showing name
(truncated), format badge, and size for each file. First 5 files
are visible by default with a "show more" toggle for the rest.
Clicking a file selects it in the viewer. Applies to all tools.
This commit is contained in:
SnapOtter
2026-05-11 16:42:24 +08:00
parent 5b868886ba
commit a7a17fbff4
+50 -13
View File
@@ -57,28 +57,40 @@ function canBrowserPreview(url: string, filename?: string | null): boolean {
return BROWSER_PREVIEWABLE_EXTS.has(ext); return BROWSER_PREVIEWABLE_EXTS.has(ext);
} }
function getFileFormat(name: string): string {
const ext = name.split(".").pop()?.toLowerCase() ?? "";
return ext.toUpperCase() || "?";
}
const COLLAPSED_LIMIT = 5;
/** File selection indicator shown in left panel */ /** File selection indicator shown in left panel */
function FileSelectionInfo({ function FileSelectionInfo({
files, files,
selectedFileName, selectedIndex,
selectedFileSize, onSelect,
onClear, onClear,
onAddMore, onAddMore,
}: { }: {
files: File[]; files: File[];
selectedFileName: string | null; selectedIndex: number;
selectedFileSize: number | null; onSelect: (index: number) => void;
onClear: () => void; onClear: () => void;
onAddMore: () => void; onAddMore: () => void;
}) { }) {
const [expanded, setExpanded] = useState(false);
if (files.length === 0) { if (files.length === 0) {
return ( return (
<p className="text-xs text-muted-foreground italic">Drop or upload an image to get started</p> <p className="text-xs text-muted-foreground italic">Drop or upload an image to get started</p>
); );
} }
const showToggle = files.length > COLLAPSED_LIMIT;
const visible = expanded ? files : files.slice(0, COLLAPSED_LIMIT);
return ( return (
<div className="space-y-1"> <div className="space-y-1.5">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-xs font-medium text-foreground">Files ({files.length})</span> <span className="text-xs font-medium text-foreground">Files ({files.length})</span>
<button <button
@@ -89,13 +101,38 @@ function FileSelectionInfo({
+ Add more + Add more
</button> </button>
</div> </div>
<div className="flex items-center gap-1.5 text-xs text-foreground bg-muted rounded px-2 py-1.5">
<CheckCircle2 className="h-3.5 w-3.5 text-green-500 shrink-0" /> <div className="space-y-0.5">
<span className="truncate flex-1">{selectedFileName ?? files[0].name}</span> {visible.map((file, i) => {
<span className="text-muted-foreground shrink-0 ml-1"> const isSelected = i === selectedIndex;
{formatFileSize(selectedFileSize ?? files[0].size)} return (
</span> <button
key={`${file.name}-${i}`}
type="button"
onClick={() => onSelect(i)}
className={`w-full flex items-center gap-1.5 text-xs rounded px-2 py-1.5 text-left transition-colors ${isSelected ? "bg-primary/10 text-foreground" : "text-muted-foreground hover:bg-muted"}`}
>
{isSelected && <CheckCircle2 className="h-3 w-3 text-primary shrink-0" />}
<span className="truncate flex-1 min-w-0">{file.name}</span>
<span className="shrink-0 text-[10px] text-muted-foreground/70">
{getFileFormat(file.name)}
</span>
<span className="shrink-0 text-[10px] tabular-nums">{formatFileSize(file.size)}</span>
</button>
);
})}
</div> </div>
{showToggle && (
<button
type="button"
onClick={() => setExpanded(!expanded)}
className="text-xs text-primary hover:text-primary/80"
>
{expanded ? "Show less" : `Show ${files.length - COLLAPSED_LIMIT} more`}
</button>
)}
<button <button
type="button" type="button"
onClick={onClear} onClick={onClear}
@@ -609,8 +646,8 @@ export function ToolPage() {
<h3 className="text-sm font-medium text-muted-foreground">Files</h3> <h3 className="text-sm font-medium text-muted-foreground">Files</h3>
<FileSelectionInfo <FileSelectionInfo
files={files} files={files}
selectedFileName={selectedFileName} selectedIndex={selectedIndex}
selectedFileSize={selectedFileSize} onSelect={setSelectedIndex}
onClear={reset} onClear={reset}
onAddMore={handleAddMore} onAddMore={handleAddMore}
/> />