mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): revise generation mode logic
- Canvas generation mode is replace with a boolean `sendToCanvas` flag. When off, images generated on the canvas go to the gallery. When on, they get added to the staging area. - When an image result is received, if its destination is the canvas, staging is automatically started. - Updated queue list to show the destination column. - Added `IconSwitch` component to represent binary choices, used for the new `sendToCanvas` flag and image viewer toggle. - Remove the queue actions menu in `QueueControls`. Move the queue count badge to the cancel button. - Redo layout of `QueueControls` to prevent duplicate queue count badges. - Fix issue where gallery and options panels could show thru transparent regions of queue tab. - Disable panel hotkeys when on mm/queue tabs.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
|
||||
@@ -7,6 +7,7 @@ import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import { useIsImageViewerOpen } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||
import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
|
||||
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
|
||||
import ParametersPanelTextToImage from 'features/ui/components/ParametersPanels/ParametersPanelTextToImage';
|
||||
@@ -89,8 +90,14 @@ export const AppContent = memo(() => {
|
||||
|
||||
const galleryPanel = usePanel(galleryPanelUsePanelOptions);
|
||||
|
||||
useHotkeys('g', galleryPanel.toggle, [galleryPanel.toggle]);
|
||||
useHotkeys(['t', 'o'], optionsPanel.toggle, [optionsPanel.toggle]);
|
||||
useHotkeys('g', galleryPanel.toggle, { enabled: shouldShowGalleryPanel }, [
|
||||
galleryPanel.toggle,
|
||||
shouldShowGalleryPanel,
|
||||
]);
|
||||
useHotkeys(['t', 'o'], optionsPanel.toggle, { enabled: shouldShowOptionsPanel }, [
|
||||
optionsPanel.toggle,
|
||||
shouldShowOptionsPanel,
|
||||
]);
|
||||
useHotkeys(
|
||||
'shift+r',
|
||||
() => {
|
||||
@@ -133,21 +140,26 @@ export const AppContent = memo(() => {
|
||||
storage={panelStorage}
|
||||
>
|
||||
<Panel order={0} collapsible style={panelStyles} {...optionsPanel.panelProps}>
|
||||
<TabMountGate tab="generation">
|
||||
<TabVisibilityGate tab="generation">
|
||||
<ParametersPanelTextToImage />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="upscaling">
|
||||
<TabVisibilityGate tab="upscaling">
|
||||
<ParametersPanelUpscale />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="workflows">
|
||||
<TabVisibilityGate tab="workflows">
|
||||
<NodeEditorPanelGroup />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<TabMountGate tab="generation">
|
||||
<TabVisibilityGate tab="generation">
|
||||
<ParametersPanelTextToImage />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="upscaling">
|
||||
<TabVisibilityGate tab="upscaling">
|
||||
<ParametersPanelUpscale />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
<TabMountGate tab="workflows">
|
||||
<TabVisibilityGate tab="workflows">
|
||||
<NodeEditorPanelGroup />
|
||||
</TabVisibilityGate>
|
||||
</TabMountGate>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Panel>
|
||||
<ResizeHandle id="options-main-handle" orientation="vertical" {...optionsPanel.resizeHandleProps} />
|
||||
<Panel id="main-panel" order={1} minSize={20} style={panelStyles}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { ButtonGroup, Flex, Icon, IconButton, Portal, spinAnimation } from '@invoke-ai/ui-library';
|
||||
import CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton';
|
||||
import { ClearAllQueueIconButton } from 'features/queue/components/ClearQueueIconButton';
|
||||
import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip';
|
||||
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
|
||||
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
|
||||
import type { UsePanelReturn } from 'features/ui/hooks/usePanel';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCircleNotchBold, PiSlidersHorizontalBold } from 'react-icons/pi';
|
||||
import { PiCircleNotchBold, PiSlidersHorizontalBold, PiTrashSimpleBold } from 'react-icons/pi';
|
||||
import { RiSparklingFill } from 'react-icons/ri';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
@@ -23,6 +23,7 @@ type Props = {
|
||||
const FloatingSidePanelButtons = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { queueBack, isLoading, isDisabled } = useQueueBack();
|
||||
const clearQueue = useClearQueue();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
|
||||
const queueButtonIcon = useMemo(() => {
|
||||
@@ -71,7 +72,17 @@ const FloatingSidePanelButtons = (props: Props) => {
|
||||
</QueueButtonTooltip>
|
||||
<CancelCurrentQueueItemIconButton sx={floatingButtonStyles} />
|
||||
</ButtonGroup>
|
||||
<ClearAllQueueIconButton sx={floatingButtonStyles} />
|
||||
<IconButton
|
||||
isDisabled={clearQueue.isDisabled}
|
||||
isLoading={clearQueue.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
tooltip={t('queue.clearTooltip')}
|
||||
icon={<PiTrashSimpleBold />}
|
||||
colorScheme="error"
|
||||
onClick={clearQueue.openDialog}
|
||||
data-testid={t('queue.clear')}
|
||||
sx={floatingButtonStyles}
|
||||
/>
|
||||
</Flex>
|
||||
</Portal>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import { selectIsSDXL } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
|
||||
import { CompositingSettingsAccordion } from 'features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion';
|
||||
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
|
||||
@@ -64,7 +63,6 @@ const ParametersPanelTextToImage = () => {
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full" flexDir="column" gap={2}>
|
||||
<QueueControls />
|
||||
<StylePresetMenuTrigger />
|
||||
<Flex w="full" h="full" position="relative">
|
||||
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
|
||||
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
|
||||
import { UpscaleSettingsAccordion } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion';
|
||||
@@ -23,7 +22,6 @@ const ParametersPanelUpscale = () => {
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full" flexDir="column" gap={2}>
|
||||
<QueueControls />
|
||||
<StylePresetMenuTrigger />
|
||||
<Flex w="full" h="full" position="relative">
|
||||
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
|
||||
|
||||
@@ -3,30 +3,33 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { memo, type ReactElement, useCallback } from 'react';
|
||||
import { forwardRef, memo, type ReactElement, useCallback } from 'react';
|
||||
|
||||
export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(setActiveTab(tab));
|
||||
}, [dispatch, tab]);
|
||||
export const TabButton = memo(
|
||||
forwardRef(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }, ref) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(setActiveTab(tab));
|
||||
}, [dispatch, tab]);
|
||||
|
||||
return (
|
||||
<Tooltip label={label} placement="end">
|
||||
<IconButton
|
||||
p={0}
|
||||
onClick={onClick}
|
||||
icon={icon}
|
||||
size="md"
|
||||
fontSize="24px"
|
||||
variant="appTab"
|
||||
data-selected={activeTabName === tab}
|
||||
aria-label={label}
|
||||
data-testid={label}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Tooltip label={label} placement="end">
|
||||
<IconButton
|
||||
ref={ref}
|
||||
p={0}
|
||||
onClick={onClick}
|
||||
icon={icon}
|
||||
size="md"
|
||||
fontSize="24px"
|
||||
variant="appTab"
|
||||
data-selected={activeTabName === tab}
|
||||
aria-label={label}
|
||||
data-testid={label}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
TabButton.displayName = 'TabButton';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { memo } from 'react';
|
||||
|
||||
const ModelManagerTab = () => {
|
||||
return (
|
||||
<Flex w="full" h="full" gap="2">
|
||||
<Flex layerStyle="body" w="full" h="full" gap="2">
|
||||
<ModelManager />
|
||||
<ModelPane />
|
||||
</Flex>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { memo } from 'react';
|
||||
|
||||
const QueueTab = () => {
|
||||
return (
|
||||
<Flex w="full" h="full">
|
||||
<Flex layerStyle="body" w="full" h="full">
|
||||
<QueueTabContent />
|
||||
</Flex>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user