diff --git a/frontend/apps/artcraft/app/src/components/signaled/TopBar/TaskQueue.tsx b/frontend/apps/artcraft/app/src/components/signaled/TopBar/TaskQueue.tsx index a21a377eb7..3a563d35d1 100644 --- a/frontend/apps/artcraft/app/src/components/signaled/TopBar/TaskQueue.tsx +++ b/frontend/apps/artcraft/app/src/components/signaled/TopBar/TaskQueue.tsx @@ -51,6 +51,7 @@ import { import { coverImageCache } from "~/pages/PageImageTo3DObject/ImageTo3DStore"; import { useCreditsState } from "@storyteller/credits"; import { getMetaForTask, cleanupOldEntries } from "./taskEnqueueMeta"; +import { twMerge } from "tailwind-merge"; type InProgressTask = { id: string; @@ -103,6 +104,90 @@ const formatTimeLeft = (ms: number): string => { return `~ ${seconds}s left`; }; +const PromptLine = ({ + prompt, + className, +}: { + prompt: string; + className?: string; +}) => { + const [marqueePlaying, setMarqueePlaying] = useState(false); + const [promptOverflows, setPromptOverflows] = useState(false); + const promptRef = useRef(null); + useEffect(() => { + const el = promptRef.current; + if (el) setPromptOverflows(el.scrollWidth > el.clientWidth); + }, [prompt]); + + return ( + 300 ? prompt.slice(0, 300) + "\u2026" : prompt} + position="bottom" + strategy="fixed" + className="max-w-[280px] text-wrap text-xs" + zIndex={50} + delay={400} + > +
setMarqueePlaying(true) : undefined + } + onMouseLeave={ + promptOverflows ? () => setMarqueePlaying(false) : undefined + } + > +
+ {prompt} +
+
+
+ ); +}; + +const CopyPromptButton = ({ prompt }: { prompt: string }) => { + const [copied, setCopied] = useState(false); + return ( + + + + ); +}; + const InProgressCard = ({ task, onDismiss, @@ -118,61 +203,40 @@ const InProgressCard = ({ ? formatTimeLeft(task.estimatedTimeLeftMs) : null; const isSeedance2 = task.modelType === "seedance_2p0"; - const hasRefImages = - task.refImageUrls && task.refImageUrls.length > 0; + const hasRefImages = task.refImageUrls && task.refImageUrls.length > 0; const thumbnailContent = hasRefImages ? ( - -
- Reference { - e.currentTarget.style.display = "none"; - }} - /> -
-
- -
- {task.refImageUrls!.length > 1 && ( -
- +{task.refImageUrls!.length - 1} -
- )} -
- - ) : ( - -
+
+ Reference { + e.currentTarget.style.display = "none"; + }} + /> +
+
- + {task.refImageUrls!.length > 1 && ( +
+ +{task.refImageUrls!.length - 1} +
+ )} +
+ ) : ( +
+ +
); return ( @@ -199,7 +263,7 @@ const InProgressCard = ({ )}
-
+
{progressPercent}%
@@ -219,21 +283,25 @@ const InProgressCard = ({
{timeLabel && ( -
{timeLabel}
+
{timeLabel}
+ )} + {task.prompt && } + +
+ {task.prompt && } + {onDismiss && ( + )}
- {onDismiss && ( - - )} ); @@ -255,39 +323,29 @@ const CompletedCard = ({ role={onClick ? "button" : undefined} tabIndex={onClick ? 0 : -1} > - -
- {task.thumbnailUrl ? ( - {task.title} { - e.currentTarget.src = getPlaceholderForMediaClass( - task.mediaFileClass, - ); - e.currentTarget.style.opacity = "0.3"; - // Set the `data-brokenurl` property for debugging the broken images: - (e.currentTarget as HTMLImageElement).dataset.brokenurl = - task.thumbnailUrl; - }} - className="h-full w-full object-cover" - /> - ) : ( -
- Done -
- )} -
-
-
+
+ {task.thumbnailUrl ? ( + {task.title} { + e.currentTarget.src = getPlaceholderForMediaClass( + task.mediaFileClass, + ); + e.currentTarget.style.opacity = "0.3"; + // Set the `data-brokenurl` property for debugging the broken images: + (e.currentTarget as HTMLImageElement).dataset.brokenurl = + task.thumbnailUrl; + }} + className="h-full w-full object-cover" + /> + ) : ( +
+ Done +
+ )} +
+
{task.title}
@@ -301,19 +359,23 @@ const CompletedCard = ({ {task.completedAt.toISOString()}
)} + {task.prompt && } +
+
+ {task.prompt && } + {onDismiss && ( + + )}
- {onDismiss && ( - - )} ); }; @@ -346,70 +408,41 @@ const FailedCard = ({ task: FailedTask; onDismiss?: () => void; }) => { - const [copied, setCopied] = useState(false); - const [marqueePlaying, setMarqueePlaying] = useState(false); - const [promptOverflows, setPromptOverflows] = useState(false); - const promptRef = useRef(null); - useEffect(() => { - const el = promptRef.current; - if (el) setPromptOverflows(el.scrollWidth > el.clientWidth); - }, [task.prompt]); const statusLabel = FAILED_STATUS_LABEL[task.status] || "Failed"; - const hasRefImages = - task.refImageUrls && task.refImageUrls.length > 0; + const hasRefImages = task.refImageUrls && task.refImageUrls.length > 0; const thumbnailContent = hasRefImages ? ( - -
- Reference { - e.currentTarget.style.display = "none"; - }} - /> -
-
- -
- {task.refImageUrls!.length > 1 && ( -
- +{task.refImageUrls!.length - 1} -
- )} -
- - ) : ( - -
+
+ Reference { + e.currentTarget.style.display = "none"; + }} + /> +
+
- + {task.refImageUrls!.length > 1 && ( +
+ +{task.refImageUrls!.length - 1} +
+ )} +
+ ) : ( +
+ +
); return ( @@ -434,90 +467,37 @@ const FailedCard = ({ {task.failureReason && (
+
+ {task.failureReason} +
+
+ {task.failureMessage} +
+
+ ) : ( + task.failureReason + ) + } position="bottom" strategy="fixed" className="max-w-[280px] text-wrap bg-danger text-xs" zIndex={50} delay={300} > -
+
{task.failureReason}
)}
- {task.failureMessage && ( -
- -
- {task.failureMessage} -
-
-
- )} - {task.prompt && ( - 300 ? task.prompt.slice(0, 300) + "…" : task.prompt} - position="bottom" - strategy="fixed" - className="max-w-[280px] text-wrap text-xs" - zIndex={50} - delay={400} - > -
setMarqueePlaying(true) : undefined} - onMouseLeave={promptOverflows ? () => setMarqueePlaying(false) : undefined} - > -
- {task.prompt} -
-
-
- )} + {task.prompt && }
- {task.prompt && ( - - - - )} + {task.prompt && } {onDismiss && (