refactor(ui): navigation api

This commit is contained in:
psychedelicious
2025-07-04 12:09:51 +10:00
parent bcced8a5e8
commit ff84b0a495
6 changed files with 50 additions and 68 deletions

View File

@@ -1,8 +1,9 @@
import 'dockview/dist/styles/dockview.css';
import 'features/ui/styles/dockview-theme-invoke.css';
import { TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import Loading from 'common/components/Loading/Loading';
import { useDndMonitor } from 'features/dnd/useDndMonitor';
import {
selectWithCanvasTab,
@@ -17,15 +18,16 @@ import { CanvasTabAutoLayout } from 'features/ui/layouts/canvas-tab-auto-layout'
import { GenerateTabAutoLayout } from 'features/ui/layouts/generate-tab-auto-layout';
import { UpscalingTabAutoLayout } from 'features/ui/layouts/upscaling-tab-auto-layout';
import { WorkflowsTabAutoLayout } from 'features/ui/layouts/workflows-tab-auto-layout';
import { selectActiveTabIndex } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useState } from 'react';
import ModelManagerTab from './tabs/ModelManagerTab';
import QueueTab from './tabs/QueueTab';
export const AppContent = memo(() => {
useDndMonitor();
const tabIndex = useAppSelector(selectActiveTabIndex);
const tab = useAppSelector(selectActiveTab);
const [isLoading, setIsLoading] = useState(true);
const withGenerateTab = useAppSelector(selectWithGenerateTab);
const withCanvasTab = useAppSelector(selectWithCanvasTab);
const withUpscalingTab = useAppSelector(selectWithUpscalingTab);
@@ -34,41 +36,18 @@ export const AppContent = memo(() => {
const withQueueTab = useAppSelector(selectWithQueueTab);
return (
<Tabs index={tabIndex} display="flex" w="full" h="full" p={0} overflow="hidden">
<Flex position="relative" w="full" h="full" overflow="hidden">
<VerticalNavBar />
<TabPanels w="full" h="full" p={0}>
{withGenerateTab && (
<TabPanel w="full" h="full" p={0}>
<GenerateTabAutoLayout />
</TabPanel>
)}
{withCanvasTab && (
<TabPanel w="full" h="full" p={0}>
<CanvasTabAutoLayout />
</TabPanel>
)}
{withUpscalingTab && (
<TabPanel w="full" h="full" p={0}>
<UpscalingTabAutoLayout />
</TabPanel>
)}
{withWorkflowsTab && (
<TabPanel w="full" h="full" p={0}>
<WorkflowsTabAutoLayout />
</TabPanel>
)}
{withModelsTab && (
<TabPanel w="full" h="full" p={0}>
<ModelManagerTab />
</TabPanel>
)}
{withQueueTab && (
<TabPanel w="full" h="full" p={0}>
<QueueTab />
</TabPanel>
)}
</TabPanels>
</Tabs>
<Flex position="relative" w="full" h="full" overflow="hidden">
{withGenerateTab && tab === 'generate' && <GenerateTabAutoLayout setIsLoading={setIsLoading} />}
{withCanvasTab && tab === 'canvas' && <CanvasTabAutoLayout setIsLoading={setIsLoading} />}
{withUpscalingTab && tab === 'upscaling' && <UpscalingTabAutoLayout setIsLoading={setIsLoading} />}
{withWorkflowsTab && tab === 'workflows' && <WorkflowsTabAutoLayout setIsLoading={setIsLoading} />}
{withModelsTab && tab === 'models' && <ModelManagerTab />}
{withQueueTab && tab === 'queue' && <QueueTab />}
{isLoading && <Loading />}
</Flex>
</Flex>
);
});
AppContent.displayName = 'AppContent';

View File

@@ -56,7 +56,6 @@ import {
} from './shared';
import { TabWithLaunchpadIcon } from './TabWithLaunchpadIcon';
import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator';
import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible';
const tabComponents = {
[DEFAULT_TAB_ID]: TabWithoutCloseButton,
@@ -326,28 +325,30 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
return { main, left, right } satisfies Record<string, IGridviewPanel>;
};
export const CanvasTabAutoLayout = memo(() => {
export const CanvasTabAutoLayout = memo(({ setIsLoading }: { setIsLoading: (isLoading: boolean) => void }) => {
const rootRef = useRef<HTMLDivElement>(null);
const [rootApi, setRootApi] = useState<GridviewApi | null>(null);
const onReady = useCallback<IGridviewReactProps['onReady']>(({ api }) => {
setRootApi(api);
}, []);
useResizeMainPanelOnFirstVisit(rootApi, rootRef);
useEffect(() => {
setIsLoading(true);
if (!rootApi) {
return;
}
initializeRootPanelLayout(rootApi);
// Focus the launchpad panel once it's ready
navigationApi.focusPanel('canvas', LAUNCHPAD_PANEL_ID);
setTimeout(() => {
setIsLoading(false);
}, 300);
return () => {
navigationApi.unregisterTab('canvas');
};
}, [rootApi]);
}, [rootApi, setIsLoading]);
return (
<AutoLayoutProvider tab="canvas" rootRef={rootRef}>

View File

@@ -51,7 +51,6 @@ import {
} from './shared';
import { TabWithLaunchpadIcon } from './TabWithLaunchpadIcon';
import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator';
import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible';
const tabComponents = {
[DEFAULT_TAB_ID]: TabWithoutCloseButton,
@@ -288,28 +287,30 @@ export const initializeRootPanelLayout = (layoutApi: GridviewApi) => {
return { main, left, right } satisfies Record<string, IGridviewPanel>;
};
export const GenerateTabAutoLayout = memo(() => {
export const GenerateTabAutoLayout = memo(({ setIsLoading }: { setIsLoading: (isLoading: boolean) => void }) => {
const rootRef = useRef<HTMLDivElement>(null);
const [rootApi, setRootApi] = useState<GridviewApi | null>(null);
const onReady = useCallback<IGridviewReactProps['onReady']>(({ api }) => {
setRootApi(api);
}, []);
useResizeMainPanelOnFirstVisit(rootApi, rootRef);
useEffect(() => {
setIsLoading(true);
if (!rootApi) {
return;
}
initializeRootPanelLayout(rootApi);
// Focus the launchpad panel once it's ready
navigationApi.focusPanel('generate', LAUNCHPAD_PANEL_ID);
setTimeout(() => {
setIsLoading(false);
}, 300);
return () => {
navigationApi.unregisterTab('generate');
};
}, [rootApi]);
}, [rootApi, setIsLoading]);
return (
<AutoLayoutProvider tab="generate" rootRef={rootRef}>

View File

@@ -14,7 +14,7 @@ type Waiter = {
timeoutId: ReturnType<typeof setTimeout> | null;
};
const PANEL_ENABLED_TABS: TabName[] = ['canvas', 'generate', 'workflows', 'queue'];
const PANEL_ENABLED_TABS: TabName[] = ['canvas', 'generate', 'workflows', 'upscaling'];
export class NavigationApi {
private panels: Map<string, PanelType> = new Map();

View File

@@ -51,7 +51,6 @@ import {
import { TabWithLaunchpadIcon } from './TabWithLaunchpadIcon';
import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator';
import { UpscalingTabLeftPanel } from './UpscalingTabLeftPanel';
import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible';
const tabComponents = {
[DEFAULT_TAB_ID]: TabWithoutCloseButton,
@@ -288,28 +287,29 @@ export const initializeRootPanelLayout = (layoutApi: GridviewApi) => {
return { main, left, right } satisfies Record<string, IGridviewPanel>;
};
export const UpscalingTabAutoLayout = memo(() => {
export const UpscalingTabAutoLayout = memo(({ setIsLoading }: { setIsLoading: (isLoading: boolean) => void }) => {
const rootRef = useRef<HTMLDivElement>(null);
const [rootApi, setRootApi] = useState<GridviewApi | null>(null);
const onReady = useCallback<IGridviewReactProps['onReady']>(({ api }) => {
setRootApi(api);
}, []);
useResizeMainPanelOnFirstVisit(rootApi, rootRef);
useEffect(() => {
setIsLoading(true);
if (!rootApi) {
return;
}
initializeRootPanelLayout(rootApi);
// Focus the launchpad panel once it's ready
navigationApi.focusPanel('upscaling', LAUNCHPAD_PANEL_ID);
setTimeout(() => {
setIsLoading(false);
}, 300);
return () => {
navigationApi.unregisterTab('upscaling');
};
}, [rootApi]);
}, [rootApi, setIsLoading]);
return (
<AutoLayoutProvider tab="upscaling" rootRef={rootRef}>

View File

@@ -53,7 +53,6 @@ import {
} from './shared';
import { TabWithLaunchpadIcon } from './TabWithLaunchpadIcon';
import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator';
import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible';
const tabComponents = {
[DEFAULT_TAB_ID]: TabWithoutCloseButton,
@@ -305,28 +304,30 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
return { main, left, right } satisfies Record<string, IGridviewPanel>;
};
export const WorkflowsTabAutoLayout = memo(() => {
export const WorkflowsTabAutoLayout = memo(({ setIsLoading }: { setIsLoading: (isLoading: boolean) => void }) => {
const rootRef = useRef<HTMLDivElement>(null);
const [rootApi, setRootApi] = useState<GridviewApi | null>(null);
const onReady = useCallback<IGridviewReactProps['onReady']>(({ api }) => {
setRootApi(api);
}, []);
useResizeMainPanelOnFirstVisit(rootApi, rootRef);
useEffect(() => {
setIsLoading(true);
if (!rootApi) {
return;
}
initializeRootPanelLayout(rootApi);
// Focus the launchpad panel once it's ready
navigationApi.focusPanel('workflows', LAUNCHPAD_PANEL_ID);
setTimeout(() => {
setIsLoading(false);
}, 300);
return () => {
navigationApi.unregisterTab('workflows');
};
}, [rootApi]);
}, [rootApi, setIsLoading]);
return (
<AutoLayoutProvider tab="workflows" rootRef={rootRef}>