fix: keep local edit prompts aligned while panning
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-05-20 16:48:42 +08:00
parent 65b3e2f191
commit 0f91db7661

View File

@ -1869,6 +1869,7 @@ export function ImageNode({
const [boxCutoutDragEnd, setBoxCutoutDragEnd] =
useState<BoxCutoutPoint | null>(null);
const [localEditMode, setLocalEditMode] = useState(false);
const [localEditViewportRevision, setLocalEditViewportRevision] = useState(0);
const [localEditRegions, setLocalEditRegions] = useState<LocalEditRegion[]>(
() => getLocalEditRegionsFromMetadata(data.metadata)
);
@ -2047,6 +2048,7 @@ export function ImageNode({
>({});
const localEditPromptDraftRefs = useRef<Record<string, string>>({});
const localEditRegionsRef = useRef<LocalEditRegion[]>(localEditRegions);
const localEditViewportFrameRef = useRef<number | null>(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,
},
},