Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions invokeai/frontend/web/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ module.exports = {
'no-promise-executor-return': 'error',
// https://eslint.org/docs/latest/rules/require-await
'require-await': 'error',
// Restrict setActiveTab calls to only use-navigation-api.tsx
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.name="setActiveTab"]',
message:
'setActiveTab() can only be called from use-navigation-api.tsx. Use navigationApi.switchToTab() instead.',
},
],
// TODO: ENABLE THIS RULE BEFORE v6.0.0
'react/display-name': 'off',
'no-restricted-properties': [
Expand Down Expand Up @@ -56,6 +65,15 @@ module.exports = {
],
},
overrides: [
/**
* Allow setActiveTab calls only in use-navigation-api.tsx
*/
{
files: ['**/use-navigation-api.tsx'],
rules: {
'no-restricted-syntax': 'off',
},
},
/**
* Overrides for stories
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { PartialAppConfig } from 'app/types/invokeai';
import { useFocusRegionWatcher } from 'common/hooks/focus';
import { useCloseChakraTooltipsOnDragFix } from 'common/hooks/useCloseChakraTooltipsOnDragFix';
import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
import { useDndMonitor } from 'features/dnd/useDndMonitor';
import { useDynamicPromptsWatcher } from 'features/dynamicPrompts/hooks/useDynamicPromptsWatcher';
import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast';
import { useWorkflowBuilderWatcher } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher';
Expand Down Expand Up @@ -45,6 +46,7 @@ export const GlobalHookIsolator = memo(
useSyncLoggingConfig();
useCloseChakraTooltipsOnDragFix();
useNavigationApi();
useDndMonitor();

// Persistent subscription to the queue counts query - canvas relies on this to know if there are pending
// and/or in progress canvas sessions.
Expand Down
23 changes: 12 additions & 11 deletions invokeai/frontend/web/src/app/hooks/useStudioInitAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
} from 'features/nodes/store/workflowLibrarySlice';
import { $isStylePresetsMenuOpen, activeStylePresetIdChanged } from 'features/stylePresets/store/stylePresetSlice';
import { toast } from 'features/toast/toast';
import { activeTabCanvasRightPanelChanged, setActiveTab } from 'features/ui/store/uiSlice';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { activeTabCanvasRightPanelChanged } from 'features/ui/store/uiSlice';
import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
import { atom } from 'nanostores';
import { useCallback, useEffect } from 'react';
Expand Down Expand Up @@ -122,17 +123,17 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
);

const handleLoadWorkflow = useCallback(
async (workflowId: string) => {
(workflowId: string) => {
// This shows a toast
await loadWorkflowWithDialog({
loadWorkflowWithDialog({
type: 'library',
data: workflowId,
onSuccess: () => {
store.dispatch(setActiveTab('workflows'));
navigationApi.switchToTab('workflows');
},
});
},
[loadWorkflowWithDialog, store]
[loadWorkflowWithDialog]
);

const handleSelectStylePreset = useCallback(
Expand All @@ -146,7 +147,7 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
return;
}
store.dispatch(activeStylePresetIdChanged(stylePresetId));
store.dispatch(setActiveTab('canvas'));
navigationApi.switchToTab('canvas');
toast({
title: t('toast.stylePresetLoaded'),
status: 'info',
Expand All @@ -169,20 +170,20 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
break;
case 'workflows':
// Go to the workflows tab
store.dispatch(setActiveTab('workflows'));
navigationApi.switchToTab('workflows');
break;
case 'upscaling':
// Go to the upscaling tab
store.dispatch(setActiveTab('upscaling'));
navigationApi.switchToTab('upscaling');
break;
case 'viewAllWorkflows':
// Go to the workflows tab and open the workflow library modal
store.dispatch(setActiveTab('workflows'));
navigationApi.switchToTab('workflows');
$isWorkflowLibraryModalOpen.set(true);
break;
case 'viewAllWorkflowsRecommended':
// Go to the workflows tab and open the workflow library modal with the recommended workflows view
store.dispatch(setActiveTab('workflows'));
navigationApi.switchToTab('workflows');
$isWorkflowLibraryModalOpen.set(true);
store.dispatch(workflowLibraryViewChanged('defaults'));
store.dispatch(workflowLibraryTagsReset());
Expand All @@ -194,7 +195,7 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
break;
case 'viewAllStylePresets':
// Go to the canvas tab and open the style presets menu
store.dispatch(setActiveTab('canvas'));
navigationApi.switchToTab('canvas');
$isStylePresetsMenuOpen.set(true);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,16 @@ const Loading = () => {
return (
<Flex
position="absolute"
width="100dvw"
height="100dvh"
alignItems="center"
justifyContent="center"
bg="#151519"
top={0}
right={0}
bottom={0}
left={0}
bg="hsl(220 12% 10% / 1)" // base.900
inset={0}
zIndex={99999}
>
<Image src={InvokeLogoWhite} w="8rem" h="8rem" />
<Spinner
label="Loading"
color="grey"
color="hsl(220 12% 68% / 1)" // base.300
position="absolute"
size="sm"
width="24px !important"
Expand Down
14 changes: 7 additions & 7 deletions invokeai/frontend/web/src/common/hooks/useGlobalHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrent
import { useInvoke } from 'features/queue/hooks/useInvoke';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { navigationApi } from 'features/ui/layouts/navigation-api';

import { getFocusedRegion } from './focus';

Expand Down Expand Up @@ -69,7 +69,7 @@ export const useGlobalHotkeys = () => {
id: 'selectGenerateTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('generate'));
navigationApi.switchToTab('generate');
},
dependencies: [dispatch],
});
Expand All @@ -78,7 +78,7 @@ export const useGlobalHotkeys = () => {
id: 'selectCanvasTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('canvas'));
navigationApi.switchToTab('canvas');
},
dependencies: [dispatch],
});
Expand All @@ -87,7 +87,7 @@ export const useGlobalHotkeys = () => {
id: 'selectUpscalingTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('upscaling'));
navigationApi.switchToTab('upscaling');
},
dependencies: [dispatch],
});
Expand All @@ -96,7 +96,7 @@ export const useGlobalHotkeys = () => {
id: 'selectWorkflowsTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('workflows'));
navigationApi.switchToTab('workflows');
},
dependencies: [dispatch],
});
Expand All @@ -105,7 +105,7 @@ export const useGlobalHotkeys = () => {
id: 'selectModelsTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('models'));
navigationApi.switchToTab('models');
},
options: {
enabled: isModelManagerEnabled,
Expand All @@ -117,7 +117,7 @@ export const useGlobalHotkeys = () => {
id: 'selectQueueTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('queue'));
navigationApi.switchToTab('queue');
},
dependencies: [dispatch, isModelManagerEnabled],
});
Expand Down
20 changes: 20 additions & 0 deletions invokeai/frontend/web/src/common/util/createDeferredPromise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type Deferred<T> = {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (error: Error) => void;
};

/**
* Create a promise and expose its resolve and reject callbacks.
*/
export const createDeferredPromise = <T>(): Deferred<T> => {
let resolve!: (value: T) => void;
let reject!: (error: Error) => void;

const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});

return { promise, resolve, reject };
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Button, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { WORKSPACE_PANEL_ID } from 'features/ui/layouts/shared';
import { memo, useCallback } from 'react';
Expand All @@ -13,10 +12,9 @@ import { LaunchpadUseALayoutImageButton } from './LaunchpadUseALayoutImageButton

export const CanvasLaunchpadPanel = memo(() => {
const { t } = useTranslation();
const { tab } = useAutoLayoutContext();
const focusCanvas = useCallback(() => {
navigationApi.focusPanelInTab(tab, WORKSPACE_PANEL_ID);
}, [tab]);
navigationApi.focusPanel('canvas', WORKSPACE_PANEL_ID);
}, []);
return (
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { Alert, Button, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { InitialStateMainModelPicker } from 'features/controlLayers/components/SimpleSession/InitialStateMainModelPicker';
import { LaunchpadAddStyleReference } from 'features/controlLayers/components/SimpleSession/LaunchpadAddStyleReference';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { memo, useCallback } from 'react';

import { LaunchpadGenerateFromTextButton } from './LaunchpadGenerateFromTextButton';

export const GenerateLaunchpadPanel = memo(() => {
const dispatch = useAppDispatch();
const newCanvasSession = useCallback(() => {
dispatch(setActiveTab('canvas'));
}, [dispatch]);
navigationApi.switchToTab('canvas');
}, []);

return (
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
import { LaunchpadButton } from 'features/controlLayers/components/SimpleSession/LaunchpadButton';
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
import { memo, useCallback } from 'react';
import { PiCursorTextBold, PiTextAaBold } from 'react-icons/pi';

const focusOnPrompt = (el: HTMLElement) => {
const promptElement = el.querySelector('.positive-prompt-textarea');
const focusOnPrompt = () => {
const promptElement = document.querySelector('.positive-prompt-textarea');
if (promptElement instanceof HTMLTextAreaElement) {
promptElement.focus();
promptElement.select();
}
};

export const LaunchpadGenerateFromTextButton = memo((props: { extraAction?: () => void }) => {
const { rootRef } = useAutoLayoutContext();
const onClick = useCallback(() => {
const el = rootRef.current;
if (!el) {
return;
}
focusOnPrompt(el);
focusOnPrompt();
props.extraAction?.();
}, [props, rootRef]);
}, [props]);
return (
<LaunchpadButton onClick={onClick} position="relative" gap={8}>
<Icon as={PiTextAaBold} boxSize={8} color="base.500" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const BoardsPanel = memo(() => {
const { tab } = useAutoLayoutContext();
const collapsibleApi = useCollapsibleGridviewPanel(
tab,
'right',
BOARDS_PANEL_ID,
'vertical',
BOARD_PANEL_DEFAULT_HEIGHT_PX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const GalleryPanel = memo(() => {
const { tab } = useAutoLayoutContext();
const collapsibleApi = useCollapsibleGridviewPanel(
tab,
'right',
GALLERY_PANEL_ID,
'vertical',
GALLERY_PANEL_DEFAULT_HEIGHT_PX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {

const onClickNewCanvasWithRasterLayerFromImage = useCallback(async () => {
const { dispatch, getState } = store;
await navigationApi.focusPanel('canvas', WORKSPACE_PANEL_ID);
await newCanvasFromImage({ imageDTO, withResize: false, type: 'raster_layer', dispatch, getState });
navigationApi.focusPanelInTab('canvas', WORKSPACE_PANEL_ID);
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
Expand All @@ -31,8 +31,8 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {

const onClickNewCanvasWithControlLayerFromImage = useCallback(async () => {
const { dispatch, getState } = store;
await navigationApi.focusPanel('canvas', WORKSPACE_PANEL_ID);
await newCanvasFromImage({ imageDTO, withResize: false, type: 'control_layer', dispatch, getState });
navigationApi.focusPanelInTab('canvas', WORKSPACE_PANEL_ID);
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
Expand All @@ -42,8 +42,8 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {

const onClickNewCanvasWithRasterLayerFromImageWithResize = useCallback(async () => {
const { dispatch, getState } = store;
await navigationApi.focusPanel('canvas', WORKSPACE_PANEL_ID);
await newCanvasFromImage({ imageDTO, withResize: true, type: 'raster_layer', dispatch, getState });
navigationApi.focusPanelInTab('canvas', WORKSPACE_PANEL_ID);
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
Expand All @@ -53,8 +53,8 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {

const onClickNewCanvasWithControlLayerFromImageWithResize = useCallback(async () => {
const { dispatch, getState } = store;
await navigationApi.focusPanel('canvas', WORKSPACE_PANEL_ID);
await newCanvasFromImage({ imageDTO, withResize: true, type: 'control_layer', dispatch, getState });
navigationApi.focusPanelInTab('canvas', WORKSPACE_PANEL_ID);
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
Expand Down
Loading