mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 18:14:56 -05:00
wip
This commit is contained in:
@@ -3,7 +3,7 @@ import { useStore } from '@nanostores/react';
|
||||
import { GlobalHookIsolator } from 'app/components/GlobalHookIsolator';
|
||||
import { GlobalModalIsolator } from 'app/components/GlobalModalIsolator';
|
||||
import type { StudioInitAction } from 'app/hooks/useStudioInitAction';
|
||||
import { $didStudioInit } from 'app/hooks/useStudioInitAction';
|
||||
import { $globalIsLoading } from 'app/store/nanostores/globalIsLoading';
|
||||
import type { PartialAppConfig } from 'app/types/invokeai';
|
||||
import Loading from 'common/components/Loading/Loading';
|
||||
import { useClearStorage } from 'common/hooks/useClearStorage';
|
||||
@@ -20,7 +20,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const App = ({ config = DEFAULT_CONFIG, studioInitAction }: Props) => {
|
||||
const didStudioInit = useStore($didStudioInit);
|
||||
const globalIsLoading = useStore($globalIsLoading);
|
||||
const clearStorage = useClearStorage();
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
@@ -33,7 +33,7 @@ const App = ({ config = DEFAULT_CONFIG, studioInitAction }: Props) => {
|
||||
<ErrorBoundary onReset={handleReset} FallbackComponent={AppErrorBoundaryFallback}>
|
||||
<Box id="invoke-app-wrapper" w="100dvw" h="100dvh" position="relative" overflow="hidden">
|
||||
<AppContent />
|
||||
{!didStudioInit && <Loading />}
|
||||
{globalIsLoading && <Loading />}
|
||||
</Box>
|
||||
<GlobalHookIsolator config={config} studioInitAction={studioInitAction} />
|
||||
<GlobalModalIsolator />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { $didStudioInit } from 'app/hooks/useStudioInitAction';
|
||||
import { atom, computed } from 'nanostores';
|
||||
import { flushSync } from 'react-dom';
|
||||
|
||||
export const $isLayoutLoading = atom(false);
|
||||
export const setIsLayoutLoading = (isLoading: boolean) => {
|
||||
flushSync(() => {
|
||||
$isLayoutLoading.set(isLoading);
|
||||
});
|
||||
};
|
||||
export const $globalIsLoading = computed([$didStudioInit, $isLayoutLoading], (didStudioInit, isLayoutLoading) => {
|
||||
return !didStudioInit || isLayoutLoading;
|
||||
});
|
||||
@@ -26,7 +26,7 @@ import { CanvasHUD } from 'features/controlLayers/components/HUD/CanvasHUD';
|
||||
import { InvokeCanvasComponent } from 'features/controlLayers/components/InvokeCanvasComponent';
|
||||
import { SelectObject } from 'features/controlLayers/components/SelectObject/SelectObject';
|
||||
import { CanvasSessionContextProvider } from 'features/controlLayers/components/SimpleSession/context';
|
||||
import { InitialState } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { StagingAreaItemsList } from 'features/controlLayers/components/SimpleSession/StagingAreaItemsList';
|
||||
import { StagingAreaToolbar } from 'features/controlLayers/components/StagingArea/StagingAreaToolbar';
|
||||
import { CanvasToolbar } from 'features/controlLayers/components/Toolbar/CanvasToolbar';
|
||||
@@ -84,7 +84,7 @@ export const AdvancedSession = memo(({ id }: { id: string | null }) => {
|
||||
</TabList>
|
||||
<TabPanels w="full" h="full">
|
||||
<TabPanel w="full" h="full" justifyContent="center">
|
||||
<InitialState />
|
||||
<GenerateLaunchpadPanel />
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full">
|
||||
<FocusRegionWrapper region="canvas" sx={FOCUS_REGION_STYLES}>
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { Divider, Flex, type SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons';
|
||||
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList';
|
||||
import { EntityListSelectedEntityActionBar } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectHasEntities } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { ParamDenoisingStrength } from './ParamDenoisingStrength';
|
||||
|
||||
const FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
};
|
||||
|
||||
export const CanvasLayersPanelContent = memo(() => {
|
||||
export const CanvasLayersPanel = memo(() => {
|
||||
const hasEntities = useAppSelector(selectHasEntities);
|
||||
|
||||
return (
|
||||
<FocusRegionWrapper region="layers" sx={FOCUS_REGION_STYLES}>
|
||||
<CanvasManagerProviderGate>
|
||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||
<EntityListSelectedEntityActionBar />
|
||||
<Divider py={0} />
|
||||
@@ -27,8 +22,8 @@ export const CanvasLayersPanelContent = memo(() => {
|
||||
{!hasEntities && <CanvasAddEntityButtons />}
|
||||
{hasEntities && <CanvasEntityList />}
|
||||
</Flex>
|
||||
</FocusRegionWrapper>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasLayersPanelContent.displayName = 'CanvasLayersPanelContent';
|
||||
CanvasLayersPanel.displayName = 'CanvasLayersPanel';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { InitialStateMainModelPicker } from 'features/controlLayers/components/S
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
export const InitialState = memo(() => {
|
||||
export const GenerateLaunchpadPanel = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const newCanvasSession = useCallback(() => {
|
||||
dispatch(setActiveTab('canvas'));
|
||||
@@ -43,4 +43,4 @@ export const InitialState = memo(() => {
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
InitialState.displayName = 'InitialState';
|
||||
GenerateLaunchpadPanel.displayName = 'InitialState';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InitialState } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
@@ -25,7 +25,7 @@ export const SimpleSession = memo(() => {
|
||||
</TabList>
|
||||
<TabPanels w="full" h="full">
|
||||
<TabPanel w="full" h="full" justifyContent="center">
|
||||
<InitialState />
|
||||
<GenerateLaunchpadPanel />
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full">
|
||||
<Flex flexDir="column" w="full" h="full">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { memo } from 'react';
|
||||
|
||||
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
|
||||
|
||||
export const BoardsListPanelContent = memo(() => {
|
||||
export const BoardsPanel = memo(() => {
|
||||
const boardSearchDisclosure = useStore($boardSearchIsOpen);
|
||||
return (
|
||||
<Flex flexDir="column" w="full" h="full" p={2}>
|
||||
@@ -23,4 +23,4 @@ export const BoardsListPanelContent = memo(() => {
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
BoardsListPanelContent.displayName = 'BoardsListPanelContent';
|
||||
BoardsPanel.displayName = 'BoardsPanel';
|
||||
|
||||
@@ -46,7 +46,7 @@ const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0, width: '10
|
||||
const selectGalleryView = createSelector(selectGallerySlice, (gallery) => gallery.galleryView);
|
||||
const selectSearchTerm = createSelector(selectGallerySlice, (gallery) => gallery.searchTerm);
|
||||
|
||||
export const Gallery = memo(() => {
|
||||
export const GalleryPanel = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const galleryView = useAppSelector(selectGalleryView);
|
||||
@@ -117,4 +117,4 @@ export const Gallery = memo(() => {
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
Gallery.displayName = 'Gallery';
|
||||
GalleryPanel.displayName = 'Gallery';
|
||||
|
||||
@@ -17,8 +17,9 @@ export const GalleryTopBar = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const boardSearchText = useAppSelector(selectBoardSearchText);
|
||||
const boardSearchDisclosure = useBoardSearchDisclosure();
|
||||
const api = useAutoLayoutContext();
|
||||
const boardsPanel = useCollapsibleGridviewPanel(api, 'boards', 'vertical', 256);
|
||||
const $api = useAutoLayoutContext();
|
||||
const api = useStore($api);
|
||||
const boardsPanel = useCollapsibleGridviewPanel(api, 'Boards', 'vertical', 256);
|
||||
const isBoardsPanelCollapsed = useStore(boardsPanel.$isCollapsed);
|
||||
|
||||
const onClickBoardSearch = useCallback(() => {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const GenerationProgressPanel = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2}>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
));
|
||||
GenerationProgressPanel.displayName = 'GenerationProgressPanel';
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ImageViewerPanel = memo(() => (
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2} gap={2}>
|
||||
<ViewerToolbar />
|
||||
<Divider />
|
||||
<ImageViewer />
|
||||
</Flex>
|
||||
));
|
||||
ImageViewerPanel.displayName = 'ImageViewerPanel';
|
||||
@@ -1,27 +1,13 @@
|
||||
import 'dockview/dist/styles/dockview.css';
|
||||
import 'features/ui/styles/dockview-theme-invoke.css';
|
||||
|
||||
import { TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useDndMonitor } from 'features/dnd/useDndMonitor';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { VerticalNavBar } from 'features/ui/components/VerticalNavBar';
|
||||
import type { UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||
import { usePanel } from 'features/ui/hooks/usePanel';
|
||||
import { CanvasTabAutoLayout } from 'features/ui/layouts/canvas-tab-auto-layout';
|
||||
import { GenerateTabAutoLayout } from 'features/ui/layouts/generate-tab-auto-layout';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import {
|
||||
$isLeftPanelOpen,
|
||||
$isRightPanelOpen,
|
||||
LEFT_PANEL_MIN_SIZE_PX,
|
||||
RIGHT_PANEL_MIN_SIZE_PX,
|
||||
selectWithLeftPanel,
|
||||
selectWithRightPanel,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { AutoLayout } from 'features/ui/layouts/AutoLayout';
|
||||
import { $isLeftPanelOpen, $isRightPanelOpen } from 'features/ui/store/uiSlice';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useMemo, useRef } from 'react';
|
||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||
import { memo } from 'react';
|
||||
|
||||
const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%', minWidth: 0 };
|
||||
|
||||
@@ -29,97 +15,88 @@ const onLeftPanelCollapse = (isCollapsed: boolean) => $isLeftPanelOpen.set(!isCo
|
||||
const onRightPanelCollapse = (isCollapsed: boolean) => $isRightPanelOpen.set(!isCollapsed);
|
||||
|
||||
export const AppContent = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const imperativePanelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
// const tab = useAppSelector(selectActiveTab);
|
||||
// const imperativePanelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
useDndMonitor();
|
||||
|
||||
const withLeftPanel = useAppSelector(selectWithLeftPanel);
|
||||
const leftPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
() => ({
|
||||
id: 'left-panel',
|
||||
minSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
defaultSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
imperativePanelGroupRef,
|
||||
panelGroupDirection: 'horizontal',
|
||||
onCollapse: onLeftPanelCollapse,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
const leftPanel = usePanel(leftPanelUsePanelOptions);
|
||||
useRegisteredHotkeys({
|
||||
id: 'toggleLeftPanel',
|
||||
category: 'app',
|
||||
callback: leftPanel.toggle,
|
||||
options: { enabled: withLeftPanel },
|
||||
dependencies: [leftPanel.toggle, withLeftPanel],
|
||||
});
|
||||
// const withLeftPanel = useAppSelector(selectWithLeftPanel);
|
||||
// const leftPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
// () => ({
|
||||
// id: 'left-panel',
|
||||
// minSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
// defaultSizePx: LEFT_PANEL_MIN_SIZE_PX,
|
||||
// imperativePanelGroupRef,
|
||||
// panelGroupDirection: 'horizontal',
|
||||
// onCollapse: onLeftPanelCollapse,
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
// const leftPanel = usePanel(leftPanelUsePanelOptions);
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'toggleLeftPanel',
|
||||
// category: 'app',
|
||||
// callback: leftPanel.toggle,
|
||||
// options: { enabled: withLeftPanel },
|
||||
// dependencies: [leftPanel.toggle, withLeftPanel],
|
||||
// });
|
||||
|
||||
const withRightPanel = useAppSelector(selectWithRightPanel);
|
||||
const rightPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
() => ({
|
||||
id: 'right-panel',
|
||||
minSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
defaultSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
imperativePanelGroupRef,
|
||||
panelGroupDirection: 'horizontal',
|
||||
onCollapse: onRightPanelCollapse,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
const rightPanel = usePanel(rightPanelUsePanelOptions);
|
||||
useRegisteredHotkeys({
|
||||
id: 'toggleRightPanel',
|
||||
category: 'app',
|
||||
callback: rightPanel.toggle,
|
||||
options: { enabled: withRightPanel },
|
||||
dependencies: [rightPanel.toggle, withRightPanel],
|
||||
});
|
||||
// const withRightPanel = useAppSelector(selectWithRightPanel);
|
||||
// const rightPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
// () => ({
|
||||
// id: 'right-panel',
|
||||
// minSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
// defaultSizePx: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
// imperativePanelGroupRef,
|
||||
// panelGroupDirection: 'horizontal',
|
||||
// onCollapse: onRightPanelCollapse,
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
// const rightPanel = usePanel(rightPanelUsePanelOptions);
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'toggleRightPanel',
|
||||
// category: 'app',
|
||||
// callback: rightPanel.toggle,
|
||||
// options: { enabled: withRightPanel },
|
||||
// dependencies: [rightPanel.toggle, withRightPanel],
|
||||
// });
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'resetPanelLayout',
|
||||
category: 'app',
|
||||
callback: () => {
|
||||
leftPanel.reset();
|
||||
rightPanel.reset();
|
||||
},
|
||||
dependencies: [leftPanel.reset, rightPanel.reset],
|
||||
});
|
||||
useRegisteredHotkeys({
|
||||
id: 'togglePanels',
|
||||
category: 'app',
|
||||
callback: () => {
|
||||
if (leftPanel.isCollapsed || rightPanel.isCollapsed) {
|
||||
leftPanel.expand();
|
||||
rightPanel.expand();
|
||||
} else {
|
||||
leftPanel.collapse();
|
||||
rightPanel.collapse();
|
||||
}
|
||||
},
|
||||
dependencies: [
|
||||
leftPanel.isCollapsed,
|
||||
rightPanel.isCollapsed,
|
||||
leftPanel.expand,
|
||||
rightPanel.expand,
|
||||
leftPanel.collapse,
|
||||
rightPanel.collapse,
|
||||
],
|
||||
});
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'resetPanelLayout',
|
||||
// category: 'app',
|
||||
// callback: () => {
|
||||
// leftPanel.reset();
|
||||
// rightPanel.reset();
|
||||
// },
|
||||
// dependencies: [leftPanel.reset, rightPanel.reset],
|
||||
// });
|
||||
// useRegisteredHotkeys({
|
||||
// id: 'togglePanels',
|
||||
// category: 'app',
|
||||
// callback: () => {
|
||||
// if (leftPanel.isCollapsed || rightPanel.isCollapsed) {
|
||||
// leftPanel.expand();
|
||||
// rightPanel.expand();
|
||||
// } else {
|
||||
// leftPanel.collapse();
|
||||
// rightPanel.collapse();
|
||||
// }
|
||||
// },
|
||||
// dependencies: [
|
||||
// leftPanel.isCollapsed,
|
||||
// rightPanel.isCollapsed,
|
||||
// leftPanel.expand,
|
||||
// rightPanel.expand,
|
||||
// leftPanel.collapse,
|
||||
// rightPanel.collapse,
|
||||
// ],
|
||||
// });
|
||||
|
||||
return (
|
||||
<Tabs index={tab === 'generate' ? 0 : 1} variant="unstyled" w="full" h="full" display="flex" p={0}>
|
||||
<TabList>
|
||||
<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>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<Flex id="invoke-app-tabs" w="full" h="full" p={0} overflow="hidden">
|
||||
<VerticalNavBar />
|
||||
<AutoLayout />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
AppContent.displayName = 'AppContent';
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasLayersPanel } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { BoardsPanel } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { GalleryPanel } from 'features/gallery/components/Gallery';
|
||||
import { GalleryTopBar } from 'features/gallery/components/GalleryTopBar';
|
||||
import { selectBoardSearchText } from 'features/gallery/store/gallerySelectors';
|
||||
import { HorizontalResizeHandle } from 'features/ui/components/tabs/ResizeHandle';
|
||||
@@ -71,18 +71,18 @@ export const RightPanelContent = memo(() => {
|
||||
<GalleryTopBar boardsListPanel={boardsListPanel} boardSearchDisclosure={boardSearchDisclosure} />
|
||||
<PanelGroup ref={imperativePanelGroupRef} direction="vertical" autoSaveId="boards-list-panel">
|
||||
<Panel order={0} id="boards-panel" collapsible {...boardsListPanel.panelProps}>
|
||||
<BoardsListPanelContent boardSearchDisclosure={boardSearchDisclosure} />
|
||||
<BoardsPanel boardSearchDisclosure={boardSearchDisclosure} />
|
||||
</Panel>
|
||||
<HorizontalResizeHandle id="boards-list-to-gallery-panel-handle" {...boardsListPanel.resizeHandleProps} />
|
||||
<Panel order={1} id="gallery-wrapper-panel" collapsible {...galleryPanel.panelProps}>
|
||||
<Gallery />
|
||||
<GalleryPanel />
|
||||
</Panel>
|
||||
{tab === 'canvas' && (
|
||||
<>
|
||||
<HorizontalResizeHandle id="gallery-panel-to-layers-handle" {...galleryPanel.resizeHandleProps} />
|
||||
<Panel order={2} id="canvas-layers-panel" collapsible {...canvasLayersPanel.panelProps}>
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasLayersPanelContent />
|
||||
<CanvasLayersPanel />
|
||||
</CanvasManagerProviderGate>
|
||||
</Panel>
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { IconButton, Tab, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
@@ -25,8 +25,7 @@ export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: React
|
||||
|
||||
return (
|
||||
<Tooltip label={label} placement="end">
|
||||
<Tab
|
||||
as={IconButton}
|
||||
<IconButton
|
||||
p={0}
|
||||
ref={ref}
|
||||
onClick={selectTab}
|
||||
|
||||
@@ -1,41 +1,301 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { GridviewApi, IGridviewReactProps } from 'dockview';
|
||||
import { GridviewReact, Orientation } from 'dockview';
|
||||
import type { DockviewApi, GridviewApi, IDockviewReactProps, IGridviewReactProps } from 'dockview';
|
||||
import { DockviewReact, GridviewReact, Orientation } from 'dockview';
|
||||
import { CanvasLayersPanel } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
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 { AutoLayoutProvider } from 'features/ui/layouts/auto-layout-context';
|
||||
import { canvasTabComponents, initializeCanvasTabLayout } from 'features/ui/layouts/canvas-tab-auto-layout';
|
||||
import { generateTabComponents, initializeGenerateTabLayout } from 'features/ui/layouts/generate-tab-auto-layout';
|
||||
import { CanvasWorkspacePanel } from 'features/ui/layouts/CanvasWorkspacePanel';
|
||||
import { GenerateLeftPanel } from 'features/ui/layouts/generate-tab-auto-layout';
|
||||
import { TabWithoutCloseButton } from 'features/ui/layouts/TabWithoutCloseButton';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_MIN_SIZE_PX } from 'features/ui/store/uiSlice';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { dockviewTheme } from 'features/ui/styles/theme';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
|
||||
const components: IGridviewReactProps['components'] = {
|
||||
...generateTabComponents,
|
||||
...canvasTabComponents,
|
||||
export const dockviewComponents: IDockviewReactProps['components'] = {
|
||||
// Shared components
|
||||
ImageViewer: ImageViewerPanel,
|
||||
GenerationProgress: GenerationProgressPanel,
|
||||
// Generate tab
|
||||
GenerateLaunchpad: GenerateLaunchpadPanel,
|
||||
// Upscaling tab
|
||||
UpscalingLaunchpad: GenerateLaunchpadPanel,
|
||||
// Workflows tab
|
||||
WorkflowsLaunchpad: GenerateLaunchpadPanel,
|
||||
// Canvas tab
|
||||
CanvasLaunchpad: GenerateLaunchpadPanel,
|
||||
CanvasWorkspace: CanvasWorkspacePanel,
|
||||
};
|
||||
|
||||
const getGenerateLaunchpadPanel = (api: DockviewApi) => {
|
||||
return (
|
||||
api.getPanel('GenerateLaunchpad') ||
|
||||
api.addPanel({
|
||||
id: 'GenerateLaunchpad',
|
||||
component: 'GenerateLaunchpad',
|
||||
title: 'Launchpad',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getCanvasLaunchpadPanel = (api: DockviewApi) => {
|
||||
return (
|
||||
api.getPanel('CanvasLaunchpad') ||
|
||||
api.addPanel({
|
||||
id: 'CanvasLaunchpad',
|
||||
component: 'CanvasLaunchpad',
|
||||
title: 'Launchpad',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getCanvasWorkspacePanel = (api: DockviewApi) => {
|
||||
return (
|
||||
api.getPanel('CanvasWorkspace') ||
|
||||
api.addPanel({
|
||||
id: 'CanvasWorkspace',
|
||||
component: 'CanvasWorkspace',
|
||||
title: 'Canvas',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getImageViewerPanel = (api: DockviewApi) => {
|
||||
return (
|
||||
api.getPanel('ImageViewer') ||
|
||||
api.addPanel({
|
||||
id: 'ImageViewer',
|
||||
component: 'ImageViewer',
|
||||
title: 'Image Viewer',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getGenerationProgressPanel = (api: DockviewApi) => {
|
||||
return (
|
||||
api.getPanel('GenerationProgress') ||
|
||||
api.addPanel({
|
||||
id: 'GenerationProgress',
|
||||
component: 'GenerationProgress',
|
||||
title: 'Generation Progress',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const syncMainPanelLayout = (tab: TabName, api: DockviewApi) => {
|
||||
if (tab === 'generate') {
|
||||
const GenerateLaunchpad = getGenerateLaunchpadPanel(api);
|
||||
const ImageViewer = getImageViewerPanel(api);
|
||||
const GenerationProgress = getGenerationProgressPanel(api);
|
||||
const panelsToKeep = [GenerateLaunchpad.id, ImageViewer.id, GenerationProgress.id];
|
||||
for (const panel of api.panels) {
|
||||
if (!panelsToKeep.includes(panel.id)) {
|
||||
api.removePanel(panel);
|
||||
}
|
||||
}
|
||||
} else if (tab === 'canvas') {
|
||||
const CanvasLaunchpad = getCanvasLaunchpadPanel(api);
|
||||
const CanvasWorkspace = getCanvasWorkspacePanel(api);
|
||||
const ImageViewer = getImageViewerPanel(api);
|
||||
const GenerationProgress = getGenerationProgressPanel(api);
|
||||
const panelsToKeep = [CanvasLaunchpad.id, CanvasWorkspace.id, ImageViewer.id, GenerationProgress.id];
|
||||
for (const panel of api.panels) {
|
||||
if (!panelsToKeep.includes(panel.id)) {
|
||||
api.removePanel(panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MainPanel = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const [api, setApi] = useState<DockviewApi | null>(null);
|
||||
const onReady = useCallback<IDockviewReactProps['onReady']>((event) => {
|
||||
console.log('MainPanel onReady', event.api);
|
||||
setApi(event.api);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (api) {
|
||||
syncMainPanelLayout(tab, api);
|
||||
}
|
||||
}, [api, tab]);
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full">
|
||||
<DockviewReact
|
||||
disableDnd={true}
|
||||
locked={true}
|
||||
disableFloatingGroups={true}
|
||||
dndEdges={false}
|
||||
defaultTabComponent={TabWithoutCloseButton}
|
||||
components={dockviewComponents}
|
||||
onReady={onReady}
|
||||
theme={dockviewTheme}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
MainPanel.displayName = 'MainPanel';
|
||||
|
||||
export const gridviewComponents: IGridviewReactProps['components'] = {
|
||||
// Shared components
|
||||
Gallery: GalleryPanel,
|
||||
Boards: BoardsPanel,
|
||||
Main: MainPanel,
|
||||
GenerateLeft: GenerateLeftPanel,
|
||||
CanvasLeft: GenerateLeftPanel,
|
||||
CanvasLayers: CanvasLayersPanel,
|
||||
};
|
||||
|
||||
const syncGridviewLayout = (tab: TabName, api: GridviewApi) => {
|
||||
if (tab === 'generate') {
|
||||
const MainPanel =
|
||||
api.getPanel('Main') ??
|
||||
api.addPanel({
|
||||
id: 'Main',
|
||||
component: 'Main',
|
||||
});
|
||||
|
||||
const GenerateLeftPanel =
|
||||
api.getPanel('GenerateLeft') ??
|
||||
api.addPanel({
|
||||
id: 'GenerateLeft',
|
||||
component: 'GenerateLeft',
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: MainPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const GalleryPanel =
|
||||
api.getPanel('Gallery') ??
|
||||
api.addPanel({
|
||||
id: 'Gallery',
|
||||
component: 'Gallery',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 232,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: MainPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const BoardsPanel =
|
||||
api.getPanel('Boards') ??
|
||||
api.addPanel({
|
||||
id: 'Boards',
|
||||
component: 'Boards',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 36,
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: GalleryPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const panelsToKeep = [MainPanel.id, GenerateLeftPanel.id, GalleryPanel.id, BoardsPanel.id];
|
||||
for (const panel of api.panels) {
|
||||
if (!panelsToKeep.includes(panel.id)) {
|
||||
api.removePanel(panel);
|
||||
}
|
||||
}
|
||||
} else if (tab === 'canvas') {
|
||||
const MainPanel =
|
||||
api.getPanel('Main') ??
|
||||
api.addPanel({
|
||||
id: 'Main',
|
||||
component: 'Main',
|
||||
});
|
||||
|
||||
const CanvasLeftPanel =
|
||||
api.getPanel('CanvasLeft') ??
|
||||
api.addPanel({
|
||||
id: 'CanvasLeft',
|
||||
component: 'CanvasLeft',
|
||||
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
|
||||
position: {
|
||||
direction: 'left',
|
||||
referencePanel: MainPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const GalleryPanel =
|
||||
api.getPanel('Gallery') ??
|
||||
api.addPanel({
|
||||
id: 'Gallery',
|
||||
component: 'Gallery',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 232,
|
||||
position: {
|
||||
direction: 'right',
|
||||
referencePanel: MainPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const BoardsPanel =
|
||||
api.getPanel('Boards') ??
|
||||
api.addPanel({
|
||||
id: 'Boards',
|
||||
component: 'Boards',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 36,
|
||||
position: {
|
||||
direction: 'above',
|
||||
referencePanel: GalleryPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const CanvasLayersPanel =
|
||||
api.getPanel('CanvasLayers') ??
|
||||
api.addPanel({
|
||||
id: 'CanvasLayers',
|
||||
component: 'CanvasLayers',
|
||||
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
|
||||
minimumHeight: 232,
|
||||
position: {
|
||||
direction: 'below',
|
||||
referencePanel: GalleryPanel.id,
|
||||
},
|
||||
});
|
||||
|
||||
const panelsToKeep = [MainPanel.id, CanvasLeftPanel.id, GalleryPanel.id, BoardsPanel.id, CanvasLayersPanel.id];
|
||||
for (const panel of api.panels) {
|
||||
if (!panelsToKeep.includes(panel.id)) {
|
||||
api.removePanel(panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AutoLayout = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const [api, setApi] = useState<GridviewApi | null>(null);
|
||||
const syncLayout = useCallback((tab: TabName, api: GridviewApi) => {
|
||||
if (tab === 'generate') {
|
||||
initializeGenerateTabLayout(api);
|
||||
} else if (tab === 'canvas') {
|
||||
initializeCanvasTabLayout(api);
|
||||
}
|
||||
}, []);
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>((event) => {
|
||||
setApi(event.api);
|
||||
}, []);
|
||||
const $api = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
$api.set(event.api);
|
||||
},
|
||||
[$api]
|
||||
);
|
||||
useEffect(() => {
|
||||
const api = $api.get();
|
||||
if (api) {
|
||||
syncLayout(tab, api);
|
||||
syncGridviewLayout(tab, api);
|
||||
}
|
||||
}, [api, syncLayout, tab]);
|
||||
}, [$api, tab]);
|
||||
return (
|
||||
<AutoLayoutProvider api={api}>
|
||||
<AutoLayoutProvider $api={$api}>
|
||||
<GridviewReact
|
||||
className="dockview-theme-invoke"
|
||||
components={components}
|
||||
components={gridviewComponents}
|
||||
onReady={onReady}
|
||||
orientation={Orientation.VERTICAL}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { ContextMenu, Divider, Flex, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasAlertsInvocationProgress } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsInvocationProgress';
|
||||
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
|
||||
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
|
||||
import { CanvasContextMenuGlobalMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuGlobalMenuItems';
|
||||
import { CanvasContextMenuSelectedEntityMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems';
|
||||
import { CanvasDropArea } from 'features/controlLayers/components/CanvasDropArea';
|
||||
import { Filter } from 'features/controlLayers/components/Filters/Filter';
|
||||
import { CanvasHUD } from 'features/controlLayers/components/HUD/CanvasHUD';
|
||||
import { InvokeCanvasComponent } from 'features/controlLayers/components/InvokeCanvasComponent';
|
||||
import { SelectObject } from 'features/controlLayers/components/SelectObject/SelectObject';
|
||||
import { CanvasSessionContextProvider } from 'features/controlLayers/components/SimpleSession/context';
|
||||
import { StagingAreaItemsList } from 'features/controlLayers/components/SimpleSession/StagingAreaItemsList';
|
||||
import { StagingAreaToolbar } from 'features/controlLayers/components/StagingArea/StagingAreaToolbar';
|
||||
import { CanvasToolbar } from 'features/controlLayers/components/Toolbar/CanvasToolbar';
|
||||
import { Transform } from 'features/controlLayers/components/Transform/Transform';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectDynamicGrid, selectShowHUD } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi';
|
||||
|
||||
const MenuContent = memo(() => {
|
||||
return (
|
||||
<CanvasManagerProviderGate>
|
||||
<MenuList>
|
||||
<CanvasContextMenuSelectedEntityMenuItems />
|
||||
<CanvasContextMenuGlobalMenuItems />
|
||||
</MenuList>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
MenuContent.displayName = 'MenuContent';
|
||||
|
||||
const canvasBgSx = {
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
overflow: 'hidden',
|
||||
bg: 'base.900',
|
||||
'&[data-dynamic-grid="true"]': {
|
||||
bg: 'base.850',
|
||||
},
|
||||
};
|
||||
|
||||
export const CanvasWorkspacePanel = memo(() => {
|
||||
const dynamicGrid = useAppSelector(selectDynamicGrid);
|
||||
const showHUD = useAppSelector(selectShowHUD);
|
||||
const canvasId = useAppSelector(selectCanvasSessionId);
|
||||
|
||||
const renderMenu = useCallback(() => {
|
||||
return <MenuContent />;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
tabIndex={-1}
|
||||
borderRadius="base"
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
height="full"
|
||||
width="full"
|
||||
gap={2}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasToolbar />
|
||||
</CanvasManagerProviderGate>
|
||||
<Divider />
|
||||
<ContextMenu<HTMLDivElement> renderMenu={renderMenu} withLongPress={false}>
|
||||
{(ref) => (
|
||||
<Flex ref={ref} sx={canvasBgSx} data-dynamic-grid={dynamicGrid}>
|
||||
<InvokeCanvasComponent />
|
||||
<CanvasManagerProviderGate>
|
||||
<Flex
|
||||
position="absolute"
|
||||
flexDir="column"
|
||||
top={1}
|
||||
insetInlineStart={1}
|
||||
pointerEvents="none"
|
||||
gap={2}
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{showHUD && <CanvasHUD />}
|
||||
<CanvasAlertsSelectedEntityStatus />
|
||||
<CanvasAlertsPreserveMask />
|
||||
<CanvasAlertsInvocationProgress />
|
||||
</Flex>
|
||||
<Flex position="absolute" top={1} insetInlineEnd={1}>
|
||||
<Menu>
|
||||
<MenuButton as={IconButton} icon={<PiDotsThreeOutlineVerticalFill />} colorScheme="base" />
|
||||
<MenuContent />
|
||||
</Menu>
|
||||
</Flex>
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
)}
|
||||
</ContextMenu>
|
||||
{canvasId !== null && (
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasSessionContextProvider type="advanced" id={canvasId}>
|
||||
<Flex
|
||||
position="absolute"
|
||||
flexDir="column"
|
||||
bottom={4}
|
||||
gap={2}
|
||||
align="center"
|
||||
justify="center"
|
||||
left={4}
|
||||
right={4}
|
||||
>
|
||||
<Flex position="relative" maxW="full" w="full" h={108}>
|
||||
<StagingAreaItemsList />
|
||||
</Flex>
|
||||
<Flex gap={2}>
|
||||
<StagingAreaToolbar />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</CanvasSessionContextProvider>
|
||||
</CanvasManagerProviderGate>
|
||||
)}
|
||||
<Flex position="absolute" bottom={4}>
|
||||
<CanvasManagerProviderGate>
|
||||
<Filter />
|
||||
<Transform />
|
||||
<SelectObject />
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasDropArea />
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CanvasWorkspacePanel.displayName = 'CanvasPanel';
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
|
||||
import type { IDockviewPanelHeaderProps } from 'dockview';
|
||||
import { useCallback, useEffect, useId, useRef } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
export const TabWithoutCloseButton = (props: IDockviewPanelHeaderProps) => {
|
||||
const id = useId();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const setActive = useCallback(() => {
|
||||
if (!props.api.isActive) {
|
||||
@@ -13,18 +12,7 @@ export const TabWithoutCloseButton = (props: IDockviewPanelHeaderProps) => {
|
||||
}, [props.api]);
|
||||
|
||||
useCallbackOnDragEnter(setActive, ref, 300);
|
||||
|
||||
useEffect(() => {
|
||||
const el = document.querySelector(`[data-id="${id}"]`);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
const parentTab = el.closest('.dv-tab');
|
||||
if (!parentTab) {
|
||||
return;
|
||||
}
|
||||
parentTab.setAttribute('draggable', 'false');
|
||||
}, [id]);
|
||||
console.log(props.api.title);
|
||||
|
||||
return (
|
||||
<Flex ref={ref}>
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import type { GridviewApi } from 'dockview';
|
||||
import type { Atom } from 'nanostores';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
const AutoLayoutContext = createContext<GridviewApi | null>(null);
|
||||
const AutoLayoutContext = createContext<Atom<GridviewApi | null> | null>(null);
|
||||
|
||||
export const AutoLayoutProvider = (props: PropsWithChildren<{ api: GridviewApi | null }>) => {
|
||||
return <AutoLayoutContext.Provider value={props.api}>{props.children}</AutoLayoutContext.Provider>;
|
||||
export const AutoLayoutProvider = (props: PropsWithChildren<{ $api: Atom<GridviewApi | null> }>) => {
|
||||
return <AutoLayoutContext.Provider value={props.$api}>{props.children}</AutoLayoutContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAutoLayoutContext = () => {
|
||||
const api = useContext(AutoLayoutContext);
|
||||
if (!api) {
|
||||
throw new Error('useAutoLayoutContext must be used within an AutoLayoutProvider');
|
||||
}
|
||||
return api;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Box, ContextMenu, Divider, Flex, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
|
||||
import { $isLayoutLoading } from 'app/store/nanostores/globalIsLoading';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { GridviewApi, IDockviewReactProps, IGridviewReactProps } from 'dockview';
|
||||
import { DockviewReact, GridviewReact, Orientation } from 'dockview';
|
||||
@@ -8,13 +9,13 @@ import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/compone
|
||||
import { CanvasContextMenuGlobalMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuGlobalMenuItems';
|
||||
import { CanvasContextMenuSelectedEntityMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems';
|
||||
import { CanvasDropArea } from 'features/controlLayers/components/CanvasDropArea';
|
||||
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { CanvasLayersPanel } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { Filter } from 'features/controlLayers/components/Filters/Filter';
|
||||
import { CanvasHUD } from 'features/controlLayers/components/HUD/CanvasHUD';
|
||||
import { InvokeCanvasComponent } from 'features/controlLayers/components/InvokeCanvasComponent';
|
||||
import { SelectObject } from 'features/controlLayers/components/SelectObject/SelectObject';
|
||||
import { CanvasSessionContextProvider } from 'features/controlLayers/components/SimpleSession/context';
|
||||
import { InitialState } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { StagingAreaItemsList } from 'features/controlLayers/components/SimpleSession/StagingAreaItemsList';
|
||||
import { StagingAreaToolbar } from 'features/controlLayers/components/StagingArea/StagingAreaToolbar';
|
||||
import { CanvasToolbar } from 'features/controlLayers/components/Toolbar/CanvasToolbar';
|
||||
@@ -22,14 +23,15 @@ import { Transform } from 'features/controlLayers/components/Transform/Transform
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectDynamicGrid, selectShowHUD } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { BoardsPanel } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { GalleryPanel } from 'features/gallery/components/Gallery';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import ParametersPanelTextToImage from 'features/ui/components/ParametersPanels/ParametersPanelTextToImage';
|
||||
import { AutoLayoutProvider } from 'features/ui/layouts/auto-layout-context';
|
||||
import { components } from 'features/ui/layouts/components';
|
||||
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';
|
||||
@@ -60,7 +62,7 @@ const canvasBgSx = {
|
||||
},
|
||||
};
|
||||
|
||||
export const CanvasPanel = memo(() => {
|
||||
export const CanvasWorkspacePanel = memo(() => {
|
||||
const dynamicGrid = useAppSelector(selectDynamicGrid);
|
||||
const showHUD = useAppSelector(selectShowHUD);
|
||||
const canvasId = useAppSelector(selectCanvasSessionId);
|
||||
@@ -151,11 +153,11 @@ export const CanvasPanel = memo(() => {
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CanvasPanel.displayName = 'CanvasPanel';
|
||||
CanvasWorkspacePanel.displayName = 'CanvasPanel';
|
||||
|
||||
const LayersPanelContent = memo(() => (
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasLayersPanelContent />
|
||||
<CanvasLayersPanel />
|
||||
</CanvasManagerProviderGate>
|
||||
));
|
||||
LayersPanelContent.displayName = 'LayersPanelContent';
|
||||
@@ -177,8 +179,8 @@ const ProgressPanelContent = memo(() => (
|
||||
ProgressPanelContent.displayName = 'ProgressPanelContent';
|
||||
|
||||
const mainPanelComponents: IDockviewReactProps['components'] = {
|
||||
welcome: InitialState,
|
||||
canvas: CanvasPanel,
|
||||
canvasLaunchpad: GenerateLaunchpadPanel,
|
||||
canvas: CanvasWorkspacePanel,
|
||||
viewer: ViewerPanelContent,
|
||||
progress: ProgressPanelContent,
|
||||
};
|
||||
@@ -186,9 +188,9 @@ const mainPanelComponents: IDockviewReactProps['components'] = {
|
||||
const onReadyMainPanel: IDockviewReactProps['onReady'] = (event) => {
|
||||
const { api } = event;
|
||||
api.addPanel({
|
||||
id: 'welcome',
|
||||
component: 'welcome',
|
||||
title: 'Launchpad',
|
||||
id: 'canvasLaunchpad',
|
||||
component: 'canvasLaunchpad',
|
||||
title: 'canvasLaunchpad',
|
||||
});
|
||||
api.addPanel({
|
||||
id: 'canvas',
|
||||
@@ -196,7 +198,7 @@ const onReadyMainPanel: IDockviewReactProps['onReady'] = (event) => {
|
||||
title: 'Canvas',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: 'welcome',
|
||||
referencePanel: 'canvasLaunchpad',
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
@@ -205,7 +207,7 @@ const onReadyMainPanel: IDockviewReactProps['onReady'] = (event) => {
|
||||
title: 'Image Viewer',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: 'welcome',
|
||||
referencePanel: 'canvasLaunchpad',
|
||||
},
|
||||
});
|
||||
api.addPanel({
|
||||
@@ -214,7 +216,7 @@ const onReadyMainPanel: IDockviewReactProps['onReady'] = (event) => {
|
||||
title: 'Generation Progress',
|
||||
position: {
|
||||
direction: 'within',
|
||||
referencePanel: 'welcome',
|
||||
referencePanel: 'canvasLaunchpad',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -243,7 +245,7 @@ const MainPanel = memo(() => {
|
||||
disableFloatingGroups={true}
|
||||
dndEdges={false}
|
||||
defaultTabComponent={TabWithoutCloseButton}
|
||||
components={mainPanelComponents}
|
||||
components={components}
|
||||
onReady={onReadyMainPanel}
|
||||
theme={dockviewTheme}
|
||||
/>
|
||||
@@ -264,11 +266,13 @@ const Left = memo(() => {
|
||||
});
|
||||
Left.displayName = 'Left';
|
||||
|
||||
const Null = () => null;
|
||||
|
||||
export const canvasTabComponents: IGridviewReactProps['components'] = {
|
||||
left: Left,
|
||||
main: MainPanel,
|
||||
boards: BoardsListPanelContent,
|
||||
gallery: Gallery,
|
||||
boards: BoardsPanel,
|
||||
gallery: GalleryPanel,
|
||||
layers: LayersPanelContent,
|
||||
};
|
||||
|
||||
@@ -322,8 +326,10 @@ export const initializeCanvasTabLayout = (api: GridviewApi) => {
|
||||
export const CanvasTabAutoLayout = memo(() => {
|
||||
const [api, setApi] = useState<GridviewApi | null>(null);
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>((event) => {
|
||||
$isLayoutLoading.set(true);
|
||||
setApi(event.api);
|
||||
initializeCanvasTabLayout(event.api);
|
||||
$isLayoutLoading.set(false);
|
||||
}, []);
|
||||
return (
|
||||
<AutoLayoutProvider api={api}>
|
||||
|
||||
29
invokeai/frontend/web/src/features/ui/layouts/components.ts
Normal file
29
invokeai/frontend/web/src/features/ui/layouts/components.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { IDockviewReactProps, IGridviewReactProps } from 'dockview';
|
||||
import { CanvasLayersPanel } from 'features/controlLayers/components/CanvasLayersPanelContent';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
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 { CanvasWorkspacePanel } from 'features/ui/layouts/canvas-tab-auto-layout';
|
||||
import { GenerateLeftPanel } from 'features/ui/layouts/generate-tab-auto-layout';
|
||||
|
||||
export const components: IDockviewReactProps['components'] & IGridviewReactProps['components'] = {
|
||||
// Shared components
|
||||
Gallery: GalleryPanel,
|
||||
Boards: BoardsPanel,
|
||||
ImageViewer: ImageViewerPanel,
|
||||
GenerationProgress: GenerationProgressPanel,
|
||||
// Generate tab
|
||||
GenerateLaunchpad: GenerateLaunchpadPanel,
|
||||
GenerateLeft: GenerateLeftPanel,
|
||||
// Upscaling tab
|
||||
UpscalingLaunchpad: GenerateLaunchpadPanel,
|
||||
// Workflows tab
|
||||
WorkflowsLaunchpad: GenerateLaunchpadPanel,
|
||||
// Canvas tab
|
||||
CanvasLaunchpad: GenerateLaunchpadPanel,
|
||||
CanvasLayers: CanvasLayersPanel,
|
||||
CanvasWorkspace: CanvasWorkspacePanel,
|
||||
CanvasLeft: GenerateLeftPanel,
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Box, Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { $isLayoutLoading } from 'app/store/nanostores/globalIsLoading';
|
||||
import type { GridviewApi, IDockviewReactProps, IGridviewReactProps } from 'dockview';
|
||||
import { DockviewReact, GridviewReact, Orientation } from 'dockview';
|
||||
import { InitialState } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { BoardsListPanelContent } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { Gallery } from 'features/gallery/components/Gallery';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/InitialState';
|
||||
import { BoardsPanel } from 'features/gallery/components/BoardsListPanelContent';
|
||||
import { GalleryPanel } from 'features/gallery/components/Gallery';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
@@ -32,7 +33,7 @@ const ProgressPanelContent = memo(() => (
|
||||
ProgressPanelContent.displayName = 'ProgressPanelContent';
|
||||
|
||||
const mainPanelComponents: IDockviewReactProps['components'] = {
|
||||
welcome: InitialState,
|
||||
welcome: GenerateLaunchpadPanel,
|
||||
viewer: ViewerPanelContent,
|
||||
progress: ProgressPanelContent,
|
||||
};
|
||||
@@ -97,7 +98,7 @@ const MainPanel = memo(() => {
|
||||
});
|
||||
MainPanel.displayName = 'MainPanel';
|
||||
|
||||
const Left = memo(() => {
|
||||
export const GenerateLeftPanel = memo(() => {
|
||||
return (
|
||||
<Flex flexDir="column" w="full" h="full" gap={2} py={2} pe={2}>
|
||||
<QueueControls />
|
||||
@@ -107,13 +108,13 @@ const Left = memo(() => {
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
Left.displayName = 'Left';
|
||||
GenerateLeftPanel.displayName = 'GenerateLeftPanel';
|
||||
|
||||
export const generateTabComponents: IGridviewReactProps['components'] = {
|
||||
left: Left,
|
||||
left: GenerateLeftPanel,
|
||||
main: MainPanel,
|
||||
boards: BoardsListPanelContent,
|
||||
gallery: Gallery,
|
||||
boards: BoardsPanel,
|
||||
gallery: GalleryPanel,
|
||||
};
|
||||
|
||||
export const initializeGenerateTabLayout = (api: GridviewApi) => {
|
||||
@@ -157,9 +158,10 @@ export const initializeGenerateTabLayout = (api: GridviewApi) => {
|
||||
export const GenerateTabAutoLayout = memo(() => {
|
||||
const [api, setApi] = useState<GridviewApi | null>(null);
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>((event) => {
|
||||
console.log('GenerateTabAutoLayout onReady');
|
||||
$isLayoutLoading.set(true);
|
||||
setApi(event.api);
|
||||
initializeGenerateTabLayout(event.api);
|
||||
$isLayoutLoading.set(false);
|
||||
}, []);
|
||||
return (
|
||||
<AutoLayoutProvider api={api}>
|
||||
|
||||
Reference in New Issue
Block a user