mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 18:14:56 -05:00
feat(ui): get all tabs working w/ new layout
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { Flex, Heading } from '@invoke-ai/ui-library';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const WorkflowsLaunchpadPanel = memo(() => {
|
||||
return (
|
||||
<Flex flexDir="column" h="full" w="full" alignItems="center" justifyContent="center" gap={2}>
|
||||
<Flex flexDir="column" w="full" h="full" justifyContent="center" gap={4} px={12} maxW={768}>
|
||||
<Heading mb={4}>Go deep with Workflows.</Heading>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
WorkflowsLaunchpadPanel.displayName = 'WorkflowsLaunchpadPanel';
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { ReactFlowProvider } from '@xyflow/react';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { AddNodeCmdk } from 'features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk';
|
||||
@@ -29,21 +30,23 @@ const NodeEditor = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="workflows" layerStyle="first" sx={FOCUS_REGION_STYLES}>
|
||||
{data && (
|
||||
<>
|
||||
<Flow />
|
||||
<AddNodeCmdk />
|
||||
<TopLeftPanel />
|
||||
<TopCenterPanel />
|
||||
<TopRightPanel />
|
||||
<BottomLeftPanel />
|
||||
<MinimapPanel />
|
||||
</>
|
||||
)}
|
||||
<WorkflowEditorSettings />
|
||||
{isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={PiFlowArrowBold} />}
|
||||
</FocusRegionWrapper>
|
||||
<ReactFlowProvider>
|
||||
<FocusRegionWrapper region="workflows" layerStyle="first" sx={FOCUS_REGION_STYLES}>
|
||||
{data && (
|
||||
<>
|
||||
<Flow />
|
||||
<AddNodeCmdk />
|
||||
<TopLeftPanel />
|
||||
<TopCenterPanel />
|
||||
<TopRightPanel />
|
||||
<BottomLeftPanel />
|
||||
<MinimapPanel />
|
||||
</>
|
||||
)}
|
||||
<WorkflowEditorSettings />
|
||||
{isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={PiFlowArrowBold} />}
|
||||
</FocusRegionWrapper>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { PublishWorkflowPanelContent } from 'features/nodes/components/sidePanel
|
||||
import { ActiveWorkflowDescription } from 'features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription';
|
||||
import { ActiveWorkflowNameAndActions } from 'features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions';
|
||||
import { selectWorkflowMode } from 'features/nodes/store/workflowLibrarySlice';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { ViewModeLeftPanelContent } from './viewMode/ViewModeLeftPanelContent';
|
||||
@@ -18,13 +19,16 @@ const WorkflowsTabLeftPanel = () => {
|
||||
const isInPublishFlow = useStore($isInPublishFlow);
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full" gap={2} flexDir="column">
|
||||
{isInPublishFlow && <PublishWorkflowPanelContent />}
|
||||
{!isInPublishFlow && <ActiveWorkflowNameAndActions />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'view' && <ActiveWorkflowDescription />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'view' && <ViewModeLeftPanelContent />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'edit' && <EditModeLeftPanelContent />}
|
||||
{isPublished && <PublishedWorkflowPanelContent />}
|
||||
<Flex flexDir="column" w="full" h="full" gap={2} py={2} pe={2}>
|
||||
<QueueControls />
|
||||
<Flex w="full" h="full" gap={2} flexDir="column">
|
||||
{isInPublishFlow && <PublishWorkflowPanelContent />}
|
||||
{!isInPublishFlow && <ActiveWorkflowNameAndActions />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'view' && <ActiveWorkflowDescription />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'view' && <ViewModeLeftPanelContent />}
|
||||
{!isInPublishFlow && !isPublished && mode === 'edit' && <EditModeLeftPanelContent />}
|
||||
{isPublished && <PublishedWorkflowPanelContent />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import InvocationCacheStatus from './InvocationCacheStatus';
|
||||
@@ -11,18 +9,9 @@ import QueueTabQueueControls from './QueueTabQueueControls';
|
||||
|
||||
const QueueTabContent = () => {
|
||||
const isInvocationCacheEnabled = useFeatureStatus('invocationCache');
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
display={activeTabName === 'queue' ? undefined : 'none'}
|
||||
hidden={activeTabName !== 'queue'}
|
||||
borderRadius="base"
|
||||
w="full"
|
||||
h="full"
|
||||
flexDir="column"
|
||||
gap={2}
|
||||
>
|
||||
<Flex borderRadius="base" w="full" h="full" flexDir="column" gap={2}>
|
||||
<Flex gap={2} w="full">
|
||||
<QueueTabQueueControls />
|
||||
<QueueStatus />
|
||||
|
||||
@@ -78,11 +78,12 @@ export const useHotkeyData = (): HotkeysData => {
|
||||
addHotkey('app', 'invokeFront', ['mod+shift+enter']);
|
||||
addHotkey('app', 'cancelQueueItem', ['shift+x']);
|
||||
addHotkey('app', 'clearQueue', ['mod+shift+x']);
|
||||
addHotkey('app', 'selectCanvasTab', ['1']);
|
||||
addHotkey('app', 'selectUpscalingTab', ['2']);
|
||||
addHotkey('app', 'selectWorkflowsTab', ['3']);
|
||||
addHotkey('app', 'selectModelsTab', ['4'], isModelManagerEnabled);
|
||||
addHotkey('app', 'selectQueueTab', isModelManagerEnabled ? ['5'] : ['4']);
|
||||
addHotkey('app', 'selectGenerateTab', ['1']);
|
||||
addHotkey('app', 'selectCanvasTab', ['2']);
|
||||
addHotkey('app', 'selectUpscalingTab', ['3']);
|
||||
addHotkey('app', 'selectWorkflowsTab', ['4']);
|
||||
addHotkey('app', 'selectModelsTab', ['5'], isModelManagerEnabled);
|
||||
addHotkey('app', 'selectQueueTab', isModelManagerEnabled ? ['6'] : ['5']);
|
||||
addHotkey('app', 'focusPrompt', ['alt+a']);
|
||||
addHotkey('app', 'toggleLeftPanel', ['t', 'o']);
|
||||
addHotkey('app', 'toggleRightPanel', ['g']);
|
||||
|
||||
@@ -8,11 +8,16 @@ import { VerticalNavBar } from 'features/ui/components/VerticalNavBar';
|
||||
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 { $isLeftPanelOpen, $isRightPanelOpen } from 'features/ui/store/uiSlice';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { TabMountGate } from './TabMountGate';
|
||||
import ModelManagerTab from './tabs/ModelManagerTab';
|
||||
import QueueTab from './tabs/QueueTab';
|
||||
|
||||
const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%', minWidth: 0 };
|
||||
|
||||
const onLeftPanelCollapse = (isCollapsed: boolean) => $isLeftPanelOpen.set(!isCollapsed);
|
||||
@@ -103,15 +108,36 @@ export const AppContent = memo(() => {
|
||||
<VerticalNavBar />
|
||||
</TabList>
|
||||
<TabPanels w="full" h="full" p={0}>
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<GenerateTabAutoLayout />
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<CanvasTabAutoLayout />
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<UpscalingTabAutoLayout />
|
||||
</TabPanel>
|
||||
<TabMountGate tab="generate">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<GenerateTabAutoLayout />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="canvas">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<CanvasTabAutoLayout />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="upscaling">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<UpscalingTabAutoLayout />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="workflows">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<WorkflowsTabAutoLayout />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="models">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<ModelManagerTab />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="queue">
|
||||
<TabPanel w="full" h="full" p={0}>
|
||||
<QueueTab />
|
||||
</TabPanel>
|
||||
</TabMountGate>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ReactFlowProvider } from '@xyflow/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import NodeEditor from 'features/nodes/components/NodeEditor';
|
||||
@@ -9,11 +8,7 @@ export const WorkflowsMainPanel = memo(() => {
|
||||
const mode = useAppSelector(selectWorkflowMode);
|
||||
|
||||
if (mode === 'edit') {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<NodeEditor />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
return <NodeEditor />;
|
||||
}
|
||||
|
||||
return <ImageViewer />;
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import type { GridviewApi, IDockviewReactProps, IGridviewReactProps } from 'dockview';
|
||||
import { DockviewReact, GridviewReact, Orientation } from 'dockview';
|
||||
import { WorkflowsLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/WorkflowsLaunchpadPanel';
|
||||
import { BoardsPanel } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { GalleryPanel } from 'features/gallery/components/Gallery';
|
||||
import { GenerationProgressPanel } from 'features/gallery/components/ImageViewer/GenerationProgressPanel';
|
||||
import { ImageViewerPanel } from 'features/gallery/components/ImageViewer/ImageViewerPanel';
|
||||
import NodeEditor from 'features/nodes/components/NodeEditor';
|
||||
import WorkflowsTabLeftPanel from 'features/nodes/components/sidePanel/WorkflowsTabLeftPanel';
|
||||
import { AutoLayoutProvider } from 'features/ui/layouts/auto-layout-context';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_MIN_SIZE_PX } from 'features/ui/store/uiSlice';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { useOnFirstVisible } from './use-on-first-visible';
|
||||
|
||||
const LAUNCHPAD_PANEL_ID = 'launchpad';
|
||||
const WORKSPACE_PANEL_ID = 'workspace';
|
||||
const VIEWER_PANEL_ID = 'viewer';
|
||||
const PROGRESS_PANEL_ID = 'progress';
|
||||
|
||||
const dockviewComponents: IDockviewReactProps['components'] = {
|
||||
[LAUNCHPAD_PANEL_ID]: WorkflowsLaunchpadPanel,
|
||||
[WORKSPACE_PANEL_ID]: NodeEditor,
|
||||
[VIEWER_PANEL_ID]: ImageViewerPanel,
|
||||
[PROGRESS_PANEL_ID]: GenerationProgressPanel,
|
||||
};
|
||||
|
||||
const onReadyMainPanel: IDockviewReactProps['onReady'] = (event) => {
|
||||
const { api } = event;
|
||||
api.addPanel({
|
||||
id: LAUNCHPAD_PANEL_ID,
|
||||
component: LAUNCHPAD_PANEL_ID,
|
||||
title: 'Launchpad',
|
||||
});
|
||||
api.addPanel({
|
||||
id: WORKSPACE_PANEL_ID,
|
||||
component: WORKSPACE_PANEL_ID,
|
||||
title: 'Workflow Editor',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: LAUNCHPAD_PANEL_ID,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: VIEWER_PANEL_ID,
|
||||
component: VIEWER_PANEL_ID,
|
||||
title: 'Image Viewer',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: LAUNCHPAD_PANEL_ID,
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
id: PROGRESS_PANEL_ID,
|
||||
component: PROGRESS_PANEL_ID,
|
||||
title: 'Generation Progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: LAUNCHPAD_PANEL_ID,
|
||||
},
|
||||
});
|
||||
|
||||
api.getPanel(LAUNCHPAD_PANEL_ID)?.api.setActive();
|
||||
|
||||
const disposables = [
|
||||
api.onWillShowOverlay((e) => {
|
||||
if (e.kind === 'header_space' || e.kind === 'tab') {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
}),
|
||||
];
|
||||
|
||||
return () => {
|
||||
disposables.forEach((disposable) => {
|
||||
disposable.dispose();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const MainPanel = memo(() => {
|
||||
return (
|
||||
<DockviewReact
|
||||
disableDnd={true}
|
||||
locked={true}
|
||||
disableFloatingGroups={true}
|
||||
dndEdges={false}
|
||||
defaultTabComponent={TabWithoutCloseButton}
|
||||
components={dockviewComponents}
|
||||
onReady={onReadyMainPanel}
|
||||
theme={dockviewTheme}
|
||||
/>
|
||||
);
|
||||
});
|
||||
MainPanel.displayName = 'MainPanel';
|
||||
|
||||
const LEFT_PANEL_ID = 'left';
|
||||
const MAIN_PANEL_ID = 'main';
|
||||
const BOARDS_PANEL_ID = 'boards';
|
||||
const GALLERY_PANEL_ID = 'gallery';
|
||||
|
||||
export const gridviewComponents: IGridviewReactProps['components'] = {
|
||||
[LEFT_PANEL_ID]: WorkflowsTabLeftPanel,
|
||||
[MAIN_PANEL_ID]: MainPanel,
|
||||
[BOARDS_PANEL_ID]: BoardsPanel,
|
||||
[GALLERY_PANEL_ID]: GalleryPanel,
|
||||
};
|
||||
|
||||
export const initializeLayout = (api: GridviewApi) => {
|
||||
api.addPanel({
|
||||
id: MAIN_PANEL_ID,
|
||||
component: MAIN_PANEL_ID,
|
||||
// priority: LayoutPriority.High,
|
||||
});
|
||||
api.addPanel({
|
||||
id: LEFT_PANEL_ID,
|
||||
component: LEFT_PANEL_ID,
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: MAIN_PANEL_ID,
|
||||
},
|
||||
// priority: LayoutPriority.High,
|
||||
});
|
||||
api.addPanel({
|
||||
id: GALLERY_PANEL_ID,
|
||||
component: GALLERY_PANEL_ID,
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 232,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: MAIN_PANEL_ID,
|
||||
},
|
||||
// priority: LayoutPriority.High,
|
||||
});
|
||||
api.addPanel({
|
||||
id: BOARDS_PANEL_ID,
|
||||
component: BOARDS_PANEL_ID,
|
||||
minimumHeight: 36,
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: GALLERY_PANEL_ID,
|
||||
},
|
||||
// priority: LayoutPriority.High,
|
||||
});
|
||||
api.getPanel(LEFT_PANEL_ID)?.api.setSize({ width: LEFT_PANEL_MIN_SIZE_PX });
|
||||
api.getPanel(BOARDS_PANEL_ID)?.api.setSize({ height: 256, width: RIGHT_PANEL_MIN_SIZE_PX });
|
||||
api.getPanel(MAIN_PANEL_ID)?.api.setActive();
|
||||
};
|
||||
|
||||
export const WorkflowsTabAutoLayout = memo(() => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const $api = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
$api.set(event.api);
|
||||
initializeLayout(event.api);
|
||||
},
|
||||
[$api]
|
||||
);
|
||||
const resizeMainPanelOnFirstVisible = useCallback(() => {
|
||||
const api = $api.get();
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
const mainPanel = api.getPanel(MAIN_PANEL_ID);
|
||||
if (!mainPanel) {
|
||||
return;
|
||||
}
|
||||
if (mainPanel.width !== 0) {
|
||||
return;
|
||||
}
|
||||
let count = 0;
|
||||
const setSize = () => {
|
||||
if (count++ > 50) {
|
||||
return;
|
||||
}
|
||||
mainPanel.api.setSize({ width: Number.MAX_SAFE_INTEGER });
|
||||
if (mainPanel.width === 0) {
|
||||
requestAnimationFrame(setSize);
|
||||
return;
|
||||
}
|
||||
};
|
||||
setSize();
|
||||
}, [$api]);
|
||||
useOnFirstVisible(ref, resizeMainPanelOnFirstVisible);
|
||||
|
||||
return (
|
||||
<AutoLayoutProvider $api={$api}>
|
||||
<GridviewReact
|
||||
ref={ref}
|
||||
className="dockview-theme-invoke"
|
||||
components={gridviewComponents}
|
||||
onReady={onReady}
|
||||
orientation={Orientation.VERTICAL}
|
||||
/>
|
||||
</AutoLayoutProvider>
|
||||
);
|
||||
});
|
||||
WorkflowsTabAutoLayout.displayName = 'WorkflowsTabAutoLayout';
|
||||
Reference in New Issue
Block a user