feat(ui): revert tabs to txt2img/img2img

This commit is contained in:
psychedelicious 2023-05-08 22:57:05 +10:00
parent 33c69359c2
commit c4b3a24ed7
41 changed files with 535 additions and 379 deletions

View File

@ -9,7 +9,7 @@ import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton'
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
import { Box, Flex, Grid, Portal, useColorMode } from '@chakra-ui/react';
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel';
import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel';
import Lightbox from 'features/lightbox/components/Lightbox';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
@ -28,9 +28,7 @@ import { configChanged } from 'features/system/store/configSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useLogger } from 'app/logging/useLogger';
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
import ResizableDrawer from 'features/ui/components/common/ResizableDrawer/ResizableDrawer';
import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
import CreateParametersDrawer from 'features/ui/components/CreateParametersDrawer';
import ParametersDrawer from 'features/ui/components/ParametersDrawer';
const DEFAULT_CONFIG = {};
@ -91,8 +89,8 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
</Grid>
</ImageUploader>
<ImageGalleryPanel />
<CreateParametersDrawer />
<GalleryDrawer />
<ParametersDrawer />
<AnimatePresence>
{!isApplicationReady && !loadingOverridden && (

View File

@ -12,8 +12,9 @@ import { addImageResultReceivedListener } from './listeners/invocationComplete';
import { addImageUploadedListener } from './listeners/imageUploaded';
import { addRequestedImageDeletionListener } from './listeners/imageDeleted';
import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas';
import { addUserInvokedCreateListener } from './listeners/userInvokedCreate';
import { addUserInvokedNodesListener } from './listeners/userInvokedNodes';
import { addUserInvokedTextToImageListener } from './listeners/userInvokedTextToImage';
import { addUserInvokedImageToImageListener } from './listeners/userInvokedImageToImage';
export const listenerMiddleware = createListenerMiddleware();
@ -39,5 +40,6 @@ addImageResultReceivedListener();
addRequestedImageDeletionListener();
addUserInvokedCanvasListener();
addUserInvokedCreateListener();
addUserInvokedNodesListener();
addUserInvokedTextToImageListener();
addUserInvokedImageToImageListener();

View File

@ -1,6 +1,6 @@
import { startAppListening } from '..';
import { sessionCreated, sessionInvoked } from 'services/thunks/session';
import { buildCanvasGraphAndBlobs } from 'features/nodes/util/buildCanvasGraph';
import { buildCanvasGraphAndBlobs } from 'features/nodes/util/graphBuilders/buildCanvasGraph';
import { log } from 'app/logging/useLogger';
import { canvasGraphBuilt } from 'features/nodes/store/actions';
import { imageUploaded } from 'services/thunks/image';

View File

@ -0,0 +1,24 @@
import { startAppListening } from '..';
import { buildImageToImageGraph } from 'features/nodes/util/graphBuilders/buildImageToImageGraph';
import { sessionCreated } from 'services/thunks/session';
import { log } from 'app/logging/useLogger';
import { imageToImageGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions';
const moduleLog = log.child({ namespace: 'invoke' });
export const addUserInvokedImageToImageListener = () => {
startAppListening({
predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'image',
effect: (action, { getState, dispatch }) => {
const state = getState();
const graph = buildImageToImageGraph(state);
dispatch(imageToImageGraphBuilt(graph));
moduleLog({ data: graph }, 'Image to Image graph built');
dispatch(sessionCreated({ graph }));
},
});
};

View File

@ -1,6 +1,6 @@
import { startAppListening } from '..';
import { sessionCreated } from 'services/thunks/session';
import { buildNodesGraph } from 'features/nodes/util/buildNodesGraph';
import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGraph';
import { log } from 'app/logging/useLogger';
import { nodesGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions';

View File

@ -1,22 +1,22 @@
import { startAppListening } from '..';
import { buildLinearGraph } from 'features/nodes/util/buildLinearGraph';
import { buildTextToImageGraph } from 'features/nodes/util/graphBuilders/buildTextToImageGraph';
import { sessionCreated } from 'services/thunks/session';
import { log } from 'app/logging/useLogger';
import { createGraphBuilt } from 'features/nodes/store/actions';
import { textToImageGraphBuilt } from 'features/nodes/store/actions';
import { userInvoked } from 'app/store/actions';
const moduleLog = log.child({ namespace: 'invoke' });
export const addUserInvokedCreateListener = () => {
export const addUserInvokedTextToImageListener = () => {
startAppListening({
predicate: (action): action is ReturnType<typeof userInvoked> =>
userInvoked.match(action) && action.payload === 'text',
effect: (action, { getState, dispatch }) => {
const state = getState();
const graph = buildLinearGraph(state);
dispatch(createGraphBuilt(graph));
moduleLog({ data: graph }, 'Create graph built');
const graph = buildTextToImageGraph(state);
dispatch(textToImageGraphBuilt(graph));
moduleLog({ data: graph }, 'Text to Image graph built');
dispatch(sessionCreated({ graph }));
},

View File

@ -7,7 +7,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { useCallback } from 'react';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
const ImagePromptHeading = () => {
const InitialImageButtons = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -38,4 +38,4 @@ const ImagePromptHeading = () => {
);
};
export default ImagePromptHeading;
export default InitialImageButtons;

View File

@ -7,7 +7,7 @@ const SelectImagePlaceholder = () => {
sx={{
w: 'full',
h: 'full',
bg: 'base.800',
// bg: 'base.800',
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',

View File

@ -3,6 +3,7 @@ import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
import {
setActiveTab,
toggleGalleryPanel,
toggleParametersPanel,
togglePinGalleryPanel,
@ -58,4 +59,20 @@ export const useGlobalHotkeys = () => {
useHotkeys(['shift+g'], () => {
dispatch(togglePinGalleryPanel());
});
useHotkeys('1', () => {
dispatch(setActiveTab('text'));
});
useHotkeys('2', () => {
dispatch(setActiveTab('image'));
});
useHotkeys('3', () => {
dispatch(setActiveTab('unifiedCanvas'));
});
useHotkeys('4', () => {
dispatch(setActiveTab('nodes'));
});
};

View File

@ -441,13 +441,13 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
{t('parameters.sendToUnifiedCanvas')}
</IAIButton>
<IAIButton
{/* <IAIButton
size="sm"
onClick={handleCopyImage}
leftIcon={<FaCopy />}
>
{t('parameters.copyImage')}
</IAIButton>
</IAIButton> */}
<IAIButton
size="sm"
onClick={handleCopyImageLink}

View File

@ -30,18 +30,18 @@ import useResolution from 'common/hooks/useResolution';
import { Flex } from '@chakra-ui/react';
import { memo } from 'react';
const GALLERY_TAB_WIDTHS: Record<
InvokeTabName,
{ galleryMinWidth: number; galleryMaxWidth: number }
> = {
// txt2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// img2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
generate: { galleryMinWidth: 200, galleryMaxWidth: 500 },
unifiedCanvas: { galleryMinWidth: 200, galleryMaxWidth: 200 },
nodes: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// postprocessing: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// training: { galleryMinWidth: 200, galleryMaxWidth: 500 },
};
// const GALLERY_TAB_WIDTHS: Record<
// InvokeTabName,
// { galleryMinWidth: number; galleryMaxWidth: number }
// > = {
// txt2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// img2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// generate: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// unifiedCanvas: { galleryMinWidth: 200, galleryMaxWidth: 200 },
// nodes: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// postprocessing: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// training: { galleryMinWidth: 200, galleryMaxWidth: 500 },
// };
const galleryPanelSelector = createSelector(
[
@ -73,34 +73,34 @@ const galleryPanelSelector = createSelector(
}
);
const ImageGalleryPanel = () => {
const GalleryDrawer = () => {
const dispatch = useAppDispatch();
const {
shouldPinGallery,
shouldShowGallery,
galleryImageMinimumWidth,
activeTabName,
isStaging,
isResizable,
isLightboxOpen,
// activeTabName,
// isStaging,
// isResizable,
// isLightboxOpen,
} = useAppSelector(galleryPanelSelector);
const handleSetShouldPinGallery = () => {
dispatch(togglePinGalleryPanel());
dispatch(requestCanvasRescale());
};
// const handleSetShouldPinGallery = () => {
// dispatch(togglePinGalleryPanel());
// dispatch(requestCanvasRescale());
// };
const handleToggleGallery = () => {
dispatch(toggleGalleryPanel());
shouldPinGallery && dispatch(requestCanvasRescale());
};
// const handleToggleGallery = () => {
// dispatch(toggleGalleryPanel());
// shouldPinGallery && dispatch(requestCanvasRescale());
// };
const handleCloseGallery = () => {
dispatch(setShouldShowGallery(false));
shouldPinGallery && dispatch(requestCanvasRescale());
};
const resolution = useResolution();
// const resolution = useResolution();
// useHotkeys(
// 'g',
@ -229,4 +229,4 @@ const ImageGalleryPanel = () => {
// return renderImageGallery();
};
export default memo(ImageGalleryPanel);
export default memo(GalleryDrawer);

View File

@ -2,7 +2,7 @@ import { Box } from '@chakra-ui/react';
import { RootState } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import { buildNodesGraph } from '../util/buildNodesGraph';
import { buildNodesGraph } from '../util/graphBuilders/buildNodesGraph';
const NodeGraphOverlay = () => {
const state = useAppSelector((state: RootState) => state);

View File

@ -1,12 +1,18 @@
import { createAction, isAnyOf } from '@reduxjs/toolkit';
import { Graph } from 'services/api';
export const createGraphBuilt = createAction<Graph>('nodes/createGraphBuilt');
export const textToImageGraphBuilt = createAction<Graph>(
'nodes/textToImageGraphBuilt'
);
export const imageToImageGraphBuilt = createAction<Graph>(
'nodes/imageToImageGraphBuilt'
);
export const canvasGraphBuilt = createAction<Graph>('nodes/canvasGraphBuilt');
export const nodesGraphBuilt = createAction<Graph>('nodes/nodesGraphBuilt');
export const isAnyGraphBuilt = isAnyOf(
createGraphBuilt,
textToImageGraphBuilt,
imageToImageGraphBuilt,
canvasGraphBuilt,
nodesGraphBuilt
);

View File

@ -10,17 +10,17 @@ import {
RangeInvocation,
TextToImageInvocation,
} from 'services/api';
import { buildImg2ImgNode } from './linearGraphBuilder/buildImageToImageNode';
import { buildTxt2ImgNode } from './linearGraphBuilder/buildTextToImageNode';
import { buildRangeNode } from './linearGraphBuilder/buildRangeNode';
import { buildIterateNode } from './linearGraphBuilder/buildIterateNode';
import { buildEdges } from './linearGraphBuilder/buildEdges';
import { buildImg2ImgNode } from '../nodeBuilders/buildImageToImageNode';
import { buildTxt2ImgNode } from '../nodeBuilders/buildTextToImageNode';
import { buildRangeNode } from '../nodeBuilders/buildRangeNode';
import { buildIterateNode } from '../nodeBuilders/buildIterateNode';
import { buildEdges } from '../edgeBuilders/buildEdges';
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
import { getCanvasData } from 'features/canvas/util/getCanvasData';
import { getGenerationMode } from './getGenerationMode';
import { getGenerationMode } from '../getGenerationMode';
import { v4 as uuidv4 } from 'uuid';
import { log } from 'app/logging/useLogger';
import { buildInpaintNode } from './linearGraphBuilder/buildInpaintNode';
import { buildInpaintNode } from '../nodeBuilders/buildInpaintNode';
const moduleLog = log.child({ namespace: 'buildCanvasGraph' });

View File

@ -1,19 +1,15 @@
import { RootState } from 'app/store/store';
import { Graph } from 'services/api';
import { buildImg2ImgNode } from './linearGraphBuilder/buildImageToImageNode';
import { buildTxt2ImgNode } from './linearGraphBuilder/buildTextToImageNode';
import { buildRangeNode } from './linearGraphBuilder/buildRangeNode';
import { buildIterateNode } from './linearGraphBuilder/buildIterateNode';
import { buildEdges } from './linearGraphBuilder/buildEdges';
import { buildImg2ImgNode } from '../nodeBuilders/buildImageToImageNode';
import { buildRangeNode } from '../nodeBuilders/buildRangeNode';
import { buildIterateNode } from '../nodeBuilders/buildIterateNode';
import { buildEdges } from '../edgeBuilders/buildEdges';
/**
* Builds the Linear workflow graph.
*/
export const buildLinearGraph = (state: RootState): Graph => {
// The base node is either a txt2img or img2img node
const baseNode = state.generation.isImageToImageEnabled
? buildImg2ImgNode(state)
: buildTxt2ImgNode(state);
export const buildImageToImageGraph = (state: RootState): Graph => {
const baseNode = buildImg2ImgNode(state);
// We always range and iterate nodes, no matter the iteration count
// This is required to provide the correct seeds to the backend engine

View File

@ -2,7 +2,7 @@ import { Graph } from 'services/api';
import { v4 as uuidv4 } from 'uuid';
import { cloneDeep, reduce } from 'lodash-es';
import { RootState } from 'app/store/store';
import { InputFieldValue } from '../types/types';
import { InputFieldValue } from 'features/nodes/types/types';
/**
* We need to do special handling for some fields

View File

@ -0,0 +1,35 @@
import { RootState } from 'app/store/store';
import { Graph } from 'services/api';
import { buildTxt2ImgNode } from '../nodeBuilders/buildTextToImageNode';
import { buildRangeNode } from '../nodeBuilders/buildRangeNode';
import { buildIterateNode } from '../nodeBuilders/buildIterateNode';
import { buildEdges } from '../edgeBuilders/buildEdges';
/**
* Builds the Linear workflow graph.
*/
export const buildTextToImageGraph = (state: RootState): Graph => {
const baseNode = buildTxt2ImgNode(state);
// We always range and iterate nodes, no matter the iteration count
// This is required to provide the correct seeds to the backend engine
const rangeNode = buildRangeNode(state);
const iterateNode = buildIterateNode();
// Build the edges for the nodes selected.
const edges = buildEdges(baseNode, rangeNode, iterateNode);
// Assemble!
const graph = {
nodes: {
[rangeNode.id]: rangeNode,
[iterateNode.id]: iterateNode,
[baseNode.id]: baseNode,
},
edges,
};
// TODO: hires fix requires latent space upscaling; we don't have nodes for this yet
return graph;
};

View File

@ -15,8 +15,6 @@ export const buildImg2ImgNode = (
const nodeId = uuidv4();
const { generation, system, models } = state;
const { selectedModelName } = models;
const {
prompt,
negativePrompt,
@ -26,10 +24,13 @@ export const buildImg2ImgNode = (
height,
cfgScale,
sampler,
seamless,
model,
img2imgStrength: strength,
shouldFitToWidthHeight: fit,
shouldRandomizeSeed,
shouldUseSeamless,
seamlessXAxis,
seamlessYAxis,
} = generation;
const initialImage = initialImageSelector(state);
@ -48,9 +49,7 @@ export const buildImg2ImgNode = (
height,
cfg_scale: cfgScale,
scheduler: sampler as ImageToImageInvocation['scheduler'],
seamless,
model: selectedModelName,
progress_images: true,
model,
image: initialImage
? {
image_name: initialImage.name,

View File

@ -10,8 +10,6 @@ export const buildTxt2ImgNode = (
const nodeId = uuidv4();
const { generation, models } = state;
const { selectedModelName } = models;
const {
prompt,
negativePrompt,
@ -21,8 +19,8 @@ export const buildTxt2ImgNode = (
height,
cfgScale: cfg_scale,
sampler,
seamless,
shouldRandomizeSeed,
model,
} = generation;
const textToImageNode: NonNullable<TextToImageInvocation> = {
@ -34,9 +32,7 @@ export const buildTxt2ImgNode = (
height,
cfg_scale,
scheduler: sampler as TextToImageInvocation['scheduler'],
seamless,
model: selectedModelName,
progress_images: true,
model,
};
if (!shouldRandomizeSeed) {

View File

@ -5,7 +5,7 @@ import { setShouldFitToWidthHeight } from 'features/parameters/store/generationS
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function ImageFit() {
export default function ImageToImageFit() {
const dispatch = useAppDispatch();
const shouldFitToWidthHeight = useAppSelector(

View File

@ -13,7 +13,7 @@ import {
import { motion } from 'framer-motion';
import IAIButton from 'common/components/IAIButton';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import IAIIconButton from 'common/components/IAIIconButton';
@ -21,16 +21,16 @@ import { useTranslation } from 'react-i18next';
import InitialImagePreview from './InitialImagePreview';
import { useState } from 'react';
import { FaUndo, FaUpload } from 'react-icons/fa';
import ImagePromptHeading from 'common/components/ImageToImageSettingsHeader';
import InitialImageButtons from 'common/components/ImageToImageSettingsHeader';
export default function ImageToImageSettings() {
const { t } = useTranslation();
return (
<VStack gap={2} w="full" alignItems="stretch">
<ImagePromptHeading />
<InitialImageButtons />
<InitialImagePreview />
<ImageToImageStrength />
<ImageFit />
<ImageToImageFit />
</VStack>
);
}

View File

@ -0,0 +1,36 @@
import { Flex } from '@chakra-ui/react';
import InitialImagePreview from './InitialImagePreview';
import InitialImageButtons from 'common/components/ImageToImageButtons';
const InitialImageDisplay = () => {
return (
<Flex
sx={{
position: 'relative',
flexDirection: 'column',
height: '100%',
width: '100%',
rowGap: 4,
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Flex
flexDirection="column"
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
gap: 4,
}}
>
<InitialImageButtons />
<InitialImagePreview />
</Flex>
</Flex>
);
};
export default InitialImageDisplay;

View File

@ -71,6 +71,7 @@ const InitialImagePreview = () => {
<Flex
sx={{
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
@ -115,7 +116,7 @@ const InitialImagePreview = () => {
)}
{!initialImage?.url && <SelectImagePlaceholder />}
</Flex>
{!isImageToImageEnabled && (
{/* {!isImageToImageEnabled && (
<Flex
sx={{
w: 'full',
@ -134,7 +135,7 @@ const InitialImagePreview = () => {
Image to Image is Disabled
</Text>
</Flex>
)}
)} */}
</Flex>
);
};

View File

@ -7,9 +7,6 @@ import { memo } from 'react';
import { ParamHiresStrength } from './ParamHiresStrength';
import { setHiresFix } from 'features/parameters/store/postprocessingSlice';
/**
* Seed & variation options. Includes iteration, seed, seed randomization, variation options.
*/
const ParamHiresCollapse = () => {
const { t } = useTranslation();
const hiresFix = useAppSelector(

View File

@ -61,12 +61,12 @@ export const modelsSlice = createSlice({
},
});
export const selectedModelSelector = (state: RootState) => {
const { selectedModelName } = state.models;
const selectedModel = selectModelsById(state, selectedModelName);
// export const selectedModelSelector = (state: RootState) => {
// const { selectedModelName } = state.models;
// const selectedModel = selectModelsById(state, selectedModelName);
return selectedModel ?? null;
};
// return selectedModel ?? null;
// };
export const {
selectAll: selectModelsAll,

View File

@ -1,115 +0,0 @@
import { isEqual } from 'lodash-es';
import ResizableDrawer from './common/ResizableDrawer/ResizableDrawer';
import TextTabParameters from './tabs/text/TextTabParameters';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector, uiSelector } from '../store/uiSelectors';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
setShouldShowParametersPanel,
toggleParametersPanel,
} from '../store/uiSlice';
import { memo } from 'react';
import { Flex } from '@chakra-ui/react';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import PinParametersPanelButton from './PinParametersPanelButton';
import { Panel, PanelGroup } from 'react-resizable-panels';
import CreateSidePanelPinned from './tabs/text/TextTabSettingsPinned';
import CreateTextParameters from './tabs/text/TextTabParameters';
import ResizeHandle from './tabs/ResizeHandle';
import CreateImageSettings from './tabs/image/ImageTabSettings';
const selector = createSelector(
[uiSelector, activeTabNameSelector, lightboxSelector],
(ui, activeTabName, lightbox) => {
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = ui;
const { isLightboxOpen } = lightbox;
return {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const CreateParametersPanel = () => {
const dispatch = useAppDispatch();
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = useAppSelector(selector);
const handleClosePanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
if (shouldPinParametersPanel) {
return null;
}
return (
<ResizableDrawer
direction="left"
isResizable={true}
isOpen={shouldShowParametersPanel}
onClose={handleClosePanel}
minWidth={500}
>
<Flex flexDir="column" position="relative" h="full" w="full">
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<PanelGroup
autoSaveId="createTab_floatingParameters"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<>
<Panel
id="createTab_textParameters"
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
<CreateTextParameters />
</Panel>
{shouldShowImageParameters && (
<>
<ResizeHandle />
<Panel
id="createTab_imageParameters"
order={1}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
<CreateImageSettings />
</Panel>
</>
)}
</>
</PanelGroup>
</Flex>
</ResizableDrawer>
);
};
export default memo(CreateParametersPanel);

View File

@ -33,7 +33,6 @@ import { useTranslation } from 'react-i18next';
import { ResourceKey } from 'i18next';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import NodeEditor from 'features/nodes/components/NodeEditor';
import GenerateWorkspace from './tabs/text/GenerateWorkspace';
import { createSelector } from '@reduxjs/toolkit';
import { BsLightningChargeFill } from 'react-icons/bs';
import { configSelector } from 'features/system/store/configSelectors';
@ -43,7 +42,7 @@ import Scrollable from './common/Scrollable';
import TextTabParameters from './tabs/text/TextTabParameters';
import PinParametersPanelButton from './PinParametersPanelButton';
import ParametersSlide from './common/ParametersSlide';
import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel';
import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
import TextTabMain from './tabs/text/TextTabMain';
@ -54,6 +53,7 @@ import UnifiedCanvasTab from './tabs/UnifiedCanvas/UnifiedCanvasTab';
import NodesTab from './tabs/Nodes/NodesTab';
import { FaImage } from 'react-icons/fa';
import ResizeHandle from './tabs/ResizeHandle';
import ImageTab from './tabs/image/ImageTab';
export interface InvokeTabInfo {
id: InvokeTabName;
@ -70,7 +70,7 @@ const tabs: InvokeTabInfo[] = [
{
id: 'image',
icon: <Icon as={FaImage} sx={{ boxSize: 5 }} />,
content: <TextTab />,
content: <ImageTab />,
},
{
id: 'unifiedCanvas',
@ -114,22 +114,6 @@ const InvokeTabs = () => {
const dispatch = useAppDispatch();
useHotkeys('1', () => {
dispatch(setActiveTab('text'));
});
useHotkeys('2', () => {
dispatch(setActiveTab('image'));
});
useHotkeys('3', () => {
dispatch(setActiveTab('unifiedCanvas'));
});
useHotkeys('4', () => {
dispatch(setActiveTab('nodes'));
});
// Lightbox Hotkey
useHotkeys(
'z',
@ -200,7 +184,7 @@ const InvokeTabs = () => {
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<Panel id="tabContent">
<Panel id="main">
<TabPanels style={{ height: '100%', width: '100%' }}>
{tabPanels}
</TabPanels>
@ -208,7 +192,13 @@ const InvokeTabs = () => {
{shouldPinGallery && shouldShowGallery && (
<>
<ResizeHandle />
<Panel id="gallery" order={3} defaultSize={10} minSize={10}>
<Panel
id="gallery"
order={3}
defaultSize={10}
minSize={10}
maxSize={50}
>
<ImageGalleryContent />
</Panel>
</>

View File

@ -0,0 +1,32 @@
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import { activeTabNameSelector } from '../store/uiSelectors';
import TextTabParametersDrawer from './tabs/text/TextTabParametersDrawer';
import { RootState } from 'app/store/store';
const ParametersDrawer = () => {
const activeTabName = useAppSelector(activeTabNameSelector);
const shouldPinParametersPanel = useAppSelector(
(state: RootState) => state.ui.shouldPinParametersPanel
);
if (shouldPinParametersPanel) {
return null;
}
if (activeTabName === 'text') {
return <TextTabParametersDrawer />;
}
if (activeTabName === 'image') {
return null;
}
if (activeTabName === 'unifiedCanvas') {
return null;
}
return null;
};
export default memo(ParametersDrawer);

View File

@ -7,6 +7,11 @@ import { uiSelector } from 'features/ui/store/uiSelectors';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import ResizeHandle from '../ResizeHandle';
import ImageTabParameters from './ImageTabParameters';
import ImageTabImageParameters from './ImageTabImageParameters';
import TextTabMain from '../text/TextTabMain';
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
import InitialImageDisplay from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImageDisplay';
const selector = createSelector(uiSelector, (ui) => {
const {
@ -38,20 +43,20 @@ const TextTab = () => {
return (
<PanelGroup
autoSaveId="textTab"
autoSaveId="imageTab"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
{shouldPinParametersPanel && shouldShowParametersPanel && (
<>
<Panel
id="textTab_settings"
id="imageTab_parameters"
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
{/* <TextTabSettings /> */}
<ImageTabParameters />
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
@ -59,15 +64,34 @@ const TextTab = () => {
<ResizeHandle />
</>
)}
<Panel
id="textTab_main"
order={2}
minSize={30}
onResize={() => {
dispatch(requestCanvasRescale());
}}
>
{/* <TextTabMain /> */}
<Panel id="imageTab_content" order={1}>
<PanelGroup
autoSaveId="imageTab_contentWrapper"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<Panel
id="imageTab_initImage"
order={0}
defaultSize={50}
minSize={25}
style={{ position: 'relative' }}
>
<InitialImageDisplay />
</Panel>
<ResizeHandle />
<Panel
id="imageTab_selectedImage"
order={1}
defaultSize={50}
minSize={25}
onResize={() => {
dispatch(requestCanvasRescale());
}}
>
<TextTabMain />
</Panel>
</PanelGroup>
</Panel>
</PanelGroup>
);

View File

@ -0,0 +1,61 @@
import { Box, Flex } from '@chakra-ui/react';
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import { memo } from 'react';
import OverlayScrollable from '../../common/OverlayScrollable';
import ParamPositiveConditioning from 'features/parameters/components/Parameters/ParamPositiveConditioning';
import ParamNegativeConditioning from 'features/parameters/components/Parameters/ParamNegativeConditioning';
import { createSelector } from '@reduxjs/toolkit';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import ParamIterations from 'features/parameters/components/Parameters/ParamIterations';
import ParamSteps from 'features/parameters/components/Parameters/ParamSteps';
import ParamCFGScale from 'features/parameters/components/Parameters/ParamCFGScale';
import ParamWidth from 'features/parameters/components/Parameters/ParamWidth';
import ParamHeight from 'features/parameters/components/Parameters/ParamHeight';
import ParamScheduler from 'features/parameters/components/Parameters/ParamScheduler';
import ModelSelect from 'features/system/components/ModelSelect';
import ParamSeedCollapse from 'features/parameters/components/Parameters/Seed/ParamSeedCollapse';
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ParamHiresCollapse';
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ImageToImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageFit';
import InitialImageButtons from 'common/components/ImageToImageButtons';
const selector = createSelector(
uiSelector,
(ui) => {
const { shouldUseSliders } = ui;
return { shouldUseSliders };
},
defaultSelectorOptions
);
const ImageTabParameters = () => {
const { shouldUseSliders } = useAppSelector(selector);
return (
<OverlayScrollable>
<Flex
sx={{
gap: 2,
flexDirection: 'column',
h: 'full',
w: 'full',
position: 'absolute',
}}
>
<InitialImageButtons />
<InitialImagePreview />
<ImageToImageFit />
</Flex>
</OverlayScrollable>
);
};
export default memo(ImageTabParameters);

View File

@ -0,0 +1,113 @@
import { Box, Flex } from '@chakra-ui/react';
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import { memo } from 'react';
import OverlayScrollable from '../../common/OverlayScrollable';
import ParamPositiveConditioning from 'features/parameters/components/Parameters/ParamPositiveConditioning';
import ParamNegativeConditioning from 'features/parameters/components/Parameters/ParamNegativeConditioning';
import { createSelector } from '@reduxjs/toolkit';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import ParamIterations from 'features/parameters/components/Parameters/ParamIterations';
import ParamSteps from 'features/parameters/components/Parameters/ParamSteps';
import ParamCFGScale from 'features/parameters/components/Parameters/ParamCFGScale';
import ParamWidth from 'features/parameters/components/Parameters/ParamWidth';
import ParamHeight from 'features/parameters/components/Parameters/ParamHeight';
import ParamScheduler from 'features/parameters/components/Parameters/ParamScheduler';
import ModelSelect from 'features/system/components/ModelSelect';
import ParamSeedCollapse from 'features/parameters/components/Parameters/Seed/ParamSeedCollapse';
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ParamHiresCollapse';
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ImageToImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageFit';
const selector = createSelector(
uiSelector,
(ui) => {
const { shouldUseSliders } = ui;
return { shouldUseSliders };
},
defaultSelectorOptions
);
const ImageTabParameters = () => {
const { shouldUseSliders } = useAppSelector(selector);
return (
<OverlayScrollable>
<Flex
sx={{
gap: 2,
flexDirection: 'column',
h: 'full',
w: 'full',
position: 'absolute',
}}
>
<ParamPositiveConditioning />
<ParamNegativeConditioning />
<ProcessButtons />
<Flex
sx={{
flexDirection: 'column',
gap: 2,
bg: 'base.800',
p: 4,
borderRadius: 'base',
}}
>
{shouldUseSliders ? (
<Flex sx={{ gap: 3, flexDirection: 'column' }}>
<ParamIterations />
<ParamSteps />
<ParamCFGScale />
<ParamWidth />
<ParamHeight />
<ImageToImageStrength />
<ImageToImageFit />
<Flex gap={3} w="full">
<Box flexGrow={2}>
<ParamScheduler />
</Box>
<Box flexGrow={3}>
<ModelSelect />
</Box>
</Flex>
</Flex>
) : (
<Flex sx={{ gap: 2, flexDirection: 'column' }}>
<Flex gap={3}>
<ParamIterations />
<ParamSteps />
<ParamCFGScale />
</Flex>
<ParamWidth />
<ParamHeight />
<Flex gap={3} w="full">
<Box flexGrow={2}>
<ParamScheduler />
</Box>
<Box flexGrow={3}>
<ModelSelect />
</Box>
</Flex>
<ImageToImageStrength />
<ImageToImageFit />
</Flex>
)}
</Flex>
<ParamSeedCollapse />
<ParamVariationCollapse />
<ParamNoiseCollapse />
<ParamSymmetryCollapse />
<ParamSeamlessCollapse />
</Flex>
</OverlayScrollable>
);
};
export default memo(ImageTabParameters);

View File

@ -1,53 +0,0 @@
import { memo } from 'react';
import OverlayScrollable from '../../common/OverlayScrollable';
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
import {
Box,
ButtonGroup,
Collapse,
Flex,
Heading,
HStack,
Image,
Spacer,
useDisclosure,
VStack,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import IAIButton from 'common/components/IAIButton';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import IAIIconButton from 'common/components/IAIIconButton';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { FaUndo, FaUpload } from 'react-icons/fa';
import ImagePromptHeading from 'common/components/ImageToImageSettingsHeader';
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
const CreateImageSettings = () => {
return (
<OverlayScrollable>
<Flex
sx={{
gap: 2,
flexDirection: 'column',
h: 'full',
w: 'full',
position: 'absolute',
borderRadius: 'base',
// bg: 'base.850',
// p: 2,
}}
>
<ImagePromptHeading />
<InitialImagePreview />
<ImageToImageStrength />
<ImageFit />
</Flex>
</OverlayScrollable>
);
};
export default memo(CreateImageSettings);

View File

@ -8,7 +8,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import TextTabMain from './TextTabMain';
import ResizeHandle from '../ResizeHandle';
import TextTabSettings from './TextTabParameters';
import TextTabParameters from './TextTabParameters';
const selector = createSelector(uiSelector, (ui) => {
const {
@ -47,13 +47,13 @@ const TextTab = () => {
{shouldPinParametersPanel && shouldShowParametersPanel && (
<>
<Panel
id="textTab_settings"
id="textTab_parameters"
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
<TextTabSettings />
<TextTabParameters />
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
@ -61,14 +61,7 @@ const TextTab = () => {
<ResizeHandle />
</>
)}
<Panel
id="textTab_main"
order={2}
minSize={30}
onResize={() => {
dispatch(requestCanvasRescale());
}}
>
<Panel id="textTab_content" order={2} minSize={30}>
<TextTabMain />
</Panel>
</PanelGroup>

View File

@ -1,6 +1,5 @@
import { Box, Flex } from '@chakra-ui/react';
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
const TextTabMain = () => {
return (

View File

@ -0,0 +1,73 @@
import { isEqual } from 'lodash-es';
import ResizableDrawer from '../../common/ResizableDrawer/ResizableDrawer';
import TextTabParameters from './TextTabParameters';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector, uiSelector } from '../../../store/uiSelectors';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setShouldShowParametersPanel } from '../../../store/uiSlice';
import { memo } from 'react';
import { Flex } from '@chakra-ui/react';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { Panel, PanelGroup } from 'react-resizable-panels';
const selector = createSelector(
[uiSelector, activeTabNameSelector, lightboxSelector],
(ui, activeTabName, lightbox) => {
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = ui;
const { isLightboxOpen } = lightbox;
return {
activeTabName,
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const TextTabParametersDrawer = () => {
const dispatch = useAppDispatch();
const { shouldPinParametersPanel, shouldShowParametersPanel } =
useAppSelector(selector);
const handleClosePanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
return (
<ResizableDrawer
direction="left"
isResizable={true}
isOpen={shouldShowParametersPanel}
onClose={handleClosePanel}
minWidth={500}
>
<Flex flexDir="column" position="relative" h="full" w="full">
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<TextTabParameters />
</Flex>
</ResizableDrawer>
);
};
export default memo(TextTabParametersDrawer);

View File

@ -1,69 +0,0 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import { Panel } from 'react-resizable-panels';
import CreateTextParameters from './TextTabParameters';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import ResizeHandle from '../ResizeHandle';
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import CreateImageSettings from '../image/ImageTabSettings';
const selector = createSelector(
uiSelector,
(ui) => {
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = ui;
return {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
};
},
defaultSelectorOptions
);
const CreateSidePanelPinned = () => {
const dispatch = useAppDispatch();
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = useAppSelector(selector);
return (
<>
<Panel
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
<CreateTextParameters />
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
</Panel>
{shouldShowImageParameters && (
<>
<ResizeHandle />
<Panel
order={1}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
>
<CreateImageSettings />
</Panel>
</>
)}
<ResizeHandle />
</>
);
};
export default memo(CreateSidePanelPinned);

View File

@ -24,7 +24,8 @@
"src/**/*.ts",
"src/**/*.tsx",
"*.d.ts",
"src/app/store/middleware/listenerMiddleware"
"src/app/store/middleware/listenerMiddleware",
"src/features/nodes/util/edgeBuilders"
],
"exclude": ["src/services/fixtures/*", "node_modules", "dist"],
"references": [{ "path": "./tsconfig.node.json" }]