mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): wip img2img ui
This commit is contained in:
parent
568f0aad71
commit
d40d5276dd
@ -98,7 +98,8 @@
|
|||||||
"pinOptionsPanel": "Pin Options Panel",
|
"pinOptionsPanel": "Pin Options Panel",
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"loadingInvokeAI": "Loading Invoke AI",
|
"loadingInvokeAI": "Loading Invoke AI",
|
||||||
"random": "Random"
|
"random": "Random",
|
||||||
|
"generate": "Generate"
|
||||||
},
|
},
|
||||||
"gallery": {
|
"gallery": {
|
||||||
"generations": "Generations",
|
"generations": "Generations",
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
} from 'features/system/store/systemSlice';
|
} from 'features/system/store/systemSlice';
|
||||||
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
||||||
import { ApplicationFeature } from './invokeai';
|
import { ApplicationFeature } from './invokeai';
|
||||||
|
import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
|
||||||
|
|
||||||
keepGUIAlive();
|
keepGUIAlive();
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ interface Props extends PropsWithChildren {
|
|||||||
|
|
||||||
const App = (props: Props) => {
|
const App = (props: Props) => {
|
||||||
useToastWatcher();
|
useToastWatcher();
|
||||||
|
useGlobalHotkeys();
|
||||||
|
|
||||||
const currentTheme = useAppSelector((state) => state.ui.currentTheme);
|
const currentTheme = useAppSelector((state) => state.ui.currentTheme);
|
||||||
const disabledFeatures = useAppSelector(
|
const disabledFeatures = useAppSelector(
|
||||||
|
@ -14,6 +14,7 @@ import generationReducer from 'features/parameters/store/generationSlice';
|
|||||||
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
||||||
import systemReducer from 'features/system/store/systemSlice';
|
import systemReducer from 'features/system/store/systemSlice';
|
||||||
import uiReducer from 'features/ui/store/uiSlice';
|
import uiReducer from 'features/ui/store/uiSlice';
|
||||||
|
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||||
import modelsReducer from 'features/system/store/modelSlice';
|
import modelsReducer from 'features/system/store/modelSlice';
|
||||||
import nodesReducer from 'features/nodes/store/nodesSlice';
|
import nodesReducer from 'features/nodes/store/nodesSlice';
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ const rootReducer = combineReducers({
|
|||||||
system: systemReducer,
|
system: systemReducer,
|
||||||
ui: uiReducer,
|
ui: uiReducer,
|
||||||
uploads: uploadsReducer,
|
uploads: uploadsReducer,
|
||||||
|
hotkeys: hotkeysReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rootPersistConfig = getPersistConfig({
|
const rootPersistConfig = getPersistConfig({
|
||||||
@ -75,6 +77,7 @@ const rootPersistConfig = getPersistConfig({
|
|||||||
...uiBlacklist,
|
...uiBlacklist,
|
||||||
// ...uploadsBlacklist,
|
// ...uploadsBlacklist,
|
||||||
'uploads',
|
'uploads',
|
||||||
|
'hotkeys',
|
||||||
],
|
],
|
||||||
debounce: 300,
|
debounce: 300,
|
||||||
});
|
});
|
||||||
|
@ -29,6 +29,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { FocusEvent, memo, useEffect, useMemo, useState } from 'react';
|
import { FocusEvent, memo, useEffect, useMemo, useState } from 'react';
|
||||||
import { BiReset } from 'react-icons/bi';
|
import { BiReset } from 'react-icons/bi';
|
||||||
import IAIIconButton, { IAIIconButtonProps } from './IAIIconButton';
|
import IAIIconButton, { IAIIconButtonProps } from './IAIIconButton';
|
||||||
|
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
|
|
||||||
export type IAIFullSliderProps = {
|
export type IAIFullSliderProps = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -119,7 +120,9 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
|||||||
min,
|
min,
|
||||||
numberInputMax
|
numberInputMax
|
||||||
);
|
);
|
||||||
onChange(clamped);
|
const quantized = roundDownToMultiple(clamped, step);
|
||||||
|
onChange(quantized);
|
||||||
|
setLocalInputValue(quantized);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = (v: number | string) => {
|
const handleInputChange = (v: number | string) => {
|
||||||
|
37
invokeai/frontend/web/src/common/hooks/useGlobalHotkeys.ts
Normal file
37
invokeai/frontend/web/src/common/hooks/useGlobalHotkeys.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { RootState } from 'app/store';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
|
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
|
const globalHotkeysSelector = createSelector(
|
||||||
|
(state: RootState) => state.hotkeys,
|
||||||
|
(hotkeys) => {
|
||||||
|
const { shift } = hotkeys;
|
||||||
|
return { shift };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
memoizeOptions: {
|
||||||
|
resultEqualityCheck: isEqual,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useGlobalHotkeys = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { shift } = useAppSelector(globalHotkeysSelector);
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
'*',
|
||||||
|
() => {
|
||||||
|
if (isHotkeyPressed('shift')) {
|
||||||
|
!shift && dispatch(shiftKeyPressed(true));
|
||||||
|
} else {
|
||||||
|
shift && dispatch(shiftKeyPressed(false));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ keyup: true, keydown: true },
|
||||||
|
[shift]
|
||||||
|
);
|
||||||
|
};
|
@ -59,9 +59,6 @@ const CurrentImageDisplay = () => {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ position: 'absolute', top: 0 }}>
|
|
||||||
<CurrentImageButtons />
|
|
||||||
</Box>
|
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
w: 'full',
|
w: 'full',
|
||||||
@ -83,6 +80,9 @@ const CurrentImageDisplay = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Box sx={{ position: 'absolute', top: 0 }}>
|
||||||
|
<CurrentImageButtons />
|
||||||
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,7 @@ const GALLERY_TAB_WIDTHS: Record<
|
|||||||
> = {
|
> = {
|
||||||
// txt2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
// txt2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
||||||
// img2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
// img2img: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
||||||
linear: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
generate: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
||||||
unifiedCanvas: { galleryMinWidth: 200, galleryMaxWidth: 200 },
|
unifiedCanvas: { galleryMinWidth: 200, galleryMaxWidth: 200 },
|
||||||
nodes: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
nodes: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
||||||
// postprocessing: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
// postprocessing: { galleryMinWidth: 200, galleryMaxWidth: 500 },
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent, memo } from 'react';
|
||||||
|
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
@ -7,7 +7,27 @@ import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlic
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Switch } from '@chakra-ui/react';
|
import { Switch } from '@chakra-ui/react';
|
||||||
|
|
||||||
export default function RandomizeSeed() {
|
// export default function RandomizeSeed() {
|
||||||
|
// const dispatch = useAppDispatch();
|
||||||
|
// const { t } = useTranslation();
|
||||||
|
|
||||||
|
// const shouldRandomizeSeed = useAppSelector(
|
||||||
|
// (state: RootState) => state.generation.shouldRandomizeSeed
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
// dispatch(setShouldRandomizeSeed(e.target.checked));
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <Switch
|
||||||
|
// aria-label={t('parameters.randomizeSeed')}
|
||||||
|
// isChecked={shouldRandomizeSeed}
|
||||||
|
// onChange={handleChangeShouldRandomizeSeed}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
const SeedToggle = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -16,13 +36,15 @@ export default function RandomizeSeed() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) =>
|
const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) =>
|
||||||
dispatch(setShouldRandomizeSeed(e.target.checked));
|
dispatch(setShouldRandomizeSeed(!e.target.checked));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
aria-label={t('parameters.randomizeSeed')}
|
aria-label={t('parameters.randomizeSeed')}
|
||||||
isChecked={shouldRandomizeSeed}
|
isChecked={!shouldRandomizeSeed}
|
||||||
onChange={handleChangeShouldRandomizeSeed}
|
onChange={handleChangeShouldRandomizeSeed}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default memo(SeedToggle);
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
|
export const ratioToCSSString = (
|
||||||
|
ratio: AspectRatio,
|
||||||
|
orientation: Orientation
|
||||||
|
) => {
|
||||||
|
if (orientation === 'portrait') {
|
||||||
|
return `${ratio[0]}/${ratio[1]}`;
|
||||||
|
}
|
||||||
|
return `${ratio[1]}/${ratio[0]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ratioToDisplayString = (
|
||||||
|
ratio: AspectRatio,
|
||||||
|
orientation: Orientation
|
||||||
|
) => {
|
||||||
|
if (orientation === 'portrait') {
|
||||||
|
return `${ratio[0]}:${ratio[1]}`;
|
||||||
|
}
|
||||||
|
return `${ratio[1]}:${ratio[0]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AspectRatioPreviewProps = {
|
||||||
|
ratio: AspectRatio;
|
||||||
|
orientation: Orientation;
|
||||||
|
size: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AspectRatio = [number, number];
|
||||||
|
|
||||||
|
export type Orientation = 'portrait' | 'landscape';
|
||||||
|
|
||||||
|
const AspectRatioPreview = (props: AspectRatioPreviewProps) => {
|
||||||
|
const { ratio, size, orientation } = props;
|
||||||
|
|
||||||
|
const ratioCSSString = useMemo(() => {
|
||||||
|
if (orientation === 'portrait') {
|
||||||
|
return `${ratio[0]}/${ratio[1]}`;
|
||||||
|
}
|
||||||
|
return `${ratio[1]}/${ratio[0]}`;
|
||||||
|
}, [ratio, orientation]);
|
||||||
|
|
||||||
|
const ratioDisplayString = useMemo(() => `${ratio[0]}:${ratio[1]}`, [ratio]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
w: size,
|
||||||
|
h: size,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
bg: 'base.700',
|
||||||
|
color: 'base.400',
|
||||||
|
borderRadius: 'base',
|
||||||
|
aspectRatio: ratioCSSString,
|
||||||
|
objectFit: 'contain',
|
||||||
|
...(orientation === 'landscape' ? { h: 'full' } : { w: 'full' }),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text sx={{ size: 'xs', userSelect: 'none' }}>
|
||||||
|
{ratioDisplayString}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(AspectRatioPreview);
|
@ -0,0 +1,76 @@
|
|||||||
|
import { Box, Flex, FormControl, FormLabel, Select } from '@chakra-ui/react';
|
||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { RootState } from 'app/store';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { setWidth } from 'features/parameters/store/generationSlice';
|
||||||
|
import { memo, useState } from 'react';
|
||||||
|
import AspectRatioPreview, {
|
||||||
|
AspectRatio,
|
||||||
|
Orientation,
|
||||||
|
} from './AspectRatioPreview';
|
||||||
|
|
||||||
|
const RATIOS: AspectRatio[] = [
|
||||||
|
[1, 1],
|
||||||
|
[5, 4],
|
||||||
|
[3, 2],
|
||||||
|
[16, 10],
|
||||||
|
[16, 9],
|
||||||
|
];
|
||||||
|
|
||||||
|
RATIOS.forEach((r) => {
|
||||||
|
const float = r[0] / r[1];
|
||||||
|
console.log((512 * float) / 8);
|
||||||
|
});
|
||||||
|
|
||||||
|
const dimensionsSettingsSelector = createSelector(
|
||||||
|
(state: RootState) => state.generation,
|
||||||
|
(generation) => {
|
||||||
|
const { width, height } = generation;
|
||||||
|
|
||||||
|
return { width, height };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const DimensionsSettings = () => {
|
||||||
|
const { width, height } = useAppSelector(dimensionsSettingsSelector);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const [ratioIndex, setRatioIndex] = useState(4);
|
||||||
|
const [orientation, setOrientation] = useState<Orientation>('portrait');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap={3}>
|
||||||
|
<Box flexShrink={0}>
|
||||||
|
<AspectRatioPreview
|
||||||
|
ratio={RATIOS[ratioIndex]}
|
||||||
|
orientation={orientation}
|
||||||
|
size="4rem"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Aspect Ratio</FormLabel>
|
||||||
|
<Select
|
||||||
|
onChange={(e) => {
|
||||||
|
setRatioIndex(Number(e.target.value));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{RATIOS.map((r, i) => (
|
||||||
|
<option key={r.join()} value={i}>{`${r[0]}:${r[1]}`}</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<IAISlider
|
||||||
|
label="Size"
|
||||||
|
value={width}
|
||||||
|
min={64}
|
||||||
|
max={2048}
|
||||||
|
step={8}
|
||||||
|
onChange={(v) => {
|
||||||
|
dispatch(setWidth(v));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DimensionsSettings);
|
@ -0,0 +1,38 @@
|
|||||||
|
import { Box, BoxProps } from '@chakra-ui/react';
|
||||||
|
import { RootState } from 'app/store';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { setHeight } from 'features/parameters/store/generationSlice';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const HeightSlider = (props: BoxProps) => {
|
||||||
|
const height = useAppSelector((state: RootState) => state.generation.height);
|
||||||
|
const shift = useAppSelector((state: RootState) => state.hotkeys.shift);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<IAISlider
|
||||||
|
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||||
|
label={t('parameters.height')}
|
||||||
|
value={height}
|
||||||
|
min={64}
|
||||||
|
step={shift ? 8 : 64}
|
||||||
|
max={2048}
|
||||||
|
onChange={(v) => dispatch(setHeight(v))}
|
||||||
|
handleReset={() => dispatch(setHeight(512))}
|
||||||
|
withInput
|
||||||
|
withReset
|
||||||
|
withSliderMarks
|
||||||
|
sliderNumberInputProps={{ max: 15360 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(HeightSlider);
|
@ -23,7 +23,7 @@ export default function MainHeight() {
|
|||||||
label={t('parameters.height')}
|
label={t('parameters.height')}
|
||||||
value={height}
|
value={height}
|
||||||
min={64}
|
min={64}
|
||||||
step={64}
|
step={8}
|
||||||
max={2048}
|
max={2048}
|
||||||
onChange={(v) => dispatch(setHeight(v))}
|
onChange={(v) => dispatch(setHeight(v))}
|
||||||
handleReset={() => dispatch(setHeight(512))}
|
handleReset={() => dispatch(setHeight(512))}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Box, BoxProps } from '@chakra-ui/react';
|
||||||
import { DIFFUSERS_SAMPLERS, SAMPLERS } from 'app/constants';
|
import { DIFFUSERS_SAMPLERS, SAMPLERS } from 'app/constants';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
@ -7,7 +8,7 @@ import { activeModelSelector } from 'features/system/store/systemSelectors';
|
|||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export default function MainSampler() {
|
export default function MainSampler(props: BoxProps) {
|
||||||
const sampler = useAppSelector(
|
const sampler = useAppSelector(
|
||||||
(state: RootState) => state.generation.sampler
|
(state: RootState) => state.generation.sampler
|
||||||
);
|
);
|
||||||
@ -19,14 +20,16 @@ export default function MainSampler() {
|
|||||||
dispatch(setSampler(e.target.value));
|
dispatch(setSampler(e.target.value));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISelect
|
<Box {...props}>
|
||||||
label={t('parameters.sampler')}
|
<IAISelect
|
||||||
value={sampler}
|
label={t('parameters.sampler')}
|
||||||
onChange={handleChangeSampler}
|
value={sampler}
|
||||||
validValues={
|
onChange={handleChangeSampler}
|
||||||
activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS
|
validValues={
|
||||||
}
|
activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS
|
||||||
minWidth={36}
|
}
|
||||||
/>
|
minWidth={36}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { Flex, VStack } from '@chakra-ui/react';
|
import { Divider, Flex, VStack } from '@chakra-ui/react';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/storeHooks';
|
||||||
|
import { ModelSelect } from 'exports';
|
||||||
|
import HeightSlider from './HeightSlider';
|
||||||
import MainCFGScale from './MainCFGScale';
|
import MainCFGScale from './MainCFGScale';
|
||||||
import MainHeight from './MainHeight';
|
import MainHeight from './MainHeight';
|
||||||
import MainIterations from './MainIterations';
|
import MainIterations from './MainIterations';
|
||||||
import MainSampler from './MainSampler';
|
import MainSampler from './MainSampler';
|
||||||
import MainSteps from './MainSteps';
|
import MainSteps from './MainSteps';
|
||||||
import MainWidth from './MainWidth';
|
import MainWidth from './MainWidth';
|
||||||
|
import WidthSlider from './WidthSlider';
|
||||||
|
|
||||||
export default function MainSettings() {
|
export default function MainSettings() {
|
||||||
const shouldUseSliders = useAppSelector(
|
const shouldUseSliders = useAppSelector(
|
||||||
@ -23,17 +26,18 @@ export default function MainSettings() {
|
|||||||
<MainSampler />
|
<MainSampler />
|
||||||
</VStack>
|
</VStack>
|
||||||
) : (
|
) : (
|
||||||
<Flex rowGap={2} flexDirection="column">
|
<Flex gap={3} flexDirection="column">
|
||||||
<Flex columnGap={1}>
|
<Flex gap={3}>
|
||||||
<MainIterations />
|
<MainIterations />
|
||||||
<MainSteps />
|
<MainSteps />
|
||||||
<MainCFGScale />
|
<MainCFGScale />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex columnGap={1}>
|
<Flex gap={3}>
|
||||||
<MainWidth />
|
<MainSampler flexGrow={2} />
|
||||||
<MainHeight />
|
<ModelSelect flexGrow={3} />
|
||||||
<MainSampler />
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<WidthSlider />
|
||||||
|
<HeightSlider />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ export default function MainWidth() {
|
|||||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||||
label={t('parameters.width')}
|
label={t('parameters.width')}
|
||||||
value={width}
|
value={width}
|
||||||
min={64}
|
min={8}
|
||||||
step={64}
|
step={64}
|
||||||
max={2048}
|
max={2048}
|
||||||
onChange={(v) => dispatch(setWidth(v))}
|
onChange={(v) => dispatch(setWidth(v))}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import { Box, BoxProps } from '@chakra-ui/react';
|
||||||
|
import { RootState } from 'app/store';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { setWidth } from 'features/parameters/store/generationSlice';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const WidthSlider = (props: BoxProps) => {
|
||||||
|
const width = useAppSelector((state: RootState) => state.generation.width);
|
||||||
|
const shift = useAppSelector((state: RootState) => state.hotkeys.shift);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<IAISlider
|
||||||
|
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||||
|
label={t('parameters.width')}
|
||||||
|
value={width}
|
||||||
|
min={64}
|
||||||
|
step={shift ? 8 : 64}
|
||||||
|
max={2048}
|
||||||
|
onChange={(v) => dispatch(setWidth(v))}
|
||||||
|
handleReset={() => dispatch(setWidth(512))}
|
||||||
|
withInput
|
||||||
|
withReset
|
||||||
|
withSliderMarks
|
||||||
|
sliderNumberInputProps={{ max: 15360 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(WidthSlider);
|
@ -4,7 +4,10 @@ import { Feature } from 'app/features';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { tabMap } from 'features/ui/store/tabMap';
|
import { tabMap } from 'features/ui/store/tabMap';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import {
|
||||||
|
activeTabNameSelector,
|
||||||
|
uiSelector,
|
||||||
|
} from 'features/ui/store/uiSelectors';
|
||||||
import { openAccordionItemsChanged } from 'features/ui/store/uiSlice';
|
import { openAccordionItemsChanged } from 'features/ui/store/uiSlice';
|
||||||
import { filter, map } from 'lodash';
|
import { filter, map } from 'lodash';
|
||||||
import { ReactNode, useCallback } from 'react';
|
import { ReactNode, useCallback } from 'react';
|
||||||
@ -23,7 +26,7 @@ const parametersAccordionSelector = createSelector(
|
|||||||
|
|
||||||
let openAccordions: number[] = [];
|
let openAccordions: number[] = [];
|
||||||
|
|
||||||
if (tabMap[activeTab] === 'linear') {
|
if (tabMap[activeTab] === 'generate') {
|
||||||
openAccordions = openLinearAccordionItems;
|
openAccordions = openLinearAccordionItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaPlay } from 'react-icons/fa';
|
import { FaPlay } from 'react-icons/fa';
|
||||||
import { linearGraphBuilt, sessionCreated } from 'services/thunks/session';
|
import { generateGraphBuilt, sessionCreated } from 'services/thunks/session';
|
||||||
|
|
||||||
interface InvokeButton
|
interface InvokeButton
|
||||||
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||||
@ -26,7 +26,7 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
|
|
||||||
const handleClickGenerate = () => {
|
const handleClickGenerate = () => {
|
||||||
// dispatch(generateImage(activeTabName));
|
// dispatch(generateImage(activeTabName));
|
||||||
dispatch(linearGraphBuilt());
|
dispatch(generateGraphBuilt());
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Box, BoxProps, Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
@ -30,7 +30,7 @@ const selector = createSelector(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ModelSelect = () => {
|
const ModelSelect = (props: BoxProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { allModelNames, selectedModel } = useAppSelector(selector);
|
const { allModelNames, selectedModel } = useAppSelector(selector);
|
||||||
@ -39,12 +39,9 @@ const ModelSelect = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Box {...props}>
|
||||||
style={{
|
|
||||||
paddingInlineStart: 1.5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IAISelect
|
<IAISelect
|
||||||
|
label={t('modelManager.model')}
|
||||||
style={{ fontSize: 'sm' }}
|
style={{ fontSize: 'sm' }}
|
||||||
aria-label={t('accessibility.modelSelect')}
|
aria-label={t('accessibility.modelSelect')}
|
||||||
tooltip={selectedModel?.description || ''}
|
tooltip={selectedModel?.description || ''}
|
||||||
@ -52,7 +49,7 @@ const ModelSelect = () => {
|
|||||||
validValues={allModelNames}
|
validValues={allModelNames}
|
||||||
onChange={handleChangeModel}
|
onChange={handleChangeModel}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,8 +34,6 @@ const SiteHeader = () => {
|
|||||||
>
|
>
|
||||||
<StatusIndicator />
|
<StatusIndicator />
|
||||||
|
|
||||||
<ModelSelect />
|
|
||||||
|
|
||||||
{resolution === 'desktop' ? (
|
{resolution === 'desktop' ? (
|
||||||
<SiteHeaderMenu />
|
<SiteHeaderMenu />
|
||||||
) : (
|
) : (
|
||||||
|
@ -39,7 +39,7 @@ export const floatingParametersPanelButtonSelector = createSelector(
|
|||||||
const shouldShowParametersPanelButton =
|
const shouldShowParametersPanelButton =
|
||||||
!canvasBetaLayoutCheck &&
|
!canvasBetaLayoutCheck &&
|
||||||
(!shouldPinParametersPanel || !shouldShowParametersPanel) &&
|
(!shouldPinParametersPanel || !shouldShowParametersPanel) &&
|
||||||
['linear', 'unifiedCanvas'].includes(activeTabName);
|
['generate', 'unifiedCanvas'].includes(activeTabName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldPinParametersPanel,
|
shouldPinParametersPanel,
|
||||||
|
@ -23,8 +23,10 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { ResourceKey } from 'i18next';
|
import { ResourceKey } from 'i18next';
|
||||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||||
import NodeEditor from 'features/nodes/components/NodeEditor';
|
import NodeEditor from 'features/nodes/components/NodeEditor';
|
||||||
import LinearWorkspace from './tabs/Linear/LinearWorkspace';
|
import GenerateWorkspace from './tabs/Generate/GenerateWorkspace';
|
||||||
import { FaImage } from 'react-icons/fa';
|
import { FaImage } from 'react-icons/fa';
|
||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { BsLightningChargeFill, BsLightningFill } from 'react-icons/bs';
|
||||||
|
|
||||||
export interface InvokeTabInfo {
|
export interface InvokeTabInfo {
|
||||||
id: InvokeTabName;
|
id: InvokeTabName;
|
||||||
@ -36,30 +38,36 @@ const tabIconStyles: ChakraProps['sx'] = {
|
|||||||
boxSize: 6,
|
boxSize: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildTabs = (disabledTabs: InvokeTabName[]): InvokeTabInfo[] => {
|
const tabs: InvokeTabInfo[] = [
|
||||||
const tabs: InvokeTabInfo[] = [
|
{
|
||||||
{
|
id: 'generate',
|
||||||
id: 'linear',
|
icon: <Icon as={BsLightningChargeFill} sx={{ boxSize: 5 }} />,
|
||||||
icon: <Icon as={FaImage} sx={tabIconStyles} />,
|
workarea: <GenerateWorkspace />,
|
||||||
workarea: <LinearWorkspace />,
|
},
|
||||||
},
|
{
|
||||||
{
|
id: 'unifiedCanvas',
|
||||||
id: 'unifiedCanvas',
|
icon: <Icon as={MdGridOn} sx={{ boxSize: 6 }} />,
|
||||||
icon: <Icon as={MdGridOn} sx={tabIconStyles} />,
|
workarea: <UnifiedCanvasWorkarea />,
|
||||||
workarea: <UnifiedCanvasWorkarea />,
|
},
|
||||||
},
|
{
|
||||||
{
|
id: 'nodes',
|
||||||
id: 'nodes',
|
icon: <Icon as={MdDeviceHub} sx={{ boxSize: 6 }} />,
|
||||||
icon: <Icon as={MdDeviceHub} sx={tabIconStyles} />,
|
workarea: <NodeEditor />,
|
||||||
workarea: <NodeEditor />,
|
},
|
||||||
},
|
];
|
||||||
];
|
|
||||||
return tabs.filter((tab) => !disabledTabs.includes(tab.id));
|
const enabledTabsSelector = createSelector(
|
||||||
};
|
(state: RootState) => state.ui,
|
||||||
|
(ui) => {
|
||||||
|
const { disabledTabs } = ui;
|
||||||
|
|
||||||
|
return tabs.filter((tab) => !disabledTabs.includes(tab.id));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default function InvokeTabs() {
|
export default function InvokeTabs() {
|
||||||
const activeTab = useAppSelector(activeTabIndexSelector);
|
const activeTab = useAppSelector(activeTabIndexSelector);
|
||||||
|
const enabledTabs = useAppSelector(enabledTabsSelector);
|
||||||
const isLightBoxOpen = useAppSelector(
|
const isLightBoxOpen = useAppSelector(
|
||||||
(state: RootState) => state.lightbox.isLightboxOpen
|
(state: RootState) => state.lightbox.isLightboxOpen
|
||||||
);
|
);
|
||||||
@ -72,22 +80,20 @@ export default function InvokeTabs() {
|
|||||||
(state: RootState) => state.system.disabledTabs
|
(state: RootState) => state.system.disabledTabs
|
||||||
);
|
);
|
||||||
|
|
||||||
const activeTabs = buildTabs(disabledTabs);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useHotkeys('1', () => {
|
useHotkeys('1', () => {
|
||||||
dispatch(setActiveTab(0));
|
dispatch(setActiveTab('generate'));
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotkeys('2', () => {
|
useHotkeys('2', () => {
|
||||||
dispatch(setActiveTab(1));
|
dispatch(setActiveTab('unifiedCanvas'));
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotkeys('3', () => {
|
useHotkeys('3', () => {
|
||||||
dispatch(setActiveTab(2));
|
dispatch(setActiveTab('nodes'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lightbox Hotkey
|
// Lightbox Hotkey
|
||||||
@ -111,7 +117,7 @@ export default function InvokeTabs() {
|
|||||||
|
|
||||||
const tabs = useMemo(
|
const tabs = useMemo(
|
||||||
() =>
|
() =>
|
||||||
activeTabs.map((tab) => (
|
enabledTabs.map((tab) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
hasArrow
|
hasArrow
|
||||||
@ -126,13 +132,15 @@ export default function InvokeTabs() {
|
|||||||
</Tab>
|
</Tab>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)),
|
)),
|
||||||
[t, activeTabs]
|
[t, enabledTabs]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tabPanels = useMemo(
|
const tabPanels = useMemo(
|
||||||
() =>
|
() =>
|
||||||
activeTabs.map((tab) => <TabPanel key={tab.id}>{tab.workarea}</TabPanel>),
|
enabledTabs.map((tab) => (
|
||||||
[activeTabs]
|
<TabPanel key={tab.id}>{tab.workarea}</TabPanel>
|
||||||
|
)),
|
||||||
|
[enabledTabs]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
|
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
|
||||||
|
|
||||||
const LinearContent = () => {
|
const GenerateContent = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -18,4 +18,4 @@ const LinearContent = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LinearContent;
|
export default GenerateContent;
|
@ -1,5 +1,16 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import {
|
||||||
|
AspectRatio,
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Select,
|
||||||
|
Slider,
|
||||||
|
SliderFilledTrack,
|
||||||
|
SliderThumb,
|
||||||
|
SliderTrack,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { Feature } from 'app/features';
|
import { Feature } from 'app/features';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
|
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
|
||||||
import ImageToImageToggle from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle';
|
import ImageToImageToggle from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle';
|
||||||
@ -10,6 +21,7 @@ import RandomizeSeed from 'features/parameters/components/AdvancedParameters/See
|
|||||||
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
|
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
|
||||||
import GenerateVariationsToggle from 'features/parameters/components/AdvancedParameters/Variations/GenerateVariations';
|
import GenerateVariationsToggle from 'features/parameters/components/AdvancedParameters/Variations/GenerateVariations';
|
||||||
import VariationsSettings from 'features/parameters/components/AdvancedParameters/Variations/VariationsSettings';
|
import VariationsSettings from 'features/parameters/components/AdvancedParameters/Variations/VariationsSettings';
|
||||||
|
import DimensionsSettings from 'features/parameters/components/ImageDimensions/DimensionsSettings';
|
||||||
import MainSettings from 'features/parameters/components/MainParameters/MainSettings';
|
import MainSettings from 'features/parameters/components/MainParameters/MainSettings';
|
||||||
import ParametersAccordion, {
|
import ParametersAccordion, {
|
||||||
ParametersAccordionItems,
|
ParametersAccordionItems,
|
||||||
@ -17,14 +29,15 @@ import ParametersAccordion, {
|
|||||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||||
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
|
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
|
||||||
import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
|
import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
|
||||||
import { memo, useMemo } from 'react';
|
import { findIndex } from 'lodash';
|
||||||
|
import { memo, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
|
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
|
||||||
|
|
||||||
const LinearParameters = () => {
|
const GenerateParameters = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const linearAccordions: ParametersAccordionItems = useMemo(
|
const generateAccordionItems: ParametersAccordionItems = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
// general: {
|
// general: {
|
||||||
// name: 'general',
|
// name: 'general',
|
||||||
@ -80,15 +93,16 @@ const LinearParameters = () => {
|
|||||||
gap: 2,
|
gap: 2,
|
||||||
bg: 'base.800',
|
bg: 'base.800',
|
||||||
p: 4,
|
p: 4,
|
||||||
|
pb: 6,
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MainSettings />
|
<MainSettings />
|
||||||
<ImageToImageToggle />
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<ParametersAccordion accordionItems={linearAccordions} />
|
<ImageToImageToggle />
|
||||||
|
<ParametersAccordion accordionItems={generateAccordionItems} />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(LinearParameters);
|
export default memo(GenerateParameters);
|
@ -1,15 +1,15 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/storeHooks';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import LinearContent from './LinearContent';
|
import GenerateContent from './GenerateContent';
|
||||||
import LinearParameters from './LinearParameters';
|
import GenerateParameters from './GenerateParameters';
|
||||||
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store';
|
||||||
import Scrollable from '../../common/Scrollable';
|
import Scrollable from '../../common/Scrollable';
|
||||||
import ParametersSlide from '../../common/ParametersSlide';
|
import ParametersSlide from '../../common/ParametersSlide';
|
||||||
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
|
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
|
||||||
|
|
||||||
const LinearWorkspace = () => {
|
const GenerateWorkspace = () => {
|
||||||
const shouldPinParametersPanel = useAppSelector(
|
const shouldPinParametersPanel = useAppSelector(
|
||||||
(state: RootState) => state.ui.shouldPinParametersPanel
|
(state: RootState) => state.ui.shouldPinParametersPanel
|
||||||
);
|
);
|
||||||
@ -33,7 +33,7 @@ const LinearWorkspace = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Scrollable>
|
<Scrollable>
|
||||||
<LinearParameters />
|
<GenerateParameters />
|
||||||
</Scrollable>
|
</Scrollable>
|
||||||
<PinParametersPanelButton
|
<PinParametersPanelButton
|
||||||
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
||||||
@ -42,12 +42,12 @@ const LinearWorkspace = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<ParametersSlide>
|
<ParametersSlide>
|
||||||
<LinearParameters />
|
<GenerateParameters />
|
||||||
</ParametersSlide>
|
</ParametersSlide>
|
||||||
)}
|
)}
|
||||||
<LinearContent />
|
<GenerateContent />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(LinearWorkspace);
|
export default memo(GenerateWorkspace);
|
26
invokeai/frontend/web/src/features/ui/store/hotkeysSlice.ts
Normal file
26
invokeai/frontend/web/src/features/ui/store/hotkeysSlice.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
type HotkeysState = {
|
||||||
|
shift: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialHotkeysState: HotkeysState = {
|
||||||
|
shift: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: HotkeysState = initialHotkeysState;
|
||||||
|
|
||||||
|
export const hotkeysSlice = createSlice({
|
||||||
|
name: 'hotkeys',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
shiftKeyPressed: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.shift = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { shiftKeyPressed } = hotkeysSlice.actions;
|
||||||
|
|
||||||
|
export default hotkeysSlice.reducer;
|
@ -1,7 +1,7 @@
|
|||||||
export const tabMap = [
|
export const tabMap = [
|
||||||
// 'txt2img',
|
// 'txt2img',
|
||||||
// 'img2img',
|
// 'img2img',
|
||||||
'linear',
|
'generate',
|
||||||
'unifiedCanvas',
|
'unifiedCanvas',
|
||||||
'nodes',
|
'nodes',
|
||||||
// 'postprocessing',
|
// 'postprocessing',
|
||||||
|
@ -19,6 +19,9 @@ const initialUIState: UIState = {
|
|||||||
shouldShowGallery: true,
|
shouldShowGallery: true,
|
||||||
shouldHidePreview: false,
|
shouldHidePreview: false,
|
||||||
openLinearAccordionItems: [],
|
openLinearAccordionItems: [],
|
||||||
|
disabledParameterPanels: [],
|
||||||
|
disabledTabs: [],
|
||||||
|
openGenerateAccordionItems: [],
|
||||||
openUnifiedCanvasAccordionItems: [],
|
openUnifiedCanvasAccordionItems: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,8 +99,8 @@ export const uiSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
openAccordionItemsChanged: (state, action: PayloadAction<number[]>) => {
|
openAccordionItemsChanged: (state, action: PayloadAction<number[]>) => {
|
||||||
if (tabMap[state.activeTab] === 'linear') {
|
if (tabMap[state.activeTab] === 'generate') {
|
||||||
state.openLinearAccordionItems = action.payload;
|
state.openGenerateAccordionItems = action.payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabMap[state.activeTab] === 'unifiedCanvas') {
|
if (tabMap[state.activeTab] === 'unifiedCanvas') {
|
||||||
|
@ -15,5 +15,8 @@ export interface UIState {
|
|||||||
shouldPinGallery: boolean;
|
shouldPinGallery: boolean;
|
||||||
shouldShowGallery: boolean;
|
shouldShowGallery: boolean;
|
||||||
openLinearAccordionItems: number[];
|
openLinearAccordionItems: number[];
|
||||||
|
disabledParameterPanels: string[];
|
||||||
|
disabledTabs: InvokeTabName[];
|
||||||
|
openGenerateAccordionItems: number[];
|
||||||
openUnifiedCanvasAccordionItems: number[];
|
openUnifiedCanvasAccordionItems: number[];
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { createAppAsyncThunk } from 'app/storeUtils';
|
import { createAppAsyncThunk } from 'app/storeUtils';
|
||||||
import { SessionsService } from 'services/api';
|
import { SessionsService } from 'services/api';
|
||||||
import { buildLinearGraph } from 'features/nodes/util/linearGraphBuilder/buildLinearGraph';
|
import { buildLinearGraph as buildGenerateGraph } from 'features/nodes/util/linearGraphBuilder/buildLinearGraph';
|
||||||
import { isAnyOf, isFulfilled } from '@reduxjs/toolkit';
|
import { isAnyOf, isFulfilled } from '@reduxjs/toolkit';
|
||||||
import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
|
import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
|
||||||
|
|
||||||
export const linearGraphBuilt = createAppAsyncThunk(
|
export const generateGraphBuilt = createAppAsyncThunk(
|
||||||
'api/linearGraphBuilt',
|
'api/generateGraphBuilt',
|
||||||
async (_, { dispatch, getState }) => {
|
async (_, { dispatch, getState }) => {
|
||||||
const graph = buildLinearGraph(getState());
|
const graph = buildGenerateGraph(getState());
|
||||||
|
|
||||||
dispatch(sessionCreated({ graph }));
|
dispatch(sessionCreated({ graph }));
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export const nodesGraphBuilt = createAppAsyncThunk(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const isFulfilledAnyGraphBuilt = isAnyOf(
|
export const isFulfilledAnyGraphBuilt = isAnyOf(
|
||||||
linearGraphBuilt.fulfilled,
|
generateGraphBuilt.fulfilled,
|
||||||
nodesGraphBuilt.fulfilled
|
nodesGraphBuilt.fulfilled
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user