Merge branch 'main' into scheduled-cancel

This commit is contained in:
blhook
2023-02-18 13:30:45 -08:00
463 changed files with 16925 additions and 15031 deletions

View File

@@ -13,18 +13,18 @@ const ClearCanvasHistoryButtonModal = () => {
return (
<IAIAlertDialog
title={t('unifiedcanvas:clearCanvasHistory')}
title={t('unifiedCanvas.clearCanvasHistory')}
acceptCallback={() => dispatch(clearCanvasHistory())}
acceptButtonText={t('unifiedcanvas:clearHistory')}
acceptButtonText={t('unifiedCanvas.clearHistory')}
triggerComponent={
<IAIButton size={'sm'} leftIcon={<FaTrash />} isDisabled={isStaging}>
{t('unifiedcanvas:clearCanvasHistory')}
<IAIButton size="sm" leftIcon={<FaTrash />} isDisabled={isStaging}>
{t('unifiedCanvas.clearCanvasHistory')}
</IAIButton>
}
>
<p>{t('unifiedcanvas:clearCanvasHistoryMessage')}</p>
<p>{t('unifiedCanvas.clearCanvasHistoryMessage')}</p>
<br />
<p>{t('unifiedcanvas:clearCanvasHistoryConfirm')}</p>
<p>{t('unifiedCanvas.clearCanvasHistoryConfirm')}</p>
</IAIAlertDialog>
);
};

View File

@@ -140,7 +140,7 @@ const IAICanvas = () => {
<Stage
tabIndex={-1}
ref={canvasStageRefCallback}
className={'inpainting-canvas-stage'}
className="inpainting-canvas-stage"
style={{
...(stageCursor ? { cursor: stageCursor } : {}),
}}
@@ -165,19 +165,19 @@ const IAICanvas = () => {
onWheel={handleWheel}
draggable={(tool === 'move' || isStaging) && !isModifyingBoundingBox}
>
<Layer id={'grid'} visible={shouldShowGrid}>
<Layer id="grid" visible={shouldShowGrid}>
<IAICanvasGrid />
</Layer>
<Layer
id={'base'}
id="base"
ref={canvasBaseLayerRefCallback}
listening={false}
imageSmoothingEnabled={false}
>
<IAICanvasObjectRenderer />
</Layer>
<Layer id={'mask'} visible={isMaskEnabled} listening={false}>
<Layer id="mask" visible={isMaskEnabled} listening={false}>
<IAICanvasMaskLines visible={true} listening={false} />
<IAICanvasMaskCompositer listening={false} />
</Layer>

View File

@@ -49,7 +49,7 @@ const IAICanvasBoundingBoxOverlay = () => {
offsetY={stageCoordinates.y / stageScale}
height={stageDimensions.height / stageScale}
width={stageDimensions.width / stageScale}
fill={'rgba(0,0,0,0.4)'}
fill="rgba(0,0,0,0.4)"
listening={false}
visible={shouldDarkenOutsideBoundingBox}
/>
@@ -58,10 +58,10 @@ const IAICanvasBoundingBoxOverlay = () => {
y={boundingBoxCoordinates.y}
width={boundingBoxDimensions.width}
height={boundingBoxDimensions.height}
fill={'rgb(255,255,255)'}
fill="rgb(255,255,255)"
listening={false}
visible={shouldDarkenOutsideBoundingBox}
globalCompositeOperation={'destination-out'}
globalCompositeOperation="destination-out"
/>
</Group>
);

View File

@@ -163,10 +163,10 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
width={stageDimensions.width / stageScale}
fillPatternImage={fillPatternImage}
fillPatternOffsetY={!isNumber(offset) ? 0 : offset}
fillPatternRepeat={'repeat'}
fillPatternRepeat="repeat"
fillPatternScale={{ x: 1 / stageScale, y: 1 / stageScale }}
listening={true}
globalCompositeOperation={'source-in'}
globalCompositeOperation="source-in"
{...rest}
/>
);

View File

@@ -36,7 +36,7 @@ const IAICanvasLines = (props: InpaintingCanvasLinesProps) => {
<Line
key={i}
points={line.points}
stroke={'rgb(0,0,0)'} // The lines can be any color, just need alpha > 0
stroke="rgb(0,0,0)" // The lines can be any color, just need alpha > 0
strokeWidth={line.strokeWidth * 2}
tension={0}
lineCap="round"

View File

@@ -93,8 +93,8 @@ const IAICanvasObjectRenderer = () => {
y={obj.y}
width={obj.width}
height={obj.height}
fill={'rgb(255, 255, 255)'}
globalCompositeOperation={'destination-out'}
fill="rgb(255, 255, 255)"
globalCompositeOperation="destination-out"
/>
);
}

View File

@@ -67,7 +67,7 @@ const IAICanvasStagingArea = (props: Props) => {
width={width}
height={height}
strokeWidth={1}
stroke={'white'}
stroke="white"
strokeScaleEnabled={false}
/>
<Rect
@@ -77,7 +77,7 @@ const IAICanvasStagingArea = (props: Props) => {
height={height}
dash={[4, 4]}
strokeWidth={1}
stroke={'black'}
stroke="black"
strokeScaleEnabled={false}
/>
</Group>

View File

@@ -114,42 +114,42 @@ const IAICanvasStagingAreaToolbar = () => {
return (
<Flex
pos={'absolute'}
bottom={'1rem'}
w={'100%'}
align={'center'}
justify={'center'}
pos="absolute"
bottom="1rem"
w="100%"
align="center"
justify="center"
filter="drop-shadow(0 0.5rem 1rem rgba(0,0,0))"
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
>
<ButtonGroup isAttached>
<IAIIconButton
tooltip={`${t('unifiedcanvas:previous')} (Left)`}
aria-label={`${t('unifiedcanvas:previous')} (Left)`}
tooltip={`${t('unifiedCanvas.previous')} (Left)`}
aria-label={`${t('unifiedCanvas.previous')} (Left)`}
icon={<FaArrowLeft />}
onClick={handlePrevImage}
data-selected={true}
isDisabled={isOnFirstImage}
/>
<IAIIconButton
tooltip={`${t('unifiedcanvas:next')} (Right)`}
aria-label={`${t('unifiedcanvas:next')} (Right)`}
tooltip={`${t('unifiedCanvas.next')} (Right)`}
aria-label={`${t('unifiedCanvas.next')} (Right)`}
icon={<FaArrowRight />}
onClick={handleNextImage}
data-selected={true}
isDisabled={isOnLastImage}
/>
<IAIIconButton
tooltip={`${t('unifiedcanvas:accept')} (Enter)`}
aria-label={`${t('unifiedcanvas:accept')} (Enter)`}
tooltip={`${t('unifiedCanvas.accept')} (Enter)`}
aria-label={`${t('unifiedCanvas.accept')} (Enter)`}
icon={<FaCheck />}
onClick={handleAccept}
data-selected={true}
/>
<IAIIconButton
tooltip={t('unifiedcanvas:showHide')}
aria-label={t('unifiedcanvas:showHide')}
tooltip={t('unifiedCanvas.showHide')}
aria-label={t('unifiedCanvas.showHide')}
data-alert={!shouldShowStagingImage}
icon={shouldShowStagingImage ? <FaEye /> : <FaEyeSlash />}
onClick={() =>
@@ -158,8 +158,8 @@ const IAICanvasStagingAreaToolbar = () => {
data-selected={true}
/>
<IAIIconButton
tooltip={t('unifiedcanvas:saveToGallery')}
aria-label={t('unifiedcanvas:saveToGallery')}
tooltip={t('unifiedCanvas.saveToGallery')}
aria-label={t('unifiedCanvas.saveToGallery')}
icon={<FaSave />}
onClick={() =>
dispatch(
@@ -169,8 +169,8 @@ const IAICanvasStagingAreaToolbar = () => {
data-selected={true}
/>
<IAIIconButton
tooltip={t('unifiedcanvas:discardAll')}
aria-label={t('unifiedcanvas:discardAll')}
tooltip={t('unifiedCanvas.discardAll')}
aria-label={t('unifiedCanvas.discardAll')}
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
onClick={() => dispatch(discardStagedImages())}
data-selected={true}

View File

@@ -92,8 +92,8 @@ const IAICanvasStatusText = () => {
style={{
color: activeLayerColor,
}}
>{`${t('unifiedcanvas:activeLayer')}: ${activeLayerString}`}</div>
<div>{`${t('unifiedcanvas:canvasScale')}: ${canvasScaleString}%`}</div>
>{`${t('unifiedCanvas.activeLayer')}: ${activeLayerString}`}</div>
<div>{`${t('unifiedCanvas.canvasScale')}: ${canvasScaleString}%`}</div>
{shouldPreserveMaskedArea && (
<div
style={{

View File

@@ -172,7 +172,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX}
y={brushY}
radius={radius}
stroke={'rgba(255,255,255,0.4)'}
stroke="rgba(255,255,255,0.4)"
strokeWidth={strokeWidth * 2}
strokeEnabled={true}
listening={false}
@@ -181,7 +181,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX}
y={brushY}
radius={radius}
stroke={'rgba(0,0,0,1)'}
stroke="rgba(0,0,0,1)"
strokeWidth={strokeWidth}
strokeEnabled={true}
listening={false}
@@ -192,14 +192,14 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX}
y={brushY}
radius={dotRadius * 2}
fill={'rgba(255,255,255,0.4)'}
fill="rgba(255,255,255,0.4)"
listening={false}
/>
<Circle
x={brushX}
y={brushY}
radius={dotRadius}
fill={'rgba(0,0,0,1)'}
fill="rgba(0,0,0,1)"
listening={false}
/>
</Group>

View File

@@ -269,12 +269,12 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
<Transformer
anchorCornerRadius={3}
anchorDragBoundFunc={anchorDragBoundFunc}
anchorFill={'rgba(212,216,234,1)'}
anchorFill="rgba(212,216,234,1)"
anchorSize={15}
anchorStroke={'rgb(42,42,42)'}
anchorStroke="rgb(42,42,42)"
borderDash={[4, 4]}
borderEnabled={true}
borderStroke={'black'}
borderStroke="black"
draggable={false}
enabledAnchors={tool === 'move' ? undefined : []}
flipEnabled={false}

View File

@@ -108,8 +108,8 @@ const IAICanvasMaskOptions = () => {
triggerComponent={
<ButtonGroup>
<IAIIconButton
aria-label={t('unifiedcanvas:maskingOptions')}
tooltip={t('unifiedcanvas:maskingOptions')}
aria-label={t('unifiedCanvas.maskingOptions')}
tooltip={t('unifiedCanvas.maskingOptions')}
icon={<FaMask />}
style={
layer === 'mask'
@@ -121,14 +121,14 @@ const IAICanvasMaskOptions = () => {
</ButtonGroup>
}
>
<Flex direction={'column'} gap={'0.5rem'}>
<Flex direction="column" gap="0.5rem">
<IAICheckbox
label={`${t('unifiedcanvas:enableMask')} (H)`}
label={`${t('unifiedCanvas.enableMask')} (H)`}
isChecked={isMaskEnabled}
onChange={handleToggleEnableMask}
/>
<IAICheckbox
label={t('unifiedcanvas:preserveMaskedArea')}
label={t('unifiedCanvas.preserveMaskedArea')}
isChecked={shouldPreserveMaskedArea}
onChange={(e) =>
dispatch(setShouldPreserveMaskedArea(e.target.checked))
@@ -139,8 +139,8 @@ const IAICanvasMaskOptions = () => {
color={maskColor}
onChange={(newColor) => dispatch(setMaskColor(newColor))}
/>
<IAIButton size={'sm'} leftIcon={<FaTrash />} onClick={handleClearMask}>
{t('unifiedcanvas:clearMask')} (Shift+C)
<IAIButton size="sm" leftIcon={<FaTrash />} onClick={handleClearMask}>
{t('unifiedCanvas.clearMask')} (Shift+C)
</IAIButton>
</Flex>
</IAIPopover>

View File

@@ -53,8 +53,8 @@ export default function IAICanvasRedoButton() {
return (
<IAIIconButton
aria-label={`${t('unifiedcanvas:redo')} (Ctrl+Shift+Z)`}
tooltip={`${t('unifiedcanvas:redo')} (Ctrl+Shift+Z)`}
aria-label={`${t('unifiedCanvas.redo')} (Ctrl+Shift+Z)`}
tooltip={`${t('unifiedCanvas.redo')} (Ctrl+Shift+Z)`}
icon={<FaRedo />}
onClick={handleRedo}
isDisabled={!canRedo}

View File

@@ -91,58 +91,58 @@ const IAICanvasSettingsButtonPopover = () => {
trigger="hover"
triggerComponent={
<IAIIconButton
tooltip={t('unifiedcanvas:canvasSettings')}
aria-label={t('unifiedcanvas:canvasSettings')}
tooltip={t('unifiedCanvas.canvasSettings')}
aria-label={t('unifiedCanvas.canvasSettings')}
icon={<FaWrench />}
/>
}
>
<Flex direction={'column'} gap={'0.5rem'}>
<Flex direction="column" gap="0.5rem">
<IAICheckbox
label={t('unifiedcanvas:showIntermediates')}
label={t('unifiedCanvas.showIntermediates')}
isChecked={shouldShowIntermediates}
onChange={(e) =>
dispatch(setShouldShowIntermediates(e.target.checked))
}
/>
<IAICheckbox
label={t('unifiedcanvas:showGrid')}
label={t('unifiedCanvas.showGrid')}
isChecked={shouldShowGrid}
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
/>
<IAICheckbox
label={t('unifiedcanvas:snapToGrid')}
label={t('unifiedCanvas.snapToGrid')}
isChecked={shouldSnapToGrid}
onChange={handleChangeShouldSnapToGrid}
/>
<IAICheckbox
label={t('unifiedcanvas:darkenOutsideSelection')}
label={t('unifiedCanvas.darkenOutsideSelection')}
isChecked={shouldDarkenOutsideBoundingBox}
onChange={(e) =>
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked))
}
/>
<IAICheckbox
label={t('unifiedcanvas:autoSaveToGallery')}
label={t('unifiedCanvas.autoSaveToGallery')}
isChecked={shouldAutoSave}
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
/>
<IAICheckbox
label={t('unifiedcanvas:saveBoxRegionOnly')}
label={t('unifiedCanvas.saveBoxRegionOnly')}
isChecked={shouldCropToBoundingBoxOnSave}
onChange={(e) =>
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
}
/>
<IAICheckbox
label={t('unifiedcanvas:limitStrokesToBox')}
label={t('unifiedCanvas.limitStrokesToBox')}
isChecked={shouldRestrictStrokesToBox}
onChange={(e) =>
dispatch(setShouldRestrictStrokesToBox(e.target.checked))
}
/>
<IAICheckbox
label={t('unifiedcanvas:showCanvasDebugInfo')}
label={t('unifiedCanvas.showCanvasDebugInfo')}
isChecked={shouldShowCanvasDebugInfo}
onChange={(e) =>
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))

View File

@@ -181,38 +181,38 @@ const IAICanvasToolChooserOptions = () => {
return (
<ButtonGroup isAttached>
<IAIIconButton
aria-label={`${t('unifiedcanvas:brush')} (B)`}
tooltip={`${t('unifiedcanvas:brush')} (B)`}
aria-label={`${t('unifiedCanvas.brush')} (B)`}
tooltip={`${t('unifiedCanvas.brush')} (B)`}
icon={<FaPaintBrush />}
data-selected={tool === 'brush' && !isStaging}
onClick={handleSelectBrushTool}
isDisabled={isStaging}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:eraser')} (E)`}
tooltip={`${t('unifiedcanvas:eraser')} (E)`}
aria-label={`${t('unifiedCanvas.eraser')} (E)`}
tooltip={`${t('unifiedCanvas.eraser')} (E)`}
icon={<FaEraser />}
data-selected={tool === 'eraser' && !isStaging}
isDisabled={isStaging}
onClick={handleSelectEraserTool}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:fillBoundingBox')} (Shift+F)`}
tooltip={`${t('unifiedcanvas:fillBoundingBox')} (Shift+F)`}
aria-label={`${t('unifiedCanvas.fillBoundingBox')} (Shift+F)`}
tooltip={`${t('unifiedCanvas.fillBoundingBox')} (Shift+F)`}
icon={<FaFillDrip />}
isDisabled={isStaging}
onClick={handleFillRect}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:eraseBoundingBox')} (Del/Backspace)`}
tooltip={`${t('unifiedcanvas:eraseBoundingBox')} (Del/Backspace)`}
aria-label={`${t('unifiedCanvas.eraseBoundingBox')} (Del/Backspace)`}
tooltip={`${t('unifiedCanvas.eraseBoundingBox')} (Del/Backspace)`}
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
isDisabled={isStaging}
onClick={handleEraseBoundingBox}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:colorPicker')} (C)`}
tooltip={`${t('unifiedcanvas:colorPicker')} (C)`}
aria-label={`${t('unifiedCanvas.colorPicker')} (C)`}
tooltip={`${t('unifiedCanvas.colorPicker')} (C)`}
icon={<FaEyeDropper />}
data-selected={tool === 'colorPicker' && !isStaging}
isDisabled={isStaging}
@@ -222,21 +222,16 @@ const IAICanvasToolChooserOptions = () => {
trigger="hover"
triggerComponent={
<IAIIconButton
aria-label={t('unifiedcanvas:brushOptions')}
tooltip={t('unifiedcanvas:brushOptions')}
aria-label={t('unifiedCanvas.brushOptions')}
tooltip={t('unifiedCanvas.brushOptions')}
icon={<FaSlidersH />}
/>
}
>
<Flex
minWidth={'15rem'}
direction={'column'}
gap={'1rem'}
width={'100%'}
>
<Flex gap={'1rem'} justifyContent="space-between">
<Flex minWidth="15rem" direction="column" gap="1rem" width="100%">
<Flex gap="1rem" justifyContent="space-between">
<IAISlider
label={t('unifiedcanvas:brushSize')}
label={t('unifiedCanvas.brushSize')}
value={brushSize}
withInput
onChange={(newSize) => dispatch(setBrushSize(newSize))}

View File

@@ -232,7 +232,7 @@ const IAICanvasOutpaintingControls = () => {
return (
<div className="inpainting-settings">
<IAISelect
tooltip={`${t('unifiedcanvas:layer')} (Q)`}
tooltip={`${t('unifiedCanvas.layer')} (Q)`}
tooltipProps={{ hasArrow: true, placement: 'top' }}
value={layer}
validValues={LAYER_NAMES_DICT}
@@ -245,15 +245,15 @@ const IAICanvasOutpaintingControls = () => {
<ButtonGroup isAttached>
<IAIIconButton
aria-label={`${t('unifiedcanvas:move')} (V)`}
tooltip={`${t('unifiedcanvas:move')} (V)`}
aria-label={`${t('unifiedCanvas.move')} (V)`}
tooltip={`${t('unifiedCanvas.move')} (V)`}
icon={<FaArrowsAlt />}
data-selected={tool === 'move' || isStaging}
onClick={handleSelectMoveTool}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:resetView')} (R)`}
tooltip={`${t('unifiedcanvas:resetView')} (R)`}
aria-label={`${t('unifiedCanvas.resetView')} (R)`}
tooltip={`${t('unifiedCanvas.resetView')} (R)`}
icon={<FaCrosshairs />}
onClick={handleClickResetCanvasView}
/>
@@ -261,29 +261,29 @@ const IAICanvasOutpaintingControls = () => {
<ButtonGroup isAttached>
<IAIIconButton
aria-label={`${t('unifiedcanvas:mergeVisible')} (Shift+M)`}
tooltip={`${t('unifiedcanvas:mergeVisible')} (Shift+M)`}
aria-label={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
tooltip={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
icon={<FaLayerGroup />}
onClick={handleMergeVisible}
isDisabled={isStaging}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:saveToGallery')} (Shift+S)`}
tooltip={`${t('unifiedcanvas:saveToGallery')} (Shift+S)`}
aria-label={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
icon={<FaSave />}
onClick={handleSaveToGallery}
isDisabled={isStaging}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:copyToClipboard')} (Cmd/Ctrl+C)`}
tooltip={`${t('unifiedcanvas:copyToClipboard')} (Cmd/Ctrl+C)`}
aria-label={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
tooltip={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
icon={<FaCopy />}
onClick={handleCopyImageToClipboard}
isDisabled={isStaging}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:downloadAsImage')} (Shift+D)`}
tooltip={`${t('unifiedcanvas:downloadAsImage')} (Shift+D)`}
aria-label={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
tooltip={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
icon={<FaDownload />}
onClick={handleDownloadAsImage}
isDisabled={isStaging}
@@ -296,15 +296,15 @@ const IAICanvasOutpaintingControls = () => {
<ButtonGroup isAttached>
<IAIIconButton
aria-label={`${t('common:upload')}`}
tooltip={`${t('common:upload')}`}
aria-label={`${t('common.upload')}`}
tooltip={`${t('common.upload')}`}
icon={<FaUpload />}
onClick={openUploader}
isDisabled={isStaging}
/>
<IAIIconButton
aria-label={`${t('unifiedcanvas:clearCanvas')}`}
tooltip={`${t('unifiedcanvas:clearCanvas')}`}
aria-label={`${t('unifiedCanvas.clearCanvas')}`}
tooltip={`${t('unifiedCanvas.clearCanvas')}`}
icon={<FaTrash />}
onClick={handleResetCanvas}
style={{ backgroundColor: 'var(--btn-delete-image)' }}

View File

@@ -54,8 +54,8 @@ export default function IAICanvasUndoButton() {
return (
<IAIIconButton
aria-label={`${t('unifiedcanvas:undo')} (Ctrl+Z)`}
tooltip={`${t('unifiedcanvas:undo')} (Ctrl+Z)`}
aria-label={`${t('unifiedCanvas.undo')} (Ctrl+Z)`}
tooltip={`${t('unifiedCanvas.undo')} (Ctrl+Z)`}
icon={<FaUndo />}
onClick={handleUndo}
isDisabled={!canUndo}

View File

@@ -115,7 +115,7 @@ export const mergeAndUploadCanvas =
downloadFile(url);
dispatch(
addToast({
title: i18n.t('toast:downloadImageStarted'),
title: i18n.t('toast.downloadImageStarted'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -127,7 +127,7 @@ export const mergeAndUploadCanvas =
copyImage(url, width, height);
dispatch(
addToast({
title: i18n.t('toast:imageCopied'),
title: i18n.t('toast.imageCopied'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -139,7 +139,7 @@ export const mergeAndUploadCanvas =
dispatch(addImage({ image: newImage, category: 'result' }));
dispatch(
addToast({
title: i18n.t('toast:imageSavedToGallery'),
title: i18n.t('toast.imageSavedToGallery'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -158,7 +158,7 @@ export const mergeAndUploadCanvas =
);
dispatch(
addToast({
title: i18n.t('toast:canvasMerged'),
title: i18n.t('toast.canvasMerged'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -167,6 +167,6 @@ export const mergeAndUploadCanvas =
}
dispatch(setIsProcessing(false));
dispatch(setCurrentStatus(i18n.t('common:statusConnected')));
dispatch(setCurrentStatus(i18n.t('common.statusConnected')));
dispatch(setIsCancelable(true));
};

View File

@@ -142,7 +142,7 @@ const CurrentImageButtons = () => {
await navigator.clipboard.write(data);
toast({
title: t('toast:imageCopied'),
title: t('toast.imageCopied'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -156,7 +156,7 @@ const CurrentImageButtons = () => {
)
.then(() => {
toast({
title: t('toast:imageLinkCopied'),
title: t('toast.imageLinkCopied'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -170,15 +170,15 @@ const CurrentImageButtons = () => {
if (currentImage) {
handleClickUseAsInitialImage();
toast({
title: t('toast:sentToImageToImage'),
title: t('toast.sentToImageToImage'),
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: t('toast:imageNotLoaded'),
description: t('toast:imageNotLoadedDesc'),
title: t('toast.imageNotLoaded'),
description: t('toast.imageNotLoadedDesc'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -206,15 +206,15 @@ const CurrentImageButtons = () => {
) {
handleClickUseAllParameters();
toast({
title: t('toast:parametersSet'),
title: t('toast.parametersSet'),
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: t('toast:parametersNotSet'),
description: t('toast:parametersNotSetDesc'),
title: t('toast.parametersNotSet'),
description: t('toast.parametersNotSetDesc'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -235,15 +235,15 @@ const CurrentImageButtons = () => {
if (currentImage?.metadata?.image?.seed) {
handleClickUseSeed();
toast({
title: t('toast:seedSet'),
title: t('toast.seedSet'),
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: t('toast:seedNotSet'),
description: t('toast:seedNotSetDesc'),
title: t('toast.seedNotSet'),
description: t('toast.seedNotSetDesc'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -272,15 +272,15 @@ const CurrentImageButtons = () => {
if (currentImage?.metadata?.image?.prompt) {
handleClickUsePrompt();
toast({
title: t('toast:promptSet'),
title: t('toast.promptSet'),
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: t('toast:promptNotSet'),
description: t('toast:promptNotSetDesc'),
title: t('toast.promptNotSet'),
description: t('toast.promptNotSetDesc'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -307,7 +307,7 @@ const CurrentImageButtons = () => {
handleClickUpscale();
} else {
toast({
title: t('toast:upscalingFailed'),
title: t('toast.upscalingFailed'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -341,7 +341,7 @@ const CurrentImageButtons = () => {
handleClickFixFaces();
} else {
toast({
title: t('toast:faceRestoreFailed'),
title: t('toast.faceRestoreFailed'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -373,7 +373,7 @@ const CurrentImageButtons = () => {
}
toast({
title: t('toast:sentToUnifiedCanvas'),
title: t('toast.sentToUnifiedCanvas'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -387,7 +387,7 @@ const CurrentImageButtons = () => {
handleClickShowImageDetails();
} else {
toast({
title: t('toast:metadataLoadFailed'),
title: t('toast.metadataLoadFailed'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -408,45 +408,45 @@ const CurrentImageButtons = () => {
trigger="hover"
triggerComponent={
<IAIIconButton
aria-label={`${t('parameters:sendTo')}...`}
aria-label={`${t('parameters.sendTo')}...`}
icon={<FaShareAlt />}
/>
}
>
<div className="current-image-send-to-popover">
<IAIButton
size={'sm'}
size="sm"
onClick={handleClickUseAsInitialImage}
leftIcon={<FaShare />}
>
{t('parameters:sendToImg2Img')}
{t('parameters.sendToImg2Img')}
</IAIButton>
<IAIButton
size={'sm'}
size="sm"
onClick={handleSendToCanvas}
leftIcon={<FaShare />}
>
{t('parameters:sendToUnifiedCanvas')}
{t('parameters.sendToUnifiedCanvas')}
</IAIButton>
<IAIButton
size={'sm'}
size="sm"
onClick={handleCopyImage}
leftIcon={<FaCopy />}
>
{t('parameters:copyImage')}
{t('parameters.copyImage')}
</IAIButton>
<IAIButton
size={'sm'}
size="sm"
onClick={handleCopyImageLink}
leftIcon={<FaCopy />}
>
{t('parameters:copyImageToLink')}
{t('parameters.copyImageToLink')}
</IAIButton>
<Link download={true} href={currentImage?.url}>
<IAIButton leftIcon={<FaDownload />} size={'sm'} w="100%">
{t('parameters:downloadImage')}
<IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
{t('parameters.downloadImage')}
</IAIButton>
</Link>
</div>
@@ -455,13 +455,13 @@ const CurrentImageButtons = () => {
icon={<FaExpand />}
tooltip={
!isLightboxOpen
? `${t('parameters:openInViewer')} (Z)`
: `${t('parameters:closeViewer')} (Z)`
? `${t('parameters.openInViewer')} (Z)`
: `${t('parameters.closeViewer')} (Z)`
}
aria-label={
!isLightboxOpen
? `${t('parameters:openInViewer')} (Z)`
: `${t('parameters:closeViewer')} (Z)`
? `${t('parameters.openInViewer')} (Z)`
: `${t('parameters.closeViewer')} (Z)`
}
data-selected={isLightboxOpen}
onClick={handleLightBox}
@@ -471,24 +471,24 @@ const CurrentImageButtons = () => {
<ButtonGroup isAttached={true}>
<IAIIconButton
icon={<FaQuoteRight />}
tooltip={`${t('parameters:usePrompt')} (P)`}
aria-label={`${t('parameters:usePrompt')} (P)`}
tooltip={`${t('parameters.usePrompt')} (P)`}
aria-label={`${t('parameters.usePrompt')} (P)`}
isDisabled={!currentImage?.metadata?.image?.prompt}
onClick={handleClickUsePrompt}
/>
<IAIIconButton
icon={<FaSeedling />}
tooltip={`${t('parameters:useSeed')} (S)`}
aria-label={`${t('parameters:useSeed')} (S)`}
tooltip={`${t('parameters.useSeed')} (S)`}
aria-label={`${t('parameters.useSeed')} (S)`}
isDisabled={!currentImage?.metadata?.image?.seed}
onClick={handleClickUseSeed}
/>
<IAIIconButton
icon={<FaAsterisk />}
tooltip={`${t('parameters:useAll')} (A)`}
aria-label={`${t('parameters:useAll')} (A)`}
tooltip={`${t('parameters.useAll')} (A)`}
aria-label={`${t('parameters.useAll')} (A)`}
isDisabled={
!['txt2img', 'img2img'].includes(
currentImage?.metadata?.image?.type
@@ -504,7 +504,7 @@ const CurrentImageButtons = () => {
triggerComponent={
<IAIIconButton
icon={<FaGrinStars />}
aria-label={t('parameters:restoreFaces')}
aria-label={t('parameters.restoreFaces')}
/>
}
>
@@ -519,7 +519,7 @@ const CurrentImageButtons = () => {
}
onClick={handleClickFixFaces}
>
{t('parameters:restoreFaces')}
{t('parameters.restoreFaces')}
</IAIButton>
</div>
</IAIPopover>
@@ -529,7 +529,7 @@ const CurrentImageButtons = () => {
triggerComponent={
<IAIIconButton
icon={<FaExpandArrowsAlt />}
aria-label={t('parameters:upscale')}
aria-label={t('parameters.upscale')}
/>
}
>
@@ -544,7 +544,7 @@ const CurrentImageButtons = () => {
}
onClick={handleClickUpscale}
>
{t('parameters:upscaleImage')}
{t('parameters.upscaleImage')}
</IAIButton>
</div>
</IAIPopover>
@@ -553,8 +553,8 @@ const CurrentImageButtons = () => {
<ButtonGroup isAttached={true}>
<IAIIconButton
icon={<FaCode />}
tooltip={`${t('parameters:info')} (I)`}
aria-label={`${t('parameters:info')} (I)`}
tooltip={`${t('parameters.info')} (I)`}
aria-label={`${t('parameters.info')} (I)`}
data-selected={shouldShowImageDetails}
onClick={handleClickShowImageDetails}
/>
@@ -563,8 +563,8 @@ const CurrentImageButtons = () => {
<DeleteImageModal image={currentImage}>
<IAIIconButton
icon={<FaTrash />}
tooltip={`${t('parameters:deleteImage')} (Del)`}
aria-label={`${t('parameters:deleteImage')} (Del)`}
tooltip={`${t('parameters.deleteImage')} (Del)`}
aria-label={`${t('parameters.deleteImage')} (Del)`}
isDisabled={!currentImage || !isConnected || isProcessing}
style={{ backgroundColor: 'var(--btn-delete-image)' }}
/>

View File

@@ -82,7 +82,7 @@ export default function CurrentImagePreview() {
};
return (
<div className={'current-image-preview'}>
<div className="current-image-preview">
{imageToDisplay && (
<Image
src={imageToDisplay.url}

View File

@@ -116,13 +116,13 @@ const DeleteImageModal = forwardRef(
</AlertDialogHeader>
<AlertDialogBody>
<Flex direction={'column'} gap={5}>
<Flex direction="column" gap={5}>
<Text>
Are you sure? Deleted images will be sent to the Bin. You
can restore from there if you wish to.
</Text>
<FormControl>
<Flex alignItems={'center'}>
<Flex alignItems="center">
<FormLabel mb={0}>Don&apos;t ask me again</FormLabel>
<Switch
checked={!shouldConfirmOnDelete}

View File

@@ -75,7 +75,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}
toast({
title: t('toast:promptSet'),
title: t('toast.promptSet'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -85,7 +85,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
const handleUseSeed = () => {
image.metadata && dispatch(setSeed(image.metadata.image.seed));
toast({
title: t('toast:seedSet'),
title: t('toast.seedSet'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -98,7 +98,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
dispatch(setActiveTab('img2img'));
}
toast({
title: t('toast:sentToImageToImage'),
title: t('toast.sentToImageToImage'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -115,7 +115,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}
toast({
title: t('toast:sentToUnifiedCanvas'),
title: t('toast.sentToUnifiedCanvas'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -125,7 +125,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
const handleUseAllParameters = () => {
metadata && dispatch(setAllParameters(metadata));
toast({
title: t('toast:parametersSet'),
title: t('toast.parametersSet'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -139,7 +139,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
dispatch(setActiveTab('img2img'));
dispatch(setAllImageToImageParameters(metadata));
toast({
title: t('toast:initialImageSet'),
title: t('toast.initialImageSet'),
status: 'success',
duration: 2500,
isClosable: true,
@@ -148,8 +148,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}
}
toast({
title: t('toast:initialImageNotSet'),
description: t('toast:initialImageNotSetDesc'),
title: t('toast.initialImageNotSet'),
description: t('toast.initialImageNotSetDesc'),
status: 'error',
duration: 2500,
isClosable: true,
@@ -175,12 +175,12 @@ const HoverableImage = memo((props: HoverableImageProps) => {
>
<ContextMenu.Trigger>
<Box
position={'relative'}
position="relative"
key={uuid}
className="hoverable-image"
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
userSelect={'none'}
userSelect="none"
draggable={true}
onDragStart={handleDragStart}
>
@@ -189,15 +189,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
objectFit={
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
}
rounded={'md'}
rounded="md"
src={thumbnail || url}
loading={'lazy'}
loading="lazy"
/>
<div className="hoverable-image-content" onClick={handleSelectImage}>
{isSelected && (
<Icon
width={'50%'}
height={'50%'}
width="50%"
height="50%"
as={FaCheck}
className="hoverable-image-check"
/>
@@ -207,10 +207,10 @@ const HoverableImage = memo((props: HoverableImageProps) => {
<div className="hoverable-image-delete-button">
<DeleteImageModal image={image}>
<IconButton
aria-label={t('parameters:deleteImage')}
aria-label={t('parameters.deleteImage')}
icon={<FaTrashAlt />}
size="xs"
variant={'imageHoverIconButton'}
variant="imageHoverIconButton"
fontSize={14}
isDisabled={!mayDeleteImage}
/>
@@ -221,26 +221,26 @@ const HoverableImage = memo((props: HoverableImageProps) => {
</ContextMenu.Trigger>
<ContextMenu.Content
className="hoverable-image-context-menu"
sticky={'always'}
sticky="always"
onInteractOutside={(e) => {
e.detail.originalEvent.preventDefault();
}}
>
<ContextMenu.Item onClickCapture={handleLightBox}>
{t('parameters:openInViewer')}
{t('parameters.openInViewer')}
</ContextMenu.Item>
<ContextMenu.Item
onClickCapture={handleUsePrompt}
disabled={image?.metadata?.image?.prompt === undefined}
>
{t('parameters:usePrompt')}
{t('parameters.usePrompt')}
</ContextMenu.Item>
<ContextMenu.Item
onClickCapture={handleUseSeed}
disabled={image?.metadata?.image?.seed === undefined}
>
{t('parameters:useSeed')}
{t('parameters.useSeed')}
</ContextMenu.Item>
<ContextMenu.Item
onClickCapture={handleUseAllParameters}
@@ -248,23 +248,23 @@ const HoverableImage = memo((props: HoverableImageProps) => {
!['txt2img', 'img2img'].includes(image?.metadata?.image?.type)
}
>
{t('parameters:useAll')}
{t('parameters.useAll')}
</ContextMenu.Item>
<ContextMenu.Item
onClickCapture={handleUseInitialImage}
disabled={image?.metadata?.image?.type !== 'img2img'}
>
{t('parameters:useInitImg')}
{t('parameters.useInitImg')}
</ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToImageToImage}>
{t('parameters:sendToImg2Img')}
{t('parameters.sendToImg2Img')}
</ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToCanvas}>
{t('parameters:sendToUnifiedCanvas')}
{t('parameters.sendToUnifiedCanvas')}
</ContextMenu.Item>
<ContextMenu.Item data-warning>
<DeleteImageModal image={image}>
<p>{t('parameters:deleteImage')}</p>
<p>{t('parameters.deleteImage')}</p>
</DeleteImageModal>
</ContextMenu.Item>
</ContextMenu.Content>

View File

@@ -281,7 +281,7 @@ export default function ImageGallery() {
<Resizable
minWidth={galleryMinWidth}
maxWidth={shouldPinGallery ? galleryMaxWidth : window.innerWidth}
className={'image-gallery-popup'}
className="image-gallery-popup"
handleStyles={{
left: {
width: '15px',
@@ -395,32 +395,32 @@ export default function ImageGallery() {
{shouldShowButtons ? (
<>
<IAIButton
size={'sm'}
size="sm"
data-selected={currentCategory === 'result'}
onClick={() => dispatch(setCurrentCategory('result'))}
>
{t('gallery:generations')}
{t('gallery.generations')}
</IAIButton>
<IAIButton
size={'sm'}
size="sm"
data-selected={currentCategory === 'user'}
onClick={() => dispatch(setCurrentCategory('user'))}
>
{t('gallery:uploads')}
{t('gallery.uploads')}
</IAIButton>
</>
) : (
<>
<IAIIconButton
aria-label={t('gallery:showGenerations')}
tooltip={t('gallery:showGenerations')}
aria-label={t('gallery.showGenerations')}
tooltip={t('gallery.showGenerations')}
data-selected={currentCategory === 'result'}
icon={<FaImage />}
onClick={() => dispatch(setCurrentCategory('result'))}
/>
<IAIIconButton
aria-label={t('gallery:showUploads')}
tooltip={t('gallery:showUploads')}
aria-label={t('gallery.showUploads')}
tooltip={t('gallery.showUploads')}
data-selected={currentCategory === 'user'}
icon={<FaUser />}
onClick={() => dispatch(setCurrentCategory('user'))}
@@ -433,14 +433,14 @@ export default function ImageGallery() {
<IAIPopover
isLazy
trigger="hover"
placement={'left'}
placement="left"
triggerComponent={
<IAIIconButton
size={'sm'}
aria-label={t('gallery:gallerySettings')}
size="sm"
aria-label={t('gallery.gallerySettings')}
icon={<FaWrench />}
className="image-gallery-icon-btn"
cursor={'pointer'}
cursor="pointer"
/>
}
>
@@ -452,12 +452,12 @@ export default function ImageGallery() {
min={32}
max={256}
hideTooltip={true}
label={t('gallery:galleryImageSize')}
label={t('gallery.galleryImageSize')}
/>
<IAIIconButton
size={'sm'}
aria-label={t('gallery:galleryImageResetSize')}
tooltip={t('gallery:galleryImageResetSize')}
size="sm"
aria-label={t('gallery.galleryImageResetSize')}
tooltip={t('gallery.galleryImageResetSize')}
onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
icon={<BiReset />}
data-selected={shouldPinGallery}
@@ -466,7 +466,7 @@ export default function ImageGallery() {
</div>
<div>
<IAICheckbox
label={t('gallery:maintainAspectRatio')}
label={t('gallery.maintainAspectRatio')}
isChecked={galleryImageObjectFit === 'contain'}
onChange={() =>
dispatch(
@@ -481,7 +481,7 @@ export default function ImageGallery() {
</div>
<div>
<IAICheckbox
label={t('gallery:autoSwitchNewImages')}
label={t('gallery.autoSwitchNewImages')}
isChecked={shouldAutoSwitchToNewImages}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(
@@ -492,7 +492,7 @@ export default function ImageGallery() {
</div>
<div>
<IAICheckbox
label={t('gallery:singleColumnLayout')}
label={t('gallery.singleColumnLayout')}
isChecked={shouldUseSingleGalleryColumn}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(
@@ -505,10 +505,10 @@ export default function ImageGallery() {
</IAIPopover>
<IAIIconButton
size={'sm'}
className={'image-gallery-icon-btn'}
aria-label={t('gallery:pinGallery')}
tooltip={`${t('gallery:pinGallery')} (Shift+G)`}
size="sm"
className="image-gallery-icon-btn"
aria-label={t('gallery.pinGallery')}
tooltip={`${t('gallery.pinGallery')} (Shift+G)`}
onClick={handleSetShouldPinGallery}
icon={shouldPinGallery ? <BsPinAngleFill /> : <BsPinAngle />}
/>
@@ -539,14 +539,14 @@ export default function ImageGallery() {
className="image-gallery-load-more-btn"
>
{areMoreImagesAvailable
? t('gallery:loadMore')
: t('gallery:allImagesLoaded')}
? t('gallery.loadMore')
: t('gallery.allImagesLoaded')}
</Button>
</>
) : (
<div className="image-gallery-container-placeholder">
<MdPhotoLibrary />
<p>{t('gallery:noImagesInGallery')}</p>
<p>{t('gallery.noImagesInGallery')}</p>
</div>
)}
</div>

View File

@@ -71,8 +71,8 @@ const MetadataItem = ({
<IconButton
aria-label="Use this parameter"
icon={<IoArrowUndoCircleOutline />}
size={'xs'}
variant={'ghost'}
size="xs"
variant="ghost"
fontSize={20}
onClick={onClick}
/>
@@ -83,23 +83,23 @@ const MetadataItem = ({
<IconButton
aria-label={`Copy ${label}`}
icon={<FaCopy />}
size={'xs'}
variant={'ghost'}
size="xs"
variant="ghost"
fontSize={14}
onClick={() => navigator.clipboard.writeText(value.toString())}
/>
</Tooltip>
)}
<Flex direction={labelPosition ? 'column' : 'row'}>
<Text fontWeight={'semibold'} whiteSpace={'pre-wrap'} pr={2}>
<Text fontWeight="semibold" whiteSpace="pre-wrap" pr={2}>
{label}:
</Text>
{isLink ? (
<Link href={value.toString()} isExternal wordBreak={'break-all'}>
<Link href={value.toString()} isExternal wordBreak="break-all">
{value.toString()} <ExternalLinkIcon mx="2px" />
</Link>
) : (
<Text overflowY={'scroll'} wordBreak={'break-all'}>
<Text overflowY="scroll" wordBreak="break-all">
{value.toString()}
</Text>
)}
@@ -163,10 +163,10 @@ const ImageMetadataViewer = memo(
return (
<div className={`image-metadata-viewer ${styleClass}`}>
<Flex gap={1} direction={'column'} width={'100%'}>
<Flex gap={1} direction="column" width="100%">
<Flex gap={2}>
<Text fontWeight={'semibold'}>File:</Text>
<Link href={image.url} isExternal maxW={'calc(100% - 3rem)'}>
<Text fontWeight="semibold">File:</Text>
<Link href={image.url} isExternal maxW="calc(100% - 3rem)">
{image.url.length > 64
? image.url.substring(0, 64).concat('...')
: image.url}
@@ -304,7 +304,7 @@ const ImageMetadataViewer = memo(
)}
{postprocessing && postprocessing.length > 0 && (
<>
<Heading size={'sm'}>Postprocessing</Heading>
<Heading size="sm">Postprocessing</Heading>
{postprocessing.map(
(
postprocess: InvokeAI.PostProcessedImageMetadata,
@@ -313,13 +313,8 @@ const ImageMetadataViewer = memo(
if (postprocess.type === 'esrgan') {
const { scale, strength, denoise_str } = postprocess;
return (
<Flex
key={i}
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
<Flex key={i} pl="2rem" gap={1} direction="column">
<Text size="md">{`${
i + 1
}: Upscale (ESRGAN)`}</Text>
<MetadataItem
@@ -348,13 +343,8 @@ const ImageMetadataViewer = memo(
} else if (postprocess.type === 'gfpgan') {
const { strength } = postprocess;
return (
<Flex
key={i}
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
<Flex key={i} pl="2rem" gap={1} direction="column">
<Text size="md">{`${
i + 1
}: Face restoration (GFPGAN)`}</Text>
@@ -371,13 +361,8 @@ const ImageMetadataViewer = memo(
} else if (postprocess.type === 'codeformer') {
const { strength, fidelity } = postprocess;
return (
<Flex
key={i}
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
<Flex key={i} pl="2rem" gap={1} direction="column">
<Text size="md">{`${
i + 1
}: Face restoration (Codeformer)`}</Text>
@@ -413,30 +398,30 @@ const ImageMetadataViewer = memo(
value={dreamPrompt}
/>
)}
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<Flex gap={2}>
<Tooltip label={`Copy metadata JSON`}>
<Tooltip label="Copy metadata JSON">
<IconButton
aria-label="Copy metadata JSON"
icon={<FaCopy />}
size={'xs'}
variant={'ghost'}
size="xs"
variant="ghost"
fontSize={14}
onClick={() =>
navigator.clipboard.writeText(metadataJSON)
}
/>
</Tooltip>
<Text fontWeight={'semibold'}>Metadata JSON:</Text>
<Text fontWeight="semibold">Metadata JSON:</Text>
</Flex>
<div className={'image-json-viewer'}>
<div className="image-json-viewer">
<pre>{metadataJSON}</pre>
</div>
</Flex>
</>
) : (
<Center width={'100%'} pt={10}>
<Text fontSize={'lg'} fontWeight="semibold">
<Center width="100%" pt={10}>
<Text fontSize="lg" fontWeight="semibold">
No metadata available
</Text>
</Center>

View File

@@ -23,8 +23,8 @@ export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
return (
<AccordionItem className="advanced-parameters-item">
<AccordionButton className="advanced-parameters-header">
<Flex width={'100%'} gap={'0.5rem'} align={'center'}>
<Box flexGrow={1} textAlign={'left'}>
<Flex width="100%" gap="0.5rem" align="center">
<Box flexGrow={1} textAlign="left">
{header}
</Box>
{additionalHeaderComponents}

View File

@@ -68,9 +68,9 @@ const BoundingBoxSettings = () => {
};
return (
<Flex direction="column" gap="1rem">
<Flex direction="column" gap={2}>
<IAISlider
label={t('parameters:width')}
label={t('parameters.width')}
min={64}
max={1024}
step={64}
@@ -82,9 +82,10 @@ const BoundingBoxSettings = () => {
inputReadOnly
withReset
handleReset={handleResetWidth}
sliderMarkRightOffset={-7}
/>
<IAISlider
label={t('parameters:height')}
label={t('parameters.height')}
min={64}
max={1024}
step={64}
@@ -96,6 +97,7 @@ const BoundingBoxSettings = () => {
inputReadOnly
withReset
handleReset={handleResetHeight}
sliderMarkRightOffset={-7}
/>
</Flex>
);
@@ -107,7 +109,7 @@ export const BoundingBoxSettingsHeader = () => {
const { t } = useTranslation();
return (
<Box flex="1" textAlign="left">
{t('parameters:boundingBoxHeader')}
{t('parameters.boundingBoxHeader')}
</Box>
);
};

View File

@@ -107,9 +107,9 @@ const InfillAndScalingSettings = () => {
};
return (
<Flex direction="column" gap="1rem">
<Flex direction="column" gap={4}>
<IAISelect
label={t('parameters:scaleBeforeProcessing')}
label={t('parameters.scaleBeforeProcessing')}
validValues={BOUNDING_BOX_SCALES_DICT}
value={boundingBoxScale}
onChange={handleChangeBoundingBoxScaleMethod}
@@ -118,7 +118,7 @@ const InfillAndScalingSettings = () => {
isInputDisabled={!isManual}
isResetDisabled={!isManual}
isSliderDisabled={!isManual}
label={t('parameters:scaledWidth')}
label={t('parameters.scaledWidth')}
min={64}
max={1024}
step={64}
@@ -130,12 +130,13 @@ const InfillAndScalingSettings = () => {
inputReadOnly
withReset
handleReset={handleResetScaledWidth}
sliderMarkRightOffset={-7}
/>
<IAISlider
isInputDisabled={!isManual}
isResetDisabled={!isManual}
isSliderDisabled={!isManual}
label={t('parameters:scaledHeight')}
label={t('parameters.scaledHeight')}
min={64}
max={1024}
step={64}
@@ -147,9 +148,10 @@ const InfillAndScalingSettings = () => {
inputReadOnly
withReset
handleReset={handleResetScaledHeight}
sliderMarkRightOffset={-7}
/>
<IAISelect
label={t('parameters:infillMethod')}
label={t('parameters.infillMethod')}
value={infillMethod}
validValues={availableInfillMethods}
onChange={(e) => dispatch(setInfillMethod(e.target.value))}
@@ -159,7 +161,7 @@ const InfillAndScalingSettings = () => {
isResetDisabled={infillMethod !== 'tile'}
isSliderDisabled={infillMethod !== 'tile'}
sliderMarkRightOffset={-4}
label={t('parameters:tileSize')}
label={t('parameters.tileSize')}
min={16}
max={64}
sliderNumberInputProps={{ max: 256 }}

View File

@@ -14,7 +14,7 @@ export default function SeamBlur() {
return (
<IAISlider
sliderMarkRightOffset={-4}
label={t('parameters:seamBlur')}
label={t('parameters.seamBlur')}
min={0}
max={64}
sliderNumberInputProps={{ max: 512 }}

View File

@@ -6,7 +6,7 @@ import SeamStrength from './SeamStrength';
const SeamCorrectionSettings = () => {
return (
<Flex direction="column" gap="1rem">
<Flex direction="column" gap={2}>
<SeamSize />
<SeamBlur />
<SeamStrength />

View File

@@ -15,7 +15,7 @@ export default function SeamSize() {
return (
<IAISlider
sliderMarkRightOffset={-6}
label={t('parameters:seamSize')}
label={t('parameters.seamSize')}
min={1}
max={256}
sliderNumberInputProps={{ max: 512 }}

View File

@@ -14,7 +14,7 @@ export default function SeamSteps() {
return (
<IAISlider
sliderMarkRightOffset={-4}
label={t('parameters:seamSteps')}
label={t('parameters.seamSteps')}
min={1}
max={100}
sliderNumberInputProps={{ max: 999 }}

View File

@@ -14,7 +14,7 @@ export default function SeamStrength() {
return (
<IAISlider
sliderMarkRightOffset={-7}
label={t('parameters:seamStrength')}
label={t('parameters.seamStrength')}
min={0.01}
max={0.99}
step={0.01}

View File

@@ -0,0 +1,36 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setCodeformerFidelity } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function CodeformerFidelity() {
const isGFPGANAvailable = useAppSelector(
(state: RootState) => state.system.isGFPGANAvailable
);
const codeformerFidelity = useAppSelector(
(state: RootState) => state.postprocessing.codeformerFidelity
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
isSliderDisabled={!isGFPGANAvailable}
isInputDisabled={!isGFPGANAvailable}
isResetDisabled={!isGFPGANAvailable}
label={t('parameters.codeformerFidelity')}
step={0.05}
min={0}
max={1}
onChange={(v) => dispatch(setCodeformerFidelity(v))}
handleReset={() => dispatch(setCodeformerFidelity(1))}
value={codeformerFidelity}
withReset
withSliderMarks
withInput
/>
);
}

View File

@@ -1,99 +1,23 @@
import { Flex } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
import {
setCodeformerFidelity,
setFacetoolStrength,
setFacetoolType,
} from 'features/parameters/store/postprocessingSlice';
import { createSelector } from '@reduxjs/toolkit';
import { FACETOOL_TYPES } from 'app/constants';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
import { isEqual } from 'lodash';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
const optionsSelector = createSelector(
[postprocessingSelector, systemSelector],
(
{ facetoolStrength, facetoolType, codeformerFidelity },
{ isGFPGANAvailable }
) => {
return {
facetoolStrength,
facetoolType,
codeformerFidelity,
isGFPGANAvailable,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
import { useAppSelector } from 'app/storeHooks';
import type { RootState } from 'app/store';
import FaceRestoreType from './FaceRestoreType';
import FaceRestoreStrength from './FaceRestoreStrength';
import CodeformerFidelity from './CodeformerFidelity';
/**
* Displays face-fixing/GFPGAN options (strength).
*/
const FaceRestoreSettings = () => {
const dispatch = useAppDispatch();
const {
facetoolStrength,
facetoolType,
codeformerFidelity,
isGFPGANAvailable,
} = useAppSelector(optionsSelector);
const handleChangeStrength = (v: number) => dispatch(setFacetoolStrength(v));
const handleChangeCodeformerFidelity = (v: number) =>
dispatch(setCodeformerFidelity(v));
const handleChangeFacetoolType = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setFacetoolType(e.target.value as FacetoolType));
const { t } = useTranslation();
const facetoolType = useAppSelector(
(state: RootState) => state.postprocessing.facetoolType
);
return (
<Flex direction={'column'} gap={2}>
<IAISelect
label={t('parameters:type')}
validValues={FACETOOL_TYPES.concat()}
value={facetoolType}
onChange={handleChangeFacetoolType}
/>
<IAINumberInput
isDisabled={!isGFPGANAvailable}
label={t('parameters:strength')}
step={0.05}
min={0}
max={1}
onChange={handleChangeStrength}
value={facetoolStrength}
width="90px"
isInteger={false}
/>
{facetoolType === 'codeformer' && (
<IAINumberInput
isDisabled={!isGFPGANAvailable}
label={t('parameters:codeformerFidelity')}
step={0.05}
min={0}
max={1}
onChange={handleChangeCodeformerFidelity}
value={codeformerFidelity}
width="90px"
isInteger={false}
/>
)}
<Flex direction="column" gap={2} minWidth="20rem">
<FaceRestoreType />
<FaceRestoreStrength />
{facetoolType === 'codeformer' && <CodeformerFidelity />}
</Flex>
);
};

View File

@@ -0,0 +1,36 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setFacetoolStrength } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function FaceRestoreStrength() {
const isGFPGANAvailable = useAppSelector(
(state: RootState) => state.system.isGFPGANAvailable
);
const facetoolStrength = useAppSelector(
(state: RootState) => state.postprocessing.facetoolStrength
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
isSliderDisabled={!isGFPGANAvailable}
isInputDisabled={!isGFPGANAvailable}
isResetDisabled={!isGFPGANAvailable}
label={t('parameters.strength')}
step={0.05}
min={0}
max={1}
onChange={(v) => dispatch(setFacetoolStrength(v))}
handleReset={() => dispatch(setFacetoolStrength(0.75))}
value={facetoolStrength}
withReset
withSliderMarks
withInput
/>
);
}

View File

@@ -0,0 +1,31 @@
import { FACETOOL_TYPES } from 'app/constants';
import { type RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import {
type FacetoolType,
setFacetoolType,
} from 'features/parameters/store/postprocessingSlice';
import { type ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function FaceRestoreType() {
const facetoolType = useAppSelector(
(state: RootState) => state.postprocessing.facetoolType
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleChangeFacetoolType = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setFacetoolType(e.target.value as FacetoolType));
return (
<IAISelect
label={t('parameters.type')}
validValues={FACETOOL_TYPES.concat()}
value={facetoolType}
onChange={handleChangeFacetoolType}
/>
);
}

View File

@@ -19,7 +19,7 @@ export default function ImageFit() {
return (
<IAISwitch
label={t('parameters:imageFit')}
label={t('parameters.imageFit')}
isChecked={shouldFitToWidthHeight}
onChange={handleChangeFit}
/>

View File

@@ -11,7 +11,7 @@ interface ImageToImageStrengthProps {
export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
const { t } = useTranslation();
const { label = `${t('parameters:strength')}`, styleClass } = props;
const { label = `${t('parameters.strength')}`, styleClass } = props;
const img2imgStrength = useAppSelector(
(state: RootState) => state.generation.img2imgStrength
);
@@ -36,7 +36,7 @@ export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
styleClass={styleClass}
withInput
withSliderMarks
inputWidth={'5.5rem'}
inputWidth="5.5rem"
withReset
handleReset={handleImg2ImgStrengthReset}
/>

View File

@@ -4,6 +4,7 @@ import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch';
import SubItemHook from 'common/components/SubItemHook';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import {
setHiresFix,
@@ -39,23 +40,27 @@ const HiresStrength = () => {
};
return (
<IAISlider
label={t('parameters:hiresStrength')}
step={0.01}
min={0.01}
max={0.99}
onChange={handleHiresStrength}
value={hiresStrength}
isInteger={false}
withInput
withSliderMarks
inputWidth={'5.5rem'}
withReset
handleReset={handleHiResStrengthReset}
isSliderDisabled={!hiresFix}
isInputDisabled={!hiresFix}
isResetDisabled={!hiresFix}
/>
<Flex>
<SubItemHook active={hiresFix} />
<IAISlider
label={t('parameters.hiresStrength')}
step={0.01}
min={0.01}
max={0.99}
onChange={handleHiresStrength}
value={hiresStrength}
isInteger={false}
withInput
withSliderMarks
inputWidth={'5.5rem'}
withReset
handleReset={handleHiResStrengthReset}
isSliderDisabled={!hiresFix}
isInputDisabled={!hiresFix}
isResetDisabled={!hiresFix}
sliderMarkRightOffset={-7}
/>
</Flex>
);
};
@@ -75,10 +80,10 @@ const HiresSettings = () => {
dispatch(setHiresFix(e.target.checked));
return (
<Flex gap={2} direction={'column'}>
<Flex rowGap="0.8rem" direction={'column'}>
<IAISwitch
label={t('parameters:hiresOptim')}
fontSize={'md'}
label={t('parameters.hiresOptim')}
fontSize="md"
isChecked={hiresFix}
onChange={handleChangeHiresFix}
/>

View File

@@ -3,7 +3,7 @@ import SeamlessSettings from './SeamlessSettings';
const ImageToImageOutputSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<SeamlessSettings />
</Flex>
);

View File

@@ -4,7 +4,7 @@ import SeamlessSettings from './SeamlessSettings';
const OutputSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<SeamlessSettings />
<HiresSettings />
</Flex>

View File

@@ -22,10 +22,10 @@ const SeamlessSettings = () => {
const { t } = useTranslation();
return (
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<IAISwitch
label={t('parameters:seamlessTiling')}
fontSize={'md'}
label={t('parameters.seamlessTiling')}
fontSize="md"
isChecked={seamless}
onChange={handleChangeSeamless}
/>

View File

@@ -1,6 +1,6 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setPerlin } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@@ -9,17 +9,18 @@ export default function Perlin() {
const perlin = useAppSelector((state: RootState) => state.generation.perlin);
const { t } = useTranslation();
const handleChangePerlin = (v: number) => dispatch(setPerlin(v));
return (
<IAINumberInput
label={t('parameters:perlinNoise')}
<IAISlider
label={t('parameters.perlinNoise')}
min={0}
max={1}
step={0.05}
onChange={handleChangePerlin}
onChange={(v) => dispatch(setPerlin(v))}
handleReset={() => dispatch(setPerlin(0))}
value={perlin}
isInteger={false}
withInput
withReset
withSliderMarks
/>
);
}

View File

@@ -19,7 +19,7 @@ export default function RandomizeSeed() {
return (
<IAISwitch
label={t('parameters:randomizeSeed')}
label={t('parameters.randomizeSeed')}
isChecked={shouldRandomizeSeed}
onChange={handleChangeShouldRandomizeSeed}
/>

View File

@@ -22,7 +22,7 @@ export default function Seed() {
return (
<IAINumberInput
label={t('parameters:seed')}
label={t('parameters.seed')}
step={1}
precision={0}
flexGrow={1}

View File

@@ -10,7 +10,7 @@ import Threshold from './Threshold';
*/
const SeedSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<RandomizeSeed />
<Flex gap={2}>
<Seed />

View File

@@ -18,12 +18,12 @@ export default function ShuffleSeed() {
return (
<Button
size={'sm'}
size="sm"
isDisabled={shouldRandomizeSeed}
onClick={handleClickRandomizeSeed}
padding="0 1.5rem"
>
<p>{t('parameters:shuffle')}</p>
<p>{t('parameters.shuffle')}</p>
</Button>
);
}

View File

@@ -1,6 +1,6 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setThreshold } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@@ -11,17 +11,19 @@ export default function Threshold() {
);
const { t } = useTranslation();
const handleChangeThreshold = (v: number) => dispatch(setThreshold(v));
return (
<IAINumberInput
label={t('parameters:noiseThreshold')}
<IAISlider
label={t('parameters.noiseThreshold')}
min={0}
max={1000}
step={0.1}
onChange={handleChangeThreshold}
max={1}
step={0.005}
onChange={(v) => dispatch(setThreshold(v))}
handleReset={() => dispatch(setThreshold(0))}
value={threshold}
isInteger={false}
withInput
withReset
withSliderMarks
inputWidth="6rem"
/>
);
}

View File

@@ -0,0 +1,38 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setUpscalingDenoising } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function UpscaleDenoisingStrength() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingDenoising = useAppSelector(
(state: RootState) => state.postprocessing.upscalingDenoising
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
label={t('parameters.denoisingStrength')}
value={upscalingDenoising}
min={0}
max={1}
step={0.01}
onChange={(v) => {
dispatch(setUpscalingDenoising(v));
}}
handleReset={() => dispatch(setUpscalingDenoising(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
);
}

View File

@@ -0,0 +1,36 @@
import { UPSCALING_LEVELS } from 'app/constants';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import {
setUpscalingLevel,
type UpscalingLevel,
} from 'features/parameters/store/postprocessingSlice';
import type { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function UpscaleScale() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingLevel = useAppSelector(
(state: RootState) => state.postprocessing.upscalingLevel
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChangeLevel = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setUpscalingLevel(Number(e.target.value) as UpscalingLevel));
return (
<IAISelect
isDisabled={!isESRGANAvailable}
label={t('parameters.scale')}
value={upscalingLevel}
onChange={handleChangeLevel}
validValues={UPSCALING_LEVELS}
/>
);
}

View File

@@ -1,104 +1,17 @@
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import {
setUpscalingDenoising,
setUpscalingLevel,
setUpscalingStrength,
UpscalingLevel,
} from 'features/parameters/store/postprocessingSlice';
import { createSelector } from '@reduxjs/toolkit';
import { UPSCALING_LEVELS } from 'app/constants';
import IAISelect from 'common/components/IAISelect';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
import { isEqual } from 'lodash';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
import IAISlider from 'common/components/IAISlider';
import { Flex } from '@chakra-ui/react';
const parametersSelector = createSelector(
[postprocessingSelector, systemSelector],
(
{ upscalingLevel, upscalingStrength, upscalingDenoising },
{ isESRGANAvailable }
) => {
return {
upscalingLevel,
upscalingDenoising,
upscalingStrength,
isESRGANAvailable,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
import UpscaleDenoisingStrength from './UpscaleDenoisingStrength';
import UpscaleStrength from './UpscaleStrength';
import UpscaleScale from './UpscaleScale';
/**
* Displays upscaling/ESRGAN options (level and strength).
*/
const UpscaleSettings = () => {
const dispatch = useAppDispatch();
const {
upscalingLevel,
upscalingStrength,
upscalingDenoising,
isESRGANAvailable,
} = useAppSelector(parametersSelector);
const { t } = useTranslation();
const handleChangeLevel = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setUpscalingLevel(Number(e.target.value) as UpscalingLevel));
const handleChangeStrength = (v: number) => dispatch(setUpscalingStrength(v));
return (
<Flex flexDir="column" rowGap="1rem" minWidth="20rem">
<IAISelect
isDisabled={!isESRGANAvailable}
label={t('parameters:scale')}
value={upscalingLevel}
onChange={handleChangeLevel}
validValues={UPSCALING_LEVELS}
/>
<IAISlider
label={t('parameters:denoisingStrength')}
value={upscalingDenoising}
min={0}
max={1}
step={0.01}
onChange={(v) => {
dispatch(setUpscalingDenoising(v));
}}
handleReset={() => dispatch(setUpscalingDenoising(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
<IAISlider
label={`${t('parameters:upscale')} ${t('parameters:strength')}`}
value={upscalingStrength}
min={0}
max={1}
step={0.05}
onChange={handleChangeStrength}
handleReset={() => dispatch(setUpscalingStrength(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
<Flex flexDir="column" rowGap={2} minWidth="20rem">
<UpscaleScale />
<UpscaleDenoisingStrength />
<UpscaleStrength />
</Flex>
);
};

View File

@@ -0,0 +1,35 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setUpscalingStrength } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function UpscaleStrength() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingStrength = useAppSelector(
(state: RootState) => state.postprocessing.upscalingStrength
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
label={`${t('parameters.upscale')} ${t('parameters.strength')}`}
value={upscalingStrength}
min={0}
max={1}
step={0.05}
onChange={(v) => dispatch(setUpscalingStrength(v))}
handleReset={() => dispatch(setUpscalingStrength(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
);
}

View File

@@ -18,7 +18,7 @@ export default function GenerateVariationsToggle() {
return (
<IAISwitch
isChecked={shouldGenerateVariations}
width={'auto'}
width="auto"
onChange={handleChangeShouldGenerateVariations}
/>
);

View File

@@ -24,7 +24,7 @@ export default function SeedWeights() {
return (
<IAIInput
label={t('parameters:seedWeights')}
label={t('parameters.seedWeights')}
value={seedWeights}
isInvalid={
shouldGenerateVariations &&

View File

@@ -1,6 +1,6 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setVariationAmount } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@@ -16,19 +16,22 @@ export default function VariationAmount() {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChangevariationAmount = (v: number) =>
dispatch(setVariationAmount(v));
return (
<IAINumberInput
label={t('parameters:variationAmount')}
<IAISlider
label={t('parameters.variationAmount')}
value={variationAmount}
step={0.01}
min={0}
max={1}
isDisabled={!shouldGenerateVariations}
onChange={handleChangevariationAmount}
isInteger={false}
isSliderDisabled={!shouldGenerateVariations}
isInputDisabled={!shouldGenerateVariations}
isResetDisabled={!shouldGenerateVariations}
onChange={(v) => dispatch(setVariationAmount(v))}
handleReset={() => dispatch(setVariationAmount(0.1))}
withInput
withReset
withSliderMarks
/>
);
}

View File

@@ -7,7 +7,7 @@ import VariationAmount from './VariationAmount';
*/
const VariationsSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<Flex gap={2} direction="column">
<VariationAmount />
<SeedWeights />
</Flex>

View File

@@ -1,6 +1,7 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setCfgScale } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@@ -9,13 +10,31 @@ export default function MainCFGScale() {
const cfgScale = useAppSelector(
(state: RootState) => state.generation.cfgScale
);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const { t } = useTranslation();
const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v));
return (
return shouldUseSliders ? (
<IAISlider
label={t('parameters.cfgScale')}
step={0.5}
min={1.01}
max={30}
onChange={handleChangeCfgScale}
handleReset={() => dispatch(setCfgScale(7.5))}
value={cfgScale}
sliderMarkRightOffset={-5}
sliderNumberInputProps={{ max: 200 }}
withInput
withReset
withSliderMarks
/>
) : (
<IAINumberInput
label={t('parameters:cfgScale')}
label={t('parameters.cfgScale')}
step={0.5}
min={1.01}
max={200}

View File

@@ -2,29 +2,50 @@ import { HEIGHTS } from 'app/constants';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import IAISlider from 'common/components/IAISlider';
import { setHeight } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function MainHeight() {
const height = useAppSelector((state: RootState) => state.generation.height);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const activeTabName = useAppSelector(activeTabNameSelector);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleChangeHeight = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setHeight(Number(e.target.value)));
return (
return shouldUseSliders ? (
<IAISlider
isSliderDisabled={activeTabName === 'unifiedCanvas'}
isInputDisabled={activeTabName === 'unifiedCanvas'}
isResetDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters.height')}
value={height}
min={64}
step={64}
max={2048}
onChange={(v) => dispatch(setHeight(v))}
handleReset={() => dispatch(setHeight(512))}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-8}
inputWidth="6.2rem"
sliderNumberInputProps={{ max: 15360 }}
/>
) : (
<IAISelect
isDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:height')}
label={t('parameters.height')}
value={height}
flexGrow={1}
onChange={handleChangeHeight}
onChange={(e) => dispatch(setHeight(Number(e.target.value)))}
validValues={HEIGHTS}
styleClass="main-settings-block"
width="5.5rem"
/>
);
}

View File

@@ -1,41 +1,43 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import {
GenerationState,
setIterations,
} from 'features/parameters/store/generationSlice';
import { isEqual } from 'lodash';
import IAISlider from 'common/components/IAISlider';
import { setIterations } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
const mainIterationsSelector = createSelector(
[(state: RootState) => state.generation],
(parameters: GenerationState) => {
const { iterations } = parameters;
return {
iterations,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
export default function MainIterations() {
const iterations = useAppSelector(
(state: RootState) => state.generation.iterations
);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const dispatch = useAppDispatch();
const { iterations } = useAppSelector(mainIterationsSelector);
const { t } = useTranslation();
const handleChangeIterations = (v: number) => dispatch(setIterations(v));
return (
return shouldUseSliders ? (
<IAISlider
label={t('parameters.images')}
step={1}
min={1}
max={16}
onChange={handleChangeIterations}
handleReset={() => dispatch(setIterations(1))}
value={iterations}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-5}
sliderNumberInputProps={{ max: 9999 }}
/>
) : (
<IAINumberInput
label={t('parameters:images')}
label={t('parameters.images')}
step={1}
min={1}
max={9999}

View File

@@ -1,3 +1,8 @@
import { Flex } from '@chakra-ui/react';
import { type RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks';
import { useTranslation } from 'react-i18next';
import ParametersAccordion from '../ParametersAccordion';
import MainCFGScale from './MainCFGScale';
import MainHeight from './MainHeight';
import MainIterations from './MainIterations';
@@ -8,20 +13,40 @@ import MainWidth from './MainWidth';
export const inputWidth = 'auto';
export default function MainSettings() {
return (
<div className="main-settings">
<div className="main-settings-list">
<div className="main-settings-row">
const { t } = useTranslation();
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const accordionItems = {
main: {
header: `${t('parameters.general')}`,
feature: undefined,
content: shouldUseSliders ? (
<Flex flexDir="column" rowGap={2}>
<MainIterations />
<MainSteps />
<MainCFGScale />
</div>
<div className="main-settings-row">
<MainWidth />
<MainHeight />
<MainSampler />
</div>
</div>
</div>
);
</Flex>
) : (
<Flex flexDirection="column" rowGap={2}>
<Flex gap={2}>
<MainIterations />
<MainSteps />
<MainCFGScale />
</Flex>
<Flex>
<MainWidth />
<MainHeight />
<MainSampler />
</Flex>
</Flex>
),
},
};
return <ParametersAccordion accordionInfo={accordionItems} />;
}

View File

@@ -20,13 +20,14 @@ export default function MainSampler() {
return (
<IAISelect
label={t('parameters:sampler')}
label={t('parameters.sampler')}
value={sampler}
onChange={handleChangeSampler}
validValues={
activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS
}
styleClass="main-settings-block"
minWidth="9rem"
/>
);
}

View File

@@ -1,19 +1,38 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setSteps } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function MainSteps() {
const dispatch = useAppDispatch();
const steps = useAppSelector((state: RootState) => state.generation.steps);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const { t } = useTranslation();
const handleChangeSteps = (v: number) => dispatch(setSteps(v));
return (
return shouldUseSliders ? (
<IAISlider
label={t('parameters.steps')}
min={1}
step={1}
onChange={handleChangeSteps}
handleReset={() => dispatch(setSteps(20))}
value={steps}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-6}
sliderNumberInputProps={{ max: 9999 }}
/>
) : (
<IAINumberInput
label={t('parameters:steps')}
label={t('parameters.steps')}
min={1}
max={9999}
step={1}

View File

@@ -2,30 +2,51 @@ import { WIDTHS } from 'app/constants';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import IAISlider from 'common/components/IAISlider';
import { setWidth } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function MainWidth() {
const width = useAppSelector((state: RootState) => state.generation.width);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const activeTabName = useAppSelector(activeTabNameSelector);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChangeWidth = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setWidth(Number(e.target.value)));
return (
return shouldUseSliders ? (
<IAISlider
isSliderDisabled={activeTabName === 'unifiedCanvas'}
isInputDisabled={activeTabName === 'unifiedCanvas'}
isResetDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters.width')}
value={width}
min={64}
step={64}
max={2048}
onChange={(v) => dispatch(setWidth(v))}
handleReset={() => dispatch(setWidth(512))}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-8}
inputWidth="6.2rem"
inputReadOnly
sliderNumberInputProps={{ max: 15360 }}
/>
) : (
<IAISelect
isDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:width')}
label={t('parameters.width')}
value={width}
flexGrow={1}
onChange={handleChangeWidth}
onChange={(e) => dispatch(setWidth(Number(e.target.value)))}
validValues={WIDTHS}
styleClass="main-settings-block"
width="5.5rem"
/>
);
}

View File

@@ -44,19 +44,19 @@ export default function InvokeButton(props: InvokeButton) {
<div style={{ flexGrow: 4 }}>
{iconButton ? (
<IAIIconButton
aria-label={t('parameters:invoke')}
aria-label={t('parameters.invoke')}
type="submit"
icon={<FaPlay />}
isDisabled={!isReady}
onClick={handleClickGenerate}
className="invoke-btn"
tooltip={t('parameters:invoke')}
tooltip={t('parameters.invoke')}
tooltipProps={{ placement: 'bottom' }}
{...rest}
/>
) : (
<IAIButton
aria-label={t('parameters:invoke')}
aria-label={t('parameters.invoke')}
type="submit"
isDisabled={!isReady}
onClick={handleClickGenerate}

View File

@@ -19,8 +19,8 @@ const LoopbackButton = () => {
return (
<IAIIconButton
aria-label={t('parameters:toggleLoopback')}
tooltip={t('parameters:toggleLoopback')}
aria-label={t('parameters.toggleLoopback')}
tooltip={t('parameters.toggleLoopback')}
styleClass="loopback-btn"
asCheckbox={true}
isChecked={shouldLoopback}

View File

@@ -20,7 +20,7 @@ const NegativePromptInput = () => {
value={negativePrompt}
onChange={(e) => dispatch(setNegativePrompt(e.target.value))}
background="var(--prompt-bg-color)"
placeholder={t('parameters:negativePrompts')}
placeholder={t('parameters.negativePrompts')}
_placeholder={{ fontSize: '0.8rem' }}
borderColor="var(--border-color)"
_hover={{

View File

@@ -70,8 +70,8 @@ const PromptInput = () => {
<Textarea
id="prompt"
name="prompt"
placeholder={t('parameters:promptPlaceholder')}
size={'lg'}
placeholder={t('parameters.promptPlaceholder')}
size="lg"
value={prompt}
onChange={handleChangePrompt}
onKeyDown={handleKeyDown}

View File

@@ -22,7 +22,7 @@ export interface PostprocessingState {
const initialPostprocessingState: PostprocessingState = {
codeformerFidelity: 0.75,
facetoolStrength: 0.8,
facetoolStrength: 0.75,
facetoolType: 'gfpgan',
hiresFix: false,
hiresStrength: 0.75,

View File

@@ -23,18 +23,18 @@ const EmptyTempFolderButtonModal = () => {
return (
<IAIAlertDialog
title={t('unifiedcanvas:emptyTempImageFolder')}
title={t('unifiedCanvas.emptyTempImageFolder')}
acceptCallback={acceptCallback}
acceptButtonText={t('unifiedcanvas:emptyFolder')}
acceptButtonText={t('unifiedCanvas.emptyFolder')}
triggerComponent={
<IAIButton leftIcon={<FaTrash />} size={'sm'} isDisabled={isStaging}>
{t('unifiedcanvas:emptyTempImageFolder')}
<IAIButton leftIcon={<FaTrash />} size="sm" isDisabled={isStaging}>
{t('unifiedCanvas.emptyTempImageFolder')}
</IAIButton>
}
>
<p>{t('unifiedcanvas:emptyTempImagesFolderMessage')}</p>
<p>{t('unifiedCanvas.emptyTempImagesFolderMessage')}</p>
<br />
<p>{t('unifiedcanvas:emptyTempImagesFolderConfirm')}</p>
<p>{t('unifiedCanvas.emptyTempImagesFolderConfirm')}</p>
</IAIAlertDialog>
);
};

View File

@@ -109,7 +109,7 @@ const Console = () => {
bottom: 0,
zIndex: 9999,
}}
maxHeight={'90vh'}
maxHeight="90vh"
>
<div className="console" ref={viewerRef} onScroll={handleOnScroll}>
{log.map((entry, i) => {
@@ -130,11 +130,11 @@ const Console = () => {
label={shouldAutoscroll ? 'Autoscroll On' : 'Autoscroll Off'}
>
<IconButton
className={'console-autoscroll-icon-button'}
className="console-autoscroll-icon-button"
data-autoscroll-enabled={shouldAutoscroll}
size="sm"
aria-label="Toggle autoscroll"
variant={'solid'}
variant="solid"
icon={<FaAngleDoubleDown />}
onClick={() => setShouldAutoscroll(!shouldAutoscroll)}
/>
@@ -145,11 +145,11 @@ const Console = () => {
label={shouldShowLogViewer ? 'Hide Console' : 'Show Console'}
>
<IconButton
className={'console-toggle-icon-button'}
className="console-toggle-icon-button"
data-error-seen={hasError || !wasErrorSeen}
size="sm"
position={'fixed'}
variant={'solid'}
position="fixed"
variant="solid"
aria-label="Toggle Log Viewer"
icon={shouldShowLogViewer ? <FaMinus /> : <FaCode />}
onClick={handleClickLogViewerToggle}

View File

@@ -36,263 +36,263 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
const appHotkeys = [
{
title: t('hotkeys:invoke.title'),
desc: t('hotkeys:invoke.desc'),
title: t('hotkeys.invoke.title'),
desc: t('hotkeys.invoke.desc'),
hotkey: 'Ctrl+Enter',
},
{
title: t('hotkeys:cancel.title'),
desc: t('hotkeys:cancel.desc'),
title: t('hotkeys.cancel.title'),
desc: t('hotkeys.cancel.desc'),
hotkey: 'Shift+X',
},
{
title: t('hotkeys:focusPrompt.title'),
desc: t('hotkeys:focusPrompt.desc'),
title: t('hotkeys.focusPrompt.title'),
desc: t('hotkeys.focusPrompt.desc'),
hotkey: 'Alt+A',
},
{
title: t('hotkeys:toggleOptions.title'),
desc: t('hotkeys:toggleOptions.desc'),
title: t('hotkeys.toggleOptions.title'),
desc: t('hotkeys.toggleOptions.desc'),
hotkey: 'O',
},
{
title: t('hotkeys:pinOptions.title'),
desc: t('hotkeys:pinOptions.desc'),
title: t('hotkeys.pinOptions.title'),
desc: t('hotkeys.pinOptions.desc'),
hotkey: 'Shift+O',
},
{
title: t('hotkeys:toggleViewer.title'),
desc: t('hotkeys:toggleViewer.desc'),
title: t('hotkeys.toggleViewer.title'),
desc: t('hotkeys.toggleViewer.desc'),
hotkey: 'Z',
},
{
title: t('hotkeys:toggleGallery.title'),
desc: t('hotkeys:toggleGallery.desc'),
title: t('hotkeys.toggleGallery.title'),
desc: t('hotkeys.toggleGallery.desc'),
hotkey: 'G',
},
{
title: t('hotkeys:maximizeWorkSpace.title'),
desc: t('hotkeys:maximizeWorkSpace.desc'),
title: t('hotkeys.maximizeWorkSpace.title'),
desc: t('hotkeys.maximizeWorkSpace.desc'),
hotkey: 'F',
},
{
title: t('hotkeys:changeTabs.title'),
desc: t('hotkeys:changeTabs.desc'),
title: t('hotkeys.changeTabs.title'),
desc: t('hotkeys.changeTabs.desc'),
hotkey: '1-5',
},
{
title: t('hotkeys:consoleToggle.title'),
desc: t('hotkeys:consoleToggle.desc'),
title: t('hotkeys.consoleToggle.title'),
desc: t('hotkeys.consoleToggle.desc'),
hotkey: '`',
},
];
const generalHotkeys = [
{
title: t('hotkeys:setPrompt.title'),
desc: t('hotkeys:setPrompt.desc'),
title: t('hotkeys.setPrompt.title'),
desc: t('hotkeys.setPrompt.desc'),
hotkey: 'P',
},
{
title: t('hotkeys:setSeed.title'),
desc: t('hotkeys:setSeed.desc'),
title: t('hotkeys.setSeed.title'),
desc: t('hotkeys.setSeed.desc'),
hotkey: 'S',
},
{
title: t('hotkeys:setParameters.title'),
desc: t('hotkeys:setParameters.desc'),
title: t('hotkeys.setParameters.title'),
desc: t('hotkeys.setParameters.desc'),
hotkey: 'A',
},
{
title: t('hotkeys:restoreFaces.title'),
desc: t('hotkeys:restoreFaces.desc'),
title: t('hotkeys.restoreFaces.title'),
desc: t('hotkeys.restoreFaces.desc'),
hotkey: 'Shift+R',
},
{
title: t('hotkeys:upscale.title'),
desc: t('hotkeys:upscale.desc'),
title: t('hotkeys.upscale.title'),
desc: t('hotkeys.upscale.desc'),
hotkey: 'Shift+U',
},
{
title: t('hotkeys:showInfo.title'),
desc: t('hotkeys:showInfo.desc'),
title: t('hotkeys.showInfo.title'),
desc: t('hotkeys.showInfo.desc'),
hotkey: 'I',
},
{
title: t('hotkeys:sendToImageToImage.title'),
desc: t('hotkeys:sendToImageToImage.desc'),
title: t('hotkeys.sendToImageToImage.title'),
desc: t('hotkeys.sendToImageToImage.desc'),
hotkey: 'Shift+I',
},
{
title: t('hotkeys:deleteImage.title'),
desc: t('hotkeys:deleteImage.desc'),
title: t('hotkeys.deleteImage.title'),
desc: t('hotkeys.deleteImage.desc'),
hotkey: 'Del',
},
{
title: t('hotkeys:closePanels.title'),
desc: t('hotkeys:closePanels.desc'),
title: t('hotkeys.closePanels.title'),
desc: t('hotkeys.closePanels.desc'),
hotkey: 'Esc',
},
];
const galleryHotkeys = [
{
title: t('hotkeys:previousImage.title'),
desc: t('hotkeys:previousImage.desc'),
title: t('hotkeys.previousImage.title'),
desc: t('hotkeys.previousImage.desc'),
hotkey: 'Arrow Left',
},
{
title: t('hotkeys:nextImage.title'),
desc: t('hotkeys:nextImage.desc'),
title: t('hotkeys.nextImage.title'),
desc: t('hotkeys.nextImage.desc'),
hotkey: 'Arrow Right',
},
{
title: t('hotkeys:toggleGalleryPin.title'),
desc: t('hotkeys:toggleGalleryPin.desc'),
title: t('hotkeys.toggleGalleryPin.title'),
desc: t('hotkeys.toggleGalleryPin.desc'),
hotkey: 'Shift+G',
},
{
title: t('hotkeys:increaseGalleryThumbSize.title'),
desc: t('hotkeys:increaseGalleryThumbSize.desc'),
title: t('hotkeys.increaseGalleryThumbSize.title'),
desc: t('hotkeys.increaseGalleryThumbSize.desc'),
hotkey: 'Shift+Up',
},
{
title: t('hotkeys:decreaseGalleryThumbSize.title'),
desc: t('hotkeys:decreaseGalleryThumbSize.desc'),
title: t('hotkeys.decreaseGalleryThumbSize.title'),
desc: t('hotkeys.decreaseGalleryThumbSize.desc'),
hotkey: 'Shift+Down',
},
];
const unifiedCanvasHotkeys = [
{
title: t('hotkeys:selectBrush.title'),
desc: t('hotkeys:selectBrush.desc'),
title: t('hotkeys.selectBrush.title'),
desc: t('hotkeys.selectBrush.desc'),
hotkey: 'B',
},
{
title: t('hotkeys:selectEraser.title'),
desc: t('hotkeys:selectEraser.desc'),
title: t('hotkeys.selectEraser.title'),
desc: t('hotkeys.selectEraser.desc'),
hotkey: 'E',
},
{
title: t('hotkeys:decreaseBrushSize.title'),
desc: t('hotkeys:decreaseBrushSize.desc'),
title: t('hotkeys.decreaseBrushSize.title'),
desc: t('hotkeys.decreaseBrushSize.desc'),
hotkey: '[',
},
{
title: t('hotkeys:increaseBrushSize.title'),
desc: t('hotkeys:increaseBrushSize.desc'),
title: t('hotkeys.increaseBrushSize.title'),
desc: t('hotkeys.increaseBrushSize.desc'),
hotkey: ']',
},
{
title: t('hotkeys:decreaseBrushOpacity.title'),
desc: t('hotkeys:decreaseBrushOpacity.desc'),
title: t('hotkeys.decreaseBrushOpacity.title'),
desc: t('hotkeys.decreaseBrushOpacity.desc'),
hotkey: 'Shift + [',
},
{
title: t('hotkeys:increaseBrushOpacity.title'),
desc: t('hotkeys:increaseBrushOpacity.desc'),
title: t('hotkeys.increaseBrushOpacity.title'),
desc: t('hotkeys.increaseBrushOpacity.desc'),
hotkey: 'Shift + ]',
},
{
title: t('hotkeys:moveTool.title'),
desc: t('hotkeys:moveTool.desc'),
title: t('hotkeys.moveTool.title'),
desc: t('hotkeys.moveTool.desc'),
hotkey: 'V',
},
{
title: t('hotkeys:fillBoundingBox.title'),
desc: t('hotkeys:fillBoundingBox.desc'),
title: t('hotkeys.fillBoundingBox.title'),
desc: t('hotkeys.fillBoundingBox.desc'),
hotkey: 'Shift + F',
},
{
title: t('hotkeys:eraseBoundingBox.title'),
desc: t('hotkeys:eraseBoundingBox.desc'),
title: t('hotkeys.eraseBoundingBox.title'),
desc: t('hotkeys.eraseBoundingBox.desc'),
hotkey: 'Delete / Backspace',
},
{
title: t('hotkeys:colorPicker.title'),
desc: t('hotkeys:colorPicker.desc'),
title: t('hotkeys.colorPicker.title'),
desc: t('hotkeys.colorPicker.desc'),
hotkey: 'C',
},
{
title: t('hotkeys:toggleSnap.title'),
desc: t('hotkeys:toggleSnap.desc'),
title: t('hotkeys.toggleSnap.title'),
desc: t('hotkeys.toggleSnap.desc'),
hotkey: 'N',
},
{
title: t('hotkeys:quickToggleMove.title'),
desc: t('hotkeys:quickToggleMove.desc'),
title: t('hotkeys.quickToggleMove.title'),
desc: t('hotkeys.quickToggleMove.desc'),
hotkey: 'Hold Space',
},
{
title: t('hotkeys:toggleLayer.title'),
desc: t('hotkeys:toggleLayer.desc'),
title: t('hotkeys.toggleLayer.title'),
desc: t('hotkeys.toggleLayer.desc'),
hotkey: 'Q',
},
{
title: t('hotkeys:clearMask.title'),
desc: t('hotkeys:clearMask.desc'),
title: t('hotkeys.clearMask.title'),
desc: t('hotkeys.clearMask.desc'),
hotkey: 'Shift+C',
},
{
title: t('hotkeys:hideMask.title'),
desc: t('hotkeys:hideMask.desc'),
title: t('hotkeys.hideMask.title'),
desc: t('hotkeys.hideMask.desc'),
hotkey: 'H',
},
{
title: t('hotkeys:showHideBoundingBox.title'),
desc: t('hotkeys:showHideBoundingBox.desc'),
title: t('hotkeys.showHideBoundingBox.title'),
desc: t('hotkeys.showHideBoundingBox.desc'),
hotkey: 'Shift+H',
},
{
title: t('hotkeys:mergeVisible.title'),
desc: t('hotkeys:mergeVisible.desc'),
title: t('hotkeys.mergeVisible.title'),
desc: t('hotkeys.mergeVisible.desc'),
hotkey: 'Shift+M',
},
{
title: t('hotkeys:saveToGallery.title'),
desc: t('hotkeys:saveToGallery.desc'),
title: t('hotkeys.saveToGallery.title'),
desc: t('hotkeys.saveToGallery.desc'),
hotkey: 'Shift+S',
},
{
title: t('hotkeys:copyToClipboard.title'),
desc: t('hotkeys:copyToClipboard.desc'),
title: t('hotkeys.copyToClipboard.title'),
desc: t('hotkeys.copyToClipboard.desc'),
hotkey: 'Ctrl+C',
},
{
title: t('hotkeys:downloadImage.title'),
desc: t('hotkeys:downloadImage.desc'),
title: t('hotkeys.downloadImage.title'),
desc: t('hotkeys.downloadImage.desc'),
hotkey: 'Shift+D',
},
{
title: t('hotkeys:undoStroke.title'),
desc: t('hotkeys:undoStroke.desc'),
title: t('hotkeys.undoStroke.title'),
desc: t('hotkeys.undoStroke.desc'),
hotkey: 'Ctrl+Z',
},
{
title: t('hotkeys:redoStroke.title'),
desc: t('hotkeys:redoStroke.desc'),
title: t('hotkeys.redoStroke.title'),
desc: t('hotkeys.redoStroke.desc'),
hotkey: 'Ctrl+Shift+Z, Ctrl+Y',
},
{
title: t('hotkeys:resetView.title'),
desc: t('hotkeys:resetView.desc'),
title: t('hotkeys.resetView.title'),
desc: t('hotkeys.resetView.desc'),
hotkey: 'R',
},
{
title: t('hotkeys:previousStagingImage.title'),
desc: t('hotkeys:previousStagingImage.desc'),
title: t('hotkeys.previousStagingImage.title'),
desc: t('hotkeys.previousStagingImage.desc'),
hotkey: 'Arrow Left',
},
{
title: t('hotkeys:nextStagingImage.title'),
desc: t('hotkeys:nextStagingImage.desc'),
title: t('hotkeys.nextStagingImage.title'),
desc: t('hotkeys.nextStagingImage.desc'),
hotkey: 'Arrow Right',
},
{
title: t('hotkeys:acceptStagingImage.title'),
desc: t('hotkeys:acceptStagingImage.desc'),
title: t('hotkeys.acceptStagingImage.title'),
desc: t('hotkeys.acceptStagingImage.desc'),
hotkey: 'Enter',
},
];
@@ -331,7 +331,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
<Accordion allowMultiple>
<AccordionItem>
<AccordionButton className="hotkeys-modal-button">
<h2>{t('hotkeys:appHotkeys')}</h2>
<h2>{t('hotkeys.appHotkeys')}</h2>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
@@ -341,7 +341,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
<AccordionItem>
<AccordionButton className="hotkeys-modal-button">
<h2>{t('hotkeys:generalHotkeys')}</h2>
<h2>{t('hotkeys.generalHotkeys')}</h2>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
@@ -351,7 +351,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
<AccordionItem>
<AccordionButton className="hotkeys-modal-button">
<h2>{t('hotkeys:galleryHotkeys')}</h2>
<h2>{t('hotkeys.galleryHotkeys')}</h2>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
@@ -361,7 +361,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
<AccordionItem>
<AccordionButton className="hotkeys-modal-button">
<h2>{t('hotkeys:unifiedCanvasHotkeys')}</h2>
<h2>{t('hotkeys.unifiedCanvasHotkeys')}</h2>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>

View File

@@ -9,20 +9,20 @@ import { FaLanguage } from 'react-icons/fa';
export default function LanguagePicker() {
const { t, i18n } = useTranslation();
const LANGUAGES = {
en: t('common:langEnglish'),
nl: t('common:langDutch'),
fr: t('common:langFrench'),
de: t('common:langGerman'),
it: t('common:langItalian'),
ja: t('common:langJapanese'),
pl: t('common:langPolish'),
pt_br: t('common:langBrPortuguese'),
ru: t('common:langRussian'),
zh_cn: t('common:langSimplifiedChinese'),
es: t('common:langSpanish'),
ua: t('common:langUkranian'),
ar: t('common.langArabic', { lng: 'ar' }),
nl: t('common.langDutch', { lng: 'nl' }),
en: t('common.langEnglish', { lng: 'en' }),
fr: t('common.langFrench', { lng: 'fr' }),
de: t('common.langGerman', { lng: 'de' }),
it: t('common.langItalian', { lng: 'it' }),
ja: t('common.langJapanese', { lng: 'ja' }),
pl: t('common.langPolish', { lng: 'pl' }),
pt_Br: t('common.langBrPortuguese', { lng: 'pt_Br' }),
ru: t('common.langRussian', { lng: 'ru' }),
zh_Cn: t('common.langSimplifiedChinese', { lng: 'zh_Cn' }),
es: t('common.langSpanish', { lng: 'es' }),
uk: t('common.langUkranian', { lng: 'ua' }),
};
const renderLanguagePicker = () => {
@@ -35,7 +35,6 @@ export default function LanguagePicker() {
onClick={() => i18n.changeLanguage(lang)}
className="modal-close-btn lang-select-btn"
aria-label={LANGUAGES[lang as keyof typeof LANGUAGES]}
tooltip={LANGUAGES[lang as keyof typeof LANGUAGES]}
size="sm"
minWidth="200px"
>
@@ -52,10 +51,10 @@ export default function LanguagePicker() {
trigger="hover"
triggerComponent={
<IAIIconButton
aria-label={t('common:languagePickerLabel')}
tooltip={t('common:languagePickerLabel')}
aria-label={t('common.languagePickerLabel')}
tooltip={t('common.languagePickerLabel')}
icon={<FaLanguage />}
size={'sm'}
size="sm"
variant="link"
data-variant="link"
fontSize={26}

View File

@@ -47,7 +47,7 @@ export default function AddCheckpointModel() {
function baseValidation(value: string) {
let error;
if (hasWhiteSpace(value)) error = t('modelmanager:cannotUseSpaces');
if (hasWhiteSpace(value)) error = t('modelManager.cannotUseSpaces');
return error;
}
@@ -73,8 +73,8 @@ export default function AddCheckpointModel() {
return (
<>
<IAIIconButton
aria-label={t('common:back')}
tooltip={t('common:back')}
aria-label={t('common.back')}
tooltip={t('common.back')}
onClick={() => dispatch(setAddNewModelUIOption(null))}
width="max-content"
position="absolute"
@@ -87,7 +87,7 @@ export default function AddCheckpointModel() {
<SearchModels />
<IAICheckbox
label={t('modelmanager:addManually')}
label={t('modelManager.addManually')}
isChecked={addManually}
onChange={() => setAddmanually(!addManually)}
/>
@@ -99,9 +99,9 @@ export default function AddCheckpointModel() {
>
{({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'}>
<Text fontSize={20} fontWeight="bold" alignSelf={'start'}>
{t('modelmanager:manual')}
<VStack rowGap="0.5rem">
<Text fontSize={20} fontWeight="bold" alignSelf="start">
{t('modelManager.manual')}
</Text>
{/* Name */}
<FormControl
@@ -109,9 +109,9 @@ export default function AddCheckpointModel() {
isRequired
>
<FormLabel htmlFor="name" fontSize="sm">
{t('modelmanager:name')}
{t('modelManager.name')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="name"
@@ -124,7 +124,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.name}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:nameValidationMsg')}
{t('modelManager.nameValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -136,9 +136,9 @@ export default function AddCheckpointModel() {
isRequired
>
<FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')}
{t('modelManager.description')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="description"
@@ -150,7 +150,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.description}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:descriptionValidationMsg')}
{t('modelManager.descriptionValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -162,9 +162,9 @@ export default function AddCheckpointModel() {
isRequired
>
<FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:config')}
{t('modelManager.config')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="config"
@@ -176,7 +176,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.config}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:configValidationMsg')}
{t('modelManager.configValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -188,9 +188,9 @@ export default function AddCheckpointModel() {
isRequired
>
<FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:modelLocation')}
{t('modelManager.modelLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="weights"
@@ -202,7 +202,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.weights}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:modelLocationValidationMsg')}
{t('modelManager.modelLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -211,9 +211,9 @@ export default function AddCheckpointModel() {
{/* VAE */}
<FormControl isInvalid={!!errors.vae && touched.vae}>
<FormLabel htmlFor="vae" fontSize="sm">
{t('modelmanager:vaeLocation')}
{t('modelManager.vaeLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae"
@@ -225,19 +225,19 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.vae}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeLocationValidationMsg')}
{t('modelManager.vaeLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
</FormControl>
<HStack width={'100%'}>
<HStack width="100%">
{/* Width */}
<FormControl isInvalid={!!errors.width && touched.width}>
<FormLabel htmlFor="width" fontSize="sm">
{t('modelmanager:width')}
{t('modelManager.width')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field id="width" name="width">
{({
field,
@@ -265,7 +265,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.width}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:widthValidationMsg')}
{t('modelManager.widthValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -274,9 +274,9 @@ export default function AddCheckpointModel() {
{/* Height */}
<FormControl isInvalid={!!errors.height && touched.height}>
<FormLabel htmlFor="height" fontSize="sm">
{t('modelmanager:height')}
{t('modelManager.height')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field id="height" name="height">
{({
field,
@@ -304,7 +304,7 @@ export default function AddCheckpointModel() {
<FormErrorMessage>{errors.height}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:heightValidationMsg')}
{t('modelManager.heightValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -316,7 +316,7 @@ export default function AddCheckpointModel() {
className="modal-close-btn"
isLoading={isProcessing}
>
{t('modelmanager:addModel')}
{t('modelManager.addModel')}
</IAIButton>
</VStack>
</form>

View File

@@ -54,7 +54,7 @@ export default function AddDiffusersModel() {
function baseValidation(value: string) {
let error;
if (hasWhiteSpace(value)) error = t('modelmanager:cannotUseSpaces');
if (hasWhiteSpace(value)) error = t('modelManager.cannotUseSpaces');
return error;
}
@@ -88,8 +88,8 @@ export default function AddDiffusersModel() {
return (
<Flex>
<IAIIconButton
aria-label={t('common:back')}
tooltip={t('common:back')}
aria-label={t('common.back')}
tooltip={t('common.back')}
onClick={() => dispatch(setAddNewModelUIOption(null))}
width="max-content"
position="absolute"
@@ -105,7 +105,7 @@ export default function AddDiffusersModel() {
>
{({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'}>
<VStack rowGap="0.5rem">
<FormItemWrapper>
{/* Name */}
<FormControl
@@ -113,9 +113,9 @@ export default function AddDiffusersModel() {
isRequired
>
<FormLabel htmlFor="name" fontSize="sm">
{t('modelmanager:name')}
{t('modelManager.name')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="name"
@@ -129,7 +129,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.name}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:nameValidationMsg')}
{t('modelManager.nameValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -143,9 +143,9 @@ export default function AddDiffusersModel() {
isRequired
>
<FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')}
{t('modelManager.description')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="description"
@@ -158,7 +158,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.description}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:descriptionValidationMsg')}
{t('modelManager.descriptionValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -167,22 +167,22 @@ export default function AddDiffusersModel() {
<FormItemWrapper>
<Text fontWeight="bold" fontSize="sm">
{t('modelmanager:formMessageDiffusersModelLocation')}
{t('modelManager.formMessageDiffusersModelLocation')}
</Text>
<Text
fontSize="sm"
fontStyle="italic"
color="var(--text-color-secondary)"
>
{t('modelmanager:formMessageDiffusersModelLocationDesc')}
{t('modelManager.formMessageDiffusersModelLocationDesc')}
</Text>
{/* Path */}
<FormControl isInvalid={!!errors.path && touched.path}>
<FormLabel htmlFor="path" fontSize="sm">
{t('modelmanager:modelLocation')}
{t('modelManager.modelLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="path"
@@ -194,7 +194,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.path}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:modelLocationValidationMsg')}
{t('modelManager.modelLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -203,9 +203,9 @@ export default function AddDiffusersModel() {
{/* Repo ID */}
<FormControl isInvalid={!!errors.repo_id && touched.repo_id}>
<FormLabel htmlFor="repo_id" fontSize="sm">
{t('modelmanager:repo_id')}
{t('modelManager.repo_id')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="repo_id"
@@ -217,7 +217,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.repo_id}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:repoIDValidationMsg')}
{t('modelManager.repoIDValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -227,22 +227,22 @@ export default function AddDiffusersModel() {
<FormItemWrapper>
{/* VAE Path */}
<Text fontWeight="bold">
{t('modelmanager:formMessageDiffusersVAELocation')}
{t('modelManager.formMessageDiffusersVAELocation')}
</Text>
<Text
fontSize="sm"
fontStyle="italic"
color="var(--text-color-secondary)"
>
{t('modelmanager:formMessageDiffusersVAELocationDesc')}
{t('modelManager.formMessageDiffusersVAELocationDesc')}
</Text>
<FormControl
isInvalid={!!errors.vae?.path && touched.vae?.path}
>
<FormLabel htmlFor="vae.path" fontSize="sm">
{t('modelmanager:vaeLocation')}
{t('modelManager.vaeLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae.path"
@@ -254,7 +254,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.vae?.path}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeLocationValidationMsg')}
{t('modelManager.vaeLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -265,9 +265,9 @@ export default function AddDiffusersModel() {
isInvalid={!!errors.vae?.repo_id && touched.vae?.repo_id}
>
<FormLabel htmlFor="vae.repo_id" fontSize="sm">
{t('modelmanager:vaeRepoID')}
{t('modelManager.vaeRepoID')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae.repo_id"
@@ -279,7 +279,7 @@ export default function AddDiffusersModel() {
<FormErrorMessage>{errors.vae?.repo_id}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeRepoIDValidationMsg')}
{t('modelManager.vaeRepoIDValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -291,7 +291,7 @@ export default function AddDiffusersModel() {
className="modal-close-btn"
isLoading={isProcessing}
>
{t('modelmanager:addModel')}
{t('modelManager.addModel')}
</IAIButton>
</VStack>
</form>

View File

@@ -68,15 +68,15 @@ export default function AddModel() {
return (
<>
<IAIButton
aria-label={t('modelmanager:addNewModel')}
tooltip={t('modelmanager:addNewModel')}
aria-label={t('modelManager.addNewModel')}
tooltip={t('modelManager.addNewModel')}
onClick={onOpen}
className="modal-close-btn"
size={'sm'}
size="sm"
>
<Flex columnGap={'0.5rem'} alignItems="center">
<Flex columnGap="0.5rem" alignItems="center">
<FaPlus />
{t('modelmanager:addNew')}
{t('modelManager.addNew')}
</Flex>
</IAIButton>
@@ -87,18 +87,22 @@ export default function AddModel() {
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent className="modal add-model-modal" fontFamily="Inter">
<ModalHeader>{t('modelmanager:addNewModel')}</ModalHeader>
<ModalContent
className="modal add-model-modal"
fontFamily="Inter"
margin="auto"
>
<ModalHeader>{t('modelManager.addNewModel')}</ModalHeader>
<ModalCloseButton marginTop="0.3rem" />
<ModalBody className="add-model-modal-body">
{addNewModelUIOption == null && (
<Flex columnGap="1rem">
<AddModelBox
text={t('modelmanager:addCheckpointModel')}
text={t('modelManager.addCheckpointModel')}
onClick={() => dispatch(setAddNewModelUIOption('ckpt'))}
/>
<AddModelBox
text={t('modelmanager:addDiffuserModel')}
text={t('modelManager.addDiffuserModel')}
onClick={() => dispatch(setAddNewModelUIOption('diffusers'))}
/>
</Flex>

View File

@@ -27,6 +27,7 @@ import type { InvokeModelConfigProps } from 'app/invokeai';
import type { RootState } from 'app/store';
import type { FieldInputProps, FormikProps } from 'formik';
import { isEqual, pickBy } from 'lodash';
import ModelConvert from './ModelConvert';
const selector = createSelector(
[systemSelector],
@@ -101,10 +102,11 @@ export default function CheckpointModelEdit() {
return openModel ? (
<Flex flexDirection="column" rowGap="1rem" width="100%">
<Flex alignItems="center">
<Flex alignItems="center" gap={4} justifyContent="space-between">
<Text fontSize="lg" fontWeight="bold">
{openModel}
</Text>
<ModelConvert model={openModel} />
</Flex>
<Flex
flexDirection="column"
@@ -119,16 +121,16 @@ export default function CheckpointModelEdit() {
>
{({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'} alignItems="start">
<VStack rowGap="0.5rem" alignItems="start">
{/* Description */}
<FormControl
isInvalid={!!errors.description && touched.description}
isRequired
>
<FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')}
{t('modelManager.description')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="description"
@@ -140,7 +142,7 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.description}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:descriptionValidationMsg')}
{t('modelManager.descriptionValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -152,9 +154,9 @@ export default function CheckpointModelEdit() {
isRequired
>
<FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:config')}
{t('modelManager.config')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="config"
@@ -166,7 +168,7 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.config}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:configValidationMsg')}
{t('modelManager.configValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -178,9 +180,9 @@ export default function CheckpointModelEdit() {
isRequired
>
<FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:modelLocation')}
{t('modelManager.modelLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="weights"
@@ -192,7 +194,7 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.weights}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:modelLocationValidationMsg')}
{t('modelManager.modelLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -201,9 +203,9 @@ export default function CheckpointModelEdit() {
{/* VAE */}
<FormControl isInvalid={!!errors.vae && touched.vae}>
<FormLabel htmlFor="vae" fontSize="sm">
{t('modelmanager:vaeLocation')}
{t('modelManager.vaeLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae"
@@ -215,19 +217,19 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.vae}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeLocationValidationMsg')}
{t('modelManager.vaeLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
</FormControl>
<HStack width={'100%'}>
<HStack width="100%">
{/* Width */}
<FormControl isInvalid={!!errors.width && touched.width}>
<FormLabel htmlFor="width" fontSize="sm">
{t('modelmanager:width')}
{t('modelManager.width')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field id="width" name="width">
{({
field,
@@ -254,7 +256,7 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.width}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:widthValidationMsg')}
{t('modelManager.widthValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -263,9 +265,9 @@ export default function CheckpointModelEdit() {
{/* Height */}
<FormControl isInvalid={!!errors.height && touched.height}>
<FormLabel htmlFor="height" fontSize="sm">
{t('modelmanager:height')}
{t('modelManager.height')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field id="height" name="height">
{({
field,
@@ -292,7 +294,7 @@ export default function CheckpointModelEdit() {
<FormErrorMessage>{errors.height}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:heightValidationMsg')}
{t('modelManager.heightValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -304,7 +306,7 @@ export default function CheckpointModelEdit() {
className="modal-close-btn"
isLoading={isProcessing}
>
{t('modelmanager:updateModel')}
{t('modelManager.updateModel')}
</IAIButton>
</VStack>
</form>

View File

@@ -128,16 +128,16 @@ export default function DiffusersModelEdit() {
>
{({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'} alignItems="start">
<VStack rowGap="0.5rem" alignItems="start">
{/* Description */}
<FormControl
isInvalid={!!errors.description && touched.description}
isRequired
>
<FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')}
{t('modelManager.description')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="description"
@@ -149,7 +149,7 @@ export default function DiffusersModelEdit() {
<FormErrorMessage>{errors.description}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:descriptionValidationMsg')}
{t('modelManager.descriptionValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -161,9 +161,9 @@ export default function DiffusersModelEdit() {
isRequired
>
<FormLabel htmlFor="path" fontSize="sm">
{t('modelmanager:modelLocation')}
{t('modelManager.modelLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="path"
@@ -175,7 +175,7 @@ export default function DiffusersModelEdit() {
<FormErrorMessage>{errors.path}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:modelLocationValidationMsg')}
{t('modelManager.modelLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -184,9 +184,9 @@ export default function DiffusersModelEdit() {
{/* Repo ID */}
<FormControl isInvalid={!!errors.repo_id && touched.repo_id}>
<FormLabel htmlFor="repo_id" fontSize="sm">
{t('modelmanager:repo_id')}
{t('modelManager.repo_id')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="repo_id"
@@ -198,7 +198,7 @@ export default function DiffusersModelEdit() {
<FormErrorMessage>{errors.repo_id}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:repoIDValidationMsg')}
{t('modelManager.repoIDValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -209,9 +209,9 @@ export default function DiffusersModelEdit() {
isInvalid={!!errors.vae?.path && touched.vae?.path}
>
<FormLabel htmlFor="vae.path" fontSize="sm">
{t('modelmanager:vaeLocation')}
{t('modelManager.vaeLocation')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae.path"
@@ -223,7 +223,7 @@ export default function DiffusersModelEdit() {
<FormErrorMessage>{errors.vae?.path}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeLocationValidationMsg')}
{t('modelManager.vaeLocationValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -234,9 +234,9 @@ export default function DiffusersModelEdit() {
isInvalid={!!errors.vae?.repo_id && touched.vae?.repo_id}
>
<FormLabel htmlFor="vae.repo_id" fontSize="sm">
{t('modelmanager:vaeRepoID')}
{t('modelManager.vaeRepoID')}
</FormLabel>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<Field
as={IAIInput}
id="vae.repo_id"
@@ -248,7 +248,7 @@ export default function DiffusersModelEdit() {
<FormErrorMessage>{errors.vae?.repo_id}</FormErrorMessage>
) : (
<FormHelperText margin={0}>
{t('modelmanager:vaeRepoIDValidationMsg')}
{t('modelManager.vaeRepoIDValidationMsg')}
</FormHelperText>
)}
</VStack>
@@ -259,7 +259,7 @@ export default function DiffusersModelEdit() {
className="modal-close-btn"
isLoading={isProcessing}
>
{t('modelmanager:updateModel')}
{t('modelManager.updateModel')}
</IAIButton>
</VStack>
</form>

View File

@@ -0,0 +1,293 @@
import {
Flex,
Modal,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Radio,
RadioGroup,
Text,
Tooltip,
useDisclosure,
} from '@chakra-ui/react';
import { mergeDiffusersModels } from 'app/socketio/actions';
import { type RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIButton from 'common/components/IAIButton';
import IAIInput from 'common/components/IAIInput';
import IAISelect from 'common/components/IAISelect';
import { diffusersModelsSelector } from 'features/system/store/systemSelectors';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import * as InvokeAI from 'app/invokeai';
import IAISlider from 'common/components/IAISlider';
import IAICheckbox from 'common/components/IAICheckbox';
export default function MergeModels() {
const dispatch = useAppDispatch();
const { isOpen, onOpen, onClose } = useDisclosure();
const diffusersModels = useAppSelector(diffusersModelsSelector);
const { t } = useTranslation();
const [modelOne, setModelOne] = useState<string>(
Object.keys(diffusersModels)[0]
);
const [modelTwo, setModelTwo] = useState<string>(
Object.keys(diffusersModels)[1]
);
const [modelThree, setModelThree] = useState<string>('none');
const [mergedModelName, setMergedModelName] = useState<string>('');
const [modelMergeAlpha, setModelMergeAlpha] = useState<number>(0.5);
const [modelMergeInterp, setModelMergeInterp] = useState<
'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference'
>('weighted_sum');
const [modelMergeSaveLocType, setModelMergeSaveLocType] = useState<
'root' | 'custom'
>('root');
const [modelMergeCustomSaveLoc, setModelMergeCustomSaveLoc] =
useState<string>('');
const [modelMergeForce, setModelMergeForce] = useState<boolean>(false);
const modelOneList = Object.keys(diffusersModels).filter((model) => {
if (model !== modelTwo && model !== modelThree) return model;
});
const modelTwoList = Object.keys(diffusersModels).filter((model) => {
if (model !== modelOne && model !== modelThree) return model;
});
const modelThreeList = [
'none',
...Object.keys(diffusersModels).filter((model) => {
if (model !== modelOne && model !== modelTwo) return model;
}),
];
const isProcessing = useAppSelector(
(state: RootState) => state.system.isProcessing
);
const mergeModelsHandler = () => {
let modelsToMerge: string[] = [modelOne, modelTwo, modelThree];
modelsToMerge = modelsToMerge.filter((model) => model !== 'none');
const mergeModelsInfo: InvokeAI.InvokeModelMergingProps = {
models_to_merge: modelsToMerge,
merged_model_name:
mergedModelName !== '' ? mergedModelName : modelsToMerge.join('-'),
alpha: modelMergeAlpha,
interp: modelMergeInterp,
model_merge_save_path:
modelMergeSaveLocType === 'root' ? null : modelMergeCustomSaveLoc,
force: modelMergeForce,
};
dispatch(mergeDiffusersModels(mergeModelsInfo));
};
return (
<>
<IAIButton onClick={onOpen} className="modal-close-btn" size="sm">
<Flex columnGap="0.5rem" alignItems="center">
{t('modelManager.mergeModels')}
</Flex>
</IAIButton>
<Modal
isOpen={isOpen}
onClose={onClose}
size="4xl"
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent className="modal" fontFamily="Inter" margin="auto">
<ModalHeader>{t('modelManager.mergeModels')}</ModalHeader>
<ModalCloseButton />
<Flex flexDirection="column" padding="1rem" rowGap={4}>
<Flex
flexDirection="column"
marginBottom="1rem"
padding="1rem"
borderRadius="0.3rem"
backgroundColor="var(--background-color)"
rowGap={1}
>
<Text>{t('modelManager.modelMergeHeaderHelp1')}</Text>
<Text fontSize="0.9rem" color="var(--text-color-secondary)">
{t('modelManager.modelMergeHeaderHelp2')}
</Text>
</Flex>
<Flex columnGap={4}>
<IAISelect
label={t('modelManager.modelOne')}
validValues={modelOneList}
onChange={(e) => setModelOne(e.target.value)}
/>
<IAISelect
label={t('modelManager.modelTwo')}
validValues={modelTwoList}
onChange={(e) => setModelTwo(e.target.value)}
/>
<IAISelect
label={t('modelManager.modelThree')}
validValues={modelThreeList}
onChange={(e) => {
if (e.target.value !== 'none') {
setModelThree(e.target.value);
setModelMergeInterp('add_difference');
} else {
setModelThree('none');
setModelMergeInterp('weighted_sum');
}
}}
/>
</Flex>
<IAIInput
label={t('modelManager.mergedModelName')}
value={mergedModelName}
onChange={(e) => setMergedModelName(e.target.value)}
/>
<Flex
flexDir="column"
backgroundColor="var(--background-color)"
padding="1rem 1rem"
borderRadius="0.2rem"
rowGap={2}
>
<IAISlider
label={t('modelManager.alpha')}
min={0.01}
max={0.99}
step={0.01}
value={modelMergeAlpha}
onChange={(v) => setModelMergeAlpha(v)}
withInput
withReset
handleReset={() => setModelMergeAlpha(0.5)}
withSliderMarks
sliderMarkRightOffset={-7}
/>
<Text fontSize="0.9rem" color="var(--text-color-secondary)">
{t('modelManager.modelMergeAlphaHelp')}
</Text>
</Flex>
<Flex
columnGap={4}
backgroundColor="var(--background-color)"
padding="1rem 1rem"
borderRadius="0.2rem"
>
<Text
fontWeight="bold"
fontSize="0.9rem"
color="var(--text-color-secondary)"
>
{t('modelManager.interpolationType')}
</Text>
<RadioGroup
value={modelMergeInterp}
onChange={(
v:
| 'weighted_sum'
| 'sigmoid'
| 'inv_sigmoid'
| 'add_difference'
) => setModelMergeInterp(v)}
>
<Flex columnGap={4}>
{modelThree === 'none' ? (
<>
<Radio value="weighted_sum">weighted_sum</Radio>
<Radio value="sigmoid">sigmoid</Radio>
<Radio value="inv_sigmoid">inv_sigmoid</Radio>
</>
) : (
<Radio value="add_difference">
<Tooltip
label={t(
'modelmanager:modelMergeInterpAddDifferenceHelp'
)}
>
add_difference
</Tooltip>
</Radio>
)}
</Flex>
</RadioGroup>
</Flex>
<Flex
gap={4}
flexDirection="column"
backgroundColor="var(--background-color)"
padding="1rem 1rem"
borderRadius="0.2rem"
>
<Flex columnGap={4}>
<Text
fontWeight="bold"
fontSize="0.9rem"
color="var(--text-color-secondary)"
>
{t('modelManager.mergedModelSaveLocation')}
</Text>
<RadioGroup
value={modelMergeSaveLocType}
onChange={(v: 'root' | 'custom') =>
setModelMergeSaveLocType(v)
}
>
<Flex columnGap={4}>
<Radio value="root">
{t('modelManager.invokeAIFolder')}
</Radio>
<Radio value="custom">{t('modelManager.custom')}</Radio>
</Flex>
</RadioGroup>
</Flex>
{modelMergeSaveLocType === 'custom' && (
<IAIInput
label={t('modelManager.mergedModelCustomSaveLocation')}
value={modelMergeCustomSaveLoc}
onChange={(e) => setModelMergeCustomSaveLoc(e.target.value)}
/>
)}
</Flex>
<IAICheckbox
label={t('modelManager.ignoreMismatch')}
isChecked={modelMergeForce}
onChange={(e) => setModelMergeForce(e.target.checked)}
fontWeight="bold"
/>
<IAIButton
onClick={mergeModelsHandler}
isLoading={isProcessing}
isDisabled={
modelMergeSaveLocType === 'custom' &&
modelMergeCustomSaveLoc === ''
}
className="modal modal-close-btn"
>
{t('modelManager.merge')}
</IAIButton>
</Flex>
</ModalContent>
</Modal>
</>
);
}

View File

@@ -0,0 +1,148 @@
import {
Flex,
ListItem,
Radio,
RadioGroup,
Text,
UnorderedList,
Tooltip,
} from '@chakra-ui/react';
import { convertToDiffusers } from 'app/socketio/actions';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIAlertDialog from 'common/components/IAIAlertDialog';
import IAIButton from 'common/components/IAIButton';
import IAIInput from 'common/components/IAIInput';
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
interface ModelConvertProps {
model: string;
}
export default function ModelConvert(props: ModelConvertProps) {
const { model } = props;
const model_list = useAppSelector(
(state: RootState) => state.system.model_list
);
const retrievedModel = model_list[model];
const dispatch = useAppDispatch();
const { t } = useTranslation();
const isProcessing = useAppSelector(
(state: RootState) => state.system.isProcessing
);
const isConnected = useAppSelector(
(state: RootState) => state.system.isConnected
);
const [saveLocation, setSaveLocation] = useState<string>('same');
const [customSaveLocation, setCustomSaveLocation] = useState<string>('');
useEffect(() => {
setSaveLocation('same');
}, [model]);
const modelConvertCancelHandler = () => {
setSaveLocation('same');
};
const modelConvertHandler = () => {
const modelToConvert = {
model_name: model,
save_location: saveLocation,
custom_location:
saveLocation === 'custom' && customSaveLocation !== ''
? customSaveLocation
: null,
};
dispatch(convertToDiffusers(modelToConvert));
};
return (
<IAIAlertDialog
title={`${t('modelManager.convert')} ${model}`}
acceptCallback={modelConvertHandler}
cancelCallback={modelConvertCancelHandler}
acceptButtonText={`${t('modelManager.convert')}`}
triggerComponent={
<IAIButton
size={'sm'}
aria-label={t('modelManager.convertToDiffusers')}
isDisabled={
retrievedModel.status === 'active' || isProcessing || !isConnected
}
className=" modal-close-btn"
marginRight="2rem"
>
🧨 {t('modelManager.convertToDiffusers')}
</IAIButton>
}
motionPreset="slideInBottom"
>
<Flex flexDirection="column" rowGap={4}>
<Text>{t('modelManager.convertToDiffusersHelpText1')}</Text>
<UnorderedList>
<ListItem>{t('modelManager.convertToDiffusersHelpText2')}</ListItem>
<ListItem>{t('modelManager.convertToDiffusersHelpText3')}</ListItem>
<ListItem>{t('modelManager.convertToDiffusersHelpText4')}</ListItem>
<ListItem>{t('modelManager.convertToDiffusersHelpText5')}</ListItem>
</UnorderedList>
<Text>{t('modelManager.convertToDiffusersHelpText6')}</Text>
</Flex>
<Flex flexDir="column" gap={4}>
<Flex marginTop="1rem" flexDir="column" gap={2}>
<Text fontWeight="bold">
{t('modelManager.convertToDiffusersSaveLocation')}
</Text>
<RadioGroup value={saveLocation} onChange={(v) => setSaveLocation(v)}>
<Flex gap={4}>
<Radio value="same">
<Tooltip label="Save converted model in the same folder">
{t('modelManager.sameFolder')}
</Tooltip>
</Radio>
<Radio value="root">
<Tooltip label="Save converted model in the InvokeAI root folder">
{t('modelManager.invokeRoot')}
</Tooltip>
</Radio>
<Radio value="custom">
<Tooltip label="Save converted model in a custom folder">
{t('modelManager.custom')}
</Tooltip>
</Radio>
</Flex>
</RadioGroup>
</Flex>
{saveLocation === 'custom' && (
<Flex flexDirection="column" rowGap={2}>
<Text
fontWeight="bold"
fontSize="sm"
color="var(--text-color-secondary)"
>
{t('modelManager.customSaveLocation')}
</Text>
<IAIInput
value={customSaveLocation}
onChange={(e) => {
if (e.target.value !== '')
setCustomSaveLocation(e.target.value);
}}
width="25rem"
/>
</Flex>
)}
</Flex>
</IAIAlertDialog>
);
}

View File

@@ -14,6 +14,7 @@ import { systemSelector } from 'features/system/store/systemSelectors';
import type { SystemState } from 'features/system/store/systemSlice';
import { isEqual, map } from 'lodash';
import type { ChangeEvent, ReactNode } from 'react';
import MergeModels from './MergeModels';
const modelListSelector = createSelector(
systemSelector,
@@ -139,7 +140,7 @@ const ModelList = () => {
width="max-content"
fontSize="14"
>
{t('modelmanager:checkpointModels')}
{t('modelManager.checkpointModels')}
</Text>
{ckptModelListItemsToRender}
</Box>
@@ -153,7 +154,7 @@ const ModelList = () => {
width="max-content"
fontSize="14"
>
{t('modelmanager:diffusersModels')}
{t('modelManager.diffusersModels')}
</Text>
{diffusersModelListItemsToRender}
</Box>
@@ -176,39 +177,42 @@ const ModelList = () => {
}, [models, searchText, t, isSelectedFilter]);
return (
<Flex flexDirection={'column'} rowGap="2rem" width="50%" minWidth="50%">
<Flex justifyContent={'space-between'}>
<Text fontSize={'1.4rem'} fontWeight="bold">
{t('modelmanager:availableModels')}
<Flex flexDirection="column" rowGap="2rem" width="50%" minWidth="50%">
<Flex justifyContent="space-between">
<Text fontSize="1.4rem" fontWeight="bold">
{t('modelManager.availableModels')}
</Text>
<AddModel />
<Flex gap={2}>
<AddModel />
<MergeModels />
</Flex>
</Flex>
<IAIInput
onChange={handleSearchFilter}
label={t('modelmanager:search')}
label={t('modelManager.search')}
/>
<Flex
flexDirection={'column'}
flexDirection="column"
gap={1}
maxHeight={window.innerHeight - 360}
overflow={'scroll'}
overflow="scroll"
paddingRight="1rem"
>
<Flex columnGap="0.5rem">
<ModelFilterButton
label={t('modelmanager:allModels')}
label={t('modelManager.allModels')}
onClick={() => setIsSelectedFilter('all')}
isActive={isSelectedFilter === 'all'}
/>
<ModelFilterButton
label={t('modelmanager:checkpointModels')}
label={t('modelManager.checkpointModels')}
onClick={() => setIsSelectedFilter('ckpt')}
isActive={isSelectedFilter === 'ckpt'}
/>
<ModelFilterButton
label={t('modelmanager:diffusersModels')}
label={t('modelManager.diffusersModels')}
onClick={() => setIsSelectedFilter('diffusers')}
isActive={isSelectedFilter === 'diffusers'}
/>

View File

@@ -56,7 +56,7 @@ export default function ModelListItem(props: ModelListItemProps) {
return (
<Flex
alignItems={'center'}
alignItems="center"
padding="0.5rem 0.5rem"
borderRadius="0.2rem"
backgroundColor={name === openModel ? 'var(--accent-color)' : ''}
@@ -69,47 +69,48 @@ export default function ModelListItem(props: ModelListItemProps) {
>
<Box onClick={openModelHandler} cursor="pointer">
<Tooltip label={description} hasArrow placement="bottom">
<Text fontWeight={'bold'}>{name}</Text>
<Text fontWeight="bold">{name}</Text>
</Tooltip>
</Box>
<Spacer onClick={openModelHandler} cursor="pointer" />
<Flex gap={2} alignItems="center">
<Text color={statusTextColor()}>{status}</Text>
<Button
size={'sm'}
size="sm"
onClick={handleChangeModel}
isDisabled={status === 'active' || isProcessing || !isConnected}
className="modal-close-btn"
>
{t('modelmanager:load')}
{t('modelManager.load')}
</Button>
<IAIIconButton
icon={<EditIcon />}
size={'sm'}
size="sm"
onClick={openModelHandler}
aria-label="Modify Config"
isDisabled={status === 'active' || isProcessing || !isConnected}
className=" modal-close-btn"
/>
<IAIAlertDialog
title={t('modelmanager:deleteModel')}
title={t('modelManager.deleteModel')}
acceptCallback={handleModelDelete}
acceptButtonText={t('modelmanager:delete')}
acceptButtonText={t('modelManager.delete')}
triggerComponent={
<IAIIconButton
icon={<DeleteIcon />}
size={'sm'}
aria-label={t('modelmanager:deleteConfig')}
size="sm"
aria-label={t('modelManager.deleteConfig')}
isDisabled={status === 'active' || isProcessing || !isConnected}
className=" modal-close-btn"
style={{ backgroundColor: 'var(--btn-delete-image)' }}
/>
}
>
<Flex rowGap={'1rem'} flexDirection="column">
<p style={{ fontWeight: 'bold' }}>{t('modelmanager:deleteMsg1')}</p>
<Flex rowGap="1rem" flexDirection="column">
<p style={{ fontWeight: 'bold' }}>{t('modelManager.deleteMsg1')}</p>
<p style={{ color: 'var(--text-color-secondary' }}>
{t('modelmanager:deleteMsg2')}
{t('modelManager.deleteMsg2')}
</p>
</Flex>
</IAIAlertDialog>

View File

@@ -56,13 +56,9 @@ export default function ModelManagerModal({
<ModalContent className="modal" fontFamily="Inter">
<ModalCloseButton className="modal-close-btn" />
<ModalHeader fontWeight="bold">
{t('modelmanager:modelManager')}
{t('modelManager.modelManager')}
</ModalHeader>
<Flex
padding={'0 1.5rem 1.5rem 1.5rem'}
width="100%"
columnGap={'2rem'}
>
<Flex padding="0 1.5rem 1.5rem 1.5rem" width="100%" columnGap="2rem">
<ModelList />
{openModel && model_list[openModel]['format'] === 'diffusers' ? (
<DiffusersModelEdit />

View File

@@ -3,7 +3,16 @@ import IAICheckbox from 'common/components/IAICheckbox';
import IAIIconButton from 'common/components/IAIIconButton';
import React from 'react';
import { Box, Flex, FormControl, HStack, Text, VStack } from '@chakra-ui/react';
import {
Box,
Flex,
FormControl,
HStack,
Radio,
RadioGroup,
Text,
VStack,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors';
@@ -43,18 +52,18 @@ function ModelExistsTag() {
const { t } = useTranslation();
return (
<Box
position={'absolute'}
position="absolute"
zIndex={2}
right={4}
top={4}
fontSize="0.7rem"
fontWeight={'bold'}
backgroundColor={'var(--accent-color)'}
padding={'0.2rem 0.5rem'}
fontWeight="bold"
backgroundColor="var(--accent-color)"
padding="0.2rem 0.5rem"
borderRadius="0.2rem"
alignItems={'center'}
alignItems="center"
>
{t('modelmanager:modelExists')}
{t('modelManager.modelExists')}
</Box>
);
}
@@ -87,7 +96,7 @@ function SearchModelEntry({
value={model.name}
label={
<>
<VStack alignItems={'start'}>
<VStack alignItems="start">
<p style={{ fontWeight: 'bold' }}>{model.name}</p>
<p style={{ fontStyle: 'italic' }}>{model.location}</p>
</VStack>
@@ -96,9 +105,9 @@ function SearchModelEntry({
isChecked={modelsToAdd.includes(model.name)}
isDisabled={existingModels.includes(model.location)}
onChange={foundModelsChangeHandler}
padding={'1rem'}
backgroundColor={'var(--background-color)'}
borderRadius={'0.5rem'}
padding="1rem"
backgroundColor="var(--background-color)"
borderRadius="0.5rem"
_checked={{
backgroundColor: 'var(--accent-color)',
color: 'var(--text-color)',
@@ -135,6 +144,8 @@ export default function SearchModels() {
);
const [modelsToAdd, setModelsToAdd] = React.useState<string[]>([]);
const [modelType, setModelType] = React.useState<string>('v1');
const [pathToConfig, setPathToConfig] = React.useState<string>('');
const resetSearchModelHandler = () => {
dispatch(setSearchFolder(null));
@@ -167,11 +178,19 @@ export default function SearchModels() {
const modelsToBeAdded = foundModels?.filter((foundModel) =>
modelsToAdd.includes(foundModel.name)
);
const configFiles = {
v1: 'configs/stable-diffusion/v1-inference.yaml',
v2: 'configs/stable-diffusion/v2-inference-v.yaml',
inpainting: 'configs/stable-diffusion/v1-inpainting-inference.yaml',
custom: pathToConfig,
};
modelsToBeAdded?.forEach((model) => {
const modelFormat = {
name: model.name,
description: '',
config: 'configs/stable-diffusion/v1-inference.yaml',
config: configFiles[modelType as keyof typeof configFiles],
weights: model.location,
vae: '',
width: 512,
@@ -224,12 +243,12 @@ export default function SearchModels() {
<>
{searchFolder ? (
<Flex
flexDirection={'column'}
padding={'1rem'}
backgroundColor={'var(--background-color)'}
flexDirection="column"
padding="1rem"
backgroundColor="var(--background-color)"
borderRadius="0.5rem"
rowGap={'0.5rem'}
position={'relative'}
rowGap="0.5rem"
position="relative"
>
<p
style={{
@@ -241,7 +260,7 @@ export default function SearchModels() {
borderRadius: '0.2rem',
}}
>
{t('modelmanager:checkpointFolder')}
{t('modelManager.checkpointFolder')}
</p>
<p
style={{ fontWeight: 'bold', fontSize: '0.8rem', maxWidth: '80%' }}
@@ -249,19 +268,19 @@ export default function SearchModels() {
{searchFolder}
</p>
<IAIIconButton
aria-label={t('modelmanager:scanAgain')}
tooltip={t('modelmanager:scanAgain')}
aria-label={t('modelManager.scanAgain')}
tooltip={t('modelManager.scanAgain')}
icon={<BiReset />}
position={'absolute'}
position="absolute"
right={16}
fontSize={18}
disabled={isProcessing}
onClick={() => dispatch(searchForModels(searchFolder))}
/>
<IAIIconButton
aria-label={t('modelmanager:clearCheckpointFolder')}
aria-label={t('modelManager.clearCheckpointFolder')}
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
position={'absolute'}
position="absolute"
right={5}
onClick={resetSearchModelHandler}
/>
@@ -284,13 +303,13 @@ export default function SearchModels() {
type="text"
width="lg"
size="md"
label={t('modelmanager:checkpointFolder')}
label={t('modelManager.checkpointFolder')}
/>
</FormControl>
<IAIIconButton
icon={<MdFindInPage />}
aria-label={t('modelmanager:findModels')}
tooltip={t('modelmanager:findModels')}
aria-label={t('modelManager.findModels')}
tooltip={t('modelManager.findModels')}
type="submit"
disabled={isProcessing}
/>
@@ -300,31 +319,31 @@ export default function SearchModels() {
</Formik>
)}
{foundModels && (
<Flex flexDirection={'column'} rowGap={'1rem'}>
<Flex justifyContent={'space-between'} alignItems="center">
<Flex flexDirection="column" rowGap="1rem">
<Flex justifyContent="space-between" alignItems="center">
<p>
{t('modelmanager:modelsFound')}: {foundModels.length}
{t('modelManager.modelsFound')}: {foundModels.length}
</p>
<p>
{t('modelmanager:selected')}: {modelsToAdd.length}
{t('modelManager.selected')}: {modelsToAdd.length}
</p>
</Flex>
<Flex columnGap={'0.5rem'} justifyContent={'space-between'}>
<Flex columnGap={'0.5rem'}>
<Flex columnGap="0.5rem" justifyContent="space-between">
<Flex columnGap="0.5rem">
<IAIButton
isDisabled={modelsToAdd.length === foundModels.length}
onClick={addAllToSelected}
>
{t('modelmanager:selectAll')}
{t('modelManager.selectAll')}
</IAIButton>
<IAIButton
isDisabled={modelsToAdd.length === 0}
onClick={removeAllFromSelected}
>
{t('modelmanager:deselectAll')}
{t('modelManager.deselectAll')}
</IAIButton>
<IAICheckbox
label={t('modelmanager:showExisting')}
label={t('modelManager.showExisting')}
isChecked={shouldShowExistingModelsInSearch}
onChange={() =>
dispatch(
@@ -343,9 +362,58 @@ export default function SearchModels() {
modelsToAdd.length > 0 ? 'var(--accent-color) !important' : ''
}
>
{t('modelmanager:addSelected')}
{t('modelManager.addSelected')}
</IAIButton>
</Flex>
<Flex
gap={4}
backgroundColor="var(--background-color)"
padding="1rem 1rem"
borderRadius="0.2rem"
flexDirection="column"
>
<Flex gap={4}>
<Text fontWeight="bold" color="var(--text-color-secondary)">
Pick Model Type:
</Text>
<RadioGroup
value={modelType}
onChange={(v) => setModelType(v)}
defaultValue="v1"
name="model_type"
>
<Flex gap={4}>
<Radio value="v1">{t('modelManager.v1')}</Radio>
<Radio value="v2">{t('modelManager.v2')}</Radio>
<Radio value="inpainting">
{t('modelManager.inpainting')}
</Radio>
<Radio value="custom">{t('modelManager.customConfig')}</Radio>
</Flex>
</RadioGroup>
</Flex>
{modelType === 'custom' && (
<Flex flexDirection="column" rowGap={2}>
<Text
fontWeight="bold"
fontSize="sm"
color="var(--text-color-secondary)"
>
{t('modelManager.pathToCustomConfig')}
</Text>
<IAIInput
value={pathToConfig}
onChange={(e) => {
if (e.target.value !== '') setPathToConfig(e.target.value);
}}
width="42.5rem"
/>
</Flex>
)}
</Flex>
<Flex
rowGap="1rem"
flexDirection="column"
@@ -369,7 +437,7 @@ export default function SearchModels() {
marginTop="1rem"
width="max-content"
>
{t('modelmanager:selectAndAdd')}
{t('modelManager.selectAndAdd')}
</Text>
)
) : (
@@ -381,7 +449,7 @@ export default function SearchModels() {
textAlign="center"
backgroundColor="var(--status-bad-color)"
>
{t('modelmanager:noModelsFound')}
{t('modelManager.noModelsFound')}
</Text>
)}

View File

@@ -7,6 +7,8 @@
div {
background-color: var(--progress-bar-color);
transition: width 0.2s ease-in-out;
&[data-indeterminate] {
background-color: unset;
background-image: linear-gradient(

View File

@@ -14,7 +14,7 @@ import {
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants';
import { RootState } from 'app/store';
import { type RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect';
@@ -27,9 +27,14 @@ import {
setShouldConfirmOnDelete,
setShouldDisplayGuides,
setShouldDisplayInProgressType,
type SystemState,
} from 'features/system/store/systemSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { setShouldUseCanvasBetaLayout } from 'features/ui/store/uiSlice';
import {
setShouldUseCanvasBetaLayout,
setShouldUseSliders,
} from 'features/ui/store/uiSlice';
import { type UIState } from 'features/ui/store/uiTypes';
import { isEqual, map } from 'lodash';
import { persistor } from 'persistor';
import { ChangeEvent, cloneElement, ReactElement } from 'react';
@@ -37,7 +42,7 @@ import { useTranslation } from 'react-i18next';
const selector = createSelector(
[systemSelector, uiSelector],
(system, ui) => {
(system: SystemState, ui: UIState) => {
const {
shouldDisplayInProgressType,
shouldConfirmOnDelete,
@@ -47,7 +52,7 @@ const selector = createSelector(
enableImageDebugging,
} = system;
const { shouldUseCanvasBetaLayout } = ui;
const { shouldUseCanvasBetaLayout, shouldUseSliders } = ui;
return {
shouldDisplayInProgressType,
@@ -57,6 +62,7 @@ const selector = createSelector(
saveIntermediatesInterval,
enableImageDebugging,
shouldUseCanvasBetaLayout,
shouldUseSliders,
};
},
{
@@ -100,6 +106,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
saveIntermediatesInterval,
enableImageDebugging,
shouldUseCanvasBetaLayout,
shouldUseSliders,
} = useAppSelector(selector);
/**
@@ -133,7 +140,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
<ModalOverlay />
<ModalContent className="modal settings-modal">
<ModalHeader className="settings-modal-header">
{t('common:settingsLabel')}
{t('common.settingsLabel')}
</ModalHeader>
<ModalCloseButton className="modal-close-btn" />
<ModalBody className="settings-modal-content">
@@ -143,7 +150,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
style={{ gridAutoFlow: 'row', rowGap: '0.5rem' }}
>
<IAISelect
label={t('settings:displayInProgress')}
label={t('settings.displayInProgress')}
validValues={IN_PROGRESS_IMAGE_TYPES}
value={shouldDisplayInProgressType}
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
@@ -156,7 +163,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
/>
{shouldDisplayInProgressType === 'full-res' && (
<IAINumberInput
label={t('settings:saveSteps')}
label={t('settings.saveSteps')}
min={1}
max={steps}
step={1}
@@ -169,7 +176,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
</div>
<IAISwitch
styleClass="settings-modal-item"
label={t('settings:confirmOnDelete')}
label={t('settings.confirmOnDelete')}
isChecked={shouldConfirmOnDelete}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldConfirmOnDelete(e.target.checked))
@@ -177,7 +184,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
/>
<IAISwitch
styleClass="settings-modal-item"
label={t('settings:displayHelpIcons')}
label={t('settings.displayHelpIcons')}
isChecked={shouldDisplayGuides}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldDisplayGuides(e.target.checked))
@@ -185,19 +192,27 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
/>
<IAISwitch
styleClass="settings-modal-item"
label={t('settings:useCanvasBeta')}
label={t('settings.useCanvasBeta')}
isChecked={shouldUseCanvasBetaLayout}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldUseCanvasBetaLayout(e.target.checked))
}
/>
<IAISwitch
styleClass="settings-modal-item"
label={t('settings.useSlidersForAll')}
isChecked={shouldUseSliders}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldUseSliders(e.target.checked))
}
/>
</div>
<div className="settings-modal-items">
<h2 style={{ fontWeight: 'bold' }}>Developer</h2>
<IAISwitch
styleClass="settings-modal-item"
label={t('settings:enableImageDebugging')}
label={t('settings.enableImageDebugging')}
isChecked={enableImageDebugging}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setEnableImageDebugging(e.target.checked))
@@ -206,18 +221,18 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
</div>
<div className="settings-modal-reset">
<Heading size={'md'}>{t('settings:resetWebUI')}</Heading>
<Heading size="md">{t('settings.resetWebUI')}</Heading>
<Button colorScheme="red" onClick={handleClickResetWebUI}>
{t('settings:resetWebUI')}
{t('settings.resetWebUI')}
</Button>
<Text>{t('settings:resetWebUIDesc1')}</Text>
<Text>{t('settings:resetWebUIDesc2')}</Text>
<Text>{t('settings.resetWebUIDesc1')}</Text>
<Text>{t('settings.resetWebUIDesc2')}</Text>
</div>
</ModalBody>
<ModalFooter>
<Button onClick={onSettingsModalClose} className="modal-close-btn">
{t('common:close')}
{t('common.close')}
</Button>
</ModalFooter>
</ModalContent>
@@ -232,9 +247,9 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
<ModalOverlay bg="blackAlpha.300" backdropFilter="blur(40px)" />
<ModalContent>
<ModalBody pb={6} pt={6}>
<Flex justifyContent={'center'}>
<Text fontSize={'lg'}>
<Text>{t('settings:resetComplete')}</Text>
<Flex justifyContent="center">
<Text fontSize="lg">
<Text>{t('settings.resetComplete')}</Text>
</Text>
</Flex>
</ModalBody>

View File

@@ -54,9 +54,9 @@ const SiteHeader = () => {
<ModelManagerModal>
<IAIIconButton
aria-label={t('modelmanager:modelManager')}
tooltip={t('modelmanager:modelManager')}
size={'sm'}
aria-label={t('modelManager.modelManager')}
tooltip={t('modelManager.modelManager')}
size="sm"
variant="link"
data-variant="link"
fontSize={20}
@@ -66,9 +66,9 @@ const SiteHeader = () => {
<HotkeysModal>
<IAIIconButton
aria-label={t('common:hotkeysLabel')}
tooltip={t('common:hotkeysLabel')}
size={'sm'}
aria-label={t('common.hotkeysLabel')}
tooltip={t('common.hotkeysLabel')}
size="sm"
variant="link"
data-variant="link"
fontSize={20}
@@ -81,12 +81,12 @@ const SiteHeader = () => {
<LanguagePicker />
<IAIIconButton
aria-label={t('common:reportBugLabel')}
tooltip={t('common:reportBugLabel')}
aria-label={t('common.reportBugLabel')}
tooltip={t('common.reportBugLabel')}
variant="link"
data-variant="link"
fontSize={20}
size={'sm'}
size="sm"
icon={
<Link isExternal href="http://github.com/invoke-ai/InvokeAI/issues">
<FaBug />
@@ -95,12 +95,12 @@ const SiteHeader = () => {
/>
<IAIIconButton
aria-label={t('common:githubLabel')}
tooltip={t('common:githubLabel')}
aria-label={t('common.githubLabel')}
tooltip={t('common.githubLabel')}
variant="link"
data-variant="link"
fontSize={20}
size={'sm'}
size="sm"
icon={
<Link isExternal href="http://github.com/invoke-ai/InvokeAI">
<FaGithub />
@@ -109,12 +109,12 @@ const SiteHeader = () => {
/>
<IAIIconButton
aria-label={t('common:discordLabel')}
tooltip={t('common:discordLabel')}
aria-label={t('common.discordLabel')}
tooltip={t('common.discordLabel')}
variant="link"
data-variant="link"
fontSize={20}
size={'sm'}
size="sm"
icon={
<Link isExternal href="https://discord.gg/ZmtBAhwWhy">
<FaDiscord />
@@ -124,12 +124,12 @@ const SiteHeader = () => {
<SettingsModal>
<IAIIconButton
aria-label={t('common:settingsLabel')}
tooltip={t('common:settingsLabel')}
aria-label={t('common.settingsLabel')}
tooltip={t('common.settingsLabel')}
variant="link"
data-variant="link"
fontSize={22}
size={'sm'}
size="sm"
icon={<MdSettings />}
/>
</SettingsModal>

View File

@@ -47,11 +47,11 @@ const StatusIndicator = () => {
let statusMessage = currentStatus;
const intermediateStatuses = [
t('common:statusGenerating'),
t('common:statusPreparing'),
t('common:statusSavingImage'),
t('common:statusRestoringFaces'),
t('common:statusUpscaling'),
t('common.statusGenerating'),
t('common.statusPreparing'),
t('common.statusSavingImage'),
t('common.statusRestoringFaces'),
t('common.statusUpscaling'),
];
if (intermediateStatuses.includes(statusMessage)) {

View File

@@ -19,9 +19,9 @@ export default function ThemeChanger() {
);
const THEMES = {
dark: t('common:darkTheme'),
light: t('common:lightTheme'),
green: t('common:greenTheme'),
dark: t('common.darkTheme'),
light: t('common.lightTheme'),
green: t('common.greenTheme'),
};
useEffect(() => {
@@ -46,7 +46,7 @@ export default function ThemeChanger() {
width: '6rem',
}}
leftIcon={currentTheme === theme ? <FaCheck /> : undefined}
size={'sm'}
size="sm"
onClick={() => handleChangeTheme(theme)}
key={theme}
>
@@ -63,8 +63,8 @@ export default function ThemeChanger() {
trigger="hover"
triggerComponent={
<IAIIconButton
aria-label={t('common:themeLabel')}
size={'sm'}
aria-label={t('common.themeLabel')}
size="sm"
variant="link"
data-variant="link"
fontSize={20}
@@ -72,7 +72,7 @@ export default function ThemeChanger() {
/>
}
>
<VStack align={'stretch'}>{renderThemeOptions()}</VStack>
<VStack align="stretch">{renderThemeOptions()}</VStack>
</IAIPopover>
);
}

View File

@@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import { isEqual, reduce } from 'lodash';
import { isEqual, reduce, pickBy } from 'lodash';
export const systemSelector = (state: RootState) => state.system;
@@ -28,3 +28,23 @@ export const activeModelSelector = createSelector(
},
}
);
export const diffusersModelsSelector = createSelector(
systemSelector,
(system) => {
const { model_list } = system;
const diffusersModels = pickBy(model_list, (model, key) => {
if (model.format === 'diffusers') {
return { name: key, ...model };
}
});
return diffusersModels;
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@@ -75,7 +75,7 @@ const initialSystemState: SystemState = {
currentIteration: 0,
totalIterations: 0,
currentStatus: i18n.isInitialized
? i18n.t('common:statusDisconnected')
? i18n.t('common.statusDisconnected')
: 'Disconnected',
currentStatusHasSteps: false,
model: '',
@@ -128,15 +128,15 @@ export const systemSlice = createSlice({
state.currentIteration = 0;
state.totalIterations = 0;
state.currentStatusHasSteps = false;
state.currentStatus = i18n.t('common:statusError');
state.currentStatus = i18n.t('common.statusError');
state.wasErrorSeen = false;
},
errorSeen: (state) => {
state.hasError = false;
state.wasErrorSeen = true;
state.currentStatus = state.isConnected
? i18n.t('common:statusConnected')
: i18n.t('common:statusDisconnected');
? i18n.t('common.statusConnected')
: i18n.t('common.statusDisconnected');
},
addLogEntry: (
state,
@@ -197,7 +197,7 @@ export const systemSlice = createSlice({
state.currentIteration = 0;
state.totalIterations = 0;
state.currentStatusHasSteps = false;
state.currentStatus = i18n.t('common:statusProcessingCanceled');
state.currentStatus = i18n.t('common.statusProcessingCanceled');
},
generationRequested: (state) => {
state.isProcessing = true;
@@ -207,7 +207,7 @@ export const systemSlice = createSlice({
state.currentIteration = 0;
state.totalIterations = 0;
state.currentStatusHasSteps = false;
state.currentStatus = i18n.t('common:statusPreparing');
state.currentStatus = i18n.t('common.statusPreparing');
},
setModelList: (
state,
@@ -219,7 +219,19 @@ export const systemSlice = createSlice({
state.isCancelable = action.payload;
},
modelChangeRequested: (state) => {
state.currentStatus = i18n.t('common:statusLoadingModel');
state.currentStatus = i18n.t('common.statusLoadingModel');
state.isCancelable = false;
state.isProcessing = true;
state.currentStatusHasSteps = false;
},
modelConvertRequested: (state) => {
state.currentStatus = i18n.t('common.statusConvertingModel');
state.isCancelable = false;
state.isProcessing = true;
state.currentStatusHasSteps = false;
},
modelMergingRequested: (state) => {
state.currentStatus = i18n.t('common.statusMergingModels');
state.isCancelable = false;
state.isProcessing = true;
state.currentStatusHasSteps = false;
@@ -281,6 +293,8 @@ export const {
setModelList,
setIsCancelable,
modelChangeRequested,
modelConvertRequested,
modelMergingRequested,
setSaveIntermediatesInterval,
setEnableImageDebugging,
generationRequested,

View File

@@ -0,0 +1,26 @@
import { Flex } from '@chakra-ui/react';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ParametersAccordion from 'features/parameters/components/ParametersAccordion';
import { useTranslation } from 'react-i18next';
export default function ImageToImageOptions() {
const { t } = useTranslation();
const imageToImageAccordionItems = {
imageToImage: {
header: `${t('parameters.imageToImage')}`,
feature: undefined,
content: (
<Flex gap={2} flexDir="column">
<ImageToImageStrength
label={t('parameters.img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ImageFit />
</Flex>
),
},
};
return <ParametersAccordion accordionInfo={imageToImageAccordionItems} />;
}

View File

@@ -1,10 +1,7 @@
import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ImageToImageOutputSettings from 'features/parameters/components/AdvancedParameters/Output/ImageToImageOutputSettings';
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
@@ -16,57 +13,44 @@ import ParametersAccordion from 'features/parameters/components/ParametersAccord
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
import { setHiresFix } from 'features/parameters/store/postprocessingSlice';
import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import ImageToImageOptions from './ImageToImageOptions';
export default function ImageToImagePanel() {
const { t } = useTranslation();
const imageToImageAccordions = {
seed: {
header: `${t('parameters:seed')}`,
header: `${t('parameters.seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
variations: {
header: `${t('parameters:variations')}`,
header: `${t('parameters.variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsSettings />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
face_restore: {
header: `${t('parameters:faceRestoration')}`,
header: `${t('parameters.faceRestoration')}`,
feature: Feature.FACE_CORRECTION,
content: <FaceRestoreSettings />,
additionalHeaderComponents: <FaceRestoreToggle />,
},
upscale: {
header: `${t('parameters:upscaling')}`,
header: `${t('parameters.upscaling')}`,
feature: Feature.UPSCALE,
content: <UpscaleSettings />,
additionalHeaderComponents: <UpscaleToggle />,
},
other: {
header: `${t('parameters:otherOptions')}`,
header: `${t('parameters.otherOptions')}`,
feature: Feature.OTHER,
content: <ImageToImageOutputSettings />,
},
};
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
useEffect(() => {
if (activeTabName === 'img2img') {
const handleChangeHiresFix = () => dispatch(setHiresFix(false));
handleChangeHiresFix();
}
}, [activeTabName, dispatch]);
return (
<InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem">
@@ -75,11 +59,7 @@ export default function ImageToImagePanel() {
</Flex>
<ProcessButtons />
<MainSettings />
<ImageToImageStrength
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ImageFit />
<ImageToImageOptions />
<ParametersAccordion accordionInfo={imageToImageAccordions} />
</InvokeOptionsPanel>
);

View File

@@ -18,8 +18,8 @@ export default function InitImagePreview() {
const alertMissingInitImage = () => {
toast({
title: t('toast:parametersFailed'),
description: t('toast:parametersFailedDesc'),
title: t('toast.parametersFailed'),
description: t('toast.parametersFailedDesc'),
status: 'error',
isClosable: true,
});
@@ -29,15 +29,15 @@ export default function InitImagePreview() {
return (
<>
<div className="init-image-preview-header">
<h2>{t('parameters:initialImage')}</h2>
<h2>{t('parameters.initialImage')}</h2>
<ImageUploaderIconButton />
</div>
{initialImage && (
<div className="init-image-preview">
<Image
fit={'contain'}
maxWidth={'100%'}
maxHeight={'100%'}
fit="contain"
maxWidth="100%"
maxHeight="100%"
src={
typeof initialImage === 'string' ? initialImage : initialImage.url
}

View File

@@ -9,10 +9,10 @@ export default function InitialImageOverlay() {
return initialImage ? (
<Image
fit={'contain'}
fit="contain"
src={typeof initialImage === 'string' ? initialImage : initialImage.url}
rounded={'md'}
className={'checkerboard'}
rounded="md"
className="checkerboard"
/>
) : null;
}

View File

@@ -32,7 +32,7 @@
.parameters-panel {
display: flex;
flex-direction: column;
row-gap: 1rem;
row-gap: 0.5rem;
height: 100%;
@include HideScrollbar;
background-color: var(--background-color);

View File

@@ -31,44 +31,44 @@ export interface InvokeTabInfo {
export const tabDict: Record<InvokeTabName, InvokeTabInfo> = {
txt2img: {
title: <TextToImageIcon fill={'black'} boxSize={'2.5rem'} />,
title: <TextToImageIcon fill="black" boxSize="2.5rem" />,
workarea: <TextToImageWorkarea />,
tooltip: 'Text To Image',
},
img2img: {
title: <ImageToImageIcon fill={'black'} boxSize={'2.5rem'} />,
title: <ImageToImageIcon fill="black" boxSize="2.5rem" />,
workarea: <ImageToImageWorkarea />,
tooltip: 'Image To Image',
},
unifiedCanvas: {
title: <UnifiedCanvasIcon fill={'black'} boxSize={'2.5rem'} />,
title: <UnifiedCanvasIcon fill="black" boxSize="2.5rem" />,
workarea: <UnifiedCanvasWorkarea />,
tooltip: 'Unified Canvas',
},
nodes: {
title: <NodesIcon fill={'black'} boxSize={'2.5rem'} />,
title: <NodesIcon fill="black" boxSize="2.5rem" />,
workarea: <NodesWIP />,
tooltip: 'Nodes',
},
postprocess: {
title: <PostprocessingIcon fill={'black'} boxSize={'2.5rem'} />,
title: <PostprocessingIcon fill="black" boxSize="2.5rem" />,
workarea: <PostProcessingWIP />,
tooltip: 'Post Processing',
},
training: {
title: <TrainingIcon fill={'black'} boxSize={'2.5rem'} />,
title: <TrainingIcon fill="black" boxSize="2.5rem" />,
workarea: <TrainingWIP />,
tooltip: 'Training',
},
};
function updateTabTranslations() {
tabDict.txt2img.tooltip = i18n.t('common:text2img');
tabDict.img2img.tooltip = i18n.t('common:img2img');
tabDict.unifiedCanvas.tooltip = i18n.t('common:unifiedCanvas');
tabDict.nodes.tooltip = i18n.t('common:nodes');
tabDict.postprocess.tooltip = i18n.t('common:postProcessing');
tabDict.training.tooltip = i18n.t('common:training');
tabDict.txt2img.tooltip = i18n.t('common.text2img');
tabDict.img2img.tooltip = i18n.t('common.img2img');
tabDict.unifiedCanvas.tooltip = i18n.t('common.unifiedCanvas');
tabDict.nodes.tooltip = i18n.t('common.nodes');
tabDict.postprocess.tooltip = i18n.t('common.postProcessing');
tabDict.training.tooltip = i18n.t('common.training');
}
export default function InvokeTabs() {
@@ -122,7 +122,7 @@ export default function InvokeTabs() {
key={key}
hasArrow
label={tabDict[key as keyof typeof tabDict].tooltip}
placement={'right'}
placement="right"
>
<Tab>{tabDict[key as keyof typeof tabDict].title}</Tab>
</Tooltip>
@@ -147,7 +147,7 @@ export default function InvokeTabs() {
<Tabs
isLazy
className="app-tabs"
variant={'unstyled'}
variant="unstyled"
defaultIndex={activeTab}
index={activeTab}
onChange={(index: number) => {

View File

@@ -21,30 +21,30 @@ export default function TextToImagePanel() {
const textToImageAccordions = {
seed: {
header: `${t('parameters:seed')}`,
header: `${t('parameters.seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
variations: {
header: `${t('parameters:variations')}`,
header: `${t('parameters.variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsSettings />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
face_restore: {
header: `${t('parameters:faceRestoration')}`,
header: `${t('parameters.faceRestoration')}`,
feature: Feature.FACE_CORRECTION,
content: <FaceRestoreSettings />,
additionalHeaderComponents: <FaceRestoreToggle />,
},
upscale: {
header: `${t('parameters:upscaling')}`,
header: `${t('parameters.upscaling')}`,
feature: Feature.UPSCALE,
content: <UpscaleSettings />,
additionalHeaderComponents: <UpscaleToggle />,
},
other: {
header: `${t('parameters:otherOptions')}`,
header: `${t('parameters.otherOptions')}`,
feature: Feature.OTHER,
content: <OutputSettings />,
},

View File

@@ -45,21 +45,16 @@ const UnifiedCanvasDisplayBeta = () => {
}, [dispatch]);
return (
<div className={'workarea-single-view'}>
<div className="workarea-single-view">
<Flex
flexDirection={'row'}
flexDirection="row"
width="100%"
height="100%"
columnGap={'1rem'}
columnGap="1rem"
padding="1rem"
>
<UnifiedCanvasToolbarBeta />
<Flex
width="100%"
height="100%"
flexDirection={'column'}
rowGap={'1rem'}
>
<Flex width="100%" height="100%" flexDirection="column" rowGap="1rem">
<UnifiedCanvasToolSettingsBeta />
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
</Flex>

View File

@@ -4,7 +4,7 @@ import UnifiedCanvasLimitStrokesToBox from './UnifiedCanvasLimitStrokesToBox';
export default function UnifiedCanvasBaseBrushSettings() {
return (
<Flex gap={'1rem'} alignItems="center">
<Flex gap="1rem" alignItems="center">
<UnifiedCanvasBrushSettings />
<UnifiedCanvasLimitStrokesToBox />
</Flex>

View File

@@ -4,7 +4,7 @@ import UnifiedCanvasColorPicker from './UnifiedCanvasColorPicker';
export default function UnifiedCanvasBrushSettings() {
return (
<Flex columnGap={'1rem'} alignItems="center">
<Flex columnGap="1rem" alignItems="center">
<UnifiedCanvasBrushSize />
<UnifiedCanvasColorPicker />
</Flex>

Some files were not shown because too many files have changed in this diff Show More