@ -1622,11 +1622,14 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
const nanoBananaNode = nodes . find ( ( n ) = > n . type === "nanoBanana" ) ;
const nanoBananaNode = nodes . find ( ( n ) = > n . type === "nanoBanana" ) ;
if ( ! nanoBananaNode ) return ;
if ( ! nanoBananaNode ) return ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
// Set execution state
// Set execution state
set ( {
set ( {
isRunning : true ,
isRunning : true ,
currentNodeIds : [ nanoBananaNode . id ] ,
currentNodeIds : [ nanoBananaNode . id ] ,
_abortController : new AbortC ontroller( ) ,
_abortController : c ontroller,
} ) ;
} ) ;
// Set loading state (triggers edge animations)
// Set loading state (triggers edge animations)
@ -1635,18 +1638,36 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
error : null ,
error : null ,
} ) ;
} ) ;
// Wait 5 seconds (realistic generation time)
// Cancellable 5-second delay
await new Promise ( ( resolve ) = > setTimeout ( resolve , 5000 ) ) ;
try {
await new Promise < void > ( ( resolve , reject ) = > {
const timer = setTimeout ( resolve , 5000 ) ;
signal . addEventListener ( "abort" , ( ) = > {
clearTimeout ( timer ) ;
reject ( new DOMException ( "Aborted" , "AbortError" ) ) ;
} , { once : true } ) ;
} ) ;
} catch {
// Aborted during wait — clean up and exit
updateNodeData ( nanoBananaNode . id , { status : "idle" , error : null } ) ;
set ( { isRunning : false , currentNodeIds : [ ] , _abortController : null } ) ;
return ;
}
// Load the mock output image
// Load the mock output image
const mockImageUrl = "/tutorial/owl-aviator.png" ;
const mockImageUrl = "/tutorial/owl-aviator.png" ;
try {
try {
const response = await fetch ( mockImageUrl ) ;
const response = await fetch ( mockImageUrl , { signal } ) ;
const blob = await response . blob ( ) ;
const blob = await response . blob ( ) ;
const reader = new FileReader ( ) ;
const reader = new FileReader ( ) ;
const base64Image = await new Promise < string > ( ( resolve ) = > {
const base64Image = await new Promise < string > ( ( resolve , reject ) = > {
signal . addEventListener ( "abort" , ( ) = > {
reader . abort ( ) ;
reject ( new DOMException ( "Aborted" , "AbortError" ) ) ;
} , { once : true } ) ;
reader . onloadend = ( ) = > resolve ( reader . result as string ) ;
reader . onloadend = ( ) = > resolve ( reader . result as string ) ;
reader . onerror = ( ) = > reject ( reader . error ) ;
reader . readAsDataURL ( blob ) ;
reader . readAsDataURL ( blob ) ;
} ) ;
} ) ;
@ -1658,11 +1679,14 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
selectedHistoryIndex : 0 ,
selectedHistoryIndex : 0 ,
} ) ;
} ) ;
} catch ( error ) {
} catch ( error ) {
// Fallback to error state if image not found
if ( error instanceof DOMException && error . name === "AbortError" ) {
updateNodeData ( nanoBananaNode . id , {
updateNodeData ( nanoBananaNode . id , { status : "idle" , error : null } ) ;
status : "error" ,
} else {
error : "Failed to load tutorial image" ,
updateNodeData ( nanoBananaNode . id , {
} ) ;
status : "error" ,
error : "Failed to load tutorial image" ,
} ) ;
}
}
}
// Clear execution state
// Clear execution state