UX: Improve video generation validation feedback (#965)

**UX: Improve video generation validation feedback**

- Enable "Generate" button interactions to provide actionable error
toasts instead of silent disabling.
- Add inline "Starting frame required" warning with icon when a
reference image is missing.
- Update tooltip component to support conditional disabling for better
UX control.
This commit is contained in:
BFlat authored and GitHub committed 2026-01-07 21:01:29 +07:00
1 parent 41f720ee08
commit 62b67ce2c4
2 files changed
+42 -23

No files matched your search

@@ -17,6 +17,7 @@ import {
faSpinnerThird,
faWaveformLines,
} from "@fortawesome/pro-solid-svg-icons";
import {faCircleInfo} from "@fortawesome/pro-regular-svg-icons";
import {
faRectangle,
faSquare,
@@ -249,12 +250,13 @@ export const PromptBoxVideo = ({
if (!selectedModel) {
console.warn("Cannot generate video: no model selected");
toast.error("Please select a model to generate video");
return;
}
if (selectedModel?.requiresImage && referenceImages.length === 0) {
console.warn("Cannot generate video: no reference image provided");
toast.error("Please choose a starting frame image to generate video");
toast.error("Please add a starting frame image to generate video");
return;
}
@@ -393,7 +395,7 @@ export const PromptBoxVideo = ({
<img
src={image.url}
alt="Reference preview"
className="w-full h-full object-contain"
className="h-full w-full object-contain"
/>,
);
setIsModalOpen(true);
@@ -517,26 +519,37 @@ export const PromptBoxVideo = ({
)}
</div>
<div className="flex items-center gap-2">
<Button
className="flex items-center border-none bg-primary px-3 text-sm text-white disabled:cursor-not-allowed disabled:opacity-50"
icon={!isEnqueueing ? faSparkles : undefined}
onClick={handleEnqueue}
disabled={
isEnqueueing ||
!prompt.trim() ||
modelNeedsAnImageButNoneAreSelected ||
!selectedModel
}
{modelNeedsAnImageButNoneAreSelected && (
<span className="flex items-center gap-1.5 text-xs text-red-500 font-medium animate-pulse">
<FontAwesomeIcon icon={faCircleInfo} />
Starting frame required
</span>
)}
<Tooltip
content="Add a starting image before generating"
position="top"
className="z-50"
delay={0}
disabled={!modelNeedsAnImageButNoneAreSelected}
>
{isEnqueueing ? (
<FontAwesomeIcon
icon={faSpinnerThird}
className="animate-spin text-lg"
/>
) : (
"Generate"
)}
</Button>
<div>
<Button
className="flex items-center border-none bg-primary px-3 text-sm text-white disabled:cursor-not-allowed disabled:opacity-50"
icon={!isEnqueueing ? faSparkles : undefined}
onClick={handleEnqueue}
disabled={isEnqueueing || !prompt.trim()}
>
{isEnqueueing ? (
<FontAwesomeIcon
icon={faSpinnerThird}
className="animate-spin text-lg"
/>
) : (
"Generate"
)}
</Button>
</div>
</Tooltip>
</div>
</div>
</div>
@@ -18,6 +18,7 @@ interface TooltipProps {
interactive?: boolean;
onOpenChange?: (open: boolean) => void;
zIndex?: number;
disabled?: boolean;
}
export const Tooltip = ({
@@ -32,6 +33,7 @@ export const Tooltip = ({
interactive = false,
onOpenChange,
zIndex = 10,
disabled = false,
}: TooltipProps) => {
const [isShowing, setIsShowing] = useState(false);
const triggerRef = useRef<HTMLDivElement>(null);
@@ -118,13 +120,17 @@ export const Tooltip = ({
};
useEffect(() => {
if (disabled) {
setIsShowing(false);
return;
}
if (!checkForOpenPopovers()) {
const shouldShow =
isHoveringTrigger || (interactive && isHoveringTooltip);
setIsShowing(shouldShow);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isHoveringTrigger, isHoveringTooltip, interactive]);
}, [isHoveringTrigger, isHoveringTooltip, interactive, disabled]);
useEffect(() => {
onOpenChange?.(isShowing);
@@ -135,7 +141,7 @@ export const Tooltip = ({
ref={triggerRef}
onMouseEnter={() => {
setIsHoveringTrigger(true);
if (!checkForOpenPopovers()) {
if (!checkForOpenPopovers() && !disabled) {
setIsShowing(true);
}
}}