Add Basic Hotkey Support

This commit is contained in:
blessedcoolant 2022-10-06 18:42:26 +13:00 committed by Lincoln Stein
parent 5f42d08945
commit 70bbb670ec
18 changed files with 884 additions and 490 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

483
frontend/dist/assets/index.d6634413.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvokeAI - A Stable Diffusion Toolkit</title> <title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="/assets/favicon.0d253ced.ico" /> <link rel="shortcut icon" type="icon" href="/assets/favicon.0d253ced.ico" />
<script type="module" crossorigin src="/assets/index.d9916e7a.js"></script> <script type="module" crossorigin src="/assets/index.d6634413.js"></script>
<link rel="stylesheet" href="/assets/index.853a336f.css"> <link rel="stylesheet" href="/assets/index.c485ac40.css">
</head> </head>
<body> <body>

View File

@ -23,6 +23,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.2", "react-dropzone": "^14.2.2",
"react-hotkeys-hook": "^3.4.7",
"react-icons": "^4.4.0", "react-icons": "^4.4.0",
"react-redux": "^8.0.2", "react-redux": "^8.0.2",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",

View File

@ -19,6 +19,8 @@ import { MdDelete, MdFace, MdHd, MdImage, MdInfo } from 'react-icons/md';
import InvokePopover from './InvokePopover'; import InvokePopover from './InvokePopover';
import UpscaleOptions from '../options/AdvancedOptions/Upscale/UpscaleOptions'; import UpscaleOptions from '../options/AdvancedOptions/Upscale/UpscaleOptions';
import FaceRestoreOptions from '../options/AdvancedOptions/FaceRestore/FaceRestoreOptions'; import FaceRestoreOptions from '../options/AdvancedOptions/FaceRestore/FaceRestoreOptions';
import { useHotkeys } from 'react-hotkeys-hook';
import { useToast } from '@chakra-ui/react';
const systemSelector = createSelector( const systemSelector = createSelector(
(state: RootState) => state.system, (state: RootState) => state.system,
@ -54,6 +56,8 @@ const CurrentImageButtons = ({
}: CurrentImageButtonsProps) => { }: CurrentImageButtonsProps) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const toast = useToast();
const intermediateImage = useAppSelector( const intermediateImage = useAppSelector(
(state: RootState) => state.gallery.intermediateImage (state: RootState) => state.gallery.intermediateImage
); );
@ -71,19 +75,163 @@ const CurrentImageButtons = ({
const handleClickUseAsInitialImage = () => const handleClickUseAsInitialImage = () =>
dispatch(setInitialImagePath(image.url)); dispatch(setInitialImagePath(image.url));
useHotkeys(
'shift+i',
() => {
if (image) {
handleClickUseAsInitialImage();
toast({
title: 'Sent To Image To Image',
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: 'No Image Loaded',
description: 'No image found to send to image to image module.',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[image]
);
const handleClickUseAllParameters = () => const handleClickUseAllParameters = () =>
dispatch(setAllParameters(image.metadata)); dispatch(setAllParameters(image.metadata));
useHotkeys(
'a',
() => {
if (['txt2img', 'img2img'].includes(image?.metadata?.image?.type)) {
handleClickUseAllParameters();
toast({
title: 'Parameters Set',
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: 'Parameters Not Set',
description: 'No metadata found for this image.',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[image]
);
// Non-null assertion: this button is disabled if there is no seed. // Non-null assertion: this button is disabled if there is no seed.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const handleClickUseSeed = () => dispatch(setSeed(image.metadata.image.seed)); const handleClickUseSeed = () => dispatch(setSeed(image.metadata.image.seed));
useHotkeys(
's',
() => {
if (image?.metadata?.image?.seed) {
handleClickUseSeed();
toast({
title: 'Seed Set',
status: 'success',
duration: 2500,
isClosable: true,
});
} else {
toast({
title: 'Seed Not Set',
description: 'Could not find seed for this image.',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[image]
);
const handleClickUpscale = () => dispatch(runESRGAN(image)); const handleClickUpscale = () => dispatch(runESRGAN(image));
useHotkeys(
'u',
() => {
if (
isESRGANAvailable &&
Boolean(!intermediateImage) &&
isConnected &&
!isProcessing &&
upscalingLevel
) {
handleClickUpscale();
} else {
toast({
title: 'Upscaling Failed',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[
image,
isESRGANAvailable,
intermediateImage,
isConnected,
isProcessing,
upscalingLevel,
]
);
const handleClickFixFaces = () => dispatch(runGFPGAN(image)); const handleClickFixFaces = () => dispatch(runGFPGAN(image));
useHotkeys(
'r',
() => {
if (
isGFPGANAvailable &&
Boolean(!intermediateImage) &&
isConnected &&
!isProcessing &&
gfpganStrength
) {
handleClickFixFaces();
} else {
toast({
title: 'Face Restoration Failed',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[
image,
isGFPGANAvailable,
intermediateImage,
isConnected,
isProcessing,
gfpganStrength,
]
);
const handleClickShowImageDetails = () => const handleClickShowImageDetails = () =>
setShouldShowImageDetails(!shouldShowImageDetails); setShouldShowImageDetails(!shouldShowImageDetails);
useHotkeys(
'i',
() => {
if (image) {
handleClickShowImageDetails();
} else {
toast({
title: 'Failed to load metadata',
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[image, shouldShowImageDetails]
);
return ( return (
<div className="current-image-options"> <div className="current-image-options">

View File

@ -27,6 +27,7 @@ import { deleteImage } from '../../app/socketio/actions';
import { RootState } from '../../app/store'; import { RootState } from '../../app/store';
import { setShouldConfirmOnDelete, SystemState } from '../system/systemSlice'; import { setShouldConfirmOnDelete, SystemState } from '../system/systemSlice';
import * as InvokeAI from '../../app/invokeai'; import * as InvokeAI from '../../app/invokeai';
import { useHotkeys } from 'react-hotkeys-hook';
interface DeleteImageModalProps { interface DeleteImageModalProps {
/** /**
@ -67,6 +68,14 @@ const DeleteImageModal = forwardRef(
onClose(); onClose();
}; };
useHotkeys(
'del',
() => {
shouldConfirmOnDelete ? onOpen() : handleDelete();
},
[image, shouldConfirmOnDelete]
);
const handleChangeShouldConfirmOnDelete = ( const handleChangeShouldConfirmOnDelete = (
e: ChangeEvent<HTMLInputElement> e: ChangeEvent<HTMLInputElement>
) => dispatch(setShouldConfirmOnDelete(!e.target.checked)); ) => dispatch(setShouldConfirmOnDelete(!e.target.checked));

View File

@ -4,12 +4,23 @@ import { cancelProcessing } from '../../../app/socketio/actions';
import { useAppDispatch, useAppSelector } from '../../../app/store'; import { useAppDispatch, useAppSelector } from '../../../app/store';
import IAIIconButton from '../../../common/components/IAIIconButton'; import IAIIconButton from '../../../common/components/IAIIconButton';
import { systemSelector } from '../../../common/hooks/useCheckParameters'; import { systemSelector } from '../../../common/hooks/useCheckParameters';
import { useHotkeys } from 'react-hotkeys-hook';
export default function CancelButton() { export default function CancelButton() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { isProcessing, isConnected } = useAppSelector(systemSelector); const { isProcessing, isConnected } = useAppSelector(systemSelector);
const handleClickCancel = () => dispatch(cancelProcessing()); const handleClickCancel = () => dispatch(cancelProcessing());
useHotkeys(
'shift+x',
() => {
if (isConnected || isProcessing) {
handleClickCancel();
}
},
[isConnected, isProcessing]
);
return ( return (
<IAIIconButton <IAIIconButton
icon={<MdCancel />} icon={<MdCancel />}

View File

@ -1,5 +1,5 @@
import { FormControl, Textarea } from '@chakra-ui/react'; import { FormControl, Textarea } from '@chakra-ui/react';
import { ChangeEvent, KeyboardEvent } from 'react'; import { ChangeEvent, KeyboardEvent, useRef } from 'react';
import { RootState, useAppDispatch, useAppSelector } from '../../../app/store'; import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
import { generateImage } from '../../../app/socketio/actions'; import { generateImage } from '../../../app/socketio/actions';
@ -9,6 +9,7 @@ import { isEqual } from 'lodash';
import useCheckParameters, { import useCheckParameters, {
systemSelector, systemSelector,
} from '../../../common/hooks/useCheckParameters'; } from '../../../common/hooks/useCheckParameters';
import { useHotkeys } from 'react-hotkeys-hook';
export const optionsSelector = createSelector( export const optionsSelector = createSelector(
(state: RootState) => state.options, (state: RootState) => state.options,
@ -28,6 +29,7 @@ export const optionsSelector = createSelector(
* Prompt input text area. * Prompt input text area.
*/ */
const PromptInput = () => { const PromptInput = () => {
const promptRef = useRef<HTMLTextAreaElement>(null);
const { prompt } = useAppSelector(optionsSelector); const { prompt } = useAppSelector(optionsSelector);
const { isProcessing } = useAppSelector(systemSelector); const { isProcessing } = useAppSelector(systemSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -37,6 +39,24 @@ const PromptInput = () => {
dispatch(setPrompt(e.target.value)); dispatch(setPrompt(e.target.value));
}; };
useHotkeys(
'ctrl+enter',
() => {
if (isReady) {
dispatch(generateImage());
}
},
[isReady]
);
useHotkeys(
'alt+a',
() => {
promptRef.current?.focus();
},
[]
);
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => { const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && e.shiftKey === false && isReady) { if (e.key === 'Enter' && e.shiftKey === false && isReady) {
e.preventDefault(); e.preventDefault();
@ -60,6 +80,7 @@ const PromptInput = () => {
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
resize="vertical" resize="vertical"
height={30} height={30}
ref={promptRef}
/> />
</FormControl> </FormControl>
</div> </div>

View File

@ -0,0 +1,53 @@
@use '../../../styles/Mixins/' as *;
.hotkeys-modal {
display: grid;
padding: 1rem;
background-color: var(--settings-modal-bg) !important;
row-gap: 1rem;
font-family: Inter;
h1 {
font-size: 1.2rem;
font-weight: bold;
}
}
.hotkeys-modal-items {
display: grid;
row-gap: 0.5rem;
max-height: 32rem;
overflow-y: scroll;
@include HideScrollbar;
}
.hotkey-modal-item {
display: grid;
grid-template-columns: auto max-content;
justify-content: space-between;
align-items: center;
background-color: var(--background-color);
padding: 0.5rem 1rem;
border-radius: 0.3rem;
.hotkey-info {
display: grid;
.hotkey-title {
font-weight: bold;
}
.hotkey-description {
font-size: 0.9rem;
color: var(--text-color-secondary);
}
}
.hotkey-key {
font-size: 0.8rem;
font-weight: bold;
border: 2px solid var(--settings-modal-bg);
padding: 0.2rem 0.5rem;
border-radius: 0.3rem;
}
}

View File

@ -0,0 +1,88 @@
import {
Modal,
ModalCloseButton,
ModalContent,
ModalOverlay,
useDisclosure,
} from '@chakra-ui/react';
import React, { cloneElement, ReactElement } from 'react';
import HotkeysModalItem from './HotkeysModalItem';
type HotkeysModalProps = {
/* The button to open the Settings Modal */
children: ReactElement;
};
export default function HotkeysModal({ children }: HotkeysModalProps) {
const {
isOpen: isHotkeyModalOpen,
onOpen: onHotkeysModalOpen,
onClose: onHotkeysModalClose,
} = useDisclosure();
const hotkeys = [
{ title: 'Invoke', desc: 'Generate an image', hotkey: 'Ctrl+Enter' },
{ title: 'Cancel', desc: 'Cancel image generation', hotkey: 'Shift+X' },
{
title: 'Set Seed',
desc: 'Use the seed of the current image',
hotkey: 'S',
},
{
title: 'Set Parameters',
desc: 'Use all parameters of the current image',
hotkey: 'A',
},
{ title: 'Restore Faces', desc: 'Restore the current image', hotkey: 'R' },
{ title: 'Upscale', desc: 'Upscale the current image', hotkey: 'U' },
{
title: 'Show Info',
desc: 'Show metadata info of the current image',
hotkey: 'I',
},
{
title: 'Send To Image To Image',
desc: 'Send the current image to Image to Image module',
hotkey: 'Shift+I',
},
{ title: 'Delete Image', desc: 'Delete the current image', hotkey: 'Del' },
{
title: 'Focus Prompt',
desc: 'Focus the prompt input area',
hotkey: 'Alt+A',
},
];
const renderHotkeyModalItems = () => {
const hotkeyModalItemsToRender: ReactElement[] = [];
hotkeys.forEach((hotkey, i) => {
hotkeyModalItemsToRender.push(
<HotkeysModalItem
key={i}
title={hotkey.title}
description={hotkey.desc}
hotkey={hotkey.hotkey}
/>
);
});
return hotkeyModalItemsToRender;
};
return (
<>
{cloneElement(children, {
onClick: onHotkeysModalOpen,
})}
<Modal isOpen={isHotkeyModalOpen} onClose={onHotkeysModalClose}>
<ModalOverlay />
<ModalContent className="hotkeys-modal">
<ModalCloseButton />
<h1>Keyboard Shorcuts</h1>
<div className="hotkeys-modal-items">{renderHotkeyModalItems()}</div>
</ModalContent>
</Modal>
</>
);
}

View File

@ -0,0 +1,20 @@
import React from 'react';
interface HotkeysModalProps {
hotkey: string;
title: string;
description?: string;
}
export default function HotkeysModalItem(props: HotkeysModalProps) {
const { title, hotkey, description } = props;
return (
<div className="hotkey-modal-item">
<div className="hotkey-info">
<p className="hotkey-title">{title}</p>
{description && <p className="hotkey-description">{description}</p>}
</div>
<div className="hotkey-key">{hotkey}</div>
</div>
);
}

View File

@ -2,6 +2,7 @@
.settings-modal { .settings-modal {
background-color: var(--settings-modal-bg) !important; background-color: var(--settings-modal-bg) !important;
font-family: Inter;
.settings-modal-content { .settings-modal-content {
display: grid; display: grid;

View File

@ -21,7 +21,7 @@
.site-header-right-side { .site-header-right-side {
display: grid; display: grid;
grid-template-columns: repeat(5, max-content); grid-template-columns: repeat(6, max-content);
align-items: center; align-items: center;
column-gap: 0.5rem; column-gap: 0.5rem;
} }

View File

@ -1,9 +1,11 @@
import { IconButton, Link, useColorMode } from '@chakra-ui/react'; import { IconButton, Link, useColorMode } from '@chakra-ui/react';
import { FaSun, FaMoon, FaGithub } from 'react-icons/fa'; import { FaSun, FaMoon, FaGithub } from 'react-icons/fa';
import { MdHelp, MdSettings } from 'react-icons/md'; import { MdHelp, MdKeyboard, MdSettings } from 'react-icons/md';
import InvokeAILogo from '../../assets/images/logo.png'; import InvokeAILogo from '../../assets/images/logo.png';
import HotkeysModal from './HotkeysModal/HotkeysModal';
import SettingsModal from './SettingsModal/SettingsModal'; import SettingsModal from './SettingsModal/SettingsModal';
import StatusIndicator from './StatusIndicator'; import StatusIndicator from './StatusIndicator';
@ -40,6 +42,16 @@ const SiteHeader = () => {
/> />
</SettingsModal> </SettingsModal>
<HotkeysModal>
<IconButton
aria-label="Hotkeys"
variant="link"
fontSize={24}
size={'sm'}
icon={<MdKeyboard />}
/>
</HotkeysModal>
<IconButton <IconButton
aria-label="Link to Github Issues" aria-label="Link to Github Issues"
variant="link" variant="link"

View File

@ -11,6 +11,7 @@
@use '../features/system/SiteHeader.scss'; @use '../features/system/SiteHeader.scss';
@use '../features/system/StatusIndicator.scss'; @use '../features/system/StatusIndicator.scss';
@use '../features/system/SettingsModal/SettingsModal.scss'; @use '../features/system/SettingsModal/SettingsModal.scss';
@use '../features/system/HotkeysModal/HotkeysModal.scss';
@use '../features/system/Console.scss'; @use '../features/system/Console.scss';
// options // options

View File

@ -1582,6 +1582,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64id@2.0.0, base64id@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
binary-extensions@^2.0.0: binary-extensions@^2.0.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@ -2359,6 +2364,11 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-
dependencies: dependencies:
react-is "^16.7.0" react-is "^16.7.0"
hotkeys-js@3.9.4:
version "3.9.4"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.9.4.tgz#ce1aa4c3a132b6a63a9dd5644fc92b8a9b9cbfb9"
integrity sha512-2zuLt85Ta+gIyvs4N88pCYskNrxf1TFv3LR9t5mdAZIX8BcgQQ48F2opUptvHa6m8zsy5v/a0i9mWzTrlNWU0Q==
ignore@^5.2.0: ignore@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@ -2618,7 +2628,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4.1.1: object-assign@^4, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@ -2818,6 +2828,13 @@ react-focus-lock@^2.9.1:
use-callback-ref "^1.3.0" use-callback-ref "^1.3.0"
use-sidecar "^1.1.2" use-sidecar "^1.1.2"
react-hotkeys-hook@^3.4.7:
version "3.4.7"
resolved "https://registry.yarnpkg.com/react-hotkeys-hook/-/react-hotkeys-hook-3.4.7.tgz#e16a0a85f59feed9f48d12cfaf166d7df4c96b7a"
integrity sha512-+bbPmhPAl6ns9VkXkNNyxlmCAIyDAcWbB76O4I0ntr3uWCRuIQf/aRLartUahe9chVMPj+OEzzfk3CQSjclUEQ==
dependencies:
hotkeys-js "3.9.4"
react-icons@^4.4.0: react-icons@^4.4.0:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
@ -3044,6 +3061,18 @@ socket.io-parser@~4.2.0:
"@socket.io/component-emitter" "~3.1.0" "@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1" debug "~4.3.1"
socket.io@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.2.tgz#1eb25fd380ab3d63470aa8279f8e48d922d443ac"
integrity sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~6.2.0"
socket.io-adapter "~2.4.0"
socket.io-parser "~4.2.0"
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"