Browse Source

feat(08-01): add error overlay and toast trigger to GenerateImageNode

- Import useToast and useRef for error handling
- Add useEffect to trigger persistent toast when error occurs
- Add red error overlay visible over previous output image
- Overlay shows error icon and directs user to toast for details

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
77fdea50fa
  1. 30
      src/components/nodes/GenerateImageNode.tsx

30
src/components/nodes/GenerateImageNode.tsx

@ -1,6 +1,6 @@
"use client";
import React, { useCallback, useState, useEffect, useMemo } from "react";
import React, { useCallback, useState, useEffect, useMemo, useRef } from "react";
import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
import { BaseNode } from "./BaseNode";
import { ModelParameters } from "./ModelParameters";
@ -8,6 +8,7 @@ import { useWorkflowStore, saveNanoBananaDefaults } from "@/store/workflowStore"
import { NanoBananaNodeData, AspectRatio, Resolution, ModelType, ProviderType, SelectedModel, ModelInputDef } from "@/types";
import { ProviderModel, ModelCapability } from "@/lib/providers/types";
import { ModelSearchDialog } from "@/components/modals/ModelSearchDialog";
import { useToast } from "@/components/Toast";
// All 10 aspect ratios supported by both models
const ASPECT_RATIOS: AspectRatio[] = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
@ -385,6 +386,17 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
const isNanoBananaPro = isGeminiProvider && nodeData.model === "nano-banana-pro";
const hasCarouselImages = (nodeData.imageHistory || []).length > 1;
// Track previous status to detect error transitions
const prevStatusRef = useRef(nodeData.status);
// Show toast when error occurs
useEffect(() => {
if (nodeData.status === "error" && prevStatusRef.current !== "error" && nodeData.error) {
useToast.getState().show("Generation failed", "error", true, nodeData.error);
}
prevStatusRef.current = nodeData.status;
}, [nodeData.status, nodeData.error]);
return (
<>
<BaseNode
@ -541,6 +553,22 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
</svg>
</div>
)}
{/* Error overlay when generation failed */}
{nodeData.status === "error" && (
<div className="absolute inset-0 bg-red-900/70 rounded flex flex-col items-center justify-center gap-1">
<svg
className="w-6 h-6 text-red-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-red-400 text-xs font-medium">Generation failed</span>
<span className="text-red-400/70 text-[10px]">See toast for details</span>
</div>
)}
{/* Loading overlay for carousel navigation */}
{isLoadingCarouselImage && (
<div className="absolute inset-0 bg-neutral-900/50 rounded flex items-center justify-center">

Loading…
Cancel
Save