diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index ed32818dba..5955e722c0 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -18,7 +18,7 @@ import { StylePresetModal } from 'features/stylePresets/components/StylePresetFo import { configChanged } from 'features/system/store/configSlice'; import { languageSelector } from 'features/system/store/systemSelectors'; import { AppContent } from 'features/ui/components/AppContent'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "features/ui/store/uiTypes"; import { setActiveTab } from 'features/ui/store/uiSlice'; import { useGetAndLoadLibraryWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow'; import { AnimatePresence } from 'framer-motion'; @@ -40,7 +40,7 @@ interface Props { action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters'; }; selectedWorkflowId?: string; - destination?: InvokeTabName | undefined; + destination?: TabName | undefined; } const App = ({ config = DEFAULT_CONFIG, selectedImage, selectedWorkflowId, destination }: Props) => { diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx index 5804902408..fc91524746 100644 --- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx +++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx @@ -19,7 +19,7 @@ import type { PartialAppConfig } from 'app/types/invokeai'; import Loading from 'common/components/Loading/Loading'; import AppDndContext from 'features/dnd/components/AppDndContext'; import type { WorkflowCategory } from 'features/nodes/types/workflow'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "features/ui/store/uiTypes"; import type { PropsWithChildren, ReactNode } from 'react'; import React, { lazy, memo, useEffect, useMemo } from 'react'; import { Provider } from 'react-redux'; @@ -45,7 +45,7 @@ interface Props extends PropsWithChildren { action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters'; }; selectedWorkflowId?: string; - destination?: InvokeTabName; + destination?: TabName; customStarUi?: CustomStarUi; socketOptions?: Partial; isDebugging?: boolean; diff --git a/invokeai/frontend/web/src/app/store/actions.ts b/invokeai/frontend/web/src/app/store/actions.ts index 85debfc607..390849a8af 100644 --- a/invokeai/frontend/web/src/app/store/actions.ts +++ b/invokeai/frontend/web/src/app/store/actions.ts @@ -1,7 +1,7 @@ import { createAction } from '@reduxjs/toolkit'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "features/ui/store/uiTypes"; export const enqueueRequested = createAction<{ - tabName: InvokeTabName; + tabName: TabName; prepend: boolean; }>('app/enqueueRequested'); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts index 82681c6d79..be0d9ab195 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts @@ -1,5 +1,4 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; -import { ipaAllDeleted, rasterLayerAllDeleted } from 'features/controlLayers/store/canvasV2Slice'; import { getImageUsage } from 'features/deleteImageModal/store/selectors'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { imagesApi } from 'services/api/endpoints/images'; @@ -7,39 +6,22 @@ import { imagesApi } from 'services/api/endpoints/images'; export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => { startAppListening({ matcher: imagesApi.endpoints.deleteBoardAndImages.matchFulfilled, - effect: async (action, { dispatch, getState }) => { + effect: (action, { dispatch, getState }) => { const { deleted_images } = action.payload; // Remove all deleted images from the UI - let wereLayersReset = false; let wasNodeEditorReset = false; - const wereControlAdaptersReset = false; - let wereIPAdaptersReset = false; const { nodes, canvasV2 } = getState(); + deleted_images.forEach((image_name) => { const imageUsage = getImageUsage(nodes.present, canvasV2, image_name); - if (imageUsage.isLayerImage && !wereLayersReset) { - dispatch(rasterLayerAllDeleted()); - wereLayersReset = true; - } - if (imageUsage.isNodesImage && !wasNodeEditorReset) { dispatch(nodeEditorReset()); wasNodeEditorReset = true; } - - // if (imageUsage.isControlAdapterImage && !wereControlAdaptersReset) { - // dispatch(caAllDeleted()); - // wereControlAdaptersReset = true; - // } - - if (imageUsage.isIPAdapterImage && !wereIPAdaptersReset) { - dispatch(ipaAllDeleted()); - wereIPAdaptersReset = true; - } }); }, }); diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index dc51e6ee2e..bca17a01dc 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -1,6 +1,6 @@ import type { FilterType } from 'features/controlLayers/store/types'; import type { ParameterPrecision, ParameterScheduler } from 'features/parameters/types/parameterSchemas'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "features/ui/store/uiTypes"; import type { O } from 'ts-toolbelt'; /** @@ -72,7 +72,7 @@ export type AppConfig = { maxUpscaleDimension?: number; allowPrivateBoards: boolean; allowPrivateStylePresets: boolean; - disabledTabs: InvokeTabName[]; + disabledTabs: TabName[]; disabledFeatures: AppFeature[]; disabledSDFeatures: SDFeature[]; nodesAllowlist: string[] | undefined; diff --git a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts index 93c9d73932..76b9c0dcaf 100644 --- a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts +++ b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts @@ -1,7 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { toast } from 'features/toast/toast'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { useCallback, useEffect, useState } from 'react'; import type { Accept, FileRejection } from 'react-dropzone'; import { useDropzone } from 'react-dropzone'; @@ -14,7 +14,7 @@ const accept: Accept = { 'image/jpeg': ['.jpg', '.jpeg', '.png'], }; -const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (activeTabName) => { +const selectPostUploadAction = createMemoizedSelector(selectActiveTab, (activeTabName) => { let postUploadAction: PostUploadAction = { type: 'TOAST' }; if (activeTabName === 'upscaling') { diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts index d30ee3f964..1c5bf95ba0 100644 --- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts +++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts @@ -12,7 +12,7 @@ import { isInvocationNode } from 'features/nodes/types/invocation'; import { selectUpscalelice } from 'features/parameters/store/upscaleSlice'; import { selectConfigSlice } from 'features/system/store/configSlice'; import { selectSystemSlice } from 'features/system/store/systemSlice'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import i18n from 'i18next'; import { forEach, upperFirst } from 'lodash-es'; import { useMemo } from 'react'; @@ -36,7 +36,7 @@ const createSelector = (templates: Templates, isConnected: boolean) => selectCanvasV2Slice, selectUpscalelice, selectConfigSlice, - activeTabNameSelector, + selectActiveTab, ], (system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => { const { bbox } = canvasV2; diff --git a/invokeai/frontend/web/src/common/util/PubSub/PubSub.test.ts b/invokeai/frontend/web/src/common/util/PubSub/PubSub.test.ts deleted file mode 100644 index 108d68ff88..0000000000 --- a/invokeai/frontend/web/src/common/util/PubSub/PubSub.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { PubSub } from 'common/util/PubSub/PubSub'; -import { describe, expect, it, vi } from 'vitest'; - -describe('PubSub', () => { - it('should call listener when value is published and value changes', () => { - const pubsub = new PubSub(1); - const listener = vi.fn(); - - pubsub.subscribe(listener); - pubsub.publish(42); - - expect(listener).toHaveBeenCalledWith(42, 1); - }); - - it('should not call listener if value does not change', () => { - const pubsub = new PubSub(42); - const listener = vi.fn(); - - pubsub.subscribe(listener); - pubsub.publish(42); - - expect(listener).not.toHaveBeenCalled(); - }); - - it('should handle non-primitive values', () => { - const pubsub = new PubSub<{ foo: string }>({ foo: 'bar' }); - const listener = vi.fn(); - - pubsub.subscribe(listener); - pubsub.publish({ foo: 'bar' }); - - expect(listener).toHaveBeenCalled(); - }); - - it('should call listener with old and new value', () => { - const pubsub = new PubSub(1); - const listener = vi.fn(); - - pubsub.subscribe(listener); - pubsub.publish(2); - - expect(listener).toHaveBeenCalledWith(2, 1); - }); - - it('should allow unsubscribing', () => { - const pubsub = new PubSub(1); - const listener1 = vi.fn(); - const listener2 = vi.fn(); - - const unsubscribe = pubsub.subscribe(listener1); - pubsub.subscribe(listener2); - unsubscribe(); - pubsub.publish(42); - - expect(listener1).not.toHaveBeenCalled(); - expect(listener2).toHaveBeenCalled(); - expect(pubsub.getListeners().size).toBe(1); - }); - - it('should clear all listeners', () => { - const pubsub = new PubSub(1); - const listener1 = vi.fn(); - const listener2 = vi.fn(); - - pubsub.subscribe(listener1); - pubsub.subscribe(listener2); - pubsub.off(); - pubsub.publish(42); - - expect(listener1).not.toHaveBeenCalled(); - expect(listener2).not.toHaveBeenCalled(); - expect(pubsub.getListeners().size).toBe(0); - }); - - it('should use custom compareFn', () => { - const compareFn = (a: number, b: number) => Math.abs(a) === Math.abs(b); - const pubsub = new PubSub(1, compareFn); - const listener = vi.fn(); - - pubsub.subscribe(listener); - pubsub.publish(-1); - - expect(listener).not.toHaveBeenCalled(); - }); - - it('should handle multiple listeners', () => { - const pubsub = new PubSub(1); - const listener1 = vi.fn(); - const listener2 = vi.fn(); - - pubsub.subscribe(listener1); - pubsub.subscribe(listener2); - pubsub.publish(42); - - expect(listener1).toHaveBeenCalledWith(42, 1); - expect(listener2).toHaveBeenCalledWith(42, 1); - expect(pubsub.getListeners().size).toBe(2); - }); - - it('should get the current value', () => { - const pubsub = new PubSub(42); - expect(pubsub.getValue()).toBe(42); - pubsub.publish(43); - expect(pubsub.getValue()).toBe(43); - }); - - it('should get the listeners', () => { - const pubsub = new PubSub(1); - const listener1 = vi.fn(); - const listener2 = vi.fn(); - - pubsub.subscribe(listener1); - pubsub.subscribe(listener2); - - expect(pubsub.getListeners()).toEqual(new Set([listener1, listener2])); - }); -}); diff --git a/invokeai/frontend/web/src/common/util/PubSub/PubSub.ts b/invokeai/frontend/web/src/common/util/PubSub/PubSub.ts deleted file mode 100644 index 5ae6779b74..0000000000 --- a/invokeai/frontend/web/src/common/util/PubSub/PubSub.ts +++ /dev/null @@ -1,76 +0,0 @@ -export type Listener = (newValue: T, oldValue: T) => void; -export type CompareFn = (a: T, b: T) => boolean; - -/** - * A simple PubSub implementation. - * - * @template T The type of the value to be published. - * @param initialValue The initial value to publish. - */ -export class PubSub { - private _listeners: Set> = new Set(); - private _oldValue: T; - private _compareFn: CompareFn; - - public constructor(initialValue: T, compareFn?: CompareFn) { - this._oldValue = initialValue; - this._compareFn = compareFn || ((a, b) => a === b); - } - - /** - * Subscribes to the PubSub. - * @param listener The listener to be called when the value is published. - * @returns A function that can be called to unsubscribe the listener. - */ - public subscribe = (listener: Listener): (() => void) => { - this._listeners.add(listener); - - return () => { - this.unsubscribe(listener); - }; - }; - - /** - * Unsubscribes a listener from the PubSub. - * @param listener The listener to unsubscribe. - */ - public unsubscribe = (listener: Listener): void => { - this._listeners.delete(listener); - }; - - /** - * Publishes a new value to the PubSub. - * @param newValue The new value to publish. - */ - public publish = (newValue: T): void => { - if (!this._compareFn(this._oldValue, newValue)) { - for (const listener of this._listeners) { - listener(newValue, this._oldValue); - } - this._oldValue = newValue; - } - }; - - /** - * Clears all listeners from the PubSub. - */ - public off = (): void => { - this._listeners.clear(); - }; - - /** - * Gets the current value of the PubSub. - * @returns The current value of the PubSub. - */ - public getValue = (): T | undefined => { - return this._oldValue; - }; - - /** - * Gets the listeners of the PubSub. - * @returns The listeners of the PubSub. - */ - public getListeners = (): Set> => { - return this._listeners; - }; -} diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts index a6f9ba687b..3fe07ff5bc 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts @@ -156,7 +156,7 @@ export function selectEntity(state: CanvasV2State, { id, type }: CanvasEntityIde } } -export function selectAllEntitiesOfType(state: CanvasV2State, type: CanvasEntityState['type']): CanvasEntityState[] { +function selectAllEntitiesOfType(state: CanvasV2State, type: CanvasEntityState['type']): CanvasEntityState[] { if (type === 'raster_layer') { return state.rasterLayers.entities; } else if (type === 'control_layer') { @@ -474,12 +474,10 @@ export const { // Raster layers rasterLayerAdded, rasterLayerRecalled, - rasterLayerAllDeleted, rasterLayerConvertedToControlLayer, // Control layers controlLayerAdded, controlLayerRecalled, - controlLayerAllDeleted, controlLayerConvertedToRasterLayer, controlLayerModelChanged, controlLayerControlModeChanged, @@ -489,9 +487,6 @@ export const { // IP Adapters ipaAdded, ipaRecalled, - ipaIsEnabledToggled, - ipaDeleted, - ipaAllDeleted, ipaImageChanged, ipaMethodChanged, ipaModelChanged, @@ -501,7 +496,6 @@ export const { // Regions rgAdded, rgRecalled, - rgAllDeleted, rgPositivePromptChanged, rgNegativePromptChanged, rgFillColorChanged, @@ -607,10 +601,6 @@ export const canvasV2PersistConfig: PersistConfig = { persistDenylist: [], }; -export const sessionRequested = createAction(`${canvasV2Slice.name}/sessionRequested`); export const sessionStagingAreaImageAccepted = createAction<{ index: number }>( `${canvasV2Slice.name}/sessionStagingAreaImageAccepted` ); -export const transformationApplied = createAction( - `${canvasV2Slice.name}/transformationApplied` -); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersReducers.ts index 01bc97e166..aefd73db93 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersReducers.ts @@ -57,9 +57,6 @@ export const controlLayersReducers = { state.controlLayers.entities.push(data); state.selectedEntityIdentifier = { type: 'control_layer', id: data.id }; }, - controlLayerAllDeleted: (state) => { - state.controlLayers.entities = []; - }, controlLayerConvertedToRasterLayer: { reducer: (state, action: PayloadAction<{ id: string; newId: string }>) => { const { id, newId } = action.payload; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts index 65df5cad41..75d57be7da 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts @@ -10,7 +10,7 @@ import type { import { merge } from 'lodash-es'; import { assert } from 'tsafe'; -export const selectInpaintMaskEntity = (state: CanvasV2State, id: string) => +const selectInpaintMaskEntity = (state: CanvasV2State, id: string) => state.inpaintMasks.entities.find((layer) => layer.id === id); export const selectInpaintMaskEntityOrThrow = (state: CanvasV2State, id: string) => { const entity = selectInpaintMaskEntity(state, id); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/ipAdaptersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/ipAdaptersReducers.ts index cf0081a17a..7893e46694 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/ipAdaptersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/ipAdaptersReducers.ts @@ -7,7 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { CanvasIPAdapterState, CanvasV2State, CLIPVisionModelV2, IPAdapterConfig, IPMethodV2 } from './types'; import { imageDTOToImageWithDims } from './types'; -export const selectIPAdapterEntity = (state: CanvasV2State, id: string) => +const selectIPAdapterEntity = (state: CanvasV2State, id: string) => state.ipAdapters.entities.find((ipa) => ipa.id === id); export const selectIPAdapterEntityOrThrow = (state: CanvasV2State, id: string) => { const entity = selectIPAdapterEntity(state, id); @@ -36,20 +36,6 @@ export const ipAdaptersReducers = { state.ipAdapters.entities.push(data); state.selectedEntityIdentifier = { type: 'ip_adapter', id: data.id }; }, - ipaIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => { - const { id } = action.payload; - const ipa = selectIPAdapterEntity(state, id); - if (ipa) { - ipa.isEnabled = !ipa.isEnabled; - } - }, - ipaDeleted: (state, action: PayloadAction<{ id: string }>) => { - const { id } = action.payload; - state.ipAdapters.entities = state.ipAdapters.entities.filter((ipa) => ipa.id !== id); - }, - ipaAllDeleted: (state) => { - state.ipAdapters.entities = []; - }, ipaImageChanged: { reducer: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => { const { id, imageDTO } = action.payload; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/lorasReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/lorasReducers.ts index bca3ccdd9e..d43f608346 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/lorasReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/lorasReducers.ts @@ -2,7 +2,6 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit'; import type { CanvasV2State, LoRA } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import type { LoRAModelConfig } from 'services/api/types'; -import { assert } from 'tsafe'; import { v4 as uuidv4 } from 'uuid'; export const defaultLoRAConfig: Pick = { @@ -10,12 +9,7 @@ export const defaultLoRAConfig: Pick = { isEnabled: true, }; -export const selectLoRA = (state: CanvasV2State, id: string) => state.loras.find((lora) => lora.id === id); -export const selectLoRAOrThrow = (state: CanvasV2State, id: string) => { - const lora = selectLoRA(state, id); - assert(lora, `LoRA with id ${id} not found`); - return lora; -}; +const selectLoRA = (state: CanvasV2State, id: string) => state.loras.find((lora) => lora.id === id); export const lorasReducers = { loraAdded: { diff --git a/invokeai/frontend/web/src/features/controlLayers/store/rasterLayersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/rasterLayersReducers.ts index aad77777a5..05c5257787 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/rasterLayersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/rasterLayersReducers.ts @@ -2,18 +2,12 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit'; import { deepClone } from 'common/util/deepClone'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { merge } from 'lodash-es'; -import { assert } from 'tsafe'; import type { CanvasControlLayerState, CanvasRasterLayerState, CanvasV2State } from './types'; import { initialControlNet } from './types'; -export const selectRasterLayer = (state: CanvasV2State, id: string) => +const selectRasterLayerEntity = (state: CanvasV2State, id: string) => state.rasterLayers.entities.find((layer) => layer.id === id); -export const selectLayerOrThrow = (state: CanvasV2State, id: string) => { - const layer = selectRasterLayer(state, id); - assert(layer, `Layer with id ${id} not found`); - return layer; -}; export const rasterLayersReducers = { rasterLayerAdded: { @@ -46,13 +40,10 @@ export const rasterLayersReducers = { state.rasterLayers.entities.push(data); state.selectedEntityIdentifier = { type: 'raster_layer', id: data.id }; }, - rasterLayerAllDeleted: (state) => { - state.rasterLayers.entities = []; - }, rasterLayerConvertedToControlLayer: { reducer: (state, action: PayloadAction<{ id: string; newId: string }>) => { const { id, newId } = action.payload; - const layer = selectRasterLayer(state, id); + const layer = selectRasterLayerEntity(state, id); if (!layer) { return; } diff --git a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts index 3971ee3b17..4b3a9ecf92 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts @@ -16,10 +16,10 @@ import { assert } from 'tsafe'; import type { CanvasRegionalGuidanceState } from './types'; -export const selectRegionalGuidanceEntity = (state: CanvasV2State, id: string) => { +const selectRegionalGuidanceEntity = (state: CanvasV2State, id: string) => { return state.regions.entities.find((rg) => rg.id === id); }; -export const selectRegionalGuidanceIPAdapter = (state: CanvasV2State, id: string, ipAdapterId: string) => { +const selectRegionalGuidanceIPAdapter = (state: CanvasV2State, id: string, ipAdapterId: string) => { const entity = state.regions.entities.find((rg) => rg.id === id); if (!entity) { return; @@ -85,9 +85,6 @@ export const regionsReducers = { state.regions.entities.push(data); state.selectedEntityIdentifier = { type: 'regional_guidance', id: data.id }; }, - rgAllDeleted: (state) => { - state.regions.entities = []; - }, rgPositivePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => { const { id, prompt } = action.payload; const entity = selectRegionalGuidanceEntity(state, id); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/sessionReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/sessionReducers.ts index 8d2aeaeb11..2d78e0f7f9 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/sessionReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/sessionReducers.ts @@ -1,5 +1,9 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit'; -import type { CanvasV2State, SessionMode, StagingAreaImage } from 'features/controlLayers/store/types'; +import type { + CanvasV2State, + SessionMode, + StagingAreaImage, +} from 'features/controlLayers/store/types'; export const sessionReducers = { sessionStartedStaging: (state) => { diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index 30e943b8de..8f386f6f7c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -524,9 +524,6 @@ const zCanvasRectState = z.object({ }); export type CanvasRectState = z.infer; -const zLayerEffect = z.enum(['LightnessToAlphaFilter']); -export type LayerEffect = z.infer; - const zCanvasImageState = z.object({ id: zId, type: z.literal('image'), @@ -540,7 +537,6 @@ const zCanvasObjectState = z.discriminatedUnion('type', [ zCanvasEraserLineState, zCanvasRectState, ]); -export type CanvasObjectState = z.infer; const zIPAdapterConfig = z.object({ image: zImageWithDims.nullable(), @@ -822,9 +818,6 @@ export type StageAttrs = { height: Dimensions['height']; scale: number; }; -export type PositionChangedArg = { id: string; position: Coordinate }; -export type ScaleChangedArg = { id: string; scale: Coordinate; position: Coordinate }; -export type BboxChangedArg = { id: string; bbox: Rect | null }; export type EntityIdentifierPayload = { entityIdentifier: CanvasEntityIdentifier } & T; export type EntityMovedPayload = EntityIdentifierPayload<{ position: Coordinate }>; @@ -836,7 +829,6 @@ export type EntityRasterizedPayload = EntityIdentifierPayload<{ rect: Rect; replaceObjects: boolean; }>; -export type ImageObjectAddedArg = { id: string; imageDTO: ImageDTO; position?: Coordinate }; /** * A helper type to remove `[index: string]: any;` from a type. @@ -845,9 +837,9 @@ export type ImageObjectAddedArg = { id: string; imageDTO: ImageDTO; position?: C * `RectConfig`, `ImageConfig`, etc all include `[index: string]: any;` in their type signature. * TODO(psyche): Fix this upstream. */ -export type RemoveIndexString = { - [K in keyof T as string extends K ? never : K]: T[K]; -}; +// export type RemoveIndexString = { +// [K in keyof T as string extends K ? never : K]: T[K]; +// }; export type GenerationMode = 'txt2img' | 'img2img' | 'inpaint' | 'outpaint'; diff --git a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts index ce93611ff4..da3a2a88b7 100644 --- a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts +++ b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts @@ -3,14 +3,14 @@ import { getEventCoordinates } from '@dnd-kit/utilities'; import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; import { $viewport } from 'features/nodes/store/nodesSlice'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { useCallback } from 'react'; /** * Applies scaling to the drag transform (if on node editor tab) and centers it on cursor. */ export const useScaledModifer = () => { - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const workflowsViewport = useStore($viewport); const modifier: Modifier = useCallback( ({ activatorEvent, draggingNodeRect, transform }) => { diff --git a/invokeai/frontend/web/src/features/dnd/types/index.ts b/invokeai/frontend/web/src/features/dnd/types/index.ts index 88322da95a..08d4b3adab 100644 --- a/invokeai/frontend/web/src/features/dnd/types/index.ts +++ b/invokeai/frontend/web/src/features/dnd/types/index.ts @@ -18,17 +18,6 @@ type BaseDropData = { id: string; }; -export type CurrentImageDropData = BaseDropData & { - actionType: 'SET_CURRENT_IMAGE'; -}; - -export type CAImageDropData = BaseDropData & { - actionType: 'SET_CA_IMAGE'; - context: { - id: string; - }; -}; - export type IPAImageDropData = BaseDropData & { actionType: 'SET_IPA_IMAGE'; context: { @@ -56,10 +45,6 @@ type UpscaleInitialImageDropData = BaseDropData & { actionType: 'SET_UPSCALE_INITIAL_IMAGE'; }; -export type InitialImageDropData = BaseDropData & { - actionType: 'SET_INITIAL_IMAGE'; -}; - type NodesImageDropData = BaseDropData & { actionType: 'SET_NODES_IMAGE'; context: { @@ -86,11 +71,9 @@ export type SelectForCompareDropData = BaseDropData & { }; export type TypesafeDroppableData = - | CurrentImageDropData | NodesImageDropData | AddToBoardDropData | RemoveFromBoardDropData - | CAImageDropData | IPAImageDropData | RGIPAdapterImageDropData | SelectForCompareDropData diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index a192ff4fbb..9c6b77075d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -6,7 +6,7 @@ import { MetadataLayers } from 'features/metadata/components/MetadataLayers'; import { MetadataLoRAs } from 'features/metadata/components/MetadataLoRAs'; import { MetadataT2IAdapters } from 'features/metadata/components/MetadataT2IAdapters'; import { handlers } from 'features/metadata/util/handlers'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; type Props = { @@ -14,7 +14,7 @@ type Props = { }; const ImageMetadataActions = (props: Props) => { - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const { metadata } = props; if (!metadata || Object.keys(metadata).length === 0) { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx index dfc131c87c..ee619b0eab 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx @@ -2,7 +2,7 @@ import { Flex } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton'; import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; import CurrentImageButtons from './CurrentImageButtons'; @@ -10,7 +10,7 @@ import { ViewerToggleMenu } from './ViewerToggleMenu'; export const ViewerToolbar = memo(() => { const showToggle = useAppSelector((s) => { - const tab = activeTabNameSelector(s); + const tab = selectActiveTab(s); if (tab === 'upscaling' || tab === 'workflows') { return false; } diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts b/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts index d487069dc0..6da24c7851 100644 --- a/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts +++ b/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts @@ -4,7 +4,7 @@ import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'feat import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import { activeStylePresetIdChanged } from 'features/stylePresets/store/stylePresetSlice'; import { toast } from 'features/toast/toast'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; @@ -13,8 +13,8 @@ import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; export const useImageActions = (image_name?: string) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const activeTabName = useAppSelector(activeTabNameSelector); const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId); + const activeTabName = useAppSelector(selectActiveTab); const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(image_name); const [hasMetadata, setHasMetadata] = useState(false); const [hasSeed, setHasSeed] = useState(false); diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts index 72c7249533..7f2ef381bd 100644 --- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts @@ -21,7 +21,6 @@ import { negativePromptChanged, positivePrompt2Changed, positivePromptChanged, - rasterLayerAllDeleted, rasterLayerRecalled, refinerModelChanged, rgRecalled, @@ -40,7 +39,6 @@ import { vaeSelected, } from 'features/controlLayers/store/canvasV2Slice'; import type { - CanvasControlAdapterState, CanvasIPAdapterState, CanvasRasterLayerState, CanvasRegionalGuidanceState, diff --git a/invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx b/invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx index c2f4c75c7a..73a8dfc401 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx @@ -1,7 +1,7 @@ import { Box, Flex } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; import InvocationCacheStatus from './InvocationCacheStatus'; @@ -11,7 +11,7 @@ import QueueTabQueueControls from './QueueTabQueueControls'; const QueueTabContent = () => { const isInvocationCacheEnabled = useFeatureStatus('invocationCache'); - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); return ( { const dispatch = useAppDispatch(); - const tabName = useAppSelector(activeTabNameSelector); + const tabName = useAppSelector(selectActiveTab); const { isReady } = useIsReadyToEnqueue(); const [_, { isLoading }] = useEnqueueBatchMutation({ fixedCacheKey: 'enqueueBatch', diff --git a/invokeai/frontend/web/src/features/queue/hooks/useQueueFront.ts b/invokeai/frontend/web/src/features/queue/hooks/useQueueFront.ts index f6c71dbc5a..157659b52a 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useQueueFront.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useQueueFront.ts @@ -2,13 +2,13 @@ import { enqueueRequested } from 'app/store/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { useCallback, useMemo } from 'react'; import { useEnqueueBatchMutation } from 'services/api/endpoints/queue'; export const useQueueFront = () => { const dispatch = useAppDispatch(); - const tabName = useAppSelector(activeTabNameSelector); + const tabName = useAppSelector(selectActiveTab); const { isReady } = useIsReadyToEnqueue(); const [_, { isLoading }] = useEnqueueBatchMutation({ fixedCacheKey: 'enqueueBatch', diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx index daad281fc8..b58b5540fe 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx @@ -14,7 +14,7 @@ import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedS import ParamVAEModelSelect from 'features/parameters/components/VAEModel/ParamVAEModelSelect'; import ParamVAEPrecision from 'features/parameters/components/VAEModel/ParamVAEPrecision'; import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useGetModelConfigQuery } from 'services/api/endpoints/models'; @@ -30,7 +30,7 @@ const formLabelProps2: FormLabelProps = { export const AdvancedSettingsAccordion = memo(() => { const vaeKey = useAppSelector((state) => state.canvasV2.params.vae?.key); const { currentData: vaeConfig } = useGetModelConfigQuery(vaeKey ?? skipToken); - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const selectBadges = useMemo( () => diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index 634b8a6bd3..0fd4cdcdb0 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -14,7 +14,7 @@ import ParamMainModelSelect from 'features/parameters/components/MainModel/Param import { UseDefaultSettingsButton } from 'features/parameters/components/MainModel/UseDefaultSettingsButton'; import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle'; import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelectedModelConfig } from 'services/api/hooks/useSelectedModelConfig'; @@ -26,7 +26,7 @@ const formLabelProps: FormLabelProps = { export const GenerationSettingsAccordion = memo(() => { const { t } = useTranslation(); const modelConfig = useSelectedModelConfig(); - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const selectBadges = useMemo( () => createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => { diff --git a/invokeai/frontend/web/src/features/system/hooks/useFeatureStatus.ts b/invokeai/frontend/web/src/features/system/hooks/useFeatureStatus.ts index 527405cb7d..15be057b49 100644 --- a/invokeai/frontend/web/src/features/system/hooks/useFeatureStatus.ts +++ b/invokeai/frontend/web/src/features/system/hooks/useFeatureStatus.ts @@ -2,17 +2,17 @@ import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import type { AppFeature, SDFeature } from 'app/types/invokeai'; import { selectConfigSlice } from 'features/system/store/configSlice'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "features/ui/store/uiTypes"; import { useMemo } from 'react'; -export const useFeatureStatus = (feature: AppFeature | SDFeature | InvokeTabName) => { +export const useFeatureStatus = (feature: AppFeature | SDFeature | TabName) => { const selectIsFeatureEnabled = useMemo( () => createSelector(selectConfigSlice, (config) => { return !( config.disabledFeatures.includes(feature as AppFeature) || config.disabledSDFeatures.includes(feature as SDFeature) || - config.disabledTabs.includes(feature as InvokeTabName) + config.disabledTabs.includes(feature as TabName) ); }), [feature] diff --git a/invokeai/frontend/web/src/features/ui/components/AppContent.tsx b/invokeai/frontend/web/src/features/ui/components/AppContent.tsx index cc6ee8fa0c..5f36b22b5e 100644 --- a/invokeai/frontend/web/src/features/ui/components/AppContent.tsx +++ b/invokeai/frontend/web/src/features/ui/components/AppContent.tsx @@ -18,8 +18,8 @@ import { VerticalNavBar } from 'features/ui/components/VerticalNavBar'; import type { UsePanelOptions } from 'features/ui/hooks/usePanel'; import { usePanel } from 'features/ui/hooks/usePanel'; import { usePanelStorage } from 'features/ui/hooks/usePanelStorage'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; import { $isGalleryPanelOpen, $isParametersPanelOpen } from 'features/ui/store/uiSlice'; +import type { TabName } from "features/ui/store/uiTypes"; import type { CSSProperties } from 'react'; import { memo, useMemo, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -29,8 +29,8 @@ import { Panel, PanelGroup } from 'react-resizable-panels'; import ParametersPanelUpscale from './ParametersPanels/ParametersPanelUpscale'; import ResizeHandle from './tabs/ResizeHandle'; -const TABS_WITH_GALLERY_PANEL: InvokeTabName[] = ['generation', 'upscaling', 'workflows'] as const; -const TABS_WITH_OPTIONS_PANEL: InvokeTabName[] = ['generation', 'upscaling', 'workflows'] as const; +const TABS_WITH_GALLERY_PANEL: TabName[] = ['generation', 'upscaling', 'workflows'] as const; +const TABS_WITH_OPTIONS_PANEL: TabName[] = ['generation', 'upscaling', 'workflows'] as const; const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%' }; const GALLERY_MIN_SIZE_PX = 310; @@ -38,8 +38,8 @@ const GALLERY_MIN_SIZE_PCT = 20; const OPTIONS_PANEL_MIN_SIZE_PX = 430; const OPTIONS_PANEL_MIN_SIZE_PCT = 20; -export const onGalleryPanelCollapse = (isCollapsed: boolean) => $isGalleryPanelOpen.set(!isCollapsed); -export const onParametersPanelCollapse = (isCollapsed: boolean) => $isParametersPanelOpen.set(!isCollapsed); +const onGalleryPanelCollapse = (isCollapsed: boolean) => $isGalleryPanelOpen.set(!isCollapsed); +const onParametersPanelCollapse = (isCollapsed: boolean) => $isParametersPanelOpen.set(!isCollapsed); export const AppContent = memo(() => { const panelGroupRef = useRef(null); diff --git a/invokeai/frontend/web/src/features/ui/components/TabButton.tsx b/invokeai/frontend/web/src/features/ui/components/TabButton.tsx index 493286567d..eedb140cd5 100644 --- a/invokeai/frontend/web/src/features/ui/components/TabButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/TabButton.tsx @@ -1,13 +1,13 @@ import { IconButton, Tooltip } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import type { TabName } from "../store/uiTypes"; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { setActiveTab } from 'features/ui/store/uiSlice'; import { memo, type ReactElement, useCallback } from 'react'; -export const TabButton = memo(({ tab, icon, label }: { tab: InvokeTabName; icon: ReactElement; label: string }) => { +export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }) => { const dispatch = useAppDispatch(); - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const onClick = useCallback(() => { dispatch(setActiveTab(tab)); }, [dispatch, tab]); diff --git a/invokeai/frontend/web/src/features/ui/components/TabMountGate.tsx b/invokeai/frontend/web/src/features/ui/components/TabMountGate.tsx index cbef31ca88..f131828427 100644 --- a/invokeai/frontend/web/src/features/ui/components/TabMountGate.tsx +++ b/invokeai/frontend/web/src/features/ui/components/TabMountGate.tsx @@ -1,11 +1,11 @@ import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import { selectConfigSlice } from 'features/system/store/configSlice'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "../store/uiTypes"; import type { PropsWithChildren } from 'react'; import { memo, useMemo } from 'react'; -export const TabMountGate = memo(({ tab, children }: PropsWithChildren<{ tab: InvokeTabName }>) => { +export const TabMountGate = memo(({ tab, children }: PropsWithChildren<{ tab: TabName }>) => { const selectIsTabEnabled = useMemo( () => createSelector(selectConfigSlice, (config) => !config.disabledTabs.includes(tab)), [tab] diff --git a/invokeai/frontend/web/src/features/ui/components/TabVisibilityGate.tsx b/invokeai/frontend/web/src/features/ui/components/TabVisibilityGate.tsx index d3409f8c91..8f64049b21 100644 --- a/invokeai/frontend/web/src/features/ui/components/TabVisibilityGate.tsx +++ b/invokeai/frontend/web/src/features/ui/components/TabVisibilityGate.tsx @@ -1,10 +1,10 @@ import { Box } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; -import type { InvokeTabName } from 'features/ui/store/tabMap'; +import type { TabName } from "../store/uiTypes"; import type { PropsWithChildren } from 'react'; import { memo } from 'react'; -export const TabVisibilityGate = memo(({ tab, children }: PropsWithChildren<{ tab: InvokeTabName }>) => { +export const TabVisibilityGate = memo(({ tab, children }: PropsWithChildren<{ tab: TabName }>) => { const activeTabName = useAppSelector((s) => s.ui.activeTab); return ( diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx index bc2a25f19c..c02f9e45f5 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx @@ -2,13 +2,13 @@ import { Box } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import { useScopeOnFocus } from 'common/hooks/interactionScopes'; import NodeEditor from 'features/nodes/components/NodeEditor'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo, useRef } from 'react'; import { ReactFlowProvider } from 'reactflow'; const NodesTab = () => { const mode = useAppSelector((s) => s.workflow.mode); - const activeTabName = useAppSelector(activeTabNameSelector); + const activeTabName = useAppSelector(selectActiveTab); const ref = useRef(null); useScopeOnFocus('workflows', ref); diff --git a/invokeai/frontend/web/src/features/ui/store/tabMap.tsx b/invokeai/frontend/web/src/features/ui/store/tabMap.tsx deleted file mode 100644 index b41c3e1414..0000000000 --- a/invokeai/frontend/web/src/features/ui/store/tabMap.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const TAB_NUMBER_MAP = ['generation', 'upscaling', 'workflows', 'models', 'queue'] as const; - -export type InvokeTabName = (typeof TAB_NUMBER_MAP)[number]; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts b/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts index 05cefe43c5..f11e53a6da 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts @@ -1,21 +1,4 @@ import { createSelector } from '@reduxjs/toolkit'; -import { selectConfigSlice } from 'features/system/store/configSlice'; import { selectUiSlice } from 'features/ui/store/uiSlice'; -import { isString } from 'lodash-es'; -import { TAB_NUMBER_MAP } from './tabMap'; - -export const activeTabNameSelector = createSelector( - selectUiSlice, - /** - * Previously `activeTab` was an integer, but now it's a string. - * Default to first tab in case user has integer. - */ - (ui) => (isString(ui.activeTab) ? ui.activeTab : 'generation') -); - -export const activeTabIndexSelector = createSelector(selectUiSlice, selectConfigSlice, (ui, config) => { - const tabs = TAB_NUMBER_MAP.filter((t) => !config.disabledTabs.includes(t)); - const idx = tabs.indexOf(ui.activeTab); - return idx === -1 ? 0 : idx; -}); +export const selectActiveTab = createSelector(selectUiSlice, (ui) => ui.activeTab); diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 0c6f8aa02d..855f7ab2f1 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -4,7 +4,7 @@ import type { PersistConfig, RootState } from 'app/store/store'; import { workflowLoadRequested } from 'features/nodes/store/actions'; import { atom } from 'nanostores'; -import type { InvokeTabName } from './tabMap'; +import type { TabName } from "./uiTypes"; import type { UIState } from './uiTypes'; const initialUIState: UIState = { @@ -21,7 +21,7 @@ export const uiSlice = createSlice({ name: 'ui', initialState: initialUIState, reducers: { - setActiveTab: (state, action: PayloadAction) => { + setActiveTab: (state, action: PayloadAction) => { state.activeTab = action.payload; }, setShouldShowImageDetails: (state, action: PayloadAction) => { diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index c72043190f..378626180f 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -1,4 +1,5 @@ -import type { InvokeTabName } from './tabMap'; + +export type TabName = 'generation' | 'upscaling' | 'workflows' | 'models' | 'queue'; export interface UIState { /** @@ -8,7 +9,7 @@ export interface UIState { /** * The currently active tab. */ - activeTab: InvokeTabName; + activeTab: TabName; /** * Whether or not to show image details, e.g. metadata, workflow, etc. */ diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 3a91eb36a7..8d0d894f5f 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -110,12 +110,6 @@ export const isSpandrelImageToImageModelConfig = ( return config.type === 'spandrel_image_to_image'; }; -export const isControlAdapterModelConfig = ( - config: AnyModelConfig -): config is ControlNetModelConfig | T2IAdapterModelConfig | IPAdapterModelConfig => { - return isControlNetModelConfig(config) || isT2IAdapterModelConfig(config) || isIPAdapterModelConfig(config); -}; - export const isControlNetOrT2IAdapterModelConfig = ( config: AnyModelConfig ): config is ControlNetModelConfig | T2IAdapterModelConfig => { @@ -191,11 +185,6 @@ export type OutputFields = Extract< // Node Outputs export type ImageOutput = S['ImageOutput']; -export type CAImagePostUploadAction = { - type: 'SET_CA_IMAGE'; - id: string; -}; - export type IPALayerImagePostUploadAction = { type: 'SET_IPA_IMAGE'; id: string; @@ -230,7 +219,6 @@ export type PostUploadAction = | NodesAction | ToastAction | AddToBatchAction - | CAImagePostUploadAction | IPALayerImagePostUploadAction | RGIPAdapterImagePostUploadAction | UpscaleInitialImageAction;