import { Button } from '@vertesia/ui/core';
import { useUserSession } from '@vertesia/ui/session';
import {
ChevronDownIcon,
ChevronRightIcon,
FileIcon,
FolderIcon,
FolderOpenIcon,
Loader2Icon,
PackageIcon,
RefreshCwIcon,
} from 'lucide-react';
import React, { useCallback, useState } from 'react';
import { useUITranslation } from '../../../i18n/index.js';
import { useArtifacts, type ArtifactTreeNode } from './hooks/useArtifacts.js';
// ---------------------------------------------------------------------------
// Tree node component
// ---------------------------------------------------------------------------
interface TreeNodeProps {
node: ArtifactTreeNode;
depth: number;
runId: string;
onDownload: (relativePath: string) => void;
downloadingPath: string | null;
}
//** Convert a raw directory segment (e.g. "out_files") into a readable label ("Out Files"). */
function formatDirectoryLabel(name: string): string {
return name
.replace(/[_-]/g, ' ')
.replace(/\b\w/g, (c) => c.toUpperCase());
}
function TreeNode({ node, depth, runId, onDownload, downloadingPath }: TreeNodeProps) {
const [expanded, setExpanded] = useState(true);
if (node.isDirectory) {
return (
{expanded && node.children.map((child) => (
))}
);
}
const isDownloading = downloadingPath === node.path;
return (
);
}
// ---------------------------------------------------------------------------
// Main tab component
// ---------------------------------------------------------------------------
interface ArtifactsTabProps {
runId?: string;
refreshKey?: number;
}
function ArtifactsTabComponent({ runId, refreshKey = 0 }: ArtifactsTabProps) {
const { t } = useUITranslation();
const { client } = useUserSession();
const { tree, flatFiles, isLoading, error, refresh } = useArtifacts(client, runId, refreshKey);
const [downloadingPath, setDownloadingPath] = useState(null);
const handleDownload = useCallback(async (relativePath: string) => {
if (!runId) return;
setDownloadingPath(relativePath);
// Open the tab synchronously (before the await) so the browser treats it as
// a direct user action and doesn't block it as a popup.
const newTab = window.open('', '_blank');
try {
const { url } = await client.files.getArtifactDownloadUrl(runId, relativePath, 'attachment');
if (newTab) {
newTab.location.href = url;
}
} catch (err) {
console.error('Failed to get artifact download URL:', err);
newTab?.close();
} finally {
setDownloadingPath(null);
}
}, [client, runId]);
if (!runId) {
return (
);
}
if (isLoading && flatFiles.length === 0) {
return (
{t('agent.loadingArtifacts')}
);
}
if (error) {
return (
{error}
);
}
if (flatFiles.length === 0) {
return (
{t('agent.noArtifactsYet')}
);
}
return (
{/* Top bar */}
{flatFiles.length} file{flatFiles.length !== 1 ? 's' : ''}
{/* Tree */}
{tree.map((node) => (
))}
);
}
export const ArtifactsTab = React.memo(ArtifactsTabComponent);