mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-17 03:51:26 -05:00
feat(ui): revise app layout strategy, add interaction scopes for hotkeys
This commit is contained in:
@@ -5,9 +5,6 @@ import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import TopPanel from 'features/nodes/components/flow/panels/TopPanel/TopPanel';
|
||||
import { LoadWorkflowFromGraphModal } from 'features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal';
|
||||
import { SaveWorkflowAsDialog } from 'features/workflowLibrary/components/SaveWorkflowAsDialog/SaveWorkflowAsDialog';
|
||||
import type { AnimationProps } from 'framer-motion';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdDeviceHub } from 'react-icons/md';
|
||||
@@ -18,28 +15,6 @@ import { Flow } from './flow/Flow';
|
||||
import BottomLeftPanel from './flow/panels/BottomLeftPanel/BottomLeftPanel';
|
||||
import MinimapPanel from './flow/panels/MinimapPanel/MinimapPanel';
|
||||
|
||||
const isReadyMotionStyles: CSSProperties = {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
const notIsReadyMotionStyles: CSSProperties = {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
const initial: AnimationProps['initial'] = {
|
||||
opacity: 0,
|
||||
};
|
||||
const animate: AnimationProps['animate'] = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.2 },
|
||||
};
|
||||
const exit: AnimationProps['exit'] = {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.2 },
|
||||
};
|
||||
|
||||
const NodeEditor = () => {
|
||||
const { data, isLoading } = useGetOpenAPISchemaQuery();
|
||||
const { t } = useTranslation();
|
||||
@@ -53,37 +28,18 @@ const NodeEditor = () => {
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<AnimatePresence>
|
||||
{data && (
|
||||
<motion.div initial={initial} animate={animate} exit={exit} style={isReadyMotionStyles}>
|
||||
<Flow />
|
||||
<AddNodePopover />
|
||||
<TopPanel />
|
||||
<BottomLeftPanel />
|
||||
<MinimapPanel />
|
||||
<SaveWorkflowAsDialog />
|
||||
<LoadWorkflowFromGraphModal />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<AnimatePresence>
|
||||
{isLoading && (
|
||||
<motion.div initial={initial} animate={animate} exit={exit} style={notIsReadyMotionStyles}>
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
position="relative"
|
||||
width="full"
|
||||
height="full"
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<IAINoContentFallback label={t('nodes.loadingNodes')} icon={MdDeviceHub} />
|
||||
</Flex>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{data && (
|
||||
<>
|
||||
<Flow />
|
||||
<AddNodePopover />
|
||||
<TopPanel />
|
||||
<BottomLeftPanel />
|
||||
<MinimapPanel />
|
||||
<SaveWorkflowAsDialog />
|
||||
<LoadWorkflowFromGraphModal />
|
||||
</>
|
||||
)}
|
||||
{isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={MdDeviceHub} />}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Combobox, Flex, Popover, PopoverAnchor, PopoverBody, PopoverContent } f
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch, useAppStore } from 'app/store/storeHooks';
|
||||
import type { SelectInstance } from 'chakra-react-select';
|
||||
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
|
||||
import { useBuildNode } from 'features/nodes/hooks/useBuildNode';
|
||||
import {
|
||||
$cursorPos,
|
||||
@@ -67,6 +68,7 @@ const AddNodePopover = () => {
|
||||
const pendingConnection = useStore($pendingConnection);
|
||||
const isOpen = useStore($isAddNodePopoverOpen);
|
||||
const store = useAppStore();
|
||||
const isWorkflowsActive = useStore(INTERACTION_SCOPES.workflows.$isActive);
|
||||
|
||||
const filteredTemplates = useMemo(() => {
|
||||
// If we have a connection in progress, we need to filter the node choices
|
||||
@@ -214,14 +216,7 @@ const AddNodePopover = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleHotkeyClose: HotkeyCallback = useCallback(() => {
|
||||
if ($isAddNodePopoverOpen.get()) {
|
||||
closeAddNodePopover();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useHotkeys(['shift+a', 'space'], handleHotkeyOpen);
|
||||
useHotkeys(['escape'], handleHotkeyClose, { enableOnFormTags: ['TEXTAREA'] });
|
||||
useHotkeys(['shift+a', 'space'], handleHotkeyOpen, { enabled: isWorkflowsActive }, [isWorkflowsActive]);
|
||||
|
||||
const noOptionsMessage = useCallback(() => t('nodes.noMatchingNodes'), [t]);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch, useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { INTERACTION_SCOPES, useScopeImperativeApi } from 'common/hooks/interactionScopes';
|
||||
import { useConnection } from 'features/nodes/hooks/useConnection';
|
||||
import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste';
|
||||
import { useSyncExecutionState } from 'features/nodes/hooks/useExecutionState';
|
||||
@@ -79,16 +80,13 @@ export const Flow = memo(() => {
|
||||
const cancelConnection = useReactFlowStore(selectCancelConnection);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const store = useAppStore();
|
||||
const isWorkflowsActive = useStore(INTERACTION_SCOPES.workflows.$isActive);
|
||||
const workflowsScopeApi = useScopeImperativeApi('workflows');
|
||||
|
||||
useWorkflowWatcher();
|
||||
useSyncExecutionState();
|
||||
const [borderRadius] = useToken('radii', ['base']);
|
||||
|
||||
const flowStyles = useMemo<CSSProperties>(
|
||||
() => ({
|
||||
borderRadius,
|
||||
}),
|
||||
[borderRadius]
|
||||
);
|
||||
const flowStyles = useMemo<CSSProperties>(() => ({ borderRadius }), [borderRadius]);
|
||||
|
||||
const onNodesChange: OnNodesChange = useCallback(
|
||||
(nodeChanges) => {
|
||||
@@ -121,7 +119,8 @@ export const Flow = memo(() => {
|
||||
const { onCloseGlobal } = useGlobalMenuClose();
|
||||
const handlePaneClick = useCallback(() => {
|
||||
onCloseGlobal();
|
||||
}, [onCloseGlobal]);
|
||||
workflowsScopeApi.add();
|
||||
}, [onCloseGlobal, workflowsScopeApi]);
|
||||
|
||||
const onInit: OnInit = useCallback((flow) => {
|
||||
$flow.set(flow);
|
||||
@@ -237,7 +236,7 @@ export const Flow = memo(() => {
|
||||
},
|
||||
[dispatch, store]
|
||||
);
|
||||
useHotkeys(['Ctrl+a', 'Meta+a'], onSelectAllHotkey);
|
||||
useHotkeys(['Ctrl+a', 'Meta+a'], onSelectAllHotkey, { enabled: isWorkflowsActive }, [isWorkflowsActive]);
|
||||
|
||||
const onPasteHotkey = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user