mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into dev/diffusers
This commit is contained in:
commit
07ac88baed
48
frontend/dist/assets/index-legacy-8e84772c.js
vendored
48
frontend/dist/assets/index-legacy-8e84772c.js
vendored
File diff suppressed because one or more lines are too long
48
frontend/dist/assets/index-legacy-f27062cd.js
vendored
Normal file
48
frontend/dist/assets/index-legacy-f27062cd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
623
frontend/dist/assets/index.d864890e.js
vendored
623
frontend/dist/assets/index.d864890e.js
vendored
File diff suppressed because one or more lines are too long
623
frontend/dist/assets/index.d8f54146.js
vendored
Normal file
623
frontend/dist/assets/index.d8f54146.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
frontend/dist/index.html
vendored
6
frontend/dist/index.html
vendored
@ -8,8 +8,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
||||
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
|
||||
<script type="module" crossorigin src="./assets/index.d864890e.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.81f1c71c.css">
|
||||
<script type="module" crossorigin src="./assets/index.d8f54146.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.8ee30fa0.css">
|
||||
<script type="module">try{import.meta.url;import("_").catch(()=>1);}catch(e){}window.__vite_is_modern_browser=true;</script>
|
||||
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
|
||||
</head>
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
|
||||
<script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/polyfills-legacy-dde3a68a.js"></script>
|
||||
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-8e84772c.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
|
||||
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-f27062cd.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -40,6 +40,7 @@ export type IAIFullSliderProps = {
|
||||
sliderMarkRightOffset?: number;
|
||||
withInput?: boolean;
|
||||
isInteger?: boolean;
|
||||
width?: string | number;
|
||||
inputWidth?: string | number;
|
||||
inputReadOnly?: boolean;
|
||||
withReset?: boolean;
|
||||
@ -71,6 +72,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
|
||||
max = 100,
|
||||
step = 1,
|
||||
onChange,
|
||||
width = '100%',
|
||||
tooltipSuffix = '',
|
||||
withSliderMarks = false,
|
||||
sliderMarkLeftOffset = 0,
|
||||
@ -161,6 +163,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
focusThumbOnChange={false}
|
||||
isDisabled={isSliderDisabled}
|
||||
width={width}
|
||||
{...rest}
|
||||
>
|
||||
{withSliderMarks && (
|
||||
|
@ -3,8 +3,11 @@ import _ from 'lodash';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
|
||||
import {
|
||||
clearMask,
|
||||
resetCanvasInteractionState,
|
||||
setIsMaskEnabled,
|
||||
setShouldShowBoundingBox,
|
||||
setShouldSnapToGrid,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
@ -24,6 +27,8 @@ const selector = createSelector(
|
||||
shouldLockBoundingBox,
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
@ -33,6 +38,8 @@ const selector = createSelector(
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -44,13 +51,62 @@ const selector = createSelector(
|
||||
|
||||
const useInpaintingCanvasHotkeys = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { activeTabName, shouldShowBoundingBox, tool, isStaging } =
|
||||
useAppSelector(selector);
|
||||
const {
|
||||
activeTabName,
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
const previousToolRef = useRef<CanvasTool | null>(null);
|
||||
|
||||
const canvasStage = getCanvasStage();
|
||||
|
||||
// Beta Keys
|
||||
const handleClearMask = () => dispatch(clearMask());
|
||||
|
||||
useHotkeys(
|
||||
['shift+c'],
|
||||
() => {
|
||||
handleClearMask();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleToggleEnableMask = () =>
|
||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||
|
||||
useHotkeys(
|
||||
['h'],
|
||||
() => {
|
||||
handleToggleEnableMask();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[isMaskEnabled]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['n'],
|
||||
() => {
|
||||
dispatch(setShouldSnapToGrid(!shouldSnapToGrid));
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[shouldSnapToGrid]
|
||||
);
|
||||
//
|
||||
|
||||
useHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
|
@ -70,6 +70,9 @@ const PromptInput = () => {
|
||||
resize="vertical"
|
||||
height={30}
|
||||
ref={promptRef}
|
||||
_placeholder={{
|
||||
color: 'var(--text-color-secondary)',
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
@ -55,6 +55,7 @@ export interface OptionsState {
|
||||
upscalingStrength: number;
|
||||
variationAmount: number;
|
||||
width: number;
|
||||
shouldUseCanvasBetaLayout: boolean;
|
||||
}
|
||||
|
||||
const initialOptionsState: OptionsState = {
|
||||
@ -101,6 +102,7 @@ const initialOptionsState: OptionsState = {
|
||||
upscalingStrength: 0.75,
|
||||
variationAmount: 0.1,
|
||||
width: 512,
|
||||
shouldUseCanvasBetaLayout: false,
|
||||
};
|
||||
|
||||
const initialState: OptionsState = initialOptionsState;
|
||||
@ -396,6 +398,9 @@ export const optionsSlice = createSlice({
|
||||
setInfillMethod: (state, action: PayloadAction<string>) => {
|
||||
state.infillMethod = action.payload;
|
||||
},
|
||||
setShouldUseCanvasBetaLayout: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldUseCanvasBetaLayout = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -451,6 +456,7 @@ export const {
|
||||
setUpscalingStrength,
|
||||
setVariationAmount,
|
||||
setWidth,
|
||||
setShouldUseCanvasBetaLayout,
|
||||
} = optionsSlice.actions;
|
||||
|
||||
export default optionsSlice.reducer;
|
||||
|
@ -23,8 +23,9 @@ const selector = createSelector(
|
||||
},
|
||||
''
|
||||
);
|
||||
const activeDesc = model_list[activeModel].description;
|
||||
|
||||
return { models, activeModel, isProcessing };
|
||||
return { models, activeModel, isProcessing, activeDesc };
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
@ -35,7 +36,8 @@ const selector = createSelector(
|
||||
|
||||
const ModelSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { models, activeModel, isProcessing } = useAppSelector(selector);
|
||||
const { models, activeModel, isProcessing, activeDesc } =
|
||||
useAppSelector(selector);
|
||||
const handleChangeModel = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
dispatch(requestModelChange(e.target.value));
|
||||
};
|
||||
@ -48,6 +50,7 @@ const ModelSelect = () => {
|
||||
>
|
||||
<IAISelect
|
||||
style={{ fontSize: '0.8rem' }}
|
||||
tooltip={activeDesc}
|
||||
isDisabled={isProcessing}
|
||||
value={activeModel}
|
||||
validValues={models}
|
||||
|
@ -32,10 +32,11 @@ import IAISelect from 'common/components/IAISelect';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { optionsSelector } from 'features/options/store/optionsSelectors';
|
||||
import { setShouldUseCanvasBetaLayout } from 'features/options/store/optionsSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector, optionsSelector],
|
||||
(system) => {
|
||||
(system, options) => {
|
||||
const {
|
||||
shouldDisplayInProgressType,
|
||||
shouldConfirmOnDelete,
|
||||
@ -45,6 +46,8 @@ const selector = createSelector(
|
||||
enableImageDebugging,
|
||||
} = system;
|
||||
|
||||
const { shouldUseCanvasBetaLayout } = options;
|
||||
|
||||
return {
|
||||
shouldDisplayInProgressType,
|
||||
shouldConfirmOnDelete,
|
||||
@ -52,6 +55,7 @@ const selector = createSelector(
|
||||
models: _.map(model_list, (_model, key) => key),
|
||||
saveIntermediatesInterval,
|
||||
enableImageDebugging,
|
||||
shouldUseCanvasBetaLayout,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -93,6 +97,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
shouldDisplayGuides,
|
||||
saveIntermediatesInterval,
|
||||
enableImageDebugging,
|
||||
shouldUseCanvasBetaLayout,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
/**
|
||||
@ -173,6 +178,14 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
dispatch(setShouldDisplayGuides(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAISwitch
|
||||
styleClass="settings-modal-item"
|
||||
label={'Use Canvas Beta Layout'}
|
||||
isChecked={shouldUseCanvasBetaLayout}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldUseCanvasBetaLayout(e.target.checked))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="settings-modal-items">
|
||||
|
@ -28,25 +28,34 @@ export const floatingSelector = createSelector(
|
||||
shouldPinOptionsPanel,
|
||||
shouldShowOptionsPanel,
|
||||
shouldHoldOptionsPanelOpen,
|
||||
shouldUseCanvasBetaLayout,
|
||||
} = options;
|
||||
|
||||
const { shouldShowGallery, shouldPinGallery, shouldHoldGalleryOpen } =
|
||||
gallery;
|
||||
|
||||
const canvasBetaLayoutCheck =
|
||||
shouldUseCanvasBetaLayout && activeTabName === 'unifiedCanvas';
|
||||
|
||||
const shouldShowOptionsPanelButton =
|
||||
!canvasBetaLayoutCheck &&
|
||||
!(
|
||||
shouldShowOptionsPanel ||
|
||||
(shouldHoldOptionsPanelOpen && !shouldPinOptionsPanel)
|
||||
) && ['txt2img', 'img2img', 'unifiedCanvas'].includes(activeTabName);
|
||||
) &&
|
||||
['txt2img', 'img2img', 'unifiedCanvas'].includes(activeTabName);
|
||||
|
||||
const shouldShowGalleryButton =
|
||||
!(shouldShowGallery || (shouldHoldGalleryOpen && !shouldPinGallery)) &&
|
||||
['txt2img', 'img2img', 'unifiedCanvas'].includes(activeTabName);
|
||||
|
||||
const shouldShowProcessButtons =
|
||||
!canvasBetaLayoutCheck &&
|
||||
(!shouldPinOptionsPanel || !shouldShowOptionsPanel);
|
||||
|
||||
return {
|
||||
shouldPinOptionsPanel,
|
||||
shouldShowProcessButtons:
|
||||
!shouldPinOptionsPanel || !shouldShowOptionsPanel,
|
||||
shouldShowProcessButtons,
|
||||
shouldShowOptionsPanelButton,
|
||||
shouldShowOptionsPanel,
|
||||
shouldShowGallery,
|
||||
|
@ -39,6 +39,7 @@
|
||||
column-gap: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inpainting-canvas-area {
|
||||
display: flex;
|
||||
@ -59,11 +60,11 @@
|
||||
|
||||
.inpainting-canvas-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.inpainting-canvas-wrapper {
|
||||
position: relative;
|
||||
@ -80,8 +81,6 @@
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inpainting-options-btn {
|
||||
min-height: 2rem;
|
||||
|
@ -0,0 +1,71 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
// import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import IAICanvasResizer from 'features/canvas/components/IAICanvasResizer';
|
||||
import _ from 'lodash';
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import IAICanvasOutpaintingControls from 'features/canvas/components/IAICanvasToolbar/IAICanvasToolbar';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import UnifiedCanvasToolbarBeta from './UnifiedCanvasToolbarBeta';
|
||||
import UnifiedCanvasToolSettingsBeta from './UnifiedCanvasToolSettingsBeta';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const { doesCanvasNeedScaling } = canvas;
|
||||
return {
|
||||
doesCanvasNeedScaling,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasDisplayBeta = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { doesCanvasNeedScaling } = useAppSelector(selector);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
|
||||
const resizeCallback = _.debounce(() => {
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
}, 250);
|
||||
|
||||
window.addEventListener('resize', resizeCallback);
|
||||
|
||||
return () => window.removeEventListener('resize', resizeCallback);
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className={'workarea-single-view'}>
|
||||
<Flex
|
||||
flexDirection={'row'}
|
||||
width="100%"
|
||||
height="100%"
|
||||
columnGap={'1rem'}
|
||||
padding="1rem"
|
||||
>
|
||||
<UnifiedCanvasToolbarBeta />
|
||||
<Flex
|
||||
width="100%"
|
||||
height="100%"
|
||||
flexDirection={'column'}
|
||||
rowGap={'1rem'}
|
||||
>
|
||||
<UnifiedCanvasToolSettingsBeta />
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedCanvasDisplayBeta;
|
@ -0,0 +1,13 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings';
|
||||
import UnifiedCanvasLimitStrokesToBox from './UnifiedCanvasLimitStrokesToBox';
|
||||
|
||||
export default function UnifiedCanvasBaseBrushSettings() {
|
||||
return (
|
||||
<Flex gap={'1rem'} alignItems="center">
|
||||
<UnifiedCanvasBrushSettings />
|
||||
<UnifiedCanvasLimitStrokesToBox />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import UnifiedCanvasBrushSize from './UnifiedCanvasBrushSize';
|
||||
import UnifiedCanvasColorPicker from './UnifiedCanvasColorPicker';
|
||||
|
||||
export default function UnifiedCanvasBrushSettings() {
|
||||
return (
|
||||
<Flex columnGap={'1rem'} alignItems="center">
|
||||
<UnifiedCanvasBrushSize />
|
||||
<UnifiedCanvasColorPicker />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setBrushSize } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export default function UnifiedCanvasBrushSize() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const brushSize = useAppSelector(
|
||||
(state: RootState) => state.canvas.brushSize
|
||||
);
|
||||
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
useHotkeys(
|
||||
['BracketLeft'],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.max(brushSize - 5, 5)));
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['BracketRight'],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.min(brushSize + 5, 500)));
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label="Size"
|
||||
value={brushSize}
|
||||
withInput
|
||||
onChange={(newSize) => dispatch(setBrushSize(newSize))}
|
||||
sliderNumberInputProps={{ max: 500 }}
|
||||
inputReadOnly={false}
|
||||
width={'100px'}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
||||
import { clearMask } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasClearMask() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleClearMask = () => dispatch(clearMask());
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
size={'sm'}
|
||||
leftIcon={<FaTrash />}
|
||||
onClick={handleClearMask}
|
||||
tooltip="Clear Mask (Shift+C)"
|
||||
>
|
||||
Clear
|
||||
</IAIButton>
|
||||
);
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setBrushColor, setMaskColor } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
const { brushColor, maskColor, layer } = canvas;
|
||||
return {
|
||||
brushColor,
|
||||
maskColor,
|
||||
layer,
|
||||
isStaging,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasColorPicker() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { brushColor, maskColor, layer, isStaging } = useAppSelector(selector);
|
||||
|
||||
const currentColorDisplay = () => {
|
||||
if (layer === 'base')
|
||||
return `rgba(${brushColor.r},${brushColor.g},${brushColor.b},${brushColor.a})`;
|
||||
if (layer === 'mask')
|
||||
return `rgba(${maskColor.r},${maskColor.g},${maskColor.b},${maskColor.a})`;
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
['shift+BracketLeft'],
|
||||
() => {
|
||||
dispatch(
|
||||
setBrushColor({
|
||||
...brushColor,
|
||||
a: _.clamp(brushColor.a - 0.05, 0.05, 1),
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushColor]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+BracketRight'],
|
||||
() => {
|
||||
dispatch(
|
||||
setBrushColor({
|
||||
...brushColor,
|
||||
a: _.clamp(brushColor.a + 0.05, 0.05, 1),
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushColor]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
triggerComponent={
|
||||
<Box
|
||||
style={{
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
minWidth: '30px',
|
||||
minHeight: '30px',
|
||||
borderRadius: '99999999px',
|
||||
backgroundColor: currentColorDisplay(),
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Flex minWidth={'15rem'} direction={'column'} gap={'1rem'} width={'100%'}>
|
||||
{layer === 'base' && (
|
||||
<IAIColorPicker
|
||||
style={{
|
||||
width: '100%',
|
||||
paddingTop: '0.5rem',
|
||||
paddingBottom: '0.5rem',
|
||||
}}
|
||||
color={brushColor}
|
||||
onChange={(newColor) => dispatch(setBrushColor(newColor))}
|
||||
/>
|
||||
)}
|
||||
{layer === 'mask' && (
|
||||
<IAIColorPicker
|
||||
style={{
|
||||
width: '100%',
|
||||
paddingTop: '0.5rem',
|
||||
paddingBottom: '0.5rem',
|
||||
}}
|
||||
color={maskColor}
|
||||
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
export default function UnifiedCanvasDarkenOutsideSelection() {
|
||||
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldDarkenOutsideBoundingBox
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Darken Outside"
|
||||
isChecked={shouldDarkenOutsideBoundingBox}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked))
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setIsMaskEnabled } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
export default function UnifiedCanvasEnableMask() {
|
||||
const isMaskEnabled = useAppSelector(
|
||||
(state: RootState) => state.canvas.isMaskEnabled
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleToggleEnableMask = () =>
|
||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Enable Mask (H)"
|
||||
isChecked={isMaskEnabled}
|
||||
onChange={handleToggleEnableMask}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setShouldRestrictStrokesToBox } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
export default function UnifiedCanvasLimitStrokesToBox() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shouldRestrictStrokesToBox = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldRestrictStrokesToBox
|
||||
);
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Limit To Box"
|
||||
isChecked={shouldRestrictStrokesToBox}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldRestrictStrokesToBox(e.target.checked))
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings';
|
||||
import UnifiedCanvasClearMask from './UnifiedCanvasClearMask';
|
||||
import UnifiedCanvasEnableMask from './UnifiedCanvasEnableMask';
|
||||
import UnifiedCanvasPreserveMask from './UnifiedCanvasPreserveMask';
|
||||
|
||||
export default function UnifiedCanvasMaskBrushSettings() {
|
||||
return (
|
||||
<Flex gap={'1rem'} alignItems="center">
|
||||
<UnifiedCanvasBrushSettings />
|
||||
<UnifiedCanvasEnableMask />
|
||||
<UnifiedCanvasPreserveMask />
|
||||
<UnifiedCanvasClearMask />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { Flex } from '@chakra-ui/layout';
|
||||
import React from 'react';
|
||||
import UnifiedCanvasDarkenOutsideSelection from './UnifiedCanvasDarkenOutsideSelection';
|
||||
import UnifiedCanvasShowGrid from './UnifiedCanvasShowGrid';
|
||||
import UnifiedCanvasSnapToGrid from './UnifiedCanvasSnapToGrid';
|
||||
|
||||
export default function UnifiedCanvasMoveSettings() {
|
||||
return (
|
||||
<Flex alignItems={'center'} gap="1rem">
|
||||
<UnifiedCanvasShowGrid />
|
||||
<UnifiedCanvasSnapToGrid />
|
||||
<UnifiedCanvasDarkenOutsideSelection />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setShouldPreserveMaskedArea } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
export default function UnifiedCanvasPreserveMask() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shouldPreserveMaskedArea = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldPreserveMaskedArea
|
||||
);
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Preserve Masked"
|
||||
isChecked={shouldPreserveMaskedArea}
|
||||
onChange={(e) => dispatch(setShouldPreserveMaskedArea(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
setShouldAutoSave,
|
||||
setShouldCropToBoundingBoxOnSave,
|
||||
setShouldShowCanvasDebugInfo,
|
||||
setShouldShowIntermediates,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { FaWrench } from 'react-icons/fa';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import EmptyTempFolderButtonModal from 'features/system/components/ClearTempFolderButtonModal';
|
||||
|
||||
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
|
||||
|
||||
export const canvasControlsSelector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
} = useAppSelector(canvasControlsSelector);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
tooltip="Canvas Settings"
|
||||
tooltipProps={{
|
||||
placement: 'bottom',
|
||||
}}
|
||||
aria-label="Canvas Settings"
|
||||
icon={<FaWrench />}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Flex direction={'column'} gap={'0.5rem'}>
|
||||
<IAICheckbox
|
||||
label="Show Intermediates"
|
||||
isChecked={shouldShowIntermediates}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldShowIntermediates(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAICheckbox
|
||||
label="Auto Save to Gallery"
|
||||
isChecked={shouldAutoSave}
|
||||
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
||||
/>
|
||||
<IAICheckbox
|
||||
label="Save Box Region Only"
|
||||
isChecked={shouldCropToBoundingBoxOnSave}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAICheckbox
|
||||
label="Show Canvas Debug Info"
|
||||
isChecked={shouldShowCanvasDebugInfo}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<ClearCanvasHistoryButtonModal />
|
||||
<EmptyTempFolderButtonModal />
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedCanvasSettings;
|
@ -0,0 +1,20 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setShouldShowGrid } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
|
||||
export default function UnifiedCanvasShowGrid() {
|
||||
const shouldShowGrid = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldShowGrid
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Show Grid"
|
||||
isChecked={shouldShowGrid}
|
||||
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setShouldSnapToGrid } from 'features/canvas/store/canvasSlice';
|
||||
import React, { ChangeEvent } from 'react';
|
||||
|
||||
export default function UnifiedCanvasSnapToGrid() {
|
||||
const shouldSnapToGrid = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldSnapToGrid
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeShouldSnapToGrid = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldSnapToGrid(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAICheckbox
|
||||
label="Snap to Grid (N)"
|
||||
isChecked={shouldSnapToGrid}
|
||||
onChange={handleChangeShouldSnapToGrid}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import UnifiedCanvasBaseBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings';
|
||||
import UnifiedCanvasMaskBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import UnifiedCanvasMoveSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const { tool, layer } = canvas;
|
||||
return {
|
||||
tool,
|
||||
layer,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasToolSettingsBeta() {
|
||||
const { tool, layer } = useAppSelector(selector);
|
||||
|
||||
return (
|
||||
<Flex height="2rem" minHeight="2rem" maxHeight="2rem" alignItems={'center'}>
|
||||
{layer == 'base' && ['brush', 'eraser', 'colorPicker'].includes(tool) && (
|
||||
<UnifiedCanvasBaseBrushSettings />
|
||||
)}
|
||||
{layer == 'mask' && ['brush', 'eraser', 'colorPicker'].includes(tool) && (
|
||||
<UnifiedCanvasMaskBrushSettings />
|
||||
)}
|
||||
{tool == 'move' && <UnifiedCanvasMoveSettings />}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaCopy } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasCopyToClipboard() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
const shouldCropToBoundingBoxOnSave = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldCropToBoundingBoxOnSave
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useHotkeys(
|
||||
['meta+c', 'ctrl+c'],
|
||||
() => {
|
||||
handleCopyImageToClipboard();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer, isProcessing]
|
||||
);
|
||||
|
||||
const handleCopyImageToClipboard = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: shouldCropToBoundingBoxOnSave ? false : true,
|
||||
cropToBoundingBox: shouldCropToBoundingBoxOnSave,
|
||||
shouldCopy: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Copy to Clipboard (Cmd/Ctrl+C)"
|
||||
tooltip="Copy to Clipboard (Cmd/Ctrl+C)"
|
||||
icon={<FaCopy />}
|
||||
onClick={handleCopyImageToClipboard}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaDownload } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasDownloadImage() {
|
||||
const dispatch = useAppDispatch();
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
const shouldCropToBoundingBoxOnSave = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldCropToBoundingBoxOnSave
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+d'],
|
||||
() => {
|
||||
handleDownloadAsImage();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer, isProcessing]
|
||||
);
|
||||
|
||||
const handleDownloadAsImage = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: shouldCropToBoundingBoxOnSave ? false : true,
|
||||
cropToBoundingBox: shouldCropToBoundingBoxOnSave,
|
||||
shouldDownload: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Download as Image (Shift+D)"
|
||||
tooltip="Download as Image (Shift+D)"
|
||||
icon={<FaDownload />}
|
||||
onClick={handleDownloadAsImage}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import useImageUploader from 'common/hooks/useImageUploader';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import React from 'react';
|
||||
import { FaUpload } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasFileUploader() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const { openUploader } = useImageUploader();
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Upload"
|
||||
tooltip="Upload"
|
||||
icon={<FaUpload />}
|
||||
onClick={openUploader}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setIsMaskEnabled, setLayer } from 'features/canvas/store/canvasSlice';
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
CanvasLayer,
|
||||
LAYER_NAMES_DICT,
|
||||
} from 'features/canvas/store/canvasTypes';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
const { layer, isMaskEnabled } = canvas;
|
||||
return { layer, isMaskEnabled, isStaging };
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasLayerSelect() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { layer, isMaskEnabled, isStaging } = useAppSelector(selector);
|
||||
|
||||
const handleToggleMaskLayer = () => {
|
||||
dispatch(setLayer(layer === 'mask' ? 'base' : 'mask'));
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
['q'],
|
||||
() => {
|
||||
handleToggleMaskLayer();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[layer]
|
||||
);
|
||||
|
||||
const handleChangeLayer = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const newLayer = e.target.value as CanvasLayer;
|
||||
dispatch(setLayer(newLayer));
|
||||
if (newLayer === 'mask' && !isMaskEnabled) {
|
||||
dispatch(setIsMaskEnabled(true));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<IAISelect
|
||||
tooltip={'Layer (Q)'}
|
||||
tooltipProps={{ hasArrow: true, placement: 'top' }}
|
||||
value={layer}
|
||||
validValues={LAYER_NAMES_DICT}
|
||||
onChange={handleChangeLayer}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaLayerGroup } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasMergeVisible() {
|
||||
const dispatch = useAppDispatch();
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+m'],
|
||||
() => {
|
||||
handleMergeVisible();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer, isProcessing]
|
||||
);
|
||||
|
||||
const handleMergeVisible = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: false,
|
||||
shouldSetAsInitialImage: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Merge Visible (Shift+M)"
|
||||
tooltip="Merge Visible (Shift+M)"
|
||||
icon={<FaLayerGroup />}
|
||||
onClick={handleMergeVisible}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setTool } from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaArrowsAlt } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasMoveTool() {
|
||||
const tool = useAppSelector((state: RootState) => state.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useHotkeys(
|
||||
['v'],
|
||||
() => {
|
||||
handleSelectMoveTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSelectMoveTool = () => dispatch(setTool('move'));
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Move Tool (V)"
|
||||
tooltip="Move Tool (V)"
|
||||
icon={<FaArrowsAlt />}
|
||||
data-selected={tool === 'move' || isStaging}
|
||||
onClick={handleSelectMoveTool}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import { Flex } from '@chakra-ui/layout';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import CancelButton from 'features/options/components/ProcessButtons/CancelButton';
|
||||
import InvokeButton from 'features/options/components/ProcessButtons/InvokeButton';
|
||||
import { setShouldShowOptionsPanel } from 'features/options/store/optionsSlice';
|
||||
import React from 'react';
|
||||
import { FaSlidersH } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasProcessingButtons() {
|
||||
const shouldPinOptionsPanel = useAppSelector(
|
||||
(state: RootState) => state.options.shouldPinOptionsPanel
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleShowOptionsPanel = () => {
|
||||
dispatch(setShouldShowOptionsPanel(true));
|
||||
if (shouldPinOptionsPanel) {
|
||||
setTimeout(() => dispatch(setDoesCanvasNeedScaling(true)), 400);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} gap="0.5rem">
|
||||
<IAIIconButton
|
||||
tooltip="Show Options Panel (O)"
|
||||
tooltipProps={{ placement: 'top' }}
|
||||
aria-label="Show Options Panel"
|
||||
onClick={handleShowOptionsPanel}
|
||||
>
|
||||
<FaSlidersH />
|
||||
</IAIIconButton>
|
||||
<Flex>
|
||||
<InvokeButton iconButton />
|
||||
</Flex>
|
||||
<Flex>
|
||||
<CancelButton width={'100%'} height={'40px'} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
resetCanvas,
|
||||
resizeAndScaleCanvas,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import React from 'react';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasResetCanvas() {
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
const handleResetCanvas = () => {
|
||||
dispatch(resetCanvas());
|
||||
dispatch(resizeAndScaleCanvas());
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Clear Canvas"
|
||||
tooltip="Clear Canvas"
|
||||
icon={<FaTrash />}
|
||||
onClick={handleResetCanvas}
|
||||
style={{ backgroundColor: 'var(--btn-delete-image)' }}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { resetCanvasView } from 'features/canvas/store/canvasSlice';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaCrosshairs } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasResetView() {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useHotkeys(
|
||||
['r'],
|
||||
() => {
|
||||
handleResetCanvasView();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
const handleResetCanvasView = () => {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
if (!canvasBaseLayer) return;
|
||||
const clientRect = canvasBaseLayer.getClientRect({
|
||||
skipTransform: true,
|
||||
});
|
||||
dispatch(
|
||||
resetCanvasView({
|
||||
contentRect: clientRect,
|
||||
})
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Reset View (R)"
|
||||
tooltip="Reset View (R)"
|
||||
icon={<FaCrosshairs />}
|
||||
onClick={handleResetCanvasView}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasSaveToGallery() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
const shouldCropToBoundingBoxOnSave = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldCropToBoundingBoxOnSave
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useHotkeys(
|
||||
['shift+s'],
|
||||
() => {
|
||||
handleSaveToGallery();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer, isProcessing]
|
||||
);
|
||||
|
||||
const handleSaveToGallery = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: shouldCropToBoundingBoxOnSave ? false : true,
|
||||
cropToBoundingBox: shouldCropToBoundingBoxOnSave,
|
||||
shouldSaveToGallery: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Save to Gallery (Shift+S)"
|
||||
tooltip="Save to Gallery (Shift+S)"
|
||||
icon={<FaSave />}
|
||||
onClick={handleSaveToGallery}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
addEraseRect,
|
||||
addFillRect,
|
||||
setBrushColor,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
FaEraser,
|
||||
FaEyeDropper,
|
||||
FaFillDrip,
|
||||
FaPaintBrush,
|
||||
FaPlus,
|
||||
} from 'react-icons/fa';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector, systemSelector],
|
||||
(canvas, isStaging, system) => {
|
||||
const { isProcessing } = system;
|
||||
const { tool } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
isStaging,
|
||||
isProcessing,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasToolSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
|
||||
useHotkeys(
|
||||
['b'],
|
||||
() => {
|
||||
handleSelectBrushTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['e'],
|
||||
() => {
|
||||
handleSelectEraserTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[tool]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['c'],
|
||||
() => {
|
||||
handleSelectColorPickerTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[tool]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+f'],
|
||||
() => {
|
||||
handleFillRect();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
}
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['delete', 'backspace'],
|
||||
() => {
|
||||
handleEraseBoundingBox();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
}
|
||||
);
|
||||
|
||||
const handleSelectBrushTool = () => dispatch(setTool('brush'));
|
||||
const handleSelectEraserTool = () => dispatch(setTool('eraser'));
|
||||
const handleSelectColorPickerTool = () => dispatch(setTool('colorPicker'));
|
||||
const handleFillRect = () => dispatch(addFillRect());
|
||||
const handleEraseBoundingBox = () => dispatch(addEraseRect());
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} gap={'0.5rem'}>
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label="Brush Tool (B)"
|
||||
tooltip="Brush Tool (B)"
|
||||
icon={<FaPaintBrush />}
|
||||
data-selected={tool === 'brush' && !isStaging}
|
||||
onClick={handleSelectBrushTool}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Eraser Tool (E)"
|
||||
tooltip="Eraser Tool (E)"
|
||||
icon={<FaEraser />}
|
||||
data-selected={tool === 'eraser' && !isStaging}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleSelectEraserTool}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label="Fill Bounding Box (Shift+F)"
|
||||
tooltip="Fill Bounding Box (Shift+F)"
|
||||
icon={<FaFillDrip />}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleFillRect}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Erase Bounding Box Area (Delete/Backspace)"
|
||||
tooltip="Erase Bounding Box Area (Delete/Backspace)"
|
||||
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleEraseBoundingBox}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label="Color Picker (C)"
|
||||
tooltip="Color Picker (C)"
|
||||
icon={<FaEyeDropper />}
|
||||
data-selected={tool === 'colorPicker' && !isStaging}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleSelectColorPickerTool}
|
||||
width={'max-content'}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedCanvasToolSelect;
|
@ -0,0 +1,59 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
import IAICanvasUndoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton';
|
||||
import IAICanvasRedoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton';
|
||||
import UnifiedCanvasLayerSelect from './UnifiedCanvasToolbar/UnifiedCanvasLayerSelect';
|
||||
import UnifiedCanvasToolSelect from './UnifiedCanvasToolbar/UnifiedCanvasToolSelect';
|
||||
import UnifiedCanvasSettings from './UnifiedCanvasToolSettings/UnifiedCanvasSettings';
|
||||
import UnifiedCanvasMoveTool from './UnifiedCanvasToolbar/UnifiedCanvasMoveTool';
|
||||
import UnifiedCanvasResetView from './UnifiedCanvasToolbar/UnifiedCanvasResetView';
|
||||
import UnifiedCanvasMergeVisible from './UnifiedCanvasToolbar/UnifiedCanvasMergeVisible';
|
||||
import UnifiedCanvasSaveToGallery from './UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery';
|
||||
import UnifiedCanvasCopyToClipboard from './UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard';
|
||||
import UnifiedCanvasDownloadImage from './UnifiedCanvasToolbar/UnifiedCanvasDownloadImage';
|
||||
import UnifiedCanvasFileUploader from './UnifiedCanvasToolbar/UnifiedCanvasFileUploader';
|
||||
import UnifiedCanvasResetCanvas from './UnifiedCanvasToolbar/UnifiedCanvasResetCanvas';
|
||||
import UnifiedCanvasProcessingButtons from './UnifiedCanvasToolbar/UnifiedCanvasProcessingButtons';
|
||||
import { RootState, useAppSelector } from 'app/store';
|
||||
|
||||
const UnifiedCanvasToolbarBeta = () => {
|
||||
const shouldShowOptionsPanel = useAppSelector(
|
||||
(state: RootState) => state.options.shouldShowOptionsPanel
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} rowGap="0.5rem" width="6rem">
|
||||
<UnifiedCanvasLayerSelect />
|
||||
<UnifiedCanvasToolSelect />
|
||||
|
||||
<Flex gap={'0.5rem'}>
|
||||
<UnifiedCanvasMoveTool />
|
||||
<UnifiedCanvasResetView />
|
||||
</Flex>
|
||||
|
||||
<Flex columnGap={'0.5rem'}>
|
||||
<UnifiedCanvasMergeVisible />
|
||||
<UnifiedCanvasSaveToGallery />
|
||||
</Flex>
|
||||
<Flex columnGap={'0.5rem'}>
|
||||
<UnifiedCanvasCopyToClipboard />
|
||||
<UnifiedCanvasDownloadImage />
|
||||
</Flex>
|
||||
|
||||
<Flex gap={'0.5rem'}>
|
||||
<IAICanvasUndoButton />
|
||||
<IAICanvasRedoButton />
|
||||
</Flex>
|
||||
|
||||
<Flex gap={'0.5rem'}>
|
||||
<UnifiedCanvasFileUploader />
|
||||
<UnifiedCanvasResetCanvas />
|
||||
</Flex>
|
||||
|
||||
<UnifiedCanvasSettings />
|
||||
{!shouldShowOptionsPanel && <UnifiedCanvasProcessingButtons />}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedCanvasToolbarBeta;
|
@ -1,14 +1,23 @@
|
||||
import UnifiedCanvasPanel from './UnifiedCanvasPanel';
|
||||
import UnifiedCanvasDisplay from './UnifiedCanvasDisplay';
|
||||
import InvokeWorkarea from 'features/tabs/components/InvokeWorkarea';
|
||||
import { RootState, useAppSelector } from 'app/store';
|
||||
import UnifiedCanvasDisplayBeta from './UnifiedCanvasBeta/UnifiedCanvasDisplayBeta';
|
||||
|
||||
export default function UnifiedCanvasWorkarea() {
|
||||
const shouldUseCanvasBetaLayout = useAppSelector(
|
||||
(state: RootState) => state.options.shouldUseCanvasBetaLayout
|
||||
);
|
||||
return (
|
||||
<InvokeWorkarea
|
||||
optionsPanel={<UnifiedCanvasPanel />}
|
||||
styleClass="inpainting-workarea-overrides"
|
||||
>
|
||||
{shouldUseCanvasBetaLayout ? (
|
||||
<UnifiedCanvasDisplayBeta />
|
||||
) : (
|
||||
<UnifiedCanvasDisplay />
|
||||
)}
|
||||
</InvokeWorkarea>
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
VERSION=$(grep ^VERSION ../setup.py | awk '{ print $3 }' | sed "s/'//g" )
|
||||
VERSION="$VERSION-p1"
|
||||
|
||||
echo "Be certain that you're in the 'installer' directory before continuing."
|
||||
read -p "Press any key to continue, or CTRL-C to exit..."
|
||||
|
@ -22,7 +22,6 @@ set PYTHON_URL=https://www.python.org/downloads/windows/
|
||||
set MINIMUM_PYTHON_VERSION=3.9.0
|
||||
set PYTHON_URL=https://www.python.org/downloads/release/python-3109/
|
||||
|
||||
|
||||
set err_msg=An error has occurred and the script could not continue.
|
||||
|
||||
@rem --------------------------- Intro -------------------------------
|
||||
@ -65,23 +64,21 @@ if %errorlevel% == 1 (
|
||||
@rem Cleanup
|
||||
del /q .tmp1 .tmp2
|
||||
|
||||
echo Updating PIP...
|
||||
call python -m pip install --no-warn-script-location -q --upgrade pip
|
||||
|
||||
@rem --------------------- Get the requirements file ------------
|
||||
echo.
|
||||
echo Setting up requirements file for your system.
|
||||
copy /y environments-and-requirements\requirements-win-colab-cuda.txt .\requirements.txt
|
||||
|
||||
@rem --------------------- Get the root directory for installation ------------
|
||||
set rootdir=""
|
||||
set response=""
|
||||
set selection=""
|
||||
set "rootdir="
|
||||
set "response="
|
||||
set "selection="
|
||||
:pick_rootdir
|
||||
if %rootdir% neq "" goto :done
|
||||
if defined rootdir goto :done
|
||||
set /p selection=Select the path to install InvokeAI's directory into [%UserProfile%]:
|
||||
if %selection% == "" set selection=%UserProfile%
|
||||
set dest=%selection%\invokeai
|
||||
if not defined selection set selection=%UserProfile%
|
||||
set selection=%selection:"=%
|
||||
set dest="%selection%\invokeai"
|
||||
if exist %dest% (
|
||||
set response=y
|
||||
set /p response=The directory %dest% exists. Do you wish to resume install from a previous attempt? [Y/n]:
|
||||
@ -93,16 +90,18 @@ set selection=""
|
||||
set response=y
|
||||
set /p response="You have chosen to install InvokeAI into %rootdir%. OK? [Y/n]: "
|
||||
if !response! == "" set response=y
|
||||
if /I !response! neq y set rootdir=""
|
||||
if /I !response! neq y set "rootdir="
|
||||
goto :pick_rootdir
|
||||
:done
|
||||
|
||||
set rootdir=%rootdir:"=%
|
||||
|
||||
@rem ---------------------- Initialize the runtime directory ---------------------
|
||||
echo.
|
||||
echo *** Creating Runtime Directory %rootdir% ***
|
||||
if not exist %rootdir% mkdir %rootdir%
|
||||
if not exist "%rootdir%" mkdir "%rootdir%"
|
||||
@rem for unknown reasons the mkdir works but returns an error code
|
||||
if not exist %rootdir% (
|
||||
if not exist "%rootdir%" (
|
||||
set err_msg=Could not create the directory %rootdir%. Please check the directory's permissions and try again.
|
||||
goto :err_exit
|
||||
)
|
||||
@ -111,7 +110,7 @@ echo Successful.
|
||||
@rem --------------------------- Create and populate .venv ---------------------------
|
||||
echo.
|
||||
echo ** Creating Virtual Environment for InvokeAI **
|
||||
call python -mvenv %rootdir%\.venv
|
||||
call python -mvenv "%rootdir%\.venv"
|
||||
if %errorlevel% neq 0 (
|
||||
set err_msg=Could not create virtual environment %rootdir%\.venv. Please check the directory's permissions and try again.
|
||||
goto :err_exit
|
||||
@ -120,18 +119,24 @@ echo Successful.
|
||||
|
||||
echo.
|
||||
echo *** Installing InvokeAI Requirements ***
|
||||
call %rootdir%\.venv\Scripts\activate.bat
|
||||
echo Activating environment
|
||||
call "%rootdir%\.venv\Scripts\activate.bat"
|
||||
|
||||
set PYTHON=%rootdir%\.venv\Scripts\python
|
||||
echo updating pip with "%PYTHON%"
|
||||
call "%PYTHON%" -mensurepip --upgrade
|
||||
|
||||
copy environments-and-requirements\requirements-win-colab-cuda.txt .\requirements.txt
|
||||
call python -mpip install -r requirements.txt
|
||||
call "%PYTHON%" -mpip install --prefer-binary -r requirements.txt
|
||||
if %errorlevel% neq 0 (
|
||||
set err_msg=Installation of requirements failed. See above for errors and check %TROUBLESHOOTING% for potential solutions.
|
||||
set err_msg=Requirements installation failed. See above for errors and check %TROUBLESHOOTING% for potential solutions.
|
||||
goto :err_exit
|
||||
)
|
||||
echo Installation successful.
|
||||
|
||||
echo.
|
||||
echo *** Installing InvokeAI Modules and Executables ***
|
||||
call python -mpip install %INVOKE_AI_SRC%
|
||||
call "%PYTHON%" -mpip install %INVOKE_AI_SRC%
|
||||
if %errorlevel% neq 0 (
|
||||
set err_msg=Installation of InvokeAI failed. See above for errors and check %TROUBLESHOOTING% for potential solutions.
|
||||
goto :err_exit
|
||||
@ -139,21 +144,21 @@ if %errorlevel% neq 0 (
|
||||
echo Installation successful.
|
||||
|
||||
@rem --------------------------- Set up the root directory ---------------------------
|
||||
xcopy /E /Y .\templates\rootdir %rootdir%
|
||||
xcopy /E /Y .\templates\rootdir "%rootdir%"
|
||||
PUSHD "%rootdir%"
|
||||
call .venv\Scripts\python .venv\Scripts\configure_invokeai.py --root="%rootdir%"
|
||||
call "%PYTHON%" .venv\Scripts\configure_invokeai.py "--root=%rootdir%"
|
||||
if %errorlevel% neq 0 (
|
||||
set err_msg=Configuration failed. See above for error messages and check %TROUBLESHOOTING% for potential solutions.
|
||||
goto :err_exit
|
||||
)
|
||||
POPD
|
||||
copy .\templates\invoke.bat.in %rootdir%\invoke.bat
|
||||
copy .\templates\update.bat.in %rootdir%\update.bat
|
||||
copy .\templates\invoke.bat.in "%rootdir%\invoke.bat"
|
||||
copy .\templates\update.bat.in "%rootdir%\update.bat"
|
||||
|
||||
@rem so that update.bat works
|
||||
mkdir %rootdir%\environments-and-requirements
|
||||
xcopy /I /Y .\environments-and-requirements %rootdir%\environments-and-requirements
|
||||
copy .\requirements.txt %rootdir%\requirements.txt
|
||||
mkdir "%rootdir%\environments-and-requirements"
|
||||
xcopy /I /Y .\environments-and-requirements "%rootdir%\environments-and-requirements"
|
||||
copy .\requirements.txt "%rootdir%\requirements.txt"
|
||||
|
||||
|
||||
echo.
|
||||
@ -213,3 +218,4 @@ echo The installer will exit now.
|
||||
pause
|
||||
exit /b
|
||||
|
||||
pause
|
||||
|
@ -126,17 +126,17 @@ do
|
||||
read -e -p "InvokeAI will be installed into $ROOTDIR. OK? [y]: " input
|
||||
RESPONSE=${input:='y'}
|
||||
if [ "$RESPONSE" == 'y' ]; then
|
||||
if [ -e $ROOTDIR ]; then
|
||||
if [ -e "$ROOTDIR" ]; then
|
||||
echo
|
||||
read -e -p "Directory $ROOTDIR already exists. Do you want to resume an interrupted install? [y]: " input
|
||||
read -e -p "Directory "$ROOTDIR" already exists. Do you want to resume an interrupted install? [y]: " input
|
||||
RESPONSE=${input:='y'}
|
||||
if [ "$RESPONSE" != 'y' ]; then
|
||||
ROOTDIR=""
|
||||
fi
|
||||
else
|
||||
mkdir -p $ROOTDIR
|
||||
mkdir -p "$ROOTDIR"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Could not create $ROOTDIR. Try again with a different install location."
|
||||
echo "Could not create "$ROOTDIR". Try again with a different install location."
|
||||
ROOTDIR=""
|
||||
fi
|
||||
fi
|
||||
@ -149,18 +149,19 @@ done
|
||||
echo
|
||||
echo "** Creating Virtual Environment for InvokeAI **"
|
||||
|
||||
$PYTHON -mpip install --upgrade pip
|
||||
$PYTHON -mvenv $ROOTDIR/.venv
|
||||
_err_exit $? "Python failed to create virtual environment $ROOTDIR/.venv. Please see $TROUBLESHOOTING for help."
|
||||
$PYTHON -mvenv "$ROOTDIR"/.venv
|
||||
_err_exit $? "Python failed to create virtual environment "$ROOTDIR"/.venv. Please see $TROUBLESHOOTING for help."
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
echo
|
||||
echo "** Activating Virtual Environment for InvokeAI **"
|
||||
|
||||
source $ROOTDIR/.venv/bin/activate
|
||||
_err_exit $? "Failed to activate virtual evironment $ROOTDIR/.venv. Please see $TROUBLESHOOTING for help."
|
||||
source "$ROOTDIR"/.venv/bin/activate
|
||||
_err_exit $? "Failed to activate virtual evironment "$ROOTDIR"/.venv. Please see $TROUBLESHOOTING for help."
|
||||
|
||||
PYTHON=$ROOTDIR/.venv/bin/python
|
||||
PYTHON="$ROOTDIR"/.venv/bin/python
|
||||
$PYTHON -mensurepip --upgrade
|
||||
$PYTHON -mpip install --upgrade pip
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
echo
|
||||
@ -179,7 +180,7 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
$PYTHON -mpip install -r requirements.txt
|
||||
$PYTHON -mpip install --prefer-binary -r requirements.txt
|
||||
_err_exit $? "Failed to install InvokeAI's dependencies."
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
@ -189,29 +190,29 @@ $PYTHON -mpip install $INVOKE_AI_SRC
|
||||
_err_exit $? "Installation of InvokeAI failed."
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
echo " *** Setting Up Root Directory $ROOTDIR *** "
|
||||
cp -pr templates/rootdir/* $ROOTDIR/
|
||||
cp templates/invoke.sh.in $ROOTDIR/invoke.sh
|
||||
chmod a+rx $ROOTDIR/invoke.sh
|
||||
cp templates/update.sh.in $ROOTDIR/update.sh
|
||||
chmod a+rx $ROOTDIR/update.sh
|
||||
echo " *** Setting Up Root Directory "$ROOTDIR" *** "
|
||||
cp -pr templates/rootdir/* "$ROOTDIR"/
|
||||
cp templates/invoke.sh.in "$ROOTDIR"/invoke.sh
|
||||
chmod a+rx "$ROOTDIR"/invoke.sh
|
||||
cp templates/update.sh.in "$ROOTDIR"/update.sh
|
||||
chmod a+rx "$ROOTDIR"/update.sh
|
||||
|
||||
# This allows the updater to work!
|
||||
cp -pr environments-and-requirements requirements.txt $ROOTDIR/
|
||||
cp -pr environments-and-requirements requirements.txt "$ROOTDIR/"
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
echo
|
||||
echo "*** Confguring InvokeAI ***"
|
||||
pushd $ROOTDIR
|
||||
./.venv/bin/configure_invokeai.py --root=$ROOTDIR
|
||||
pushd "$ROOTDIR" >/dev/null
|
||||
$PYTHON ./.venv/bin/configure_invokeai.py --root="$ROOTDIR"
|
||||
_err_exit $? "Initial configuration failed. Please see above error messages and $TROUBLESHOOTING for help."
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
popd
|
||||
cp templates/invoke.sh.in $ROOTDIR/invoke.sh
|
||||
chmod a+rx $ROOTDIR/invoke.sh
|
||||
cp templates/invoke.sh.in "$ROOTDIR"/invoke.sh
|
||||
chmod a+rx "$ROOTDIR"/invoke.sh
|
||||
|
||||
cp templates/update.sh.in $ROOTDIR/update.sh
|
||||
chmod a+rx $ROOTDIR/update.sh
|
||||
cp templates/update.sh.in "$ROOTDIR"/update.sh
|
||||
chmod a+rx "$ROOTDIR"/update.sh
|
||||
|
||||
echo "You may now run InvokeAI by entering the directory $ROOTDIR and running invoke.sh"
|
||||
|
@ -84,6 +84,8 @@ class Concepts(object):
|
||||
better to store the concept name (unique) than the concept trigger
|
||||
(not necessarily unique!)
|
||||
'''
|
||||
if not prompt:
|
||||
return prompt
|
||||
triggers = self.match_trigger.findall(prompt)
|
||||
if not triggers:
|
||||
return prompt
|
||||
|
@ -18,7 +18,12 @@ from argparse import Namespace
|
||||
Globals = Namespace()
|
||||
|
||||
# This is usually overwritten by the command line and/or environment variables
|
||||
Globals.root = osp.abspath(os.environ.get('INVOKEAI_ROOT') or osp.abspath(osp.join(os.environ.get('VIRTUAL_ENV'),'..')) or osp.expanduser('~/invokeai'))
|
||||
if os.environ.get('INVOKEAI_ROOT'):
|
||||
Globals.root = osp.abspath(os.environ.get('INVOKEAI_ROOT'))
|
||||
elif os.environ.get('VIRTUAL_ENV'):
|
||||
Globals.root = osp.abspath(osp.join(os.environ.get('VIRTUAL_ENV'), '..'))
|
||||
else:
|
||||
Globals.root = osp.abspath(osp.expanduser('~/invokeai'))
|
||||
|
||||
# Where to look for the initialization file
|
||||
Globals.initfile = 'invokeai.init'
|
||||
|
Loading…
Reference in New Issue
Block a user