140 lines
5.0 KiB
TypeScript
140 lines
5.0 KiB
TypeScript
"use client";
|
||
|
||
import { AlertCircle, ImagePlus, Loader2 } from "lucide-react";
|
||
import type React from "react";
|
||
import { GenerationDotPlaceholder } from "@/components/generation-dot-placeholder";
|
||
import type { RequestFailureDetails } from "@/lib/request-error";
|
||
|
||
interface ImageNodeEmptyStateProps {
|
||
detailProgressLabel?: string;
|
||
fileInputRef: React.Ref<HTMLInputElement>;
|
||
generationProgress?: number | null;
|
||
failure?: RequestFailureDetails;
|
||
requestId?: string;
|
||
onImageUpload: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||
onSelectFile: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||
status?: string;
|
||
viewportScale?: number;
|
||
}
|
||
|
||
export function ImageNodeDeferredLoadState() {
|
||
return (
|
||
<div className="flex h-full w-full items-center justify-center bg-slate-50 text-slate-400 text-xs dark:bg-zinc-900 dark:text-zinc-500">
|
||
<div className="flex flex-col items-center gap-2">
|
||
<Loader2 className="h-4 w-4 animate-spin" />
|
||
<span>图片即将进入视口时加载</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function ImageNodeProcessingOverlay({
|
||
detailProgressLabel,
|
||
generationProgress,
|
||
viewportScale,
|
||
}: {
|
||
detailProgressLabel?: string;
|
||
generationProgress?: number | null;
|
||
viewportScale?: number;
|
||
}) {
|
||
return (
|
||
<div className="absolute inset-0 overflow-hidden rounded-[inherit] bg-white/96 backdrop-blur-[1px]">
|
||
<GenerationDotPlaceholder
|
||
className="h-full w-full rounded-none"
|
||
compact
|
||
phase="processing"
|
||
progress={generationProgress}
|
||
title={detailProgressLabel || "正在处理图片"}
|
||
viewportScale={viewportScale}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function ImageNodeEmptyState({
|
||
detailProgressLabel,
|
||
fileInputRef,
|
||
generationProgress,
|
||
failure,
|
||
requestId,
|
||
onImageUpload,
|
||
onSelectFile,
|
||
status,
|
||
viewportScale,
|
||
}: ImageNodeEmptyStateProps) {
|
||
if (status === "uploading") {
|
||
return (
|
||
<div className="flex h-full w-full select-none flex-col items-center justify-center bg-[#f7f9fc] px-6 text-center dark:bg-zinc-900">
|
||
<span className="mb-3 flex h-12 w-12 items-center justify-center rounded-[18px] border border-[#dde5f4] bg-white text-[#5b76fe] shadow-[0_0_0_1px_rgba(219,226,238,0.22)] dark:border-white/12 dark:bg-white/8">
|
||
<Loader2 className="h-5 w-5 animate-spin" />
|
||
</span>
|
||
<span className="select-none text-sm font-medium text-[#3f4b63] dark:text-zinc-200">
|
||
正在上传图片
|
||
</span>
|
||
<span className="mt-1 select-none text-xs text-[#7a8395] dark:text-zinc-500">
|
||
上传完成后会自动显示
|
||
</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (status === "generating") {
|
||
return (
|
||
<GenerationDotPlaceholder
|
||
className="h-full w-full rounded-none"
|
||
compact
|
||
phase="processing"
|
||
progress={generationProgress}
|
||
title={detailProgressLabel || undefined}
|
||
viewportScale={viewportScale}
|
||
/>
|
||
);
|
||
}
|
||
|
||
if (status === "failed") {
|
||
const failedTitle = detailProgressLabel || "生成失败";
|
||
return (
|
||
<div role="alert" className="flex h-full w-full flex-col items-center justify-center overflow-auto bg-[#fff7f8] px-6 text-center dark:bg-red-950/40">
|
||
<span className="mb-3 flex h-12 w-12 items-center justify-center rounded-[18px] border border-[#f2d7dd] bg-white text-[#d24f63] shadow-[0_0_0_1px_rgba(242,215,221,0.22)]">
|
||
<AlertCircle className="h-5 w-5" />
|
||
</span>
|
||
<span className="select-none text-sm font-medium text-[#c84b5e]">
|
||
{failedTitle}
|
||
</span>
|
||
<span className="mt-1 text-xs text-[#b77480]">
|
||
{failure?.recommendation || (failedTitle.includes("上传") ? "请重新选择文件" : "请查看对话中的错误原因和处理建议")}
|
||
</span>
|
||
{requestId ? <span className="mt-2 select-text break-all text-xs text-[#b77480]">请求编号:{requestId}</span> : null}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="flex h-full w-full select-none flex-col items-center justify-center bg-[#f7f9fc] px-6 text-center dark:bg-zinc-900">
|
||
<span className="mb-3 flex h-12 w-12 items-center justify-center rounded-[18px] border border-[#dde5f4] bg-white text-[#5b76fe] shadow-[0_0_0_1px_rgba(219,226,238,0.22)]">
|
||
<ImagePlus className="h-5 w-5" />
|
||
</span>
|
||
<span className="select-none text-sm font-medium text-[#3f4b63]">
|
||
上传图片
|
||
</span>
|
||
<span className="mb-4 mt-1 select-none text-xs text-[#7a8395]">
|
||
添加参考图、角色图或关键帧素材
|
||
</span>
|
||
<input
|
||
accept="image/*"
|
||
className="hidden"
|
||
onChange={onImageUpload}
|
||
ref={fileInputRef}
|
||
type="file"
|
||
/>
|
||
<button
|
||
className="cursor-pointer rounded-[14px] border border-[#d7dff1] bg-white px-4 py-2 text-sm font-medium text-[#3954db] shadow-[0_0_0_1px_rgba(219,226,238,0.24)] transition-colors hover:bg-[#f4f7ff]"
|
||
onClick={onSelectFile}
|
||
type="button"
|
||
>
|
||
选择文件
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|