Files
agent-framework/python/packages/devui/frontend/src/components/ui/attachment-gallery.tsx
T
Victor Dibia 1ef24d3e91 Python: Add DevUI to AgentFramework (#781)
* add initial backend service code for devui

* add tests

* add frontendcode

* ui updates

* update readme

* ui updates and tweaks

* update ui bundle

* improve ui, add react flow base

* add react flow ui, fix background

* update ui, fix introspection bug

* update readme

* update ui build

* add support for multimodal input - both backend and frontend

* update ui build

* refactor as main framework package

* backend and tests refactor

* ui build update

* ui build update and refactor

* update pyproject.toml, update uv.lock

* update ui build

* ui update to fit oai responses types

* add backend updat and readme update

* mypy and other fixes

* add intial dev guide

* update ui and fix workflow bug

* update ui build, add thread support

* type fixes

* update workflow view

* update uv.lock

* fix workflow iport errors

* lint and other fixes

* mypy fixes

* minor update

* update ui build

* refactor to use oai dependencies directly, update examples to samples, improve typing

* readme update

* update ui and ui build

* fix workflow pyright error

* update ui, fix issues with run workflow placement, miniamp menu, etc

* make samples integrate serve

---------

Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
2025-09-22 23:30:08 +00:00

115 lines
3.3 KiB
TypeScript

/**
* AttachmentGallery - Shows uploaded files with thumbnails and remove options
*/
import { useState } from "react";
import { FileText, Image, Trash2 } from "lucide-react";
export interface AttachmentItem {
id: string;
file: File;
preview?: string; // Data URL for preview
type: "image" | "pdf" | "other";
}
interface AttachmentGalleryProps {
attachments: AttachmentItem[];
onRemoveAttachment: (id: string) => void;
className?: string;
}
export function AttachmentGallery({
attachments,
onRemoveAttachment,
className = "",
}: AttachmentGalleryProps) {
if (attachments.length === 0) return null;
return (
<div className={`flex flex-wrap gap-2 p-2 bg-muted rounded-lg ${className}`}>
{attachments.map((attachment) => (
<AttachmentPreview
key={attachment.id}
attachment={attachment}
onRemove={() => onRemoveAttachment(attachment.id)}
/>
))}
</div>
);
}
interface AttachmentPreviewProps {
attachment: AttachmentItem;
onRemove: () => void;
}
function AttachmentPreview({ attachment, onRemove }: AttachmentPreviewProps) {
const [isHovered, setIsHovered] = useState(false);
const renderPreview = () => {
switch (attachment.type) {
case "image":
return attachment.preview ? (
<img
src={attachment.preview}
alt={attachment.file.name}
className="w-full h-full object-cover"
/>
) : (
<div className="flex items-center justify-center w-full h-full bg-gray-200">
<Image className="h-6 w-6 text-gray-400" />
</div>
);
case "pdf":
return (
<div className="flex flex-col items-center justify-center w-full h-full bg-red-50">
<FileText className="h-6 w-6 text-red-500 mb-1" />
<span className="text-xs text-red-600">PDF</span>
</div>
);
default:
return (
<div className="flex flex-col items-center justify-center w-full h-full bg-gray-100">
<FileText className="h-6 w-6 text-gray-500 mb-1" />
<span className="text-xs text-gray-600">FILE</span>
</div>
);
}
};
return (
<div
className="relative w-16 h-16 rounded border overflow-hidden group cursor-pointer"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
title={attachment.file.name}
>
{renderPreview()}
{/* Dark overlay with centered delete icon on hover */}
<div
className={`absolute inset-0 bg-black/60 flex items-center justify-center transition-all duration-200 ease-in-out ${
isHovered
? 'opacity-100 backdrop-blur-sm'
: 'opacity-0 pointer-events-none'
}`}
onClick={onRemove}
>
<div className={`transition-all duration-200 ease-in-out ${
isHovered
? 'scale-100 opacity-100'
: 'scale-75 opacity-0'
}`}>
<Trash2 className="h-5 w-5 text-white drop-shadow-lg" />
</div>
</div>
{/* File name tooltip */}
<div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-75 text-white text-xs p-1 truncate opacity-0 group-hover:opacity-100 transition-opacity duration-200">
{attachment.file.name}
</div>
</div>
);
}