This commit is contained in:
psychedelicious
2025-06-19 12:42:42 +10:00
parent 7f44da4902
commit 6eecdca56c
21 changed files with 641 additions and 205 deletions

View File

@@ -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 />

View File

@@ -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;
});

View File

@@ -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}>

View File

@@ -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';

View File

@@ -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';

View File

@@ -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">

View File

@@ -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';

View File

@@ -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';

View File

@@ -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(() => {

View File

@@ -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';

View File

@@ -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';

View File

@@ -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';

View File

@@ -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>
</>

View File

@@ -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}

View File

@@ -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}
/>

View File

@@ -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';

View File

@@ -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}>

View File

@@ -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;
};

View File

@@ -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}>

View 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,
};

View File

@@ -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}>