mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add snap & autosave to HUD
This commit is contained in:
@@ -93,6 +93,7 @@
|
||||
"copy": "Copy",
|
||||
"copyError": "$t(gallery.copy) Error",
|
||||
"on": "On",
|
||||
"off": "Off",
|
||||
"or": "or",
|
||||
"checkpoint": "Checkpoint",
|
||||
"communityLabel": "Community",
|
||||
@@ -1819,6 +1820,12 @@
|
||||
"8": "8px",
|
||||
"64": "64px"
|
||||
}
|
||||
},
|
||||
"HUD": {
|
||||
"snapToGrid": "Snap",
|
||||
"bbox": "Bbox",
|
||||
"scaledBbox": "Scaled Bbox",
|
||||
"autoSave": "Auto Save"
|
||||
}
|
||||
},
|
||||
"upscaling": {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Grid } from '@invoke-ai/ui-library';
|
||||
import { CanvasHUDItemAutoSave } from 'features/controlLayers/components/HUD/CanvasHUDItemAutoSave';
|
||||
import { CanvasHUDItemBbox } from 'features/controlLayers/components/HUD/CanvasHUDItemBbox';
|
||||
import { CanvasHUDItemScaledBbox } from 'features/controlLayers/components/HUD/CanvasHUDItemScaledBbox';
|
||||
import { CanvasHUDItemSnapToGrid } from 'features/controlLayers/components/HUD/CanvasHUDItemSnapToGrid';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const CanvasHUD = memo(() => {
|
||||
return (
|
||||
<Grid
|
||||
bg="base.900"
|
||||
borderBottomEndRadius="base"
|
||||
p={2}
|
||||
gap={2}
|
||||
borderRadius="base"
|
||||
templateColumns="1fr 1fr"
|
||||
opacity={0.6}
|
||||
minW={64}
|
||||
>
|
||||
<CanvasHUDItemBbox />
|
||||
<CanvasHUDItemScaledBbox />
|
||||
<CanvasHUDItemSnapToGrid />
|
||||
<CanvasHUDItemAutoSave />
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasHUD.displayName = 'CanvasHUD';
|
||||
@@ -0,0 +1,17 @@
|
||||
import { GridItem, Text } from '@invoke-ai/ui-library';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const CanvasHUDItem = memo(({ label, value }: { label: string; value: string | number }) => {
|
||||
return (
|
||||
<>
|
||||
<GridItem>
|
||||
<Text textAlign="end">{label}: </Text>
|
||||
</GridItem>
|
||||
<GridItem fontWeight="semibold">
|
||||
<Text textAlign="end">{value}</Text>
|
||||
</GridItem>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasHUDItem.displayName = 'CanvasHUDItem';
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasHUDItem } from 'features/controlLayers/components/HUD/CanvasHUDItem';
|
||||
import { selectAutoSave } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CanvasHUDItemAutoSave = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const autoSave = useAppSelector(selectAutoSave);
|
||||
|
||||
return <CanvasHUDItem label={t('controlLayers.HUD.autoSave')} value={autoSave ? t('common.on') : t('common.off')} />;
|
||||
});
|
||||
|
||||
CanvasHUDItemAutoSave.displayName = 'CanvasHUDItemAutoSave';
|
||||
@@ -0,0 +1,17 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasHUDItem } from 'features/controlLayers/components/HUD/CanvasHUDItem';
|
||||
import { selectBbox } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectBboxRect = createSelector(selectBbox, (bbox) => bbox.rect);
|
||||
|
||||
export const CanvasHUDItemBbox = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const rect = useAppSelector(selectBboxRect);
|
||||
|
||||
return <CanvasHUDItem label={t('controlLayers.HUD.bbox')} value={`${rect.width}×${rect.height} px`} />;
|
||||
});
|
||||
|
||||
CanvasHUDItemBbox.displayName = 'CanvasHUDItemBbox';
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasHUDItem } from 'features/controlLayers/components/HUD/CanvasHUDItem';
|
||||
import { selectBbox } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectScaledSize = createSelector(selectBbox, (bbox) => bbox.scaledSize);
|
||||
|
||||
export const CanvasHUDItemScaledBbox = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const scaledSize = useAppSelector(selectScaledSize);
|
||||
|
||||
return (
|
||||
<CanvasHUDItem label={t('controlLayers.HUD.scaledBbox')} value={`${scaledSize.width}×${scaledSize.height} px`} />
|
||||
);
|
||||
});
|
||||
|
||||
CanvasHUDItemScaledBbox.displayName = 'CanvasHUDItemScaledBbox';
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasHUDItem } from 'features/controlLayers/components/HUD/CanvasHUDItem';
|
||||
import { selectSnapToGrid } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CanvasHUDItemSnapToGrid = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const snap = useAppSelector(selectSnapToGrid);
|
||||
const snapString = useMemo(() => {
|
||||
switch (snap) {
|
||||
case 'off':
|
||||
return t('controlLayers.settings.snapToGrid.off');
|
||||
case '8':
|
||||
return t('controlLayers.settings.snapToGrid.8');
|
||||
case '64':
|
||||
return t('controlLayers.settings.snapToGrid.64');
|
||||
}
|
||||
}, [snap, t]);
|
||||
|
||||
return <CanvasHUDItem label={t('controlLayers.HUD.snapToGrid')} value={snapString} />;
|
||||
});
|
||||
|
||||
CanvasHUDItemSnapToGrid.displayName = 'CanvasHUDItemSnapToGrid';
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Grid, GridItem, Text } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectBbox = createSelector(selectCanvasSlice, (canvas) => canvas.bbox);
|
||||
|
||||
export const HeadsUpDisplay = memo(() => {
|
||||
const bbox = useAppSelector(selectBbox);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
bg="base.900"
|
||||
borderBottomEndRadius="base"
|
||||
p={2}
|
||||
gap={2}
|
||||
borderRadius="base"
|
||||
templateColumns="auto auto"
|
||||
opacity={0.6}
|
||||
>
|
||||
<HUDItem label="BBox" value={`${bbox.rect.width}×${bbox.rect.height} px`} />
|
||||
<HUDItem label="Scaled BBox" value={`${bbox.scaledSize.width}×${bbox.scaledSize.height} px`} />
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
|
||||
HeadsUpDisplay.displayName = 'HeadsUpDisplay';
|
||||
|
||||
const HUDItem = memo(({ label, value }: { label: string; value: string | number }) => {
|
||||
return (
|
||||
<>
|
||||
<GridItem>
|
||||
<Text textAlign="end">{label}: </Text>
|
||||
</GridItem>
|
||||
<GridItem fontWeight="semibold">
|
||||
<Text>{value}</Text>
|
||||
</GridItem>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
HUDItem.displayName = 'HUDItem';
|
||||
@@ -4,7 +4,7 @@ import { $socket } from 'app/hooks/useSocketIO';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { useAppStore } from 'app/store/nanostores/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
||||
import { CanvasHUD } from 'features/controlLayers/components/HUD/CanvasHUD';
|
||||
import { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
@@ -99,7 +99,7 @@ export const StageComponent = memo(() => {
|
||||
/>
|
||||
{showHUD && (
|
||||
<Flex position="absolute" top={1} insetInlineStart={1} pointerEvents="none">
|
||||
<HeadsUpDisplay />
|
||||
<CanvasHUD />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -156,6 +156,7 @@ export const selectCanvasSettingsSlice = (s: RootState) => s.canvasSettings;
|
||||
const createCanvasSettingsSelector = <T>(selector: Selector<CanvasSettingsState, T>) =>
|
||||
createSelector(selectCanvasSettingsSlice, selector);
|
||||
|
||||
export const selectAutoSave = createCanvasSettingsSelector((settings) => settings.autoSave);
|
||||
export const selectDynamicGrid = createCanvasSettingsSelector((settings) => settings.dynamicGrid);
|
||||
export const selectShowHUD = createCanvasSettingsSelector((settings) => settings.showHUD);
|
||||
export const selectAutoPreviewFilter = createCanvasSettingsSelector((settings) => settings.autoPreviewFilter);
|
||||
|
||||
Reference in New Issue
Block a user