fix: portal local edit prompts outside canvas transforms
This commit is contained in:
parent
9ddae7423e
commit
852498b6bb
@ -2057,8 +2057,12 @@ export function ImageNode({
|
|||||||
// 文件输入的 ref
|
// 文件输入的 ref
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const [localEditPromptLayerElement, setLocalEditPromptLayerElement] =
|
const [localEditPromptPortalFrame, setLocalEditPromptPortalFrame] = useState<{
|
||||||
useState<HTMLDivElement | null>(null);
|
left: number;
|
||||||
|
top: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
} | null>(null);
|
||||||
const displayImageRef = useRef<HTMLImageElement>(null);
|
const displayImageRef = useRef<HTMLImageElement>(null);
|
||||||
const compareFrameRef = useRef<HTMLDivElement>(null);
|
const compareFrameRef = useRef<HTMLDivElement>(null);
|
||||||
const cropOverlayRef = useRef<HTMLDivElement>(null);
|
const cropOverlayRef = useRef<HTMLDivElement>(null);
|
||||||
@ -2493,6 +2497,59 @@ export function ImageNode({
|
|||||||
})),
|
})),
|
||||||
[localEditPromptInlineOverlays, nodeContentScale]
|
[localEditPromptInlineOverlays, nodeContentScale]
|
||||||
);
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!localEditMode ||
|
||||||
|
!data.src ||
|
||||||
|
localEditPromptViewportOverlays.length === 0 ||
|
||||||
|
typeof window === "undefined"
|
||||||
|
) {
|
||||||
|
setLocalEditPromptPortalFrame(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let animationFrame = 0;
|
||||||
|
const updatePortalFrame = () => {
|
||||||
|
const rect = containerRef.current?.getBoundingClientRect();
|
||||||
|
if (!rect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextFrame = {
|
||||||
|
left: rect.left,
|
||||||
|
top: rect.top,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
};
|
||||||
|
setLocalEditPromptPortalFrame((currentFrame) => {
|
||||||
|
if (
|
||||||
|
currentFrame &&
|
||||||
|
Math.abs(currentFrame.left - nextFrame.left) < 0.5 &&
|
||||||
|
Math.abs(currentFrame.top - nextFrame.top) < 0.5 &&
|
||||||
|
Math.abs(currentFrame.width - nextFrame.width) < 0.5 &&
|
||||||
|
Math.abs(currentFrame.height - nextFrame.height) < 0.5
|
||||||
|
) {
|
||||||
|
return currentFrame;
|
||||||
|
}
|
||||||
|
return nextFrame;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const schedulePortalFrameUpdate = () => {
|
||||||
|
window.cancelAnimationFrame(animationFrame);
|
||||||
|
animationFrame = window.requestAnimationFrame(updatePortalFrame);
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePortalFrame();
|
||||||
|
const intervalId = window.setInterval(updatePortalFrame, 250);
|
||||||
|
window.addEventListener("resize", schedulePortalFrameUpdate);
|
||||||
|
window.addEventListener("scroll", schedulePortalFrameUpdate, true);
|
||||||
|
return () => {
|
||||||
|
window.cancelAnimationFrame(animationFrame);
|
||||||
|
window.clearInterval(intervalId);
|
||||||
|
window.removeEventListener("resize", schedulePortalFrameUpdate);
|
||||||
|
window.removeEventListener("scroll", schedulePortalFrameUpdate, true);
|
||||||
|
};
|
||||||
|
}, [data.src, localEditMode, localEditPromptViewportOverlays.length]);
|
||||||
const currentPickerClickStyle = useMemo(() => {
|
const currentPickerClickStyle = useMemo(() => {
|
||||||
if (!boxCutoutMetrics || !currentClick) return null;
|
if (!boxCutoutMetrics || !currentClick) return null;
|
||||||
|
|
||||||
@ -8147,10 +8204,20 @@ export function ImageNode({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{localEditPromptLayerElement
|
{typeof document !== "undefined" && localEditPromptPortalFrame
|
||||||
? createPortal(
|
? createPortal(
|
||||||
localEditPromptViewportOverlays.map(
|
<div
|
||||||
({ region, isEditing, style }) => (
|
className="pointer-events-none fixed z-[9998] overflow-visible"
|
||||||
|
data-local-edit-control="true"
|
||||||
|
style={{
|
||||||
|
left: localEditPromptPortalFrame.left,
|
||||||
|
top: localEditPromptPortalFrame.top,
|
||||||
|
width: localEditPromptPortalFrame.width,
|
||||||
|
height: localEditPromptPortalFrame.height,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{localEditPromptViewportOverlays.map(
|
||||||
|
({ region, isEditing, style }) => (
|
||||||
<div
|
<div
|
||||||
className="nodrag nopan nowheel pointer-events-auto absolute z-[170] flex flex-col items-stretch gap-1.5"
|
className="nodrag nopan nowheel pointer-events-auto absolute z-[170] flex flex-col items-stretch gap-1.5"
|
||||||
data-canvas-no-drag="true"
|
data-canvas-no-drag="true"
|
||||||
@ -8435,9 +8502,10 @@ export function ImageNode({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
),
|
)}
|
||||||
localEditPromptLayerElement
|
</div>,
|
||||||
|
document.body
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
{localEditDraftRectStyle ? (
|
{localEditDraftRectStyle ? (
|
||||||
@ -8829,10 +8897,6 @@ export function ImageNode({
|
|||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
className="pointer-events-none absolute inset-0 z-[260] overflow-visible"
|
|
||||||
ref={setLocalEditPromptLayerElement}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<AgentExecutionSnapshotDialog
|
<AgentExecutionSnapshotDialog
|
||||||
onOpenChange={setSnapshotDialogOpen}
|
onOpenChange={setSnapshotDialogOpen}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user