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 {