diff --git a/invokeai/frontend/web/src/app/store/nanostores/authToken.ts b/invokeai/frontend/web/src/app/store/nanostores/authToken.ts index 9f07e3535e8..1b1e2137309 100644 --- a/invokeai/frontend/web/src/app/store/nanostores/authToken.ts +++ b/invokeai/frontend/web/src/app/store/nanostores/authToken.ts @@ -1,6 +1,11 @@ -import { atom } from 'nanostores'; +import { atom, computed } from 'nanostores'; /** * The user's auth token. */ export const $authToken = atom(); + +/** + * The crossOrigin value to use for all image loading. Depends on whether the user is authenticated. + */ +export const $crossOrigin = computed($authToken, (token) => (token ? 'use-credentials' : 'anonymous')); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts index 4bb4d06f7ad..8e34f2169c5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts @@ -1,5 +1,5 @@ import type { Selector, Store } from '@reduxjs/toolkit'; -import { $authToken } from 'app/store/nanostores/authToken'; +import { $authToken, $crossOrigin } from 'app/store/nanostores/authToken'; import { roundDownToMultiple, roundUpToMultiple } from 'common/util/roundDownToMultiple'; import { clamp } from 'es-toolkit/compat'; import type { @@ -494,7 +494,7 @@ export async function loadImage(src: string, fetchUrlFirst?: boolean): Promise resolve(imageElement); imageElement.onerror = (error) => reject(error); - imageElement.crossOrigin = $authToken.get() ? 'use-credentials' : 'anonymous'; + imageElement.crossOrigin = $crossOrigin.get(); imageElement.src = url; }); } diff --git a/invokeai/frontend/web/src/features/dnd/DndImage.tsx b/invokeai/frontend/web/src/features/dnd/DndImage.tsx index e67c9013a45..71488500b88 100644 --- a/invokeai/frontend/web/src/features/dnd/DndImage.tsx +++ b/invokeai/frontend/web/src/features/dnd/DndImage.tsx @@ -2,6 +2,8 @@ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'; import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; import type { ImageProps, SystemStyleObject } from '@invoke-ai/ui-library'; import { Image } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { $crossOrigin } from 'app/store/nanostores/authToken'; import { useAppStore } from 'app/store/storeHooks'; import { singleImageDndSource } from 'features/dnd/dnd'; import type { DndDragPreviewSingleImageState } from 'features/dnd/DndDragPreviewSingleImage'; @@ -29,6 +31,8 @@ type Props = { export const DndImage = memo( forwardRef(({ imageDTO, asThumbnail, ...rest }: Props, forwardedRef) => { const store = useAppStore(); + const crossOrigin = useStore($crossOrigin); + const [isDragging, setIsDragging] = useState(false); const ref = useRef(null); useImperativeHandle(forwardedRef, () => ref.current!, []); @@ -76,6 +80,7 @@ export const DndImage = memo( height={imageDTO.height} sx={sx} data-is-dragging={isDragging} + crossOrigin={!asThumbnail ? crossOrigin : undefined} {...rest} /> {dragPreviewState?.type === 'single-image' ? createSingleImageDragPreview(dragPreviewState) : null} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx index edfada8bc2d..7123a8fbf37 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx @@ -1,4 +1,6 @@ import { Box, Flex, Image } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { $crossOrigin } from 'app/store/nanostores/authToken'; import { useAppSelector } from 'app/store/storeHooks'; import { useBoolean } from 'common/hooks/useBoolean'; import { preventDefault } from 'common/util/stopPropagation'; @@ -12,6 +14,8 @@ import type { ComparisonProps } from './common'; import { fitDimsToContainer, getSecondImageDims } from './common'; export const ImageComparisonHover = memo(({ firstImage, secondImage, rect }: ComparisonProps) => { + const crossOrigin = useStore($crossOrigin); + const comparisonFit = useAppSelector(selectComparisonFit); const imageContainerRef = useRef(null); const mouseOver = useBoolean(false); @@ -53,6 +57,7 @@ export const ImageComparisonHover = memo(({ firstImage, secondImage, rect }: Com id="image-comparison-hover-first-image" src={firstImage.image_url} fallbackSrc={firstImage.thumbnail_url} + crossOrigin={crossOrigin} w={fittedDims.width} h={fittedDims.height} maxW="full" @@ -89,6 +94,7 @@ export const ImageComparisonHover = memo(({ firstImage, secondImage, rect }: Com id="image-comparison-hover-second-image" src={secondImage.image_url} fallbackSrc={secondImage.thumbnail_url} + crossOrigin={crossOrigin} w={compareImageDims.width} h={compareImageDims.height} maxW={fittedDims.width} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx index a84e842dccc..45c4201c19d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx @@ -1,4 +1,6 @@ import { Flex, Image } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { $crossOrigin } from 'app/store/nanostores/authToken'; import type { ComparisonProps } from 'features/gallery/components/ImageViewer/common'; import { ImageComparisonLabel } from 'features/gallery/components/ImageViewer/ImageComparisonLabel'; import { VerticalResizeHandle } from 'features/ui/components/tabs/ResizeHandle'; @@ -41,6 +43,8 @@ export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Comp ImageComparisonSideBySide.displayName = 'ImageComparisonSideBySide'; const SideBySideImage = memo(({ imageDTO, type }: { imageDTO: ImageDTO; type: 'first' | 'second' }) => { + const crossOrigin = useStore($crossOrigin); + return ( @@ -52,6 +56,7 @@ const SideBySideImage = memo(({ imageDTO, type }: { imageDTO: ImageDTO; type: 'f maxH="full" src={imageDTO.image_url} fallbackSrc={imageDTO.thumbnail_url} + crossOrigin={crossOrigin} objectFit="contain" borderRadius="base" /> diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx index 1f0d64aafeb..ce4bc5f083d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx @@ -1,4 +1,6 @@ import { Box, Flex, Icon, Image } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { $crossOrigin } from 'app/store/nanostores/authToken'; import { useAppSelector } from 'app/store/storeHooks'; import { preventDefault } from 'common/util/stopPropagation'; import { TRANSPARENCY_CHECKERBOARD_PATTERN_DARK_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern'; @@ -21,6 +23,7 @@ const HANDLE_LEFT_INITIAL_PX = `calc(${INITIAL_POS} - ${HANDLE_HITBOX / 2}px)`; export const ImageComparisonSlider = memo(({ firstImage, secondImage, rect }: ComparisonProps) => { const comparisonFit = useAppSelector(selectComparisonFit); + const crossOrigin = useStore($crossOrigin); // How far the handle is from the left - this will be a CSS calculation that takes into account the handle width const [left, setLeft] = useState(HANDLE_LEFT_INITIAL_PX); @@ -132,6 +135,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage, rect }: Co id="image-comparison-second-image" src={secondImage.image_url} fallbackSrc={secondImage.thumbnail_url} + crossOrigin={crossOrigin} w={compareImageDims.width} h={compareImageDims.height} maxW={fittedDims.width} @@ -154,6 +158,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage, rect }: Co id="image-comparison-first-image" src={firstImage.image_url} fallbackSrc={firstImage.thumbnail_url} + crossOrigin={crossOrigin} w={fittedDims.width} h={fittedDims.height} objectFit="cover" diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx index a258b60efee..3a960147719 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx @@ -1,4 +1,6 @@ import { Flex, Icon, Image, Tooltip } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; +import { $crossOrigin } from 'app/store/nanostores/authToken'; import { typedMemo } from 'common/util/typedMemo'; import { PiImage } from 'react-icons/pi'; @@ -6,6 +8,8 @@ const IMAGE_THUMBNAIL_SIZE = '40px'; const FALLBACK_ICON_SIZE = '24px'; const StylePresetImage = ({ presetImageUrl, imageWidth }: { presetImageUrl: string | null; imageWidth?: number }) => { + const crossOrigin = useStore($crossOrigin); + return (