From 34a09cb4ca3b3fe05a4569a627e98161a2bea066 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:17:21 +1000 Subject: [PATCH] fix(ui): fix send to canvas crash A few weeks back, we changed how the canvas scales in response to changes in window/panel size. This introduced a bug where if we the user hadn't already clicked the canvas tab once to initialize the stage elements, the stage's dimensions were zero, then the calculation of the stage's scale ends up zero, then something is divided by that zero and Konva dies. This is only a problem on Chromium browsers - somehow Firefox handles it gracefully. Now, when calculating the stage scale, never return a 0 - if it's a zero, return 1 instead. This is enough to fix the crash, but the image ends up centered on the top-left corner of the stage (the origin of the canvas). Because the canvas elements are not initialized at this point (we haven't switched tabs yet), the stage dimensions fall back to (0,0). This means the center of the stage is also (0,0) - so the image is centered on (0,0), the top-left corner of the stage. To fix this, we need to ensure we: - Change to the canvas tab before actually setting the image, so the stage elements are able to initialize - Use `flushSync` to flush DOM updates for this tab change so we actually have DOM elements to work with - Update the stage dimensions once on first load of it (so in the effect that sets up the resize observer, we update the stage dimensions) The result now is the expected behaviour - images sent to canvas do not crash and end up in the center of the canvas. --- .../web/src/features/canvas/components/IAICanvas.tsx | 2 ++ .../frontend/web/src/features/canvas/util/calculateScale.ts | 2 +- .../components/ImageContextMenu/SingleSelectionMenuItems.tsx | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx index 4f9e47282d..4a67898942 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx @@ -154,6 +154,8 @@ const IAICanvas = () => { resizeObserver.observe(containerRef.current); + dispatch(canvasResized(containerRef.current.getBoundingClientRect())); + return () => { resizeObserver.disconnect(); }; diff --git a/invokeai/frontend/web/src/features/canvas/util/calculateScale.ts b/invokeai/frontend/web/src/features/canvas/util/calculateScale.ts index 954c36869c..255cb2850b 100644 --- a/invokeai/frontend/web/src/features/canvas/util/calculateScale.ts +++ b/invokeai/frontend/web/src/features/canvas/util/calculateScale.ts @@ -8,7 +8,7 @@ const calculateScale = ( const scaleX = (containerWidth * padding) / contentWidth; const scaleY = (containerHeight * padding) / contentHeight; const scaleFit = Math.min(1, Math.min(scaleX, scaleY)); - return scaleFit; + return scaleFit ? scaleFit : 1; }; export default calculateScale; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx index e5b9d94578..c50b0c13dd 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx @@ -37,6 +37,7 @@ import { import { ImageDTO } from 'services/api/types'; import { configSelector } from '../../../system/store/configSelectors'; import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions'; +import { flushSync } from 'react-dom'; type SingleSelectionMenuItemsProps = { imageDTO: ImageDTO; @@ -115,8 +116,10 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { const handleSendToCanvas = useCallback(() => { dispatch(sentImageToCanvas()); + flushSync(() => { + dispatch(setActiveTab('unifiedCanvas')); + }); dispatch(setInitialCanvasImage(imageDTO)); - dispatch(setActiveTab('unifiedCanvas')); toaster({ title: t('toast.sentToUnifiedCanvas'),