Fixes edge cases, adds invoke button to header when options floating

This commit is contained in:
psychedelicious 2022-10-30 15:55:34 +11:00
parent e58b7a7ef9
commit 62c97dd7e6
26 changed files with 212 additions and 137 deletions

View File

@ -6,7 +6,7 @@ import Loading from '../Loading';
import { useAppDispatch } from './store';
import { requestSystemConfig } from './socketio/actions';
import { keepGUIAlive } from './utils';
import InvokeTabs, { tabMap } from '../features/tabs/InvokeTabs';
import InvokeTabs from '../features/tabs/InvokeTabs';
import ImageUploader from '../common/components/ImageUploader';
import { RootState, useAppSelector } from '../app/store';
@ -15,19 +15,23 @@ import ShowHideOptionsPanelButton from '../features/tabs/ShowHideOptionsPanelBut
import { createSelector } from '@reduxjs/toolkit';
import { GalleryState } from '../features/gallery/gallerySlice';
import { OptionsState } from '../features/options/optionsSlice';
import { activeTabNameSelector } from '../features/options/optionsSelectors';
keepGUIAlive();
const appSelector = createSelector(
[(state: RootState) => state.gallery, (state: RootState) => state.options],
(gallery: GalleryState, options: OptionsState) => {
[
(state: RootState) => state.gallery,
(state: RootState) => state.options,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, activeTabName) => {
const { shouldShowGallery, shouldHoldGalleryOpen, shouldPinGallery } =
gallery;
const {
shouldShowOptionsPanel,
shouldHoldOptionsPanelOpen,
shouldPinOptionsPanel,
activeTab,
} = options;
return {
@ -39,7 +43,7 @@ const appSelector = createSelector(
!(
shouldShowOptionsPanel ||
(shouldHoldOptionsPanelOpen && !shouldPinOptionsPanel)
) && ['txt2img', 'img2img', 'inpainting'].includes(tabMap[activeTab]),
) && ['txt2img', 'img2img', 'inpainting'].includes(activeTabName),
};
}
);

View File

@ -1,22 +1,18 @@
import { Button, ButtonProps, Tooltip } from '@chakra-ui/react';
interface Props extends ButtonProps {
export interface IAIButtonProps extends ButtonProps {
label: string;
tooltip?: string;
}
/**
* Reusable customized button component. Originally was more customized - now probably unecessary.
*
* TODO: Get rid of this.
* Reusable customized button component.
*/
const IAIButton = (props: Props) => {
const { label, tooltip = '', size = 'sm', ...rest } = props;
const IAIButton = (props: IAIButtonProps) => {
const { label, tooltip = '', ...rest } = props;
return (
<Tooltip label={tooltip}>
<Button size={size} {...rest}>
{label}
</Button>
<Button {...rest}>{label}</Button>
</Tooltip>
);
};

View File

@ -1,23 +1,11 @@
import { useCallback, ReactNode, useState, useEffect } from 'react';
import { RootState, useAppDispatch, useAppSelector } from '../../app/store';
import { tabMap } from '../../features/tabs/InvokeTabs';
import { useAppDispatch, useAppSelector } from '../../app/store';
import { FileRejection, useDropzone } from 'react-dropzone';
import { Heading, Spinner, useToast } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { OptionsState } from '../../features/options/optionsSlice';
import { uploadImage } from '../../app/socketio/actions';
import { ImageUploadDestination, UploadImagePayload } from '../../app/invokeai';
import { ImageUploaderTriggerContext } from '../../app/contexts/ImageUploaderTriggerContext';
const appSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => {
const { activeTab } = options;
return {
activeTabName: tabMap[activeTab],
};
}
);
import { activeTabNameSelector } from '../../features/options/optionsSelectors';
type ImageUploaderProps = {
children: ReactNode;
@ -26,7 +14,7 @@ type ImageUploaderProps = {
const ImageUploader = (props: ImageUploaderProps) => {
const { children } = props;
const dispatch = useAppDispatch();
const { activeTabName } = useAppSelector(appSelector);
const activeTabName = useAppSelector(activeTabNameSelector);
const toast = useToast({});
const [isHandlingUpload, setIsHandlingUpload] = useState<boolean>(false);

View File

@ -3,11 +3,11 @@ import _ from 'lodash';
import { useMemo } from 'react';
import { useAppSelector } from '../../app/store';
import { RootState } from '../../app/store';
import { activeTabNameSelector } from '../../features/options/optionsSelectors';
import { OptionsState } from '../../features/options/optionsSlice';
import { SystemState } from '../../features/system/systemSlice';
import { InpaintingState } from '../../features/tabs/Inpainting/inpaintingSlice';
import { tabMap } from '../../features/tabs/InvokeTabs';
import { validateSeedWeights } from '../util/seedWeightPairs';
export const useCheckParametersSelector = createSelector(
@ -15,8 +15,9 @@ export const useCheckParametersSelector = createSelector(
(state: RootState) => state.options,
(state: RootState) => state.system,
(state: RootState) => state.inpainting,
activeTabNameSelector
],
(options: OptionsState, system: SystemState, inpainting: InpaintingState) => {
(options: OptionsState, system: SystemState, inpainting: InpaintingState, activeTabName) => {
return {
// options
prompt: options.prompt,
@ -25,7 +26,7 @@ export const useCheckParametersSelector = createSelector(
maskPath: options.maskPath,
initialImage: options.initialImage,
seed: options.seed,
activeTabName: tabMap[options.activeTab],
activeTabName,
// system
isProcessing: system.isProcessing,
isConnected: system.isConnected,

View File

@ -0,0 +1,20 @@
import { RefObject, useEffect } from 'react';
const useClickOutsideWatcher = (
ref: RefObject<HTMLElement>,
callback: () => void
) => {
useEffect(() => {
function handleClickOutside(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) {
callback();
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref, callback]);
};
export default useClickOutsideWatcher;

View File

@ -2,22 +2,26 @@ import { RootState, useAppSelector } from '../../app/store';
import CurrentImageButtons from './CurrentImageButtons';
import { MdPhoto } from 'react-icons/md';
import CurrentImagePreview from './CurrentImagePreview';
import { tabMap } from '../tabs/InvokeTabs';
import { GalleryState } from './gallerySlice';
import { OptionsState } from '../options/optionsSlice';
import _ from 'lodash';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector } from '../options/optionsSelectors';
export const currentImageDisplaySelector = createSelector(
[(state: RootState) => state.gallery, (state: RootState) => state.options],
(gallery: GalleryState, options: OptionsState) => {
[
(state: RootState) => state.gallery,
(state: RootState) => state.options,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, activeTabName) => {
const { currentImage, intermediateImage } = gallery;
const { activeTab, shouldShowImageDetails } = options;
const { shouldShowImageDetails } = options;
return {
currentImage,
intermediateImage,
activeTabName: tabMap[activeTab],
activeTabName,
shouldShowImageDetails,
};
},
@ -32,11 +36,9 @@ export const currentImageDisplaySelector = createSelector(
* Displays the current image if there is one, plus associated actions.
*/
const CurrentImageDisplay = () => {
const {
currentImage,
intermediateImage,
activeTabName,
} = useAppSelector(currentImageDisplaySelector);
const { currentImage, intermediateImage, activeTabName } = useAppSelector(
currentImageDisplaySelector
);
const imageToDisplay = intermediateImage || currentImage;

View File

@ -7,11 +7,7 @@ import {
useToast,
} from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from '../../app/store';
import {
setCurrentImage,
setShouldHoldGalleryOpen,
setShouldShowGallery,
} from './gallerySlice';
import { setCurrentImage } from './gallerySlice';
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
import DeleteImageModal from './DeleteImageModal';
import { memo, useState } from 'react';
@ -25,7 +21,6 @@ import {
} from '../options/optionsSlice';
import * as InvokeAI from '../../app/invokeai';
import * as ContextMenu from '@radix-ui/react-context-menu';
import { tabMap } from '../tabs/InvokeTabs';
import { setImageToInpaint } from '../tabs/Inpainting/inpaintingSlice';
import { hoverableImageSelector } from './gallerySliceSelectors';
@ -118,7 +113,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
if (metadata?.image?.init_image_path) {
const response = await fetch(metadata.image.init_image_path);
if (response.ok) {
dispatch(setActiveTab(tabMap.indexOf('img2img')));
dispatch(setActiveTab('img2img'));
dispatch(setAllImageToImageParameters(metadata));
toast({
title: 'Initial Image Set',
@ -142,10 +137,10 @@ const HoverableImage = memo((props: HoverableImageProps) => {
return (
<ContextMenu.Root
// onOpenChange={(open: boolean) => {
// dispatch(setShouldHoldGalleryOpen(open));
// dispatch(setShouldShowGallery(true));
// }}
// onOpenChange={(open: boolean) => {
// dispatch(setShouldHoldGalleryOpen(open));
// dispatch(setShouldShowGallery(true));
// }}
>
<ContextMenu.Trigger>
<Box

View File

@ -33,6 +33,7 @@ import { BiReset } from 'react-icons/bi';
import IAICheckbox from '../../common/components/IAICheckbox';
import { setNeedsCache } from '../tabs/Inpainting/inpaintingSlice';
import _ from 'lodash';
import useClickOutsideWatcher from '../../common/hooks/useClickOutsideWatcher';
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
@ -110,6 +111,7 @@ export default function ImageGallery() {
};
const handleCloseGallery = () => {
if (shouldPinGallery) return;
dispatch(
setGalleryScrollPosition(
galleryContainerRef.current ? galleryContainerRef.current.scrollTop : 0
@ -117,7 +119,7 @@ export default function ImageGallery() {
);
dispatch(setShouldShowGallery(false));
dispatch(setShouldHoldGalleryOpen(false));
shouldPinGallery && dispatch(setNeedsCache(true));
// dispatch(setNeedsCache(true));
};
const handleClickLoadMore = () => {
@ -255,6 +257,8 @@ export default function ImageGallery() {
setShouldShowButtons(galleryWidth >= 280);
}, [galleryWidth]);
useClickOutsideWatcher(galleryRef, handleCloseGallery);
return (
<CSSTransition
nodeRef={galleryRef}

View File

@ -143,9 +143,7 @@ export const gallerySlice = createSlice({
if (state.shouldAutoSwitchToNewImages) {
state.currentImageUuid = uuid;
state.currentImage = newImage;
if (category === 'result') {
state.currentCategory = 'result';
}
state.currentCategory = category;
}
state.intermediateImage = undefined;
tempCategory.latest_mtime = mtime;

View File

@ -1,12 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../app/store';
import { activeTabNameSelector } from '../options/optionsSelectors';
import { OptionsState } from '../options/optionsSlice';
import { tabMap } from '../tabs/InvokeTabs';
import { GalleryState } from './gallerySlice';
export const imageGallerySelector = createSelector(
[(state: RootState) => state.gallery, (state: RootState) => state.options],
(gallery: GalleryState, options: OptionsState) => {
[
(state: RootState) => state.gallery,
(state: RootState) => state.options,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, activeTabName) => {
const {
categories,
currentCategory,
@ -21,8 +25,6 @@ export const imageGallerySelector = createSelector(
galleryWidth,
} = gallery;
const { activeTab } = options;
return {
currentImageUuid,
shouldPinGallery,
@ -31,7 +33,7 @@ export const imageGallerySelector = createSelector(
galleryImageMinimumWidth,
galleryImageObjectFit,
galleryGridTemplateColumns: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`,
activeTabName: tabMap[activeTab],
activeTabName,
shouldHoldGalleryOpen,
shouldAutoSwitchToNewImages,
images: categories[currentCategory].images,
@ -44,12 +46,12 @@ export const imageGallerySelector = createSelector(
);
export const hoverableImageSelector = createSelector(
[(state: RootState) => state.options, (state: RootState) => state.gallery],
(options: OptionsState, gallery: GalleryState) => {
[(state: RootState) => state.options, (state: RootState) => state.gallery, activeTabNameSelector],
(options: OptionsState, gallery: GalleryState, activeTabName) => {
return {
galleryImageObjectFit: gallery.galleryImageObjectFit,
galleryImageMinimumWidth: gallery.galleryImageMinimumWidth,
activeTabName: tabMap[options.activeTab],
activeTabName,
};
}
);

View File

@ -2,14 +2,13 @@ import React, { ChangeEvent } from 'react';
import { HEIGHTS } from '../../../app/constants';
import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
import IAISelect from '../../../common/components/IAISelect';
import { tabMap } from '../../tabs/InvokeTabs';
import { activeTabNameSelector } from '../optionsSelectors';
import { setHeight } from '../optionsSlice';
import { fontSize } from './MainOptions';
export default function MainHeight() {
const { activeTab, height } = useAppSelector(
(state: RootState) => state.options
);
const { height } = useAppSelector((state: RootState) => state.options);
const activeTabName = useAppSelector(activeTabNameSelector);
const dispatch = useAppDispatch();
const handleChangeHeight = (e: ChangeEvent<HTMLSelectElement>) =>
@ -17,7 +16,7 @@ export default function MainHeight() {
return (
<IAISelect
isDisabled={tabMap[activeTab] === 'inpainting'}
isDisabled={activeTabName === 'inpainting'}
label="Height"
value={height}
flexGrow={1}

View File

@ -6,6 +6,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { createSelector } from '@reduxjs/toolkit';
import { SystemState } from '../../system/systemSlice';
import _ from 'lodash';
import { IAIButtonProps } from '../../../common/components/IAIButton';
const cancelButtonSelector = createSelector(
(state: RootState) => state.system,
@ -23,7 +24,8 @@ const cancelButtonSelector = createSelector(
}
);
export default function CancelButton() {
export default function CancelButton(props: Omit<IAIButtonProps, 'label'>) {
const { ...rest } = props;
const dispatch = useAppDispatch();
const { isProcessing, isConnected, isCancelable } =
useAppSelector(cancelButtonSelector);
@ -47,6 +49,7 @@ export default function CancelButton() {
isDisabled={!isConnected || !isProcessing || !isCancelable}
onClick={handleClickCancel}
styleClass="cancel-btn"
{...rest}
/>
);
}

View File

@ -1,21 +1,33 @@
import { useHotkeys } from 'react-hotkeys-hook';
import { generateImage } from '../../../app/socketio/actions';
import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
import IAIButton from '../../../common/components/IAIButton';
import { useAppDispatch, useAppSelector } from '../../../app/store';
import IAIButton, {
IAIButtonProps,
} from '../../../common/components/IAIButton';
import useCheckParameters from '../../../common/hooks/useCheckParameters';
import { tabMap } from '../../tabs/InvokeTabs';
import { activeTabNameSelector } from '../optionsSelectors';
export default function InvokeButton() {
export default function InvokeButton(props: Omit<IAIButtonProps, 'label'>) {
const { ...rest } = props;
const dispatch = useAppDispatch();
const isReady = useCheckParameters();
const activeTab = useAppSelector(
(state: RootState) => state.options.activeTab
);
const activeTabName = useAppSelector(activeTabNameSelector);
const handleClickGenerate = () => {
dispatch(generateImage(tabMap[activeTab]));
dispatch(generateImage(activeTabName));
};
useHotkeys(
'ctrl+enter, cmd+enter',
() => {
if (isReady) {
dispatch(generateImage(activeTabName));
}
},
[isReady, activeTabName]
);
return (
<IAIButton
label="Invoke"
@ -24,6 +36,7 @@ export default function InvokeButton() {
isDisabled={!isReady}
onClick={handleClickGenerate}
className="invoke-btn"
{...rest}
/>
);
}

View File

@ -4,20 +4,20 @@
display: grid;
grid-template-columns: auto max-content;
column-gap: 0.5rem;
.invoke-btn {
@include Button(
$btn-color: var(--accent-color),
$btn-color-hover: var(--accent-color-hover),
$btn-width: 5rem
);
}
.cancel-btn {
@include Button(
$btn-color: var(--destructive-color),
$btn-color-hover: var(--destructive-color-hover),
$btn-width: 3rem
);
}
}
.invoke-btn {
@include Button(
$btn-color: var(--accent-color),
$btn-color-hover: var(--accent-color-hover),
// $btn-width: 5rem
);
}
.cancel-btn {
@include Button(
$btn-color: var(--destructive-color),
$btn-color-hover: var(--destructive-color-hover),
// $btn-width: 3rem
);
}

View File

@ -8,14 +8,14 @@ import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import useCheckParameters from '../../../common/hooks/useCheckParameters';
import { useHotkeys } from 'react-hotkeys-hook';
import { tabMap } from '../../tabs/InvokeTabs';
import { activeTabNameSelector } from '../optionsSelectors';
const promptInputSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => {
[(state: RootState) => state.options, activeTabNameSelector],
(options: OptionsState, activeTabName) => {
return {
prompt: options.prompt,
activeTabName: tabMap[options.activeTab],
activeTabName,
};
},
{
@ -38,16 +38,6 @@ const PromptInput = () => {
dispatch(setPrompt(e.target.value));
};
useHotkeys(
'ctrl+enter, cmd+enter',
() => {
if (isReady) {
dispatch(generateImage(activeTabName));
}
},
[isReady, activeTabName]
);
useHotkeys(
'alt+a',
() => {

View File

@ -0,0 +1,9 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../app/store';
import { tabMap } from '../tabs/InvokeTabs';
import { OptionsState } from './optionsSlice';
export const activeTabNameSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => tabMap[options.activeTab]
);

View File

@ -134,7 +134,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
{
title: 'Quick Toggle Brush/Eraser',
desc: 'Quick toggle between brush and eraser',
hotkey: 'Z',
hotkey: 'X',
},
{
title: 'Decrease Brush Size',

View File

@ -24,3 +24,11 @@
align-items: center;
column-gap: 0.5rem;
}
// Overrides
.process-buttons {
padding-left: 0.5rem;
button {
}
}

View File

@ -1,19 +1,37 @@
import { IconButton, Link, Tooltip, useColorMode } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import { useHotkeys } from 'react-hotkeys-hook';
import { FaSun, FaMoon, FaGithub, FaDiscord } from 'react-icons/fa';
import { MdHelp, MdKeyboard, MdSettings } from 'react-icons/md';
import { RootState, useAppSelector } from '../../app/store';
import InvokeAILogo from '../../assets/images/logo.png';
import { OptionsState } from '../options/optionsSlice';
import CancelButton from '../options/ProcessButtons/CancelButton';
import InvokeButton from '../options/ProcessButtons/InvokeButton';
import ProcessButtons from '../options/ProcessButtons/ProcessButtons';
import HotkeysModal from './HotkeysModal/HotkeysModal';
import SettingsModal from './SettingsModal/SettingsModal';
import StatusIndicator from './StatusIndicator';
const siteHeaderSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => {
const { shouldPinOptionsPanel } = options;
return { shouldShowProcessButtons: !shouldPinOptionsPanel };
},
{ memoizeOptions: { resultEqualityCheck: _.isEqual } }
);
/**
* Header, includes color mode toggle, settings button, status message.
*/
const SiteHeader = () => {
const { shouldShowProcessButtons } = useAppSelector(siteHeaderSelector);
const { colorMode, toggleColorMode } = useColorMode();
useHotkeys(
@ -36,6 +54,12 @@ const SiteHeader = () => {
<h1>
invoke <strong>ai</strong>
</h1>
{shouldShowProcessButtons && (
<div className="process-buttons process-buttons">
<InvokeButton size={'sm'} />
<CancelButton size={'sm'} />
</div>
)}
</div>
<div className="site-header-right-side">

View File

@ -8,7 +8,7 @@
padding: 0;
min-width: 2rem !important;
filter: drop-shadow(0 0 1rem var(--text-color-a3));
filter: var(--floating-button-drop-shadow);
&.left {
left: 0;

View File

@ -57,6 +57,7 @@ const InpaintingCanvas = () => {
isDrawing,
shouldLockBoundingBox,
shouldShowBoundingBox,
boundingBoxDimensions,
} = useAppSelector(inpaintingCanvasSelector);
const toast = useToast();
@ -95,7 +96,7 @@ const InpaintingCanvas = () => {
};
image.src = imageToInpaint.url;
} else {
setCanvasBgImage(null)
setCanvasBgImage(null);
}
}, [imageToInpaint, dispatch, stageScale, toast]);
@ -243,7 +244,7 @@ const InpaintingCanvas = () => {
)}
{!shouldLockBoundingBox && (
<div style={{ pointerEvents: 'none' }}>
Transforming Bounding Box (M)
{`Transforming Bounding Box ${boundingBoxDimensions.width}x${boundingBoxDimensions.height} (M)`}
</div>
)}
</div>

View File

@ -6,8 +6,8 @@ import {
useAppDispatch,
useAppSelector,
} from '../../../../app/store';
import { activeTabNameSelector } from '../../../options/optionsSelectors';
import { OptionsState } from '../../../options/optionsSlice';
import { tabMap } from '../../InvokeTabs';
import {
InpaintingState,
setIsDrawing,
@ -16,12 +16,16 @@ import {
} from '../inpaintingSlice';
const keyboardEventManagerSelector = createSelector(
[(state: RootState) => state.options, (state: RootState) => state.inpainting],
(options: OptionsState, inpainting: InpaintingState) => {
[
(state: RootState) => state.options,
(state: RootState) => state.inpainting,
activeTabNameSelector,
],
(options: OptionsState, inpainting: InpaintingState, activeTabName) => {
const { shouldShowMask, cursorPosition, shouldLockBoundingBox } =
inpainting;
return {
activeTabName: tabMap[options.activeTab],
activeTabName,
shouldShowMask,
isCursorOnCanvas: Boolean(cursorPosition),
shouldLockBoundingBox,
@ -49,7 +53,7 @@ const KeyboardEventManager = () => {
useEffect(() => {
const listener = (e: KeyboardEvent) => {
if (
!['z', ' '].includes(e.key) ||
!['x', ' '].includes(e.key) ||
activeTabName !== 'inpainting' ||
!shouldShowMask
) {
@ -83,13 +87,13 @@ const KeyboardEventManager = () => {
}
switch (e.key) {
case 'z': {
case 'x': {
dispatch(toggleTool());
break;
}
case ' ': {
if (!shouldShowMask) break;
if (e.type === 'keydown') {
dispatch(setIsDrawing(false));
}

View File

@ -1,6 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import { RootState } from '../../../app/store';
import { activeTabNameSelector } from '../../options/optionsSelectors';
import { OptionsState } from '../../options/optionsSlice';
import { tabMap } from '../InvokeTabs';
import { InpaintingState } from './inpaintingSlice';
@ -18,8 +19,12 @@ export const inpaintingCanvasLinesSelector = createSelector(
);
export const inpaintingControlsSelector = createSelector(
[(state: RootState) => state.inpainting, (state: RootState) => state.options],
(inpainting: InpaintingState, options: OptionsState) => {
[
(state: RootState) => state.inpainting,
(state: RootState) => state.options,
activeTabNameSelector,
],
(inpainting: InpaintingState, options: OptionsState, activeTabName) => {
const {
tool,
brushSize,
@ -34,7 +39,7 @@ export const inpaintingControlsSelector = createSelector(
shouldShowBoundingBox,
} = inpainting;
const { activeTab, showDualDisplay } = options;
const { showDualDisplay } = options;
return {
tool,
@ -46,7 +51,7 @@ export const inpaintingControlsSelector = createSelector(
canUndo: pastLines.length > 0,
canRedo: futureLines.length > 0,
isMaskEmpty: lines.length === 0,
activeTabName: tabMap[activeTab],
activeTabName,
showDualDisplay,
shouldShowBoundingBoxFill,
shouldShowBoundingBox,
@ -75,6 +80,7 @@ export const inpaintingCanvasSelector = createSelector(
isDrawing,
shouldLockBoundingBox,
shouldShowBoundingBox,
boundingBoxDimensions,
} = inpainting;
return {
tool,
@ -89,6 +95,7 @@ export const inpaintingCanvasSelector = createSelector(
isDrawing,
shouldLockBoundingBox,
shouldShowBoundingBox,
boundingBoxDimensions,
};
},
{

View File

@ -1,9 +1,17 @@
import { createSelector } from '@reduxjs/toolkit';
import { MouseEvent, ReactNode, useEffect, useRef } from 'react';
import {
FocusEvent,
MouseEvent,
ReactNode,
useCallback,
useEffect,
useRef,
} from 'react';
import { BsPinAngleFill } from 'react-icons/bs';
import { CSSTransition } from 'react-transition-group';
import { RootState, useAppDispatch, useAppSelector } from '../../app/store';
import IAIIconButton from '../../common/components/IAIIconButton';
import useClickOutsideWatcher from '../../common/hooks/useClickOutsideWatcher';
import {
OptionsState,
setOptionsPanelScrollPosition,
@ -50,7 +58,8 @@ const InvokeOptionsPanel = (props: Props) => {
const { children } = props;
const handleCloseOptionsPanel = () => {
const handleCloseOptionsPanel = useCallback(() => {
if (shouldPinOptionsPanel) return;
dispatch(
setOptionsPanelScrollPosition(
optionsPanelContainerRef.current
@ -60,8 +69,10 @@ const InvokeOptionsPanel = (props: Props) => {
);
dispatch(setShouldShowOptionsPanel(false));
dispatch(setShouldHoldOptionsPanelOpen(false));
shouldPinOptionsPanel && dispatch(setNeedsCache(true));
};
// dispatch(setNeedsCache(true));
}, [dispatch, shouldPinOptionsPanel]);
useClickOutsideWatcher(optionsPanelRef, handleCloseOptionsPanel);
const setCloseOptionsPanelTimer = () => {
timeoutIdRef.current = window.setTimeout(

View File

@ -25,8 +25,6 @@
--destructive-color: rgb(185, 55, 55);
--destructive-color-hover: rgb(255, 75, 75);
--text-color-a3: rgba(255, 255, 255, 0.3);
// Error status colors
--border-color-invalid: rgb(255, 80, 50);
--box-shadow-color-invalid: rgb(210, 30, 10);
@ -103,6 +101,6 @@
--context-menu-box-shadow: none;
--context-menu-bg-color-hover: rgb(30, 32, 42);
// Inpainting
--inpaint-bg-color: rgb(30, 32, 42);
// Shadows
--floating-button-drop-shadow: drop-shadow(0 0 1rem rgba(140, 101, 255, 0.5));
}

View File

@ -25,8 +25,6 @@
--destructive-color: rgb(237, 51, 51);
--destructive-color-hover: rgb(255, 55, 55);
--text-color-a3: rgba(0, 0, 0, 0.3);
// Error status colors
--border-color-invalid: rgb(255, 80, 50);
--box-shadow-color-invalid: none;
@ -104,6 +102,6 @@
0px 10px 20px -15px rgba(22, 23, 24, 0.2);
--context-menu-bg-color-hover: var(--background-color-secondary);
// Inpainting
--inpaint-bg-color: rgb(180, 182, 184);
// Shadows
--floating-button-drop-shadow: drop-shadow(0 0 1rem rgba(0, 0, 0, 0.3));
}