mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): statusindicator changes
We are now using the lefthand vertical strip for the settings menu button. This is a good place for the status indicator. Really, we only need to display something *if there is a problem*. If the app is processing, the progress bar indicates that. For the case where the panels are collapsed, I'll add the floating buttons back in some form, and we'll indicate via those if the app is processing something.
This commit is contained in:
parent
d3b6d86e74
commit
7a57bc99cf
@ -204,7 +204,7 @@ export const buttonTheme = defineStyleConfig({
|
||||
_hover: {
|
||||
bg: 'none',
|
||||
svg: {
|
||||
fill: 'base.500',
|
||||
fill: 'base.400',
|
||||
},
|
||||
},
|
||||
'&[data-selected="true"]': {
|
||||
|
@ -1,35 +1,57 @@
|
||||
import { progressAnatomy as parts } from '@chakra-ui/anatomy';
|
||||
import {
|
||||
createMultiStyleConfigHelpers,
|
||||
defineStyle,
|
||||
} from '@chakra-ui/styled-system';
|
||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
||||
import { generateStripe, getColorVar } from '@chakra-ui/theme-tools';
|
||||
|
||||
const { defineMultiStyleConfig, definePartsStyle } =
|
||||
createMultiStyleConfigHelpers(parts.keys);
|
||||
|
||||
const invokeAIFilledTrack = defineStyle((_props) => ({
|
||||
bg: 'blue.500',
|
||||
}));
|
||||
|
||||
const invokeAITrack = defineStyle((_props) => {
|
||||
return {
|
||||
bg: 'base.800',
|
||||
};
|
||||
});
|
||||
|
||||
const invokeAI = definePartsStyle((props) => ({
|
||||
filledTrack: invokeAIFilledTrack(props),
|
||||
track: invokeAITrack(props),
|
||||
}));
|
||||
|
||||
export const progressTheme = defineMultiStyleConfig({
|
||||
baseStyle: {
|
||||
track: { borderRadius: '2px' },
|
||||
baseStyle: definePartsStyle(
|
||||
({ theme: t, colorScheme: c, hasStripe, isIndeterminate }) => {
|
||||
const bgColor = `${c}.300`;
|
||||
const addStripe = !isIndeterminate && hasStripe;
|
||||
const gradient = `linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
${getColorVar(t, bgColor)} 50%,
|
||||
transparent 100%
|
||||
)`;
|
||||
return {
|
||||
track: {
|
||||
borderRadius: '2px',
|
||||
bg: 'base.800',
|
||||
},
|
||||
variants: {
|
||||
invokeAI,
|
||||
},
|
||||
defaultProps: {
|
||||
variant: 'invokeAI',
|
||||
filledTrack: {
|
||||
borderRadius: '2px',
|
||||
...(addStripe && generateStripe()),
|
||||
...(isIndeterminate ? { bgImage: gradient } : { bgColor }),
|
||||
},
|
||||
};
|
||||
}
|
||||
),
|
||||
});
|
||||
// export const progressTheme = defineMultiStyleConfig({
|
||||
// baseStyle: definePartsStyle(
|
||||
// ({ theme: t, colorScheme: c, hasStripe, isIndeterminate }) => {
|
||||
// const bgColor = `${c}.500`;
|
||||
// const addStripe = !isIndeterminate && hasStripe;
|
||||
// const gradient = `linear-gradient(
|
||||
// to right,
|
||||
// transparent 0%,
|
||||
// ${getColorVar(t, bgColor)} 50%,
|
||||
// transparent 100%
|
||||
// )`;
|
||||
// return {
|
||||
// track: {
|
||||
// borderRadius: '2px',
|
||||
// bg: 'base.800',
|
||||
// },
|
||||
// filledTrack: {
|
||||
// borderRadius: '2px',
|
||||
// ...(addStripe && generateStripe("xs")),
|
||||
// ...(isIndeterminate ? { bgImage: gradient } : { bgColor }),
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
// ),
|
||||
// });
|
||||
|
@ -12,6 +12,7 @@ export const InvTooltip = memo(
|
||||
ref={ref}
|
||||
hasArrow={hasArrow}
|
||||
placement={placement}
|
||||
arrowSize={8}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
@ -3,14 +3,15 @@ import { cssVar } from '@chakra-ui/theme-tools';
|
||||
|
||||
const $arrowBg = cssVar('popper-arrow-bg');
|
||||
|
||||
// define the base component styles
|
||||
const baseStyle = defineStyle(() => ({
|
||||
borderRadius: 'md',
|
||||
shadow: 'dark-lg',
|
||||
bg: 'base.200',
|
||||
color: 'base.800',
|
||||
[$arrowBg.variable]: 'colors.base.200',
|
||||
pb: 1.5,
|
||||
pt: 1,
|
||||
px: 2,
|
||||
pb: 1,
|
||||
}));
|
||||
|
||||
// export the component theme
|
||||
|
@ -28,6 +28,7 @@ const selector = createMemoizedSelector([stateSelector], (state) => {
|
||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||
state.config.sd.iterations;
|
||||
const { iterations } = state.generation;
|
||||
const isLoadingDynamicPrompts = state.dynamicPrompts.isLoading;
|
||||
|
||||
return {
|
||||
iterations,
|
||||
@ -37,15 +38,14 @@ const selector = createMemoizedSelector([stateSelector], (state) => {
|
||||
inputMax,
|
||||
step: coarseStep,
|
||||
fineStep,
|
||||
isLoadingDynamicPrompts,
|
||||
};
|
||||
});
|
||||
|
||||
export const InvokeQueueBackButton = memo(() => {
|
||||
const isLoadingDynamicPrompts = useAppSelector(
|
||||
(state) => state.dynamicPrompts.isLoading
|
||||
);
|
||||
const { queueBack, isLoading, isDisabled } = useQueueBack();
|
||||
const { iterations, step, fineStep } = useAppSelector(selector);
|
||||
const { iterations, step, fineStep, isLoadingDynamicPrompts } =
|
||||
useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
const handleChange = useCallback(
|
||||
(v: number) => {
|
||||
|
@ -3,7 +3,6 @@ import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'
|
||||
import ClearQueueButton from 'features/queue/components/ClearQueueButton';
|
||||
import QueueFrontButton from 'features/queue/components/QueueFrontButton';
|
||||
import ProgressBar from 'features/system/components/ProgressBar';
|
||||
import StatusIndicator from 'features/system/components/StatusIndicator';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo } from 'react';
|
||||
|
||||
@ -31,10 +30,7 @@ const QueueControls = () => {
|
||||
{isPauseEnabled && <PauseProcessorButton asIconButton />} */}
|
||||
<ClearQueueButton asIconButton />
|
||||
</InvButtonGroup>
|
||||
<Flex h={5} w="full" alignItems="center">
|
||||
<StatusIndicator />
|
||||
<ProgressBar height={2} />
|
||||
</Flex>
|
||||
<ProgressBar />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
@ -16,12 +16,7 @@ const progressBarSelector = createMemoizedSelector(
|
||||
}
|
||||
);
|
||||
|
||||
type ProgressBarProps = {
|
||||
height?: number | string;
|
||||
};
|
||||
|
||||
const ProgressBar = (props: ProgressBarProps) => {
|
||||
const { height = 'full' } = props;
|
||||
const ProgressBar = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const { hasSteps, value, isConnected } = useAppSelector(progressBarSelector);
|
||||
@ -30,13 +25,12 @@ const ProgressBar = (props: ProgressBarProps) => {
|
||||
<Progress
|
||||
value={value}
|
||||
aria-label={t('accessibility.invokeProgressBar')}
|
||||
hasStripe
|
||||
isIndeterminate={
|
||||
isConnected && Boolean(queueStatus?.queue.in_progress) && !hasSteps
|
||||
}
|
||||
h={height}
|
||||
h={2}
|
||||
w="full"
|
||||
colorScheme="invokeYellow"
|
||||
colorScheme="blue"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,81 +1,28 @@
|
||||
import { Flex, Icon } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
||||
import { STATUS_TRANSLATION_KEYS } from 'features/system/store/types';
|
||||
import type { ResourceKey } from 'i18next';
|
||||
import { memo, useMemo, useRef } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCircle } from 'react-icons/fa';
|
||||
import { useHoverDirty } from 'react-use';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
const statusIndicatorSelector = createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ system }) => {
|
||||
const { isConnected, status } = system;
|
||||
|
||||
return {
|
||||
isConnected,
|
||||
statusTranslationKey: STATUS_TRANSLATION_KEYS[status],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const COLOR_MAP = {
|
||||
ok: 'invokeYellow.500',
|
||||
working: 'blue.500',
|
||||
error: 'red.500',
|
||||
};
|
||||
import { FaExclamationTriangle } from 'react-icons/fa';
|
||||
|
||||
const StatusIndicator = () => {
|
||||
const { isConnected, statusTranslationKey } = useAppSelector(
|
||||
statusIndicatorSelector
|
||||
);
|
||||
const isConnected = useAppSelector((state) => state.system.isConnected);
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef(null);
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
|
||||
const statusColor = useMemo(() => {
|
||||
if (!isConnected) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
if (queueStatus?.queue.in_progress) {
|
||||
return 'working';
|
||||
}
|
||||
|
||||
return 'ok';
|
||||
}, [queueStatus?.queue.in_progress, isConnected]);
|
||||
|
||||
const isHovered = useHoverDirty(ref);
|
||||
|
||||
return (
|
||||
<Flex ref={ref} alignItems="center" p={2} pl={0}>
|
||||
<InvTooltip
|
||||
left={10}
|
||||
bottom={-24}
|
||||
background="base.800"
|
||||
label={
|
||||
isHovered && (
|
||||
<InvText
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
pb="1px"
|
||||
userSelect="none"
|
||||
color={COLOR_MAP[statusColor]}
|
||||
label={t('common.statusDisconnected')}
|
||||
placement="end"
|
||||
shouldWrapChildren
|
||||
gutter={10}
|
||||
>
|
||||
{t(statusTranslationKey as ResourceKey)}
|
||||
</InvText>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon as={FaCircle} boxSize="0.6rem" color={COLOR_MAP[statusColor]} />
|
||||
<Icon as={FaExclamationTriangle} color="error.300" />
|
||||
</InvTooltip>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(StatusIndicator);
|
||||
|
@ -15,6 +15,7 @@ import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent
|
||||
import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup';
|
||||
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
|
||||
import SettingsMenu from 'features/system/components/SettingsModal/SettingsMenu';
|
||||
import StatusIndicator from 'features/system/components/StatusIndicator';
|
||||
import type { UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||
import { usePanel } from 'features/ui/hooks/usePanel';
|
||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
||||
@ -125,9 +126,8 @@ const InvokeTabs = () => {
|
||||
() =>
|
||||
enabledTabs.map((tab) => (
|
||||
<InvTooltip key={tab.id} label={t(tab.translationKey)} placement="end">
|
||||
<InvTab
|
||||
as={InvIconButton}
|
||||
p={0}
|
||||
<InvTab p={0}>
|
||||
<InvIconButton
|
||||
onClick={handleClickTab}
|
||||
icon={tab.icon}
|
||||
size="md"
|
||||
@ -135,7 +135,8 @@ const InvokeTabs = () => {
|
||||
variant="appTab"
|
||||
data-selected={activeTabName === tab.id}
|
||||
aria-label={t(tab.translationKey)}
|
||||
></InvTab>
|
||||
/>
|
||||
</InvTab>
|
||||
</InvTooltip>
|
||||
)),
|
||||
[enabledTabs, t, handleClickTab, activeTabName]
|
||||
@ -254,13 +255,14 @@ const InvokeTabs = () => {
|
||||
p={4}
|
||||
isLazy
|
||||
>
|
||||
<Flex flexDir="column" alignItems="center" pt={4}>
|
||||
<Flex flexDir="column" alignItems="center" pt={4} pb={2} gap={4}>
|
||||
<InvokeAILogoComponent />
|
||||
<InvTabList gap={4} pt={12} pb={4} h="full" flexDir="column">
|
||||
<InvTabList gap={4} pt={6} h="full" flexDir="column">
|
||||
{tabs}
|
||||
<Spacer />
|
||||
<SettingsMenu />
|
||||
</InvTabList>
|
||||
<Spacer />
|
||||
<StatusIndicator />
|
||||
<SettingsMenu />
|
||||
</Flex>
|
||||
<PanelGroup
|
||||
ref={panelGroupRef}
|
||||
|
@ -10,3 +10,5 @@ export const spinKeyframes = keyframes`
|
||||
`;
|
||||
|
||||
export const spinAnimation = `${spinKeyframes} 0.45s linear infinite`;
|
||||
|
||||
export const spinAnimationSlow = `${spinKeyframes} 1s linear infinite`;
|
||||
|
Loading…
Reference in New Issue
Block a user