mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): reworked hotkeys modal
- Displays all as list - Uses chakra `Kbd` component for keys - Provides search box
This commit is contained in:
parent
6bfe994622
commit
7caaf40835
@ -426,6 +426,9 @@
|
||||
"problemDeletingImagesDesc": "One or more images could not be deleted"
|
||||
},
|
||||
"hotkeys": {
|
||||
"searchHotkeys": "Search Hotkeys",
|
||||
"clearSearch": "Clear Search",
|
||||
"noHotkeysFound": "No Hotkeys Found",
|
||||
"acceptStagingImage": {
|
||||
"desc": "Accept Current Staging Area Image",
|
||||
"title": "Accept Staging Image"
|
||||
@ -434,7 +437,7 @@
|
||||
"desc": "Opens the add node menu",
|
||||
"title": "Add Nodes"
|
||||
},
|
||||
"appHotkeys": "App Hotkeys",
|
||||
"appHotkeys": "App",
|
||||
"cancel": {
|
||||
"desc": "Cancel current queue item",
|
||||
"title": "Cancel"
|
||||
@ -499,8 +502,8 @@
|
||||
"desc": "Focus the prompt input area",
|
||||
"title": "Focus Prompt"
|
||||
},
|
||||
"galleryHotkeys": "Gallery Hotkeys",
|
||||
"generalHotkeys": "General Hotkeys",
|
||||
"galleryHotkeys": "Gallery",
|
||||
"generalHotkeys": "General",
|
||||
"hideMask": {
|
||||
"desc": "Hide and unhide mask",
|
||||
"title": "Hide Mask"
|
||||
@ -521,7 +524,7 @@
|
||||
"desc": "Generate an image",
|
||||
"title": "Invoke"
|
||||
},
|
||||
"keyboardShortcuts": "Keyboard Shortcuts",
|
||||
"keyboardShortcuts": "Hotkeys",
|
||||
"maximizeWorkSpace": {
|
||||
"desc": "Close panels and maximize work area",
|
||||
"title": "Maximize Workspace"
|
||||
@ -542,7 +545,7 @@
|
||||
"desc": "Next Staging Area Image",
|
||||
"title": "Next Staging Image"
|
||||
},
|
||||
"nodesHotkeys": "Nodes Hotkeys",
|
||||
"nodesHotkeys": "Nodes",
|
||||
"pinOptions": {
|
||||
"desc": "Pin the options panel",
|
||||
"title": "Pin Options"
|
||||
@ -611,31 +614,31 @@
|
||||
"desc": "Open and close the gallery drawer",
|
||||
"title": "Toggle Gallery"
|
||||
},
|
||||
"toggleGalleryPin": {
|
||||
"desc": "Pins and unpins the gallery to the UI",
|
||||
"title": "Toggle Gallery Pin"
|
||||
"toggleOptions": {
|
||||
"desc": "Open and close the options panel",
|
||||
"title": "Toggle Options"
|
||||
},
|
||||
"toggleOptionsAndGallery": {
|
||||
"desc": "Open and close the options and gallery panels",
|
||||
"title": "Toggle Options and Gallery"
|
||||
},
|
||||
"resetOptionsAndGallery": {
|
||||
"desc": "Resets the options and gallery panels",
|
||||
"title": "Reset Options and Gallery"
|
||||
},
|
||||
"toggleLayer": {
|
||||
"desc": "Toggles mask/base layer selection",
|
||||
"title": "Toggle Layer"
|
||||
},
|
||||
"toggleOptions": {
|
||||
"desc": "Open and close the options panel",
|
||||
"title": "Toggle Options"
|
||||
},
|
||||
"toggleSnap": {
|
||||
"desc": "Toggles Snap to Grid",
|
||||
"title": "Toggle Snap"
|
||||
},
|
||||
"toggleViewer": {
|
||||
"desc": "Open and close Image Viewer",
|
||||
"title": "Toggle Viewer"
|
||||
},
|
||||
"undoStroke": {
|
||||
"desc": "Undo a brush stroke",
|
||||
"title": "Undo Stroke"
|
||||
},
|
||||
"unifiedCanvasHotkeys": "Unified Canvas Hotkeys",
|
||||
"unifiedCanvasHotkeys": "Unified Canvas",
|
||||
"upscale": {
|
||||
"desc": "Upscale the current image",
|
||||
"title": "Upscale"
|
||||
|
@ -0,0 +1,51 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Flex, Kbd, Spacer } from '@chakra-ui/react';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { memo } from 'react';
|
||||
|
||||
interface HotkeysModalProps {
|
||||
hotkeys: string[][];
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const HotkeyListItem = (props: HotkeysModalProps) => {
|
||||
const { title, hotkeys, description } = props;
|
||||
return (
|
||||
<Flex flexDir="column" gap={2} px={2}>
|
||||
<Flex lineHeight={1} gap={1} alignItems="center">
|
||||
<InvText fontWeight="semibold">{title}</InvText>
|
||||
<Spacer />
|
||||
{hotkeys.map((hotkey, index) => {
|
||||
return (
|
||||
<>
|
||||
{hotkey.map((key, index) => (
|
||||
<>
|
||||
<Kbd
|
||||
textTransform="lowercase"
|
||||
key={`${hotkey}-${key}-${index}`}
|
||||
>
|
||||
{key}
|
||||
</Kbd>
|
||||
{index !== hotkey.length - 1 && (
|
||||
<InvText as="span" fontWeight="semibold">
|
||||
+
|
||||
</InvText>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
{index !== hotkeys.length - 1 && (
|
||||
<InvText as="span" px={2} variant='subtext' fontWeight="semibold">
|
||||
or
|
||||
</InvText>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
<InvText variant="subtext">{description}</InvText>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(HotkeyListItem);
|
@ -1,11 +1,15 @@
|
||||
import { Divider, Flex, useDisclosure } from '@chakra-ui/react';
|
||||
import { InvAccordionButton } from 'common/components/InvAccordion/InvAccordionButton';
|
||||
import { CloseIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
InvAccordion,
|
||||
InvAccordionIcon,
|
||||
InvAccordionItem,
|
||||
InvAccordionPanel,
|
||||
} from 'common/components/InvAccordion/wrapper';
|
||||
Divider,
|
||||
Flex,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { InvHeading } from 'common/components/InvHeading/wrapper';
|
||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||
import { InvInput } from 'common/components/InvInput/InvInput';
|
||||
import {
|
||||
InvModal,
|
||||
InvModalBody,
|
||||
@ -15,406 +19,148 @@ import {
|
||||
InvModalHeader,
|
||||
InvModalOverlay,
|
||||
} from 'common/components/InvModal/wrapper';
|
||||
import type { ReactElement } from 'react';
|
||||
import { cloneElement, memo, useMemo } from 'react';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import type { HotkeyGroup } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { useHotkeyData } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import type { ChangeEventHandler, ReactElement } from 'react';
|
||||
import {
|
||||
cloneElement,
|
||||
Fragment,
|
||||
memo,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import HotkeysModalItem from './HotkeysModalItem';
|
||||
import HotkeyListItem from './HotkeyListItem';
|
||||
|
||||
type HotkeysModalProps = {
|
||||
/* The button to open the Settings Modal */
|
||||
children: ReactElement;
|
||||
};
|
||||
|
||||
type HotkeyList = {
|
||||
title: string;
|
||||
desc: string;
|
||||
hotkey: string;
|
||||
};
|
||||
|
||||
const renderHotkeyModalItems = (hotkeys: HotkeyList[]) => (
|
||||
<Flex flexDir="column" gap={4}>
|
||||
{hotkeys.map((hotkey, i) => (
|
||||
<Flex flexDir="column" px={2} gap={4} key={i}>
|
||||
<HotkeysModalItem
|
||||
title={hotkey.title}
|
||||
description={hotkey.desc}
|
||||
hotkey={hotkey.hotkey}
|
||||
/>
|
||||
{i < hotkeys.length - 1 && <Divider />}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const HotkeysModal = ({ children }: HotkeysModalProps) => {
|
||||
const {
|
||||
isOpen: isHotkeyModalOpen,
|
||||
onOpen: onHotkeysModalOpen,
|
||||
onClose: onHotkeysModalClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const appHotkeys = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('hotkeys.invoke.title'),
|
||||
desc: t('hotkeys.invoke.desc'),
|
||||
hotkey: 'Ctrl+Enter',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.cancel.title'),
|
||||
desc: t('hotkeys.cancel.desc'),
|
||||
hotkey: 'Shift+X',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.cancelAndClear.title'),
|
||||
desc: t('hotkeys.cancelAndClear.desc'),
|
||||
hotkey: 'Shift+Ctrl+X / Shift+Cmd+X',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.focusPrompt.title'),
|
||||
desc: t('hotkeys.focusPrompt.desc'),
|
||||
hotkey: 'Alt+A',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleOptions.title'),
|
||||
desc: t('hotkeys.toggleOptions.desc'),
|
||||
hotkey: 'T / O',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleGallery.title'),
|
||||
desc: t('hotkeys.toggleGallery.desc'),
|
||||
hotkey: 'G',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.maximizeWorkSpace.title'),
|
||||
desc: t('hotkeys.maximizeWorkSpace.desc'),
|
||||
hotkey: 'F',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.changeTabs.title'),
|
||||
desc: t('hotkeys.changeTabs.desc'),
|
||||
hotkey: '1-5',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const generalHotkeys = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('hotkeys.setPrompt.title'),
|
||||
desc: t('hotkeys.setPrompt.desc'),
|
||||
hotkey: 'P',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.setSeed.title'),
|
||||
desc: t('hotkeys.setSeed.desc'),
|
||||
hotkey: 'S',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.setParameters.title'),
|
||||
desc: t('hotkeys.setParameters.desc'),
|
||||
hotkey: 'A',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.upscale.title'),
|
||||
desc: t('hotkeys.upscale.desc'),
|
||||
hotkey: 'Shift+U',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.showInfo.title'),
|
||||
desc: t('hotkeys.showInfo.desc'),
|
||||
hotkey: 'I',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.sendToImageToImage.title'),
|
||||
desc: t('hotkeys.sendToImageToImage.desc'),
|
||||
hotkey: 'Shift+I',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.deleteImage.title'),
|
||||
desc: t('hotkeys.deleteImage.desc'),
|
||||
hotkey: 'Del',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.closePanels.title'),
|
||||
desc: t('hotkeys.closePanels.desc'),
|
||||
hotkey: 'Esc',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const galleryHotkeys = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('hotkeys.previousImage.title'),
|
||||
desc: t('hotkeys.previousImage.desc'),
|
||||
hotkey: 'Arrow Left',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.nextImage.title'),
|
||||
desc: t('hotkeys.nextImage.desc'),
|
||||
hotkey: 'Arrow Right',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseGalleryThumbSize.title'),
|
||||
desc: t('hotkeys.increaseGalleryThumbSize.desc'),
|
||||
hotkey: 'Shift+Up',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseGalleryThumbSize.title'),
|
||||
desc: t('hotkeys.decreaseGalleryThumbSize.desc'),
|
||||
hotkey: 'Shift+Down',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const unifiedCanvasHotkeys = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('hotkeys.selectBrush.title'),
|
||||
desc: t('hotkeys.selectBrush.desc'),
|
||||
hotkey: 'B',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.selectEraser.title'),
|
||||
desc: t('hotkeys.selectEraser.desc'),
|
||||
hotkey: 'E',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseBrushSize.title'),
|
||||
desc: t('hotkeys.decreaseBrushSize.desc'),
|
||||
hotkey: '[',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseBrushSize.title'),
|
||||
desc: t('hotkeys.increaseBrushSize.desc'),
|
||||
hotkey: ']',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseBrushOpacity.title'),
|
||||
desc: t('hotkeys.decreaseBrushOpacity.desc'),
|
||||
hotkey: 'Shift + [',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseBrushOpacity.title'),
|
||||
desc: t('hotkeys.increaseBrushOpacity.desc'),
|
||||
hotkey: 'Shift + ]',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.moveTool.title'),
|
||||
desc: t('hotkeys.moveTool.desc'),
|
||||
hotkey: 'V',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.fillBoundingBox.title'),
|
||||
desc: t('hotkeys.fillBoundingBox.desc'),
|
||||
hotkey: 'Shift + F',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.eraseBoundingBox.title'),
|
||||
desc: t('hotkeys.eraseBoundingBox.desc'),
|
||||
hotkey: 'Delete / Backspace',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.colorPicker.title'),
|
||||
desc: t('hotkeys.colorPicker.desc'),
|
||||
hotkey: 'C',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleSnap.title'),
|
||||
desc: t('hotkeys.toggleSnap.desc'),
|
||||
hotkey: 'N',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.quickToggleMove.title'),
|
||||
desc: t('hotkeys.quickToggleMove.desc'),
|
||||
hotkey: 'Hold Space',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleLayer.title'),
|
||||
desc: t('hotkeys.toggleLayer.desc'),
|
||||
hotkey: 'Q',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.clearMask.title'),
|
||||
desc: t('hotkeys.clearMask.desc'),
|
||||
hotkey: 'Shift+C',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.hideMask.title'),
|
||||
desc: t('hotkeys.hideMask.desc'),
|
||||
hotkey: 'H',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.showHideBoundingBox.title'),
|
||||
desc: t('hotkeys.showHideBoundingBox.desc'),
|
||||
hotkey: 'Shift+H',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.mergeVisible.title'),
|
||||
desc: t('hotkeys.mergeVisible.desc'),
|
||||
hotkey: 'Shift+M',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.saveToGallery.title'),
|
||||
desc: t('hotkeys.saveToGallery.desc'),
|
||||
hotkey: 'Shift+S',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.copyToClipboard.title'),
|
||||
desc: t('hotkeys.copyToClipboard.desc'),
|
||||
hotkey: 'Ctrl+C',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.downloadImage.title'),
|
||||
desc: t('hotkeys.downloadImage.desc'),
|
||||
hotkey: 'Shift+D',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.undoStroke.title'),
|
||||
desc: t('hotkeys.undoStroke.desc'),
|
||||
hotkey: 'Ctrl+Z',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.redoStroke.title'),
|
||||
desc: t('hotkeys.redoStroke.desc'),
|
||||
hotkey: 'Ctrl+Shift+Z, Ctrl+Y',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.resetView.title'),
|
||||
desc: t('hotkeys.resetView.desc'),
|
||||
hotkey: 'R',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.previousStagingImage.title'),
|
||||
desc: t('hotkeys.previousStagingImage.desc'),
|
||||
hotkey: 'Arrow Left',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.nextStagingImage.title'),
|
||||
desc: t('hotkeys.nextStagingImage.desc'),
|
||||
hotkey: 'Arrow Right',
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.acceptStagingImage.title'),
|
||||
desc: t('hotkeys.acceptStagingImage.desc'),
|
||||
hotkey: 'Enter',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const nodesHotkeys = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('hotkeys.addNodes.title'),
|
||||
desc: t('hotkeys.addNodes.desc'),
|
||||
hotkey: 'Shift + A / Space',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
const [hotkeyFilter, setHotkeyFilter] = useState('');
|
||||
const clearHotkeyFilter = useCallback(() => setHotkeyFilter(''), []);
|
||||
const onChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
|
||||
(e) => setHotkeyFilter(e.target.value),
|
||||
[]
|
||||
);
|
||||
const hotkeyGroups = useHotkeyData();
|
||||
const filteredHotkeyGroups = useMemo(() => {
|
||||
if (!hotkeyFilter.trim().length) {
|
||||
return hotkeyGroups;
|
||||
}
|
||||
const trimmedHotkeyFilter = hotkeyFilter.trim().toLowerCase();
|
||||
const filteredGroups: HotkeyGroup[] = [];
|
||||
hotkeyGroups.forEach((group) => {
|
||||
const filteredGroup: HotkeyGroup = {
|
||||
title: group.title,
|
||||
hotkeyListItems: [],
|
||||
};
|
||||
group.hotkeyListItems.forEach((item) => {
|
||||
if (
|
||||
item.title.toLowerCase().includes(trimmedHotkeyFilter) ||
|
||||
item.desc.toLowerCase().includes(trimmedHotkeyFilter) ||
|
||||
item.hotkeys.some((hotkey) =>
|
||||
hotkey.some((key) =>
|
||||
key.toLowerCase().includes(trimmedHotkeyFilter)
|
||||
)
|
||||
)
|
||||
) {
|
||||
filteredGroup.hotkeyListItems.push(item);
|
||||
}
|
||||
});
|
||||
if (filteredGroup.hotkeyListItems.length) {
|
||||
filteredGroups.push(filteredGroup);
|
||||
}
|
||||
});
|
||||
return filteredGroups;
|
||||
}, [hotkeyGroups, hotkeyFilter]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{cloneElement(children, {
|
||||
onClick: onHotkeysModalOpen,
|
||||
})}
|
||||
<InvModal isOpen={isHotkeyModalOpen} onClose={onHotkeysModalClose}>
|
||||
<InvModal
|
||||
isOpen={isHotkeyModalOpen}
|
||||
onClose={onHotkeysModalClose}
|
||||
isCentered
|
||||
size="2xl"
|
||||
>
|
||||
<InvModalOverlay />
|
||||
<InvModalContent>
|
||||
<InvModalContent maxH="80vh" h="80vh">
|
||||
<InvModalHeader>{t('hotkeys.keyboardShortcuts')}</InvModalHeader>
|
||||
<InvModalCloseButton />
|
||||
<InvModalBody>
|
||||
<InvAccordion allowMultiple>
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<InvAccordionItem>
|
||||
<InvAccordionButton>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{t('hotkeys.appHotkeys')}</h2>
|
||||
<InvAccordionIcon />
|
||||
</Flex>
|
||||
</InvAccordionButton>
|
||||
<InvAccordionPanel>
|
||||
{renderHotkeyModalItems(appHotkeys)}
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
<InvModalBody display="flex" flexDir="column" gap={4}>
|
||||
<InputGroup>
|
||||
<InvInput
|
||||
placeholder={t('hotkeys.searchHotkeys')}
|
||||
value={hotkeyFilter}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{hotkeyFilter.length && (
|
||||
<InputRightElement h="full" pe={2}>
|
||||
<InvIconButton
|
||||
onClick={clearHotkeyFilter}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
aria-label={t('hotkeys.clearSearch')}
|
||||
icon={<CloseIcon boxSize={3} />}
|
||||
/>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
|
||||
<InvAccordionItem>
|
||||
<InvAccordionButton>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{t('hotkeys.generalHotkeys')}</h2>
|
||||
<InvAccordionIcon />
|
||||
</Flex>
|
||||
</InvAccordionButton>
|
||||
<InvAccordionPanel>
|
||||
{renderHotkeyModalItems(generalHotkeys)}
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
|
||||
<InvAccordionItem>
|
||||
<InvAccordionButton>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{t('hotkeys.galleryHotkeys')}</h2>
|
||||
<InvAccordionIcon />
|
||||
</Flex>
|
||||
</InvAccordionButton>
|
||||
<InvAccordionPanel>
|
||||
{renderHotkeyModalItems(galleryHotkeys)}
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
|
||||
<InvAccordionItem>
|
||||
<InvAccordionButton>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{t('hotkeys.unifiedCanvasHotkeys')}</h2>
|
||||
<InvAccordionIcon />
|
||||
</Flex>
|
||||
</InvAccordionButton>
|
||||
<InvAccordionPanel>
|
||||
{renderHotkeyModalItems(unifiedCanvasHotkeys)}
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
|
||||
<InvAccordionItem>
|
||||
<InvAccordionButton>
|
||||
<Flex
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<h2>{t('hotkeys.nodesHotkeys')}</h2>
|
||||
<InvAccordionIcon />
|
||||
</Flex>
|
||||
</InvAccordionButton>
|
||||
<InvAccordionPanel>
|
||||
{renderHotkeyModalItems(nodesHotkeys)}
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
</Flex>
|
||||
</InvAccordion>
|
||||
<ScrollableContent>
|
||||
{filteredHotkeyGroups.map((group) => (
|
||||
<Flex key={group.title} pb={4} flexDir="column">
|
||||
<Flex
|
||||
ps={2}
|
||||
pb={4}
|
||||
position="sticky"
|
||||
zIndex={1}
|
||||
top={0}
|
||||
bg="base.800"
|
||||
>
|
||||
<InvHeading size="sm">{group.title}</InvHeading>
|
||||
</Flex>
|
||||
<Flex
|
||||
key={group.title}
|
||||
p={4}
|
||||
borderRadius="base"
|
||||
bg="base.750"
|
||||
flexDir="column"
|
||||
gap={4}
|
||||
>
|
||||
{group.hotkeyListItems.map((hotkey, i) => (
|
||||
<Fragment key={i}>
|
||||
<HotkeyListItem
|
||||
title={hotkey.title}
|
||||
description={hotkey.desc}
|
||||
hotkeys={hotkey.hotkeys}
|
||||
/>
|
||||
{i < group.hotkeyListItems.length - 1 && <Divider />}
|
||||
</Fragment>
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
))}
|
||||
{!filteredHotkeyGroups.length && (
|
||||
<IAINoContentFallback
|
||||
label={t('hotkeys.noHotkeysFound')}
|
||||
icon={null}
|
||||
/>
|
||||
)}
|
||||
</ScrollableContent>
|
||||
</InvModalBody>
|
||||
<InvModalFooter />
|
||||
</InvModalContent>
|
||||
|
@ -1,30 +0,0 @@
|
||||
import { Box, Grid } from '@chakra-ui/react';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { memo } from 'react';
|
||||
|
||||
interface HotkeysModalProps {
|
||||
hotkey: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const HotkeysModalItem = (props: HotkeysModalProps) => {
|
||||
const { title, hotkey, description } = props;
|
||||
return (
|
||||
<Grid
|
||||
gridTemplateColumns="auto max-content"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid>
|
||||
<InvText fontWeight="semibold">{title}</InvText>
|
||||
{description && <InvText variant="subtext">{description}</InvText>}
|
||||
</Grid>
|
||||
<Box fontSize="sm" fontWeight="semibold" px={2} py={1}>
|
||||
{hotkey}
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(HotkeysModalItem);
|
@ -0,0 +1,332 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type HotkeyListItem = {
|
||||
title: string;
|
||||
desc: string;
|
||||
hotkeys: string[][];
|
||||
};
|
||||
|
||||
export type HotkeyGroup = {
|
||||
title: string;
|
||||
hotkeyListItems: HotkeyListItem[];
|
||||
};
|
||||
|
||||
export const useHotkeyData = (): HotkeyGroup[] => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const appHotkeys = useMemo<HotkeyGroup>(
|
||||
() => ({
|
||||
title: t('hotkeys.appHotkeys'),
|
||||
hotkeyListItems: [
|
||||
{
|
||||
title: t('hotkeys.invoke.title'),
|
||||
desc: t('hotkeys.invoke.desc'),
|
||||
hotkeys: [['Ctrl', 'Enter']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.cancel.title'),
|
||||
desc: t('hotkeys.cancel.desc'),
|
||||
hotkeys: [['Shift', 'X']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.cancelAndClear.title'),
|
||||
desc: t('hotkeys.cancelAndClear.desc'),
|
||||
hotkeys: [
|
||||
['Shift', 'Ctrl', 'X'],
|
||||
['Shift', 'Cmd', 'X'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.focusPrompt.title'),
|
||||
desc: t('hotkeys.focusPrompt.desc'),
|
||||
hotkeys: [['Alt', 'A']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleOptions.title'),
|
||||
desc: t('hotkeys.toggleOptions.desc'),
|
||||
hotkeys: [['T'], ['O']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleGallery.title'),
|
||||
desc: t('hotkeys.toggleGallery.desc'),
|
||||
hotkeys: [['G']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleOptionsAndGallery.title'),
|
||||
desc: t('hotkeys.toggleOptionsAndGallery.desc'),
|
||||
hotkeys: [['F']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.resetOptionsAndGallery.title'),
|
||||
desc: t('hotkeys.resetOptionsAndGallery.desc'),
|
||||
hotkeys: [['Shift', 'R']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.maximizeWorkSpace.title'),
|
||||
desc: t('hotkeys.maximizeWorkSpace.desc'),
|
||||
hotkeys: [['F']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.changeTabs.title'),
|
||||
desc: t('hotkeys.changeTabs.desc'),
|
||||
hotkeys: [['1 - 6']],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const generalHotkeys = useMemo<HotkeyGroup>(
|
||||
() => ({
|
||||
title: t('hotkeys.generalHotkeys'),
|
||||
hotkeyListItems: [
|
||||
{
|
||||
title: t('hotkeys.setPrompt.title'),
|
||||
desc: t('hotkeys.setPrompt.desc'),
|
||||
hotkeys: [['P']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.setSeed.title'),
|
||||
desc: t('hotkeys.setSeed.desc'),
|
||||
hotkeys: [['S']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.setParameters.title'),
|
||||
desc: t('hotkeys.setParameters.desc'),
|
||||
hotkeys: [['A']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.upscale.title'),
|
||||
desc: t('hotkeys.upscale.desc'),
|
||||
hotkeys: [['Shift', 'U']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.showInfo.title'),
|
||||
desc: t('hotkeys.showInfo.desc'),
|
||||
hotkeys: [['I']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.sendToImageToImage.title'),
|
||||
desc: t('hotkeys.sendToImageToImage.desc'),
|
||||
hotkeys: [['Shift', 'I']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.deleteImage.title'),
|
||||
desc: t('hotkeys.deleteImage.desc'),
|
||||
hotkeys: [['Del']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.closePanels.title'),
|
||||
desc: t('hotkeys.closePanels.desc'),
|
||||
hotkeys: [['Esc']],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const galleryHotkeys = useMemo<HotkeyGroup>(
|
||||
() => ({
|
||||
title: t('hotkeys.galleryHotkeys'),
|
||||
hotkeyListItems: [
|
||||
{
|
||||
title: t('hotkeys.previousImage.title'),
|
||||
desc: t('hotkeys.previousImage.desc'),
|
||||
hotkeys: [['Arrow Left']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.nextImage.title'),
|
||||
desc: t('hotkeys.nextImage.desc'),
|
||||
hotkeys: [['Arrow Right']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseGalleryThumbSize.title'),
|
||||
desc: t('hotkeys.increaseGalleryThumbSize.desc'),
|
||||
hotkeys: [['Shift', 'Up']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseGalleryThumbSize.title'),
|
||||
desc: t('hotkeys.decreaseGalleryThumbSize.desc'),
|
||||
hotkeys: [['Shift', 'Down']],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const unifiedCanvasHotkeys = useMemo<HotkeyGroup>(
|
||||
() => ({
|
||||
title: t('hotkeys.unifiedCanvasHotkeys'),
|
||||
hotkeyListItems: [
|
||||
{
|
||||
title: t('hotkeys.selectBrush.title'),
|
||||
desc: t('hotkeys.selectBrush.desc'),
|
||||
hotkeys: [['B']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.selectEraser.title'),
|
||||
desc: t('hotkeys.selectEraser.desc'),
|
||||
hotkeys: [['E']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseBrushSize.title'),
|
||||
desc: t('hotkeys.decreaseBrushSize.desc'),
|
||||
hotkeys: [['[']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseBrushSize.title'),
|
||||
desc: t('hotkeys.increaseBrushSize.desc'),
|
||||
hotkeys: [[']']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.decreaseBrushOpacity.title'),
|
||||
desc: t('hotkeys.decreaseBrushOpacity.desc'),
|
||||
hotkeys: [['Shift', '[']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.increaseBrushOpacity.title'),
|
||||
desc: t('hotkeys.increaseBrushOpacity.desc'),
|
||||
hotkeys: [['Shift', ']']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.moveTool.title'),
|
||||
desc: t('hotkeys.moveTool.desc'),
|
||||
hotkeys: [['V']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.fillBoundingBox.title'),
|
||||
desc: t('hotkeys.fillBoundingBox.desc'),
|
||||
hotkeys: [['Shift', 'F']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.eraseBoundingBox.title'),
|
||||
desc: t('hotkeys.eraseBoundingBox.desc'),
|
||||
hotkeys: [['Delete', 'Backspace']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.colorPicker.title'),
|
||||
desc: t('hotkeys.colorPicker.desc'),
|
||||
hotkeys: [['C']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleSnap.title'),
|
||||
desc: t('hotkeys.toggleSnap.desc'),
|
||||
hotkeys: [['N']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.quickToggleMove.title'),
|
||||
desc: t('hotkeys.quickToggleMove.desc'),
|
||||
hotkeys: [['Hold Space']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.toggleLayer.title'),
|
||||
desc: t('hotkeys.toggleLayer.desc'),
|
||||
hotkeys: [['Q']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.clearMask.title'),
|
||||
desc: t('hotkeys.clearMask.desc'),
|
||||
hotkeys: [['Shift', 'C']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.hideMask.title'),
|
||||
desc: t('hotkeys.hideMask.desc'),
|
||||
hotkeys: [['H']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.showHideBoundingBox.title'),
|
||||
desc: t('hotkeys.showHideBoundingBox.desc'),
|
||||
hotkeys: [['Shift', 'H']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.mergeVisible.title'),
|
||||
desc: t('hotkeys.mergeVisible.desc'),
|
||||
hotkeys: [['Shift', 'M']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.saveToGallery.title'),
|
||||
desc: t('hotkeys.saveToGallery.desc'),
|
||||
hotkeys: [['Shift', 'S']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.copyToClipboard.title'),
|
||||
desc: t('hotkeys.copyToClipboard.desc'),
|
||||
hotkeys: [['Ctrl', 'C']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.downloadImage.title'),
|
||||
desc: t('hotkeys.downloadImage.desc'),
|
||||
hotkeys: [['Shift', 'D']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.undoStroke.title'),
|
||||
desc: t('hotkeys.undoStroke.desc'),
|
||||
hotkeys: [['Ctrl', 'Z']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.redoStroke.title'),
|
||||
desc: t('hotkeys.redoStroke.desc'),
|
||||
hotkeys: [
|
||||
['Ctrl', 'Shift', 'Z'],
|
||||
['Ctrl', 'Y'],
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.resetView.title'),
|
||||
desc: t('hotkeys.resetView.desc'),
|
||||
hotkeys: [['R']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.previousStagingImage.title'),
|
||||
desc: t('hotkeys.previousStagingImage.desc'),
|
||||
hotkeys: [['Arrow Left']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.nextStagingImage.title'),
|
||||
desc: t('hotkeys.nextStagingImage.desc'),
|
||||
hotkeys: [['Arrow Right']],
|
||||
},
|
||||
{
|
||||
title: t('hotkeys.acceptStagingImage.title'),
|
||||
desc: t('hotkeys.acceptStagingImage.desc'),
|
||||
hotkeys: [['Enter']],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const nodesHotkeys = useMemo<HotkeyGroup>(
|
||||
() => ({
|
||||
title: t('hotkeys.nodesHotkeys'),
|
||||
hotkeyListItems: [
|
||||
{
|
||||
title: t('hotkeys.addNodes.title'),
|
||||
desc: t('hotkeys.addNodes.desc'),
|
||||
hotkeys: [['Shift', 'A'], ['Space']],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const hotkeyGroups = useMemo<HotkeyGroup[]>(
|
||||
() => [
|
||||
appHotkeys,
|
||||
generalHotkeys,
|
||||
galleryHotkeys,
|
||||
unifiedCanvasHotkeys,
|
||||
nodesHotkeys,
|
||||
],
|
||||
[
|
||||
appHotkeys,
|
||||
generalHotkeys,
|
||||
galleryHotkeys,
|
||||
unifiedCanvasHotkeys,
|
||||
nodesHotkeys,
|
||||
]
|
||||
);
|
||||
|
||||
return hotkeyGroups;
|
||||
};
|
Loading…
Reference in New Issue
Block a user