Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
90a71cd
fix(ui): do not add save-to-canvas generations to board
psychedelicious Sep 18, 2024
759b87b
feat(ui): remove auto-save canvas setting
psychedelicious Sep 19, 2024
fc1b7b3
feat(ui): save staged image to gallery saves to the selected board
psychedelicious Sep 19, 2024
1dd51c8
fix(app): do not show intermediate images for board thumbnail
psychedelicious Sep 19, 2024
b2a35b1
fix(app): exclude intermediate images from board image count
psychedelicious Sep 19, 2024
3e5a9f3
feat(ui): toolbar layout
psychedelicious Sep 19, 2024
c9f272e
fix(ui): disable canvas hotkeys when viewer is open
psychedelicious Sep 19, 2024
471061d
feat(ui): add swatches to color picker
psychedelicious Sep 19, 2024
a6fc96f
feat(ui): add `d` hotkey to set fill color to white
psychedelicious Sep 19, 2024
375635a
feat(ui): rework canvas zoom UI in toolbar
psychedelicious Sep 19, 2024
96a6386
feat(ui): tweak toolbar layout
psychedelicious Sep 19, 2024
89631e9
feat(ui): tweak toolbar layout more
psychedelicious Sep 19, 2024
6af4b45
fix(ui): "redo" -> "Redo"
psychedelicious Sep 19, 2024
5b67977
feat(ui): add dedicated undo/redo buttons to canvas toolbar
psychedelicious Sep 19, 2024
f606a3a
feat(ui): show alert on viewer when staging on canvas
psychedelicious Sep 19, 2024
5490151
feat(ui): reworked send-to toggle
psychedelicious Sep 19, 2024
490db78
feat(ui): differentiate add layer menu buttons and add individual lay…
psychedelicious Sep 19, 2024
d95684f
feat(ui): add "new canvas from image" to image context menu
psychedelicious Sep 19, 2024
de8c984
fix(ui): node execution states not updating during generation
psychedelicious Sep 19, 2024
e94a46b
fix(ui): missing translation on node footer
psychedelicious Sep 19, 2024
22f91ed
chore(ui): lint
psychedelicious Sep 19, 2024
60b755a
tidy(ui): do not use zod validator in image usage util - just use bui…
psychedelicious Sep 19, 2024
5d46ca9
feat(ui): create canvas metadata zod schema
psychedelicious Sep 19, 2024
16b4a25
feat(ui): embed canvas metadata in send-to-gallery generations
psychedelicious Sep 19, 2024
6c4c77e
feat(ui): support embedding metadata when rasterizing composite layer
psychedelicious Sep 19, 2024
0ae8af7
feat(ui): support recalling canvas metadata
psychedelicious Sep 19, 2024
3e2beb0
fix(ui): force refetch of image when validating metadata
psychedelicious Sep 19, 2024
559384f
fix(ui): do not hide entities when locked
psychedelicious Sep 19, 2024
3ab5ff0
fix(ui): re-render tool when entity interaction state changes
psychedelicious Sep 19, 2024
65f1432
fix(ui): overflow on smaller screens
psychedelicious Sep 19, 2024
2c6166c
fix(ui): clear last progress event on invocation/queue item complete
psychedelicious Sep 19, 2024
3f74095
feat(ui): style send-to toggle like it was previously
psychedelicious Sep 19, 2024
167f225
feat(ui): lock down bbox while staging
psychedelicious Sep 19, 2024
dcb116f
fix(ui): mixed up gallery nav hotkeys
psychedelicious Sep 19, 2024
d6e9153
fix(ui): use updated algo to calc images per row in gallery for hotke…
psychedelicious Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ def get_image_count_for_board(self, board_id: str) -> int:
self._lock.acquire()
self._cursor.execute(
"""--sql
SELECT COUNT(*) FROM board_images WHERE board_id = ?;
SELECT COUNT(*)
FROM board_images
INNER JOIN images ON board_images.image_name = images.image_name
WHERE images.is_intermediate = FALSE
AND board_images.board_id = ?;
""",
(board_id,),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ def get_most_recent_image_for_board(self, board_id: str) -> Optional[ImageRecord
FROM images
JOIN board_images ON images.image_name = board_images.image_name
WHERE board_images.board_id = ?
AND images.is_intermediate = FALSE
ORDER BY images.starred DESC, images.created_at DESC
LIMIT 1;
""",
Expand Down
33 changes: 26 additions & 7 deletions invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,23 @@
},
"fitLayersToCanvas": {
"title": "Fit Layers to Canvas",
"desc": "Scales and positions the view to fit all visible layers."
"desc": "Scale and position the view to fit all visible layers."
},
"setZoomTo100Percent": {
"title": "Reset Zoom",
"desc": "Resets the canvas zoom to 100%."
"title": "Zoom to 100%",
"desc": "Set the canvas zoom to 100%."
},
"setZoomTo200Percent": {
"title": "Zoom to 200%",
"desc": "Set the canvas zoom to 200%."
},
"setZoomTo400Percent": {
"title": "Zoom to 400%",
"desc": "Set the canvas zoom to 400%."
},
"setZoomTo800Percent": {
"title": "Zoom to 800%",
"desc": "Set the canvas zoom to 800%."
},
"quickSwitch": {
"title": "Layer Quick Switch",
Expand All @@ -479,7 +491,7 @@
"desc": "Undo the last canvas action."
},
"redo": {
"title": "redo",
"title": "Redo",
"desc": "Redo the last canvas action."
},
"nextEntity": {
Expand All @@ -489,6 +501,10 @@
"prevEntity": {
"title": "Prev Layer",
"desc": "Select the previous layer in the list."
},
"setFillToWhite": {
"title": "Set Color to White",
"desc": "Set the current tool color to white."
}
},
"workflows": {
Expand Down Expand Up @@ -925,7 +941,8 @@
"prototypeDesc": "This invocation is a prototype. It may have breaking changes during app updates and may be removed at any time.",
"imageAccessError": "Unable to find image {{image_name}}, resetting to default",
"boardAccessError": "Unable to find board {{board_id}}, resetting to default",
"modelAccessError": "Unable to find model {{key}}, resetting to default"
"modelAccessError": "Unable to find model {{key}}, resetting to default",
"saveToGallery": "Save To Gallery"
},
"parameters": {
"aspect": "Aspect",
Expand Down Expand Up @@ -1711,11 +1728,13 @@
"referenceImage": "Reference Image",
"regionalReferenceImage": "Regional Reference Image",
"globalReferenceImage": "Global Reference Image",
"sendingToCanvas": "Sending to Canvas",
"sendingToGallery": "Sending to Gallery",
"sendingToCanvas": "Staging Generations on Canvas",
"sendingToGallery": "Sending Generations to Gallery",
"sendToGallery": "Send To Gallery",
"sendToGalleryDesc": "Pressing Invoke generates and saves a unique image to your gallery.",
"sendToCanvas": "Send To Canvas",
"newLayerFromImage": "New Layer from Image",
"newCanvasFromImage": "New Canvas from Image",
"copyToClipboard": "Copy to Clipboard",
"sendToCanvasDesc": "Pressing Invoke stages your work in progress on the canvas.",
"viewProgressInViewer": "View progress and outputs in the <Btn>Image Viewer</Btn>.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { bboxOptimalDimensionChanged, bboxSyncedToOptimalDimension } from 'features/controlLayers/store/canvasSlice';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
import { modelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
import { modelSelected } from 'features/parameters/store/actions';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';

Expand Down Expand Up @@ -68,6 +71,11 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}

dispatch(modelChanged({ model: newModel, previousModel: state.params.model }));
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(newModel) }));
if (!selectIsStaging(state)) {
dispatch(bboxSyncedToOptimalDimension());
}
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import type { AppDispatch, RootState } from 'app/store/store';
import type { SerializableObject } from 'common/types';
import {
bboxHeightChanged,
bboxWidthChanged,
bboxOptimalDimensionChanged,
bboxSyncedToOptimalDimension,
controlLayerModelChanged,
referenceImageIPAdapterModelChanged,
rgIPAdapterModelChanged,
} from 'features/controlLayers/store/canvasSlice';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
import {
clipEmbedModelSelected,
Expand All @@ -20,10 +21,9 @@ import {
} from 'features/controlLayers/store/paramsSlice';
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import { getEntityIdentifier } from 'features/controlLayers/store/types';
import { calculateNewSize } from 'features/parameters/components/Bbox/calculateNewSize';
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import type { Logger } from 'roarr';
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
import type { AnyModelConfig } from 'services/api/types';
Expand Down Expand Up @@ -95,15 +95,11 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
const result = zParameterModel.safeParse(defaultModelInList);
if (result.success) {
dispatch(modelChanged({ model: defaultModelInList, previousModel: currentModel }));
const { bbox } = selectCanvasSlice(state);
const optimalDimension = getOptimalDimension(defaultModelInList);
if (getIsSizeOptimal(bbox.rect.width, bbox.rect.height, optimalDimension)) {
return;
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(defaultModelInList) }));
if (!selectIsStaging(state)) {
dispatch(bboxSyncedToOptimalDimension());
}
const { width, height } = calculateNewSize(bbox.aspectRatio.value, optimalDimension * optimalDimension);

dispatch(bboxWidthChanged({ width }));
dispatch(bboxHeightChanged({ height }));
return;
}
}
Expand All @@ -116,6 +112,11 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
}

dispatch(modelChanged({ model: result.data, previousModel: currentModel }));
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(result.data) }));
if (!selectIsStaging(state)) {
dispatch(bboxSyncedToOptimalDimension());
}
};

const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import {
setCfgRescaleMultiplier,
setCfgScale,
Expand Down Expand Up @@ -96,13 +97,15 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
}
}
const setSizeOptions = { updateAspectRatio: true, clamp: true };
if (width) {

const isStaging = selectIsStaging(getState());
if (!isStaging && width) {
if (isParameterWidth(width)) {
dispatch(bboxWidthChanged({ width, ...setSizeOptions }));
}
}

if (height) {
if (!isStaging && height) {
if (isParameterHeight(height)) {
dispatch(bboxHeightChanged({ height, ...setSizeOptions }));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useBoolean } from 'common/hooks/useBoolean';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import {
selectCanvasRightPanelGalleryTab,
selectCanvasRightPanelLayersTab,
Expand Down Expand Up @@ -82,7 +83,11 @@ const ActivateCanvasButton = (props: PropsWithChildren) => {
export const CanvasAlertsSendingToCanvas = () => {
const { t } = useTranslation();
const destination = useCurrentDestination();
const isStaging = useAppSelector(selectIsStaging);
const isVisible = useMemo(() => {
if (isStaging) {
return true;
}
if (!destination) {
return false;
}
Expand All @@ -92,7 +97,7 @@ export const CanvasAlertsSendingToCanvas = () => {
}

return true;
}, [destination]);
}, [destination, isStaging]);

return (
<AlertWrapper
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MenuGroup, MenuItem } from '@invoke-ai/ui-library';
import { NewLayerIcon } from 'features/controlLayers/components/common/icons';
import {
useNewControlLayerFromBbox,
useNewGlobalReferenceImageFromBbox,
Expand All @@ -10,7 +11,7 @@ import {
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFloppyDiskBold, PiStackPlusFill } from 'react-icons/pi';
import { PiFloppyDiskBold } from 'react-icons/pi';

export const CanvasContextMenuGlobalMenuItems = memo(() => {
const { t } = useTranslation();
Expand All @@ -33,16 +34,16 @@ export const CanvasContextMenuGlobalMenuItems = memo(() => {
</MenuItem>
</MenuGroup>
<MenuGroup title={t('controlLayers.canvasContextMenu.bboxGroup')}>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newGlobalReferenceImageFromBbox}>
<MenuItem icon={<NewLayerIcon />} isDisabled={isBusy} onClick={newGlobalReferenceImageFromBbox}>
{t('controlLayers.canvasContextMenu.newGlobalReferenceImage')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newRegionalReferenceImageFromBbox}>
<MenuItem icon={<NewLayerIcon />} isDisabled={isBusy} onClick={newRegionalReferenceImageFromBbox}>
{t('controlLayers.canvasContextMenu.newRegionalReferenceImage')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newControlLayerFromBbox}>
<MenuItem icon={<NewLayerIcon />} isDisabled={isBusy} onClick={newControlLayerFromBbox}>
{t('controlLayers.canvasContextMenu.newControlLayer')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newRasterLayerFromBbox}>
<MenuItem icon={<NewLayerIcon />} isDisabled={isBusy} onClick={newRasterLayerFromBbox}>
{t('controlLayers.canvasContextMenu.newRasterLayer')}
</MenuItem>
</MenuGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import IAIDndImage from 'common/components/IAIDndImage';
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
import { useNanoid } from 'common/hooks/useNanoid';
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import type { ImageWithDims } from 'features/controlLayers/store/types';
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
Expand All @@ -27,6 +28,7 @@ type Props = {
export const IPAdapterImagePreview = memo(({ image, onChangeImage, droppableData, postUploadAction }: Props) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isStaging = useAppSelector(selectIsStaging);
const isConnected = useStore($isConnected);
const optimalDimension = useAppSelector(selectOptimalDimension);
const shift = useShiftModifier();
Expand Down Expand Up @@ -95,6 +97,7 @@ export const IPAdapterImagePreview = memo(({ image, onChangeImage, droppableData
onClick={handleSetControlImageToDimensions}
icon={<PiRulerBold size={16} />}
tooltip={shift ? t('controlLayers.useSizeIgnoreModel') : t('controlLayers.useSizeOptimizeForModel')}
isDisabled={isStaging}
/>
</Flex>
)}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
PopoverTrigger,
useShiftModifier,
} from '@invoke-ai/ui-library';
import { CanvasSettingsAutoSaveCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsAutoSaveCheckbox';
import { CanvasSettingsBboxOverlaySwitch } from 'features/controlLayers/components/Settings/CanvasSettingsBboxOverlaySwitch';
import { CanvasSettingsClearCachesButton } from 'features/controlLayers/components/Settings/CanvasSettingsClearCachesButton';
import { CanvasSettingsClearHistoryButton } from 'features/controlLayers/components/Settings/CanvasSettingsClearHistoryButton';
Expand All @@ -33,13 +32,17 @@ export const CanvasSettingsPopover = memo(() => {
return (
<Popover isLazy>
<PopoverTrigger>
<IconButton aria-label={t('common.settingsLabel')} icon={<PiGearSixFill />} variant="ghost" />
<IconButton
aria-label={t('common.settingsLabel')}
icon={<PiGearSixFill />}
variant="link"
alignSelf="stretch"
/>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<Flex direction="column" gap={2}>
<CanvasSettingsAutoSaveCheckbox />
<CanvasSettingsInvertScrollCheckbox />
<CanvasSettingsPreserveMaskCheckbox />
<CanvasSettingsClipToBboxCheckbox />
Expand Down
Loading