feat(frontend): artifact 文件卡片支持右键引用

This commit is contained in:
肖应宇 2026-04-16 15:18:14 +08:00
parent 4dbe930775
commit 3d5006af48
1 changed files with 86 additions and 62 deletions

View File

@ -10,9 +10,16 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
import { urlOfArtifact } from "@/core/artifacts/utils"; import { urlOfArtifact } from "@/core/artifacts/utils";
import { useI18n } from "@/core/i18n/hooks"; import { useI18n } from "@/core/i18n/hooks";
import { installSkill } from "@/core/skills/api"; import { installSkill } from "@/core/skills/api";
import { dispatchMentionReference } from "@/core/threads/reference-events";
import { import {
getFileExtensionDisplayName, getFileExtensionDisplayName,
getFileIcon, getFileIcon,
@ -78,69 +85,86 @@ export function ArtifactFileList({
data-testid="artifact-file-list" data-testid="artifact-file-list"
> >
{files.map((file) => ( {files.map((file) => (
<Card <ContextMenu key={file}>
key={file} <ContextMenuTrigger asChild>
className="relative cursor-pointer p-4" <Card
data-testid="artifact-file-card" className="relative cursor-pointer p-4"
onClick={() => handleClick(file)} data-testid="artifact-file-card"
> onClick={() => handleClick(file)}
<CardHeader className="pr-2 pl-1"> >
<CardTitle className="relative overflow-hidden pl-10"> <CardHeader className="pr-2 pl-1">
<div <CardTitle className="relative overflow-hidden pl-10">
className="text-sm font-normal text-ellipsis whitespace-nowrap" <div
title={getFileName(file)} className="text-sm font-normal text-ellipsis whitespace-nowrap"
> title={getFileName(file)}
{truncateMiddle(getFileName(file), 50)}
</div>
</CardTitle>
<div className="absolute top-5 left-4">
{getFileIcon(file, "size-9 stroke-[1px] stroke-[#333333]")}
</div>
<CardDescription className="pl-10 text-xs">
{getFileExtensionDisplayName(file)} file
</CardDescription>
<CardAction>
{file.endsWith(".skill") && (
<Button
variant="ghost"
disabled={!threadId || installingFile === file}
onClick={(e) => handleInstallSkill(e, file)}
>
{installingFile === file ? (
<LoaderIcon className="size-4 animate-spin" />
) : (
<PackageIcon className="size-4" />
)}
{t.common.install}
</Button>
)}
{threadId ? (
<a
href={urlOfArtifact({
filepath: file,
threadId,
download: true,
})}
target="_blank"
onClick={(e) => e.stopPropagation()}
>
<Button variant="ghost"
className="h-full! text-[var(--muted-foreground)]! hover:bg-transparent! hover:text-[#333333]!"
> >
<DownloadIcon className="size-4" /> {truncateMiddle(getFileName(file), 50)}
{t.common.download} </div>
</Button> </CardTitle>
</a> <div className="absolute top-5 left-4">
) : ( {getFileIcon(file, "size-9 stroke-[1px] stroke-[#333333]")}
<Button variant="ghost" disabled> </div>
<DownloadIcon className="size-4" /> <CardDescription className="pl-10 text-xs">
{t.common.download} {getFileExtensionDisplayName(file)} file
</Button> </CardDescription>
)} <CardAction>
</CardAction> {file.endsWith(".skill") && (
</CardHeader> <Button
</Card> variant="ghost"
disabled={!threadId || installingFile === file}
onClick={(e) => handleInstallSkill(e, file)}
>
{installingFile === file ? (
<LoaderIcon className="size-4 animate-spin" />
) : (
<PackageIcon className="size-4" />
)}
{t.common.install}
</Button>
)}
{threadId ? (
<a
href={urlOfArtifact({
filepath: file,
threadId,
download: true,
})}
target="_blank"
onClick={(e) => e.stopPropagation()}
>
<Button
variant="ghost"
className="h-full! text-[var(--muted-foreground)]! hover:bg-transparent! hover:text-[#333333]!"
>
<DownloadIcon className="size-4" />
{t.common.download}
</Button>
</a>
) : (
<Button variant="ghost" disabled>
<DownloadIcon className="size-4" />
{t.common.download}
</Button>
)}
</CardAction>
</CardHeader>
</Card>
</ContextMenuTrigger>
<ContextMenuContent className="min-w-[120px] p-1">
<ContextMenuItem
onSelect={() => {
dispatchMentionReference({
threadId,
filename: getFileName(file),
path: file,
ref_source: "artifact",
});
}}
>
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
))} ))}
</ul> </ul>
); );