diff --git a/invokeai/frontend/web/index.d.ts b/invokeai/frontend/web/index.d.ts index 06a362d3a2..4be14302cb 100644 --- a/invokeai/frontend/web/index.d.ts +++ b/invokeai/frontend/web/index.d.ts @@ -1,6 +1,7 @@ import React, { PropsWithChildren } from 'react'; import { IAIPopoverProps } from '../web/src/common/components/IAIPopover'; import { IAIIconButtonProps } from '../web/src/common/components/IAIIconButton'; +import { InvokeTabName } from 'features/ui/store/tabMap'; export {}; @@ -68,6 +69,8 @@ declare module '@invoke-ai/invoke-ai-ui' { interface InvokeProps extends PropsWithChildren { apiUrl?: string; + disabledPanels?: string[]; + disabledTabs?: InvokeTabName[]; } declare function Invoke(props: InvokeProps): JSX.Element; diff --git a/invokeai/frontend/web/src/app/App.tsx b/invokeai/frontend/web/src/app/App.tsx index 40c15b38c0..ef843b047e 100644 --- a/invokeai/frontend/web/src/app/App.tsx +++ b/invokeai/frontend/web/src/app/App.tsx @@ -13,16 +13,34 @@ import { Box, Flex, Grid, Portal, useColorMode } from '@chakra-ui/react'; import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants'; import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel'; import Lightbox from 'features/lightbox/components/Lightbox'; -import { useAppSelector } from './storeHooks'; +import { useAppDispatch, useAppSelector } from './storeHooks'; import { PropsWithChildren, useEffect } from 'react'; +import { setDisabledPanels, setDisabledTabs } from 'features/ui/store/uiSlice'; +import { InvokeTabName } from 'features/ui/store/tabMap'; keepGUIAlive(); -const App = (props: PropsWithChildren) => { +interface Props extends PropsWithChildren { + options: { + disabledPanels: string[]; + disabledTabs: InvokeTabName[]; + }; +} + +const App = (props: Props) => { useToastWatcher(); const currentTheme = useAppSelector((state) => state.ui.currentTheme); const { setColorMode } = useColorMode(); + const dispatch = useAppDispatch(); + + useEffect(() => { + dispatch(setDisabledPanels(props.options.disabledPanels)); + }, [dispatch, props.options.disabledPanels]); + + useEffect(() => { + dispatch(setDisabledTabs(props.options.disabledTabs)); + }, [dispatch, props.options.disabledTabs]); useEffect(() => { setColorMode(['light'].includes(currentTheme) ? 'light' : 'dark'); diff --git a/invokeai/frontend/web/src/component.tsx b/invokeai/frontend/web/src/component.tsx index a642516417..e83b8ff8d5 100644 --- a/invokeai/frontend/web/src/component.tsx +++ b/invokeai/frontend/web/src/component.tsx @@ -4,6 +4,7 @@ import { PersistGate } from 'redux-persist/integration/react'; import { store } from './app/store'; import { persistor } from './persistor'; import { OpenAPI } from 'services/api'; +import { InvokeTabName } from 'features/ui/store/tabMap'; import '@fontsource/inter/100.css'; import '@fontsource/inter/200.css'; import '@fontsource/inter/300.css'; @@ -24,9 +25,16 @@ const ThemeLocaleProvider = lazy(() => import('./app/ThemeLocaleProvider')); interface Props extends PropsWithChildren { apiUrl?: string; + disabledPanels?: string[]; + disabledTabs?: InvokeTabName[]; } -export default function Component({ apiUrl, children }: Props) { +export default function Component({ + apiUrl, + disabledPanels = [], + disabledTabs = [], + children, +}: Props) { useEffect(() => { if (apiUrl) OpenAPI.BASE = apiUrl; }, [apiUrl]); @@ -37,7 +45,7 @@ export default function Component({ apiUrl, children }: Props) { } persistor={persistor}> }> - {children} + {children} diff --git a/invokeai/frontend/web/src/features/parameters/components/ParametersAccordion.tsx b/invokeai/frontend/web/src/features/parameters/components/ParametersAccordion.tsx index 76277867de..e3218f71e2 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ParametersAccordion.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ParametersAccordion.tsx @@ -21,9 +21,10 @@ type ParametersAccordionsType = { const ParametersAccordion = (props: ParametersAccordionsType) => { const { accordionInfo } = props; - const openAccordions = useAppSelector( - (state: RootState) => state.system.openAccordions - ); + const { system, ui } = useAppSelector((state: RootState) => state); + + const { openAccordions } = system; + const { disabledParameterPanels } = ui; const dispatch = useAppDispatch(); @@ -39,15 +40,19 @@ const ParametersAccordion = (props: ParametersAccordionsType) => { Object.keys(accordionInfo).forEach((key) => { const { header, feature, content, additionalHeaderComponents } = accordionInfo[key]; - accordionsToRender.push( - - ); + + // do not render if panel is disabled in global state + if (disabledParameterPanels.indexOf(key) === -1) { + accordionsToRender.push( + + ); + } }); } return accordionsToRender; diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index 18db74791b..d7736eb9ea 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -45,38 +45,41 @@ const tabIconStyles: ChakraProps['sx'] = { boxSize: 6, }; -const tabInfo: InvokeTabInfo[] = [ - { - id: 'txt2img', - icon: , - workarea: , - }, - { - id: 'img2img', - icon: , - workarea: , - }, - { - id: 'unifiedCanvas', - icon: , - workarea: , - }, - { - id: 'nodes', - icon: , - workarea: , - }, - { - id: 'postprocessing', - icon: , - workarea: , - }, - { - id: 'training', - icon: , - workarea: , - }, -]; +const buildTabs = (disabledTabs: InvokeTabName[]): InvokeTabInfo[] => { + const tabs: InvokeTabInfo[] = [ + { + id: 'txt2img', + icon: , + workarea: , + }, + { + id: 'img2img', + icon: , + workarea: , + }, + { + id: 'unifiedCanvas', + icon: , + workarea: , + }, + { + id: 'nodes', + icon: , + workarea: , + }, + { + id: 'postprocessing', + icon: , + workarea: , + }, + { + id: 'training', + icon: , + workarea: , + }, + ]; + return tabs.filter((tab) => !disabledTabs.includes(tab.id)); +}; export default function InvokeTabs() { const activeTab = useAppSelector(activeTabIndexSelector); @@ -85,13 +88,10 @@ export default function InvokeTabs() { (state: RootState) => state.lightbox.isLightboxOpen ); - const shouldPinGallery = useAppSelector( - (state: RootState) => state.ui.shouldPinGallery - ); + const { shouldPinGallery, disabledTabs, shouldPinParametersPanel } = + useAppSelector((state: RootState) => state.ui); - const shouldPinParametersPanel = useAppSelector( - (state: RootState) => state.ui.shouldPinParametersPanel - ); + const activeTabs = buildTabs(disabledTabs); const { t } = useTranslation(); @@ -142,7 +142,7 @@ export default function InvokeTabs() { const tabs = useMemo( () => - tabInfo.map((tab) => ( + activeTabs.map((tab) => ( )), - [t] + [t, activeTabs] ); const tabPanels = useMemo( () => - tabInfo.map((tab) => {tab.workarea}), - [] + activeTabs.map((tab) => {tab.workarea}), + [activeTabs] ); return ( diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 3d4d68fd4c..5ec5f4f012 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -18,6 +18,8 @@ const initialtabsState: UIState = { addNewModelUIOption: null, shouldPinGallery: true, shouldShowGallery: true, + disabledParameterPanels: [], + disabledTabs: [], }; const initialState: UIState = initialtabsState; @@ -90,6 +92,12 @@ export const uiSlice = createSlice({ state.shouldShowParametersPanel = true; } }, + setDisabledPanels: (state, action: PayloadAction) => { + state.disabledParameterPanels = action.payload; + }, + setDisabledTabs: (state, action: PayloadAction) => { + state.disabledTabs = action.payload; + }, }, extraReducers(builder) { builder.addCase(initialImageSelected, (state) => { @@ -118,6 +126,8 @@ export const { togglePinParametersPanel, toggleParametersPanel, toggleGalleryPanel, + setDisabledPanels, + setDisabledTabs, } = uiSlice.actions; export default uiSlice.reducer; diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index 900ea703b4..d4d8cfdcfe 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -1,3 +1,5 @@ +import { InvokeTabName } from './tabMap'; + export type AddNewModelType = 'ckpt' | 'diffusers' | null; export interface UIState { @@ -13,4 +15,6 @@ export interface UIState { addNewModelUIOption: AddNewModelType; shouldPinGallery: boolean; shouldShowGallery: boolean; + disabledParameterPanels: string[]; + disabledTabs: InvokeTabName[]; }