diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index eb99ef4f6f..a1ffd85cae 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1656,6 +1656,7 @@ "storeNotInitialized": "Store is not initialized" }, "controlLayers": { + "autoPreviewFilter": "Auto Preview", "bookmark": "Bookmark for Quick Switch", "fitBboxToLayers": "Fit Bbox To Layers", "removeBookmark": "Remove Bookmark", diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx index 11ba3cd16d..c343487082 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx @@ -23,8 +23,11 @@ export const EntityListSelectedEntityActionBarFilterButton = memo(() => { if (!isFilterableEntityIdentifier(selectedEntityIdentifier)) { return; } - - canvasManager.filter.startFilter(selectedEntityIdentifier); + const adapter = canvasManager.getAdapter(selectedEntityIdentifier); + if (!adapter) { + return; + } + adapter.filterer.startFilter(); }, [canvasManager, selectedEntityIdentifier]); if (!selectedEntityIdentifier) { diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx index 83ce7cf28d..645d8c4e58 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx @@ -4,12 +4,7 @@ import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext'; import { selectBase } from 'features/controlLayers/store/paramsSlice'; -import { - IMAGE_FILTERS, - isControlLayerEntityIdentifier, - isFilterType, - isRasterLayerEntityIdentifier, -} from 'features/controlLayers/store/types'; +import { IMAGE_FILTERS, isFilterableEntityIdentifier, isFilterType } from 'features/controlLayers/store/types'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useControlNetAndT2IAdapterModels } from 'services/api/hooks/modelsByType'; @@ -43,26 +38,25 @@ export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onCha } // Open the filter popup by setting this entity as the filtering entity - if (!canvasManager.filter.$adapter.get()) { - // Can only filter raster and control layers - if (!isRasterLayerEntityIdentifier(entityIdentifier) && !isControlLayerEntityIdentifier(entityIdentifier)) { + if (!canvasManager.stateApi.$isFiltering.get()) { + if (!isFilterableEntityIdentifier(entityIdentifier)) { + return; + } + const adapter = canvasManager.getAdapter(entityIdentifier); + if (!adapter) { return; } // Update the filter, preferring the model's default - if (isFilterType(modelConfig.default_settings?.preprocessor)) { - canvasManager.filter.$config.set( - IMAGE_FILTERS[modelConfig.default_settings.preprocessor].buildDefaults(modelConfig.base) - ); - } else { - canvasManager.filter.$config.set(IMAGE_FILTERS.canny_image_processor.buildDefaults(modelConfig.base)); - } + const filterConfig = isFilterType(modelConfig.default_settings?.preprocessor) + ? IMAGE_FILTERS[modelConfig.default_settings.preprocessor].buildDefaults(modelConfig.base) + : IMAGE_FILTERS.canny_image_processor.buildDefaults(modelConfig.base); - canvasManager.filter.startFilter(entityIdentifier); - canvasManager.filter.previewFilter(); + adapter.filterer.startFilter(filterConfig); + adapter.filterer.previewFilter(); } }, - [canvasManager.filter, entityIdentifier, modelKey, onChangeModel] + [canvasManager, entityIdentifier, modelKey, onChangeModel] ); const getIsDisabled = useCallback( diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx index cc19d47691..531dab3936 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx @@ -1,37 +1,44 @@ -import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library'; +import { Button, ButtonGroup, Flex, FormControl, FormLabel, Heading, Spacer, Switch } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { FilterSettings } from 'features/controlLayers/components/Filters/FilterSettings'; import { FilterTypeSelect } from 'features/controlLayers/components/Filters/FilterTypeSelect'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; +import type { CanvasEntityAdapterControlLayer } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterControlLayer'; +import type { CanvasEntityAdapterRasterLayer } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterRasterLayer'; +import { + selectAutoPreviewFilter, + settingsAutoPreviewFilterToggled, +} from 'features/controlLayers/store/canvasSettingsSlice'; import { type FilterConfig, IMAGE_FILTERS } from 'features/controlLayers/store/types'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { PiCheckBold, PiShootingStarBold, PiXBold } from 'react-icons/pi'; -export const Filter = memo(() => { +const FilterBox = memo(({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => { const { t } = useTranslation(); - const canvasManager = useCanvasManager(); - const config = useStore(canvasManager.filter.$config); - const isFiltering = useStore(canvasManager.filter.$isFiltering); - const isProcessing = useStore(canvasManager.filter.$isProcessing); + const dispatch = useAppDispatch(); + const config = useStore(adapter.filterer.$filterConfig); + const isProcessing = useStore(adapter.filterer.$isProcessing); + const autoPreviewFilter = useAppSelector(selectAutoPreviewFilter); const onChangeFilterConfig = useCallback( (filterConfig: FilterConfig) => { - canvasManager.filter.$config.set(filterConfig); + adapter.filterer.$filterConfig.set(filterConfig); }, - [canvasManager.filter.$config] + [adapter.filterer.$filterConfig] ); const onChangeFilterType = useCallback( (filterType: FilterConfig['type']) => { - canvasManager.filter.$config.set(IMAGE_FILTERS[filterType].buildDefaults()); + adapter.filterer.$filterConfig.set(IMAGE_FILTERS[filterType].buildDefaults()); }, - [canvasManager.filter.$config] + [adapter.filterer.$filterConfig] ); - if (!isFiltering) { - return null; - } + const onChangeAutoPreviewFilter = useCallback(() => { + dispatch(settingsAutoPreviewFilterToggled()); + }, [dispatch]); return ( { transitionProperty="height" transitionDuration="normal" > - - {t('controlLayers.filter.filter')} - + + + {t('controlLayers.filter.filter')} + + + + {t('controlLayers.autoPreviewFilter')} + + +