From 214005d795382acbb152b0c6df3cbde075bdd943 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:57:04 +1000 Subject: [PATCH] feat(ui): generation progress tab improvements --- .../system/components/ProgressBar.tsx | 4 ++- .../ui/layouts/TabWithoutCloseButton.tsx | 4 ++- ...outCloseButtonAndWithProgressIndicator.tsx | 31 +++++++++++++++++++ .../ui/layouts/canvas-tab-auto-layout.tsx | 14 ++++++++- .../ui/layouts/generate-tab-auto-layout.tsx | 13 +++++++- .../web/src/features/ui/layouts/shared.ts | 3 ++ .../ui/layouts/upscaling-tab-auto-layout.tsx | 13 +++++++- .../ui/layouts/workflows-tab-auto-layout.tsx | 14 ++++++++- .../ui/styles/dockview-theme-invoke.css | 5 +-- 9 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButtonAndWithProgressIndicator.tsx diff --git a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx index 218ca382b8..5cb2a96660 100644 --- a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx +++ b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx @@ -1,3 +1,4 @@ +import type { ProgressProps } from '@invoke-ai/ui-library'; import { Progress } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; import { memo, useMemo } from 'react'; @@ -5,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; import { $isConnected, $lastProgressEvent } from 'services/events/stores'; -const ProgressBar = () => { +const ProgressBar = (props: ProgressProps) => { const { t } = useTranslation(); const { data: queueStatus } = useGetQueueStatusQuery(); const isConnected = useStore($isConnected); @@ -45,6 +46,7 @@ const ProgressBar = () => { h={2} w="full" colorScheme="invokeBlue" + {...props} /> ); }; diff --git a/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButton.tsx b/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButton.tsx index 84dbb1e7ad..71638d93e3 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButton.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButton.tsx @@ -15,7 +15,9 @@ export const TabWithoutCloseButton = (props: IDockviewPanelHeaderProps) => { return ( - {props.api.title ?? props.api.id} + + {props.api.title ?? props.api.id} + ); }; diff --git a/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButtonAndWithProgressIndicator.tsx b/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButtonAndWithProgressIndicator.tsx new file mode 100644 index 0000000000..3d1b960abf --- /dev/null +++ b/invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButtonAndWithProgressIndicator.tsx @@ -0,0 +1,31 @@ +import { Flex, Text } from '@invoke-ai/ui-library'; +import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter'; +import type { IDockviewPanelHeaderProps } from 'dockview'; +import ProgressBar from 'features/system/components/ProgressBar'; +import { useCallback, useRef } from 'react'; +import { useIsGenerationInProgress } from 'services/api/endpoints/queue'; + +export const TabWithoutCloseButtonAndWithProgressIndicator = (props: IDockviewPanelHeaderProps) => { + const isGenerationInProgress = useIsGenerationInProgress(); + + const ref = useRef(null); + const setActive = useCallback(() => { + if (!props.api.isActive) { + props.api.setActive(); + } + }, [props.api]); + + useCallbackOnDragEnter(setActive, ref, 300); + + return ( + + + {props.api.title ?? props.api.id} + + {isGenerationInProgress && ( + + )} + + ); +}; +TabWithoutCloseButtonAndWithProgressIndicator.displayName = 'TabWithoutCloseButtonAndWithProgressIndicator'; diff --git a/invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx b/invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx index b4e5971972..727b5342b2 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx @@ -18,6 +18,7 @@ import { CanvasTabLeftPanel } from './CanvasTabLeftPanel'; import { CanvasWorkspacePanel } from './CanvasWorkspacePanel'; import { BOARDS_PANEL_ID, + DEFAULT_TAB_ID, GALLERY_PANEL_ID, LAUNCHPAD_PANEL_ID, LAYERS_PANEL_ID, @@ -28,11 +29,18 @@ import { RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX, SETTINGS_PANEL_ID, + TAB_WITH_PROGRESS_INDICATOR_ID, VIEWER_PANEL_ID, WORKSPACE_PANEL_ID, } from './shared'; +import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator'; import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible'; +const tabComponents = { + [DEFAULT_TAB_ID]: TabWithoutCloseButton, + [TAB_WITH_PROGRESS_INDICATOR_ID]: TabWithoutCloseButtonAndWithProgressIndicator, +}; + const centerPanelComponents: IDockviewReactProps['components'] = { [LAUNCHPAD_PANEL_ID]: CanvasLaunchpadPanel, [WORKSPACE_PANEL_ID]: CanvasWorkspacePanel, @@ -45,11 +53,13 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: LAUNCHPAD_PANEL_ID, component: LAUNCHPAD_PANEL_ID, title: 'Launchpad', + tabComponent: DEFAULT_TAB_ID, }); api.addPanel({ id: WORKSPACE_PANEL_ID, component: WORKSPACE_PANEL_ID, title: 'Canvas', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -59,6 +69,7 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: VIEWER_PANEL_ID, component: VIEWER_PANEL_ID, title: 'Image Viewer', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -68,6 +79,7 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: PROGRESS_PANEL_ID, component: PROGRESS_PANEL_ID, title: 'Generation Progress', + tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -107,10 +119,10 @@ const CenterPanel = memo(() => { locked={true} disableFloatingGroups={true} dndEdges={false} - defaultTabComponent={TabWithoutCloseButton} components={centerPanelComponents} onReady={onReady} theme={dockviewTheme} + tabComponents={tabComponents} /> diff --git a/invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx b/invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx index ec87e58d46..d2254db0d3 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx @@ -16,6 +16,7 @@ import { memo, useCallback, useRef, useState } from 'react'; import { GenerateTabLeftPanel } from './GenerateTabLeftPanel'; import { BOARDS_PANEL_ID, + DEFAULT_TAB_ID, GALLERY_PANEL_ID, LAUNCHPAD_PANEL_ID, LEFT_PANEL_ID, @@ -25,10 +26,17 @@ import { RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX, SETTINGS_PANEL_ID, + TAB_WITH_PROGRESS_INDICATOR_ID, VIEWER_PANEL_ID, } from './shared'; +import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator'; import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible'; +const tabComponents = { + [DEFAULT_TAB_ID]: TabWithoutCloseButton, + [TAB_WITH_PROGRESS_INDICATOR_ID]: TabWithoutCloseButtonAndWithProgressIndicator, +}; + const centerPanelComponents: IDockviewReactProps['components'] = { [LAUNCHPAD_PANEL_ID]: GenerateLaunchpadPanel, [VIEWER_PANEL_ID]: ImageViewerPanel, @@ -40,11 +48,13 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: LAUNCHPAD_PANEL_ID, component: LAUNCHPAD_PANEL_ID, title: 'Launchpad', + tabComponent: DEFAULT_TAB_ID, }); api.addPanel({ id: VIEWER_PANEL_ID, component: VIEWER_PANEL_ID, title: 'Image Viewer', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -54,6 +64,7 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: PROGRESS_PANEL_ID, component: PROGRESS_PANEL_ID, title: 'Generation Progress', + tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -93,7 +104,7 @@ const CenterPanel = memo(() => { locked={true} disableFloatingGroups={true} dndEdges={false} - defaultTabComponent={TabWithoutCloseButton} + tabComponents={tabComponents} components={centerPanelComponents} onReady={onReady} theme={dockviewTheme} diff --git a/invokeai/frontend/web/src/features/ui/layouts/shared.ts b/invokeai/frontend/web/src/features/ui/layouts/shared.ts index 6c160e295e..ca120634c8 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/shared.ts +++ b/invokeai/frontend/web/src/features/ui/layouts/shared.ts @@ -13,5 +13,8 @@ export const LAYERS_PANEL_ID = 'layers'; export const SETTINGS_PANEL_ID = 'settings'; +export const DEFAULT_TAB_ID = 'default-tab'; +export const TAB_WITH_PROGRESS_INDICATOR_ID = 'tab-with-progress-indicator'; + export const LEFT_PANEL_MIN_SIZE_PX = 420; export const RIGHT_PANEL_MIN_SIZE_PX = 420; diff --git a/invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx b/invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx index a0a57b96f2..d805e0feb8 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx @@ -15,6 +15,7 @@ import { memo, useCallback, useRef, useState } from 'react'; import { BOARDS_PANEL_ID, + DEFAULT_TAB_ID, GALLERY_PANEL_ID, LAUNCHPAD_PANEL_ID, LEFT_PANEL_ID, @@ -24,11 +25,18 @@ import { RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX, SETTINGS_PANEL_ID, + TAB_WITH_PROGRESS_INDICATOR_ID, VIEWER_PANEL_ID, } from './shared'; +import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator'; import { UpscalingTabLeftPanel } from './UpscalingTabLeftPanel'; import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible'; +const tabComponents = { + [DEFAULT_TAB_ID]: TabWithoutCloseButton, + [TAB_WITH_PROGRESS_INDICATOR_ID]: TabWithoutCloseButtonAndWithProgressIndicator, +}; + const centerComponents: IDockviewReactProps['components'] = { [LAUNCHPAD_PANEL_ID]: UpscalingLaunchpadPanel, [VIEWER_PANEL_ID]: ImageViewerPanel, @@ -40,11 +48,13 @@ const initializeCenterLayout = (api: DockviewApi) => { id: LAUNCHPAD_PANEL_ID, component: LAUNCHPAD_PANEL_ID, title: 'Launchpad', + tabComponent: DEFAULT_TAB_ID, }); api.addPanel({ id: VIEWER_PANEL_ID, component: VIEWER_PANEL_ID, title: 'Image Viewer', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -54,6 +64,7 @@ const initializeCenterLayout = (api: DockviewApi) => { id: PROGRESS_PANEL_ID, component: PROGRESS_PANEL_ID, title: 'Generation Progress', + tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -92,7 +103,7 @@ const CenterPanel = memo(() => { locked={true} disableFloatingGroups={true} dndEdges={false} - defaultTabComponent={TabWithoutCloseButton} + tabComponents={tabComponents} components={centerComponents} onReady={onReady} theme={dockviewTheme} diff --git a/invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx b/invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx index 966e43cf88..dfb539dcd7 100644 --- a/invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx +++ b/invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx @@ -17,6 +17,7 @@ import { memo, useCallback, useRef, useState } from 'react'; import { BOARDS_PANEL_ID, + DEFAULT_TAB_ID, GALLERY_PANEL_ID, LAUNCHPAD_PANEL_ID, LEFT_PANEL_ID, @@ -26,11 +27,18 @@ import { RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX, SETTINGS_PANEL_ID, + TAB_WITH_PROGRESS_INDICATOR_ID, VIEWER_PANEL_ID, WORKSPACE_PANEL_ID, } from './shared'; +import { TabWithoutCloseButtonAndWithProgressIndicator } from './TabWithoutCloseButtonAndWithProgressIndicator'; import { useResizeMainPanelOnFirstVisit } from './use-on-first-visible'; +const tabComponents = { + [DEFAULT_TAB_ID]: TabWithoutCloseButton, + [TAB_WITH_PROGRESS_INDICATOR_ID]: TabWithoutCloseButtonAndWithProgressIndicator, +}; + const centerPanelComponents: IDockviewReactProps['components'] = { [LAUNCHPAD_PANEL_ID]: WorkflowsLaunchpadPanel, [WORKSPACE_PANEL_ID]: NodeEditor, @@ -43,11 +51,13 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: LAUNCHPAD_PANEL_ID, component: LAUNCHPAD_PANEL_ID, title: 'Launchpad', + tabComponent: DEFAULT_TAB_ID, }); api.addPanel({ id: WORKSPACE_PANEL_ID, component: WORKSPACE_PANEL_ID, title: 'Workflow Editor', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -57,6 +67,7 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: VIEWER_PANEL_ID, component: VIEWER_PANEL_ID, title: 'Image Viewer', + tabComponent: DEFAULT_TAB_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -66,6 +77,7 @@ const initializeCenterPanelLayout = (api: DockviewApi) => { id: PROGRESS_PANEL_ID, component: PROGRESS_PANEL_ID, title: 'Generation Progress', + tabComponent: TAB_WITH_PROGRESS_INDICATOR_ID, position: { direction: 'within', referencePanel: LAUNCHPAD_PANEL_ID, @@ -105,7 +117,7 @@ const CenterPanel = memo(() => { locked={true} disableFloatingGroups={true} dndEdges={false} - defaultTabComponent={TabWithoutCloseButton} + tabComponents={tabComponents} components={centerPanelComponents} onReady={onReady} theme={dockviewTheme} diff --git a/invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css b/invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css index 49aab65e53..32acdb7378 100644 --- a/invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css +++ b/invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css @@ -57,8 +57,9 @@ .dv-tab { /* margin-right: 2px; */ - padding-inline-start: var(--invoke-sizes-4); - padding-inline-end: var(--invoke-sizes-4); + /* padding-inline-start: var(--invoke-sizes-4); + padding-inline-end: var(--invoke-sizes-4); */ + padding: 0px; } .dv-inactive-group .dv-tabs-container.dv-horizontal .dv-tab:not(:first-child)::before {