diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index e230a0eee..dc128785a 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -1869,6 +1869,7 @@ export function ImageNode({ const [boxCutoutDragEnd, setBoxCutoutDragEnd] = useState(null); const [localEditMode, setLocalEditMode] = useState(false); + const [localEditViewportRevision, setLocalEditViewportRevision] = useState(0); const [localEditRegions, setLocalEditRegions] = useState( () => getLocalEditRegionsFromMetadata(data.metadata) ); @@ -2047,6 +2048,7 @@ export function ImageNode({ >({}); const localEditPromptDraftRefs = useRef>({}); const localEditRegionsRef = useRef(localEditRegions); + const localEditViewportFrameRef = useRef(null); const updateNode = useCanvasStore((state) => state.updateNode); const deleteNode = useCanvasStore((state) => state.deleteNode); @@ -2457,6 +2459,7 @@ export function ImageNode({ editingLocalEditRegionId, localEditMode, localEditRegionOverlays, + localEditViewportRevision, nodeContentScale, ]); const currentPickerClickStyle = useMemo(() => { @@ -2542,6 +2545,44 @@ export function ImageNode({ localEditRegionsRef.current = localEditRegions; }, [localEditRegions]); + useEffect(() => { + if (!localEditMode) return; + + const scheduleLocalEditViewportRefresh = () => { + setLocalEditViewportRevision((revision) => revision + 1); + if (localEditViewportFrameRef.current !== null) { + window.cancelAnimationFrame(localEditViewportFrameRef.current); + } + localEditViewportFrameRef.current = window.requestAnimationFrame(() => { + localEditViewportFrameRef.current = null; + setLocalEditViewportRevision((revision) => revision + 1); + }); + }; + + let previousViewport = useCanvasStore.getState().viewport; + const unsubscribe = useCanvasStore.subscribe((state) => { + const nextViewport = state.viewport; + if ( + nextViewport.x === previousViewport.x && + nextViewport.y === previousViewport.y && + nextViewport.zoom === previousViewport.zoom + ) { + return; + } + + previousViewport = nextViewport; + scheduleLocalEditViewportRefresh(); + }); + + return () => { + unsubscribe(); + if (localEditViewportFrameRef.current !== null) { + window.cancelAnimationFrame(localEditViewportFrameRef.current); + localEditViewportFrameRef.current = null; + } + }; + }, [localEditMode]); + const commitLocalEditRegions = useCallback( (regions: LocalEditRegion[]) => { const nextRegions = reindexLocalEditRegions( @@ -6680,7 +6721,7 @@ export function ImageNode({ prompt: params.promptText, modelVersion: params.modelVersion, status: "completed", - activeView: splatUrl ? "splat" : imageUrl ? "image" : "mesh", + activeView: meshUrl ? "mesh" : imageUrl ? "image" : splatUrl ? "splat" : "image", firstPerson: false, hotspots: [], semanticsMetadata: @@ -7017,6 +7058,7 @@ export function ImageNode({ provider: "worldlabs-3d", mode: "world-image", sourceImageUrl, + generationStartedAt: new Date().toISOString(), }, }, connections: [node.id], @@ -7071,6 +7113,7 @@ export function ImageNode({ provider: "worldlabs-3d", mode: "world-image", sourceImageUrl, + generationStartedAt: new Date().toISOString(), assetPayload: createAssetPayload, }, },