Skip to content

Commit a968f18

Browse files
feat(ui): canvas/viewer panel tabs show progress
1 parent 0161054 commit a968f18

17 files changed

+206
-105
lines changed

invokeai/frontend/web/public/locales/en.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,7 +1962,6 @@
19621962
"recalculateRects": "Recalculate Rects",
19631963
"clipToBbox": "Clip Strokes to Bbox",
19641964
"outputOnlyMaskedRegions": "Output Only Generated Regions",
1965-
"saveAllImagesToGallery": "Save All Images to Gallery",
19661965
"addLayer": "Add Layer",
19671966
"duplicate": "Duplicate",
19681967
"moveToFront": "Move to Front",
@@ -2332,7 +2331,8 @@
23322331
"alert": "Preserving Masked Region"
23332332
},
23342333
"saveAllImagesToGallery": {
2335-
"alert": "Sending images to Gallery, bypassing Canvas"
2334+
"label": "Send New Generations to Gallery",
2335+
"alert": "Sending new generations to Gallery, bypassing Canvas"
23362336
},
23372337
"isolatedStagingPreview": "Isolated Staging Preview",
23382338
"isolatedPreview": "Isolated Preview",

invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsSaveAllImagesToGalleryCheckbox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const CanvasSettingsSaveAllImagesToGalleryCheckbox = memo(() => {
1616
}, [dispatch]);
1717
return (
1818
<FormControl w="full">
19-
<FormLabel flexGrow={1}>{t('controlLayers.saveAllImagesToGallery')}</FormLabel>
19+
<FormLabel flexGrow={1}>{t('controlLayers.settings.saveAllImagesToGallery.label')}</FormLabel>
2020
<Checkbox isChecked={saveAllImagesToGallery} onChange={onChange} />
2121
</FormControl>
2222
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useGetCurrentQueueItemQuery } from 'services/api/endpoints/queue';
2+
3+
export const useCurrentQueueItemDestination = () => {
4+
const { currentQueueItemDestination } = useGetCurrentQueueItemQuery(undefined, {
5+
selectFromResult: ({ data }) => ({
6+
currentQueueItemDestination: data?.destination ?? null,
7+
}),
8+
});
9+
10+
return currentQueueItemDestination;
11+
};

invokeai/frontend/web/src/features/queue/hooks/useCurrentQueueItemId.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
1+
import { useGetCurrentQueueItemQuery } from 'services/api/endpoints/queue';
22

33
export const useCurrentQueueItemId = () => {
4-
const { currentQueueItemId } = useGetQueueStatusQuery(undefined, {
4+
const { currentQueueItemId } = useGetCurrentQueueItemQuery(undefined, {
55
selectFromResult: ({ data }) => ({
6-
currentQueueItemId: data?.queue.item_id ?? null,
6+
currentQueueItemId: data?.item_id ?? null,
77
}),
88
});
99

invokeai/frontend/web/src/features/queue/hooks/useInvoke.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useStore } from '@nanostores/react';
22
import { logger } from 'app/logging/logger';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { withResultAsync } from 'common/util/result';
5+
import { selectSaveAllImagesToGallery } from 'features/controlLayers/store/canvasSettingsSlice';
56
import { useIsWorkflowEditorLocked } from 'features/nodes/hooks/useIsWorkflowEditorLocked';
67
import { useEnqueueWorkflows } from 'features/queue/hooks/useEnqueueWorkflows';
78
import { $isReadyToEnqueue } from 'features/queue/store/readiness';
@@ -15,7 +16,6 @@ import { enqueueMutationFixedCacheKeyOptions, useEnqueueBatchMutation } from 'se
1516
import { useEnqueueCanvas } from './useEnqueueCanvas';
1617
import { useEnqueueGenerate } from './useEnqueueGenerate';
1718
import { useEnqueueUpscaling } from './useEnqueueUpscaling';
18-
import { selectSaveAllImagesToGallery } from 'features/controlLayers/store/canvasSettingsSlice';
1919

2020
const log = logger('generation');
2121

@@ -75,7 +75,7 @@ export const useInvoke = () => {
7575
} else if (tabName === 'canvas') {
7676
navigationApi.focusPanel(tabName, WORKSPACE_PANEL_ID);
7777
}
78-
}, [enqueue, tabName]);
78+
}, [enqueue, saveAllImagesToGallery, tabName]);
7979

8080
const enqueueFront = useCallback(() => {
8181
enqueue(true, false);
@@ -90,7 +90,7 @@ export const useInvoke = () => {
9090
} else if (tabName === 'canvas') {
9191
navigationApi.focusPanel(tabName, WORKSPACE_PANEL_ID);
9292
}
93-
}, [enqueue, tabName]);
93+
}, [enqueue, saveAllImagesToGallery, tabName]);
9494

9595
return { enqueueBack, enqueueFront, isLoading, isDisabled: !isReady || isLocked, enqueue };
9696
};

invokeai/frontend/web/src/features/ui/layouts/TabWithoutCloseButton.tsx renamed to invokeai/frontend/web/src/features/ui/layouts/DockviewTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { memo, useCallback, useRef } from 'react';
77
import type { PanelParameters } from './auto-layout-context';
88
import { useHackOutDvTabDraggable } from './use-hack-out-dv-tab-draggable';
99

10-
export const TabWithoutCloseButton = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
10+
export const DockviewTab = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
1111
const ref = useRef<HTMLDivElement>(null);
1212
const setActive = useCallback(() => {
1313
if (!props.api.isActive) {
@@ -31,4 +31,4 @@ export const TabWithoutCloseButton = memo((props: IDockviewPanelHeaderProps<Pane
3131
</Flex>
3232
);
3333
});
34-
TabWithoutCloseButton.displayName = 'TabWithoutCloseButton';
34+
DockviewTab.displayName = 'DockviewTab';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Flex, Text } from '@invoke-ai/ui-library';
2+
import { setFocusedRegion } from 'common/hooks/focus';
3+
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
4+
import type { IDockviewPanelHeaderProps } from 'dockview';
5+
import { useCurrentQueueItemDestination } from 'features/queue/hooks/useCurrentQueueItemDestination';
6+
import ProgressBar from 'features/system/components/ProgressBar';
7+
import { memo, useCallback, useRef } from 'react';
8+
import { useIsGenerationInProgress } from 'services/api/endpoints/queue';
9+
10+
import type { PanelParameters } from './auto-layout-context';
11+
import { useHackOutDvTabDraggable } from './use-hack-out-dv-tab-draggable';
12+
13+
export const DockviewTabCanvasViewer = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
14+
const isGenerationInProgress = useIsGenerationInProgress();
15+
const currentQueueItemDestination = useCurrentQueueItemDestination();
16+
17+
const ref = useRef<HTMLDivElement>(null);
18+
const setActive = useCallback(() => {
19+
if (!props.api.isActive) {
20+
props.api.setActive();
21+
}
22+
}, [props.api]);
23+
24+
useCallbackOnDragEnter(setActive, ref, 300);
25+
26+
const onPointerDown = useCallback(() => {
27+
setFocusedRegion(props.params.focusRegion);
28+
}, [props.params.focusRegion]);
29+
30+
useHackOutDvTabDraggable(ref);
31+
32+
return (
33+
<Flex ref={ref} position="relative" alignItems="center" h="full" onPointerDown={onPointerDown}>
34+
<Text userSelect="none" px={4}>
35+
{props.api.title ?? props.api.id}
36+
</Text>
37+
{currentQueueItemDestination === 'canvas' && isGenerationInProgress && (
38+
<ProgressBar position="absolute" bottom={0} left={0} right={0} h={1} borderRadius="none" />
39+
)}
40+
</Flex>
41+
);
42+
});
43+
DockviewTabCanvasViewer.displayName = 'DockviewTabCanvasViewer';
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Flex, Text } from '@invoke-ai/ui-library';
2+
import { useAppSelector } from 'app/store/storeHooks';
3+
import { setFocusedRegion } from 'common/hooks/focus';
4+
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
5+
import type { IDockviewPanelHeaderProps } from 'dockview';
6+
import { selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
7+
import { useCurrentQueueItemDestination } from 'features/queue/hooks/useCurrentQueueItemDestination';
8+
import ProgressBar from 'features/system/components/ProgressBar';
9+
import { memo, useCallback, useRef } from 'react';
10+
import { useIsGenerationInProgress } from 'services/api/endpoints/queue';
11+
12+
import type { PanelParameters } from './auto-layout-context';
13+
import { useHackOutDvTabDraggable } from './use-hack-out-dv-tab-draggable';
14+
15+
export const DockviewTabCanvasWorkspace = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
16+
const isGenerationInProgress = useIsGenerationInProgress();
17+
const canvasSessionId = useAppSelector(selectCanvasSessionId);
18+
const currentQueueItemDestination = useCurrentQueueItemDestination();
19+
20+
const ref = useRef<HTMLDivElement>(null);
21+
const setActive = useCallback(() => {
22+
if (!props.api.isActive) {
23+
props.api.setActive();
24+
}
25+
}, [props.api]);
26+
27+
useCallbackOnDragEnter(setActive, ref, 300);
28+
29+
const onPointerDown = useCallback(() => {
30+
setFocusedRegion(props.params.focusRegion);
31+
}, [props.params.focusRegion]);
32+
33+
useHackOutDvTabDraggable(ref);
34+
35+
return (
36+
<Flex ref={ref} position="relative" alignItems="center" h="full" onPointerDown={onPointerDown}>
37+
<Text userSelect="none" px={4}>
38+
{props.api.title ?? props.api.id}
39+
</Text>
40+
{currentQueueItemDestination === canvasSessionId && isGenerationInProgress && (
41+
<ProgressBar position="absolute" bottom={0} left={0} right={0} h={1} borderRadius="none" />
42+
)}
43+
</Flex>
44+
);
45+
});
46+
DockviewTabCanvasWorkspace.displayName = 'DockviewTabCanvasWorkspace';

invokeai/frontend/web/src/features/ui/layouts/TabWithLaunchpadIcon.tsx renamed to invokeai/frontend/web/src/features/ui/layouts/DockviewTabLaunchpad.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const TAB_ICONS: Record<TabName, IconType> = {
2727
queue: PiQueueBold,
2828
};
2929

30-
export const TabWithLaunchpadIcon = memo((props: IDockviewPanelHeaderProps) => {
30+
export const DockviewTabLaunchpad = memo((props: IDockviewPanelHeaderProps) => {
3131
const ref = useRef<HTMLDivElement>(null);
3232
const activeTab = useAppSelector(selectActiveTab);
3333

@@ -52,4 +52,4 @@ export const TabWithLaunchpadIcon = memo((props: IDockviewPanelHeaderProps) => {
5252
</Flex>
5353
);
5454
});
55-
TabWithLaunchpadIcon.displayName = 'TabWithLaunchpadIcon';
55+
DockviewTabLaunchpad.displayName = 'DockviewTabLaunchpad';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Flex, Text } from '@invoke-ai/ui-library';
2+
import { setFocusedRegion } from 'common/hooks/focus';
3+
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
4+
import type { IDockviewPanelHeaderProps } from 'dockview';
5+
import ProgressBar from 'features/system/components/ProgressBar';
6+
import { memo, useCallback, useRef } from 'react';
7+
import { useIsGenerationInProgress } from 'services/api/endpoints/queue';
8+
9+
import type { PanelParameters } from './auto-layout-context';
10+
import { useHackOutDvTabDraggable } from './use-hack-out-dv-tab-draggable';
11+
12+
export const DockviewTabProgress = memo((props: IDockviewPanelHeaderProps<PanelParameters>) => {
13+
const isGenerationInProgress = useIsGenerationInProgress();
14+
15+
const ref = useRef<HTMLDivElement>(null);
16+
const setActive = useCallback(() => {
17+
if (!props.api.isActive) {
18+
props.api.setActive();
19+
}
20+
}, [props.api]);
21+
22+
useCallbackOnDragEnter(setActive, ref, 300);
23+
24+
const onPointerDown = useCallback(() => {
25+
setFocusedRegion(props.params.focusRegion);
26+
}, [props.params.focusRegion]);
27+
28+
useHackOutDvTabDraggable(ref);
29+
30+
return (
31+
<Flex ref={ref} position="relative" alignItems="center" h="full" onPointerDown={onPointerDown}>
32+
<Text userSelect="none" px={4}>
33+
{props.api.title ?? props.api.id}
34+
</Text>
35+
{isGenerationInProgress && (
36+
<ProgressBar position="absolute" bottom={0} left={0} right={0} h={1} borderRadius="none" />
37+
)}
38+
</Flex>
39+
);
40+
});
41+
DockviewTabProgress.displayName = 'DockviewTabProgress';

0 commit comments

Comments
 (0)