mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 13:25:22 -05:00
feat(ui): switch to canvas tab when using launchpad
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { Button, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import { memo } from 'react';
|
||||
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import { WORKSPACE_PANEL_ID } from 'features/ui/layouts/shared';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
import { InitialStateMainModelPicker } from './InitialStateMainModelPicker';
|
||||
import { LaunchpadAddStyleReference } from './LaunchpadAddStyleReference';
|
||||
@@ -8,6 +10,10 @@ import { LaunchpadGenerateFromTextButton } from './LaunchpadGenerateFromTextButt
|
||||
import { LaunchpadUseALayoutImageButton } from './LaunchpadUseALayoutImageButton';
|
||||
|
||||
export const CanvasLaunchpadPanel = memo(() => {
|
||||
const ctx = useAutoLayoutContext();
|
||||
const focusCanvas = useCallback(() => {
|
||||
ctx.focusPanel(WORKSPACE_PANEL_ID);
|
||||
}, [ctx]);
|
||||
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">
|
||||
@@ -24,10 +30,10 @@ export const CanvasLaunchpadPanel = memo(() => {
|
||||
</Text>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<LaunchpadGenerateFromTextButton />
|
||||
<LaunchpadAddStyleReference />
|
||||
<LaunchpadEditImageButton />
|
||||
<LaunchpadUseALayoutImageButton />
|
||||
<LaunchpadGenerateFromTextButton extraAction={focusCanvas} />
|
||||
<LaunchpadAddStyleReference extraAction={focusCanvas} />
|
||||
<LaunchpadEditImageButton extraAction={focusCanvas} />
|
||||
<LaunchpadUseALayoutImageButton extraAction={focusCanvas} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const dndTargetData = addGlobalReferenceImageDndTarget.getData();
|
||||
|
||||
export const LaunchpadAddStyleReference = memo(() => {
|
||||
export const LaunchpadAddStyleReference = memo((props: { extraAction?: () => void }) => {
|
||||
const { dispatch, getState } = useAppStore();
|
||||
|
||||
const uploadOptions = useMemo(
|
||||
@@ -23,10 +23,11 @@ export const LaunchpadAddStyleReference = memo(() => {
|
||||
const config = getDefaultRefImageConfig(getState);
|
||||
config.image = imageDTOToImageWithDims(imageDTO);
|
||||
dispatch(refImageAdded({ overrides: { config } }));
|
||||
props.extraAction?.();
|
||||
},
|
||||
allowMultiple: false,
|
||||
}) as const,
|
||||
[dispatch, getState]
|
||||
[dispatch, getState, props]
|
||||
);
|
||||
|
||||
const uploadApi = useImageUploadButton(uploadOptions);
|
||||
|
||||
@@ -13,14 +13,15 @@ const NEW_CANVAS_OPTIONS = { type: 'raster_layer', withInpaintMask: true } as co
|
||||
|
||||
const dndTargetData = newCanvasFromImageDndTarget.getData(NEW_CANVAS_OPTIONS);
|
||||
|
||||
export const LaunchpadEditImageButton = memo(() => {
|
||||
export const LaunchpadEditImageButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { getState, dispatch } = useAppStore();
|
||||
|
||||
const onUpload = useCallback(
|
||||
(imageDTO: ImageDTO) => {
|
||||
newCanvasFromImage({ imageDTO, getState, dispatch, ...NEW_CANVAS_OPTIONS });
|
||||
props.extraAction?.();
|
||||
},
|
||||
[dispatch, getState]
|
||||
[dispatch, getState, props]
|
||||
);
|
||||
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
|
||||
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { LaunchpadButton } from 'features/controlLayers/components/SimpleSession/LaunchpadButton';
|
||||
import { memo } from 'react';
|
||||
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { PiCursorTextBold, PiTextAaBold } from 'react-icons/pi';
|
||||
|
||||
const focusOnPrompt = () => {
|
||||
const promptElement = document.getElementById('prompt');
|
||||
const focusOnPrompt = (el: HTMLElement) => {
|
||||
const promptElement = el.querySelector('.positive-prompt-textarea');
|
||||
if (promptElement instanceof HTMLTextAreaElement) {
|
||||
promptElement.focus();
|
||||
promptElement.select();
|
||||
}
|
||||
};
|
||||
|
||||
export const LaunchpadGenerateFromTextButton = memo(() => {
|
||||
export const LaunchpadGenerateFromTextButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { rootRef } = useAutoLayoutContext();
|
||||
const onClick = useCallback(() => {
|
||||
const el = rootRef.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
focusOnPrompt(el);
|
||||
props.extraAction?.();
|
||||
}, [props, rootRef]);
|
||||
return (
|
||||
<LaunchpadButton onClick={focusOnPrompt} position="relative" gap={8}>
|
||||
<LaunchpadButton onClick={onClick} position="relative" gap={8}>
|
||||
<Icon as={PiTextAaBold} boxSize={8} color="base.500" />
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">Generate from Text</Heading>
|
||||
|
||||
@@ -14,14 +14,15 @@ const NEW_CANVAS_OPTIONS = { type: 'control_layer', withResize: true } as const;
|
||||
|
||||
const dndTargetData = newCanvasFromImageDndTarget.getData(NEW_CANVAS_OPTIONS);
|
||||
|
||||
export const LaunchpadUseALayoutImageButton = memo(() => {
|
||||
export const LaunchpadUseALayoutImageButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { getState, dispatch } = useAppStore();
|
||||
|
||||
const onUpload = useCallback(
|
||||
(imageDTO: ImageDTO) => {
|
||||
newCanvasFromImage({ imageDTO, getState, dispatch, ...NEW_CANVAS_OPTIONS });
|
||||
props.extraAction?.();
|
||||
},
|
||||
[dispatch, getState]
|
||||
[dispatch, getState, props]
|
||||
);
|
||||
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export const ParamNegativePrompt = memo(() => {
|
||||
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
|
||||
<Box pos="relative" w="full">
|
||||
<Textarea
|
||||
id="negativePrompt"
|
||||
className="negative-prompt-textarea"
|
||||
name="negativePrompt"
|
||||
ref={textareaRef}
|
||||
value={prompt}
|
||||
|
||||
@@ -86,7 +86,7 @@ export const ParamPositivePrompt = memo(() => {
|
||||
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
|
||||
<Box pos="relative">
|
||||
<Textarea
|
||||
id="prompt"
|
||||
className="positive-prompt-textarea"
|
||||
name="prompt"
|
||||
ref={textareaRef}
|
||||
value={prompt}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const ParamSDXLNegativeStylePrompt = memo(() => {
|
||||
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
|
||||
<Box pos="relative">
|
||||
<Textarea
|
||||
id="prompt"
|
||||
className="negative-style-prompt-textarea"
|
||||
name="prompt"
|
||||
ref={textareaRef}
|
||||
value={prompt}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const ParamSDXLPositiveStylePrompt = memo(() => {
|
||||
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
|
||||
<Box pos="relative">
|
||||
<Textarea
|
||||
id="prompt"
|
||||
className="positive-style-prompt-textarea"
|
||||
name="prompt"
|
||||
ref={textareaRef}
|
||||
value={prompt}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { DockviewApi, GridviewApi } from 'dockview';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import type { WritableAtom } from 'nanostores';
|
||||
import { atom } from 'nanostores';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import type { PropsWithChildren, RefObject } from 'react';
|
||||
import { createContext, memo, useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
import { LEFT_PANEL_ID, LEFT_PANEL_MIN_SIZE_PX, RIGHT_PANEL_ID, RIGHT_PANEL_MIN_SIZE_PX } from './shared';
|
||||
@@ -13,6 +13,7 @@ type AutoLayoutContextValue = {
|
||||
toggleBothPanels: () => void;
|
||||
resetPanels: () => void;
|
||||
focusPanel: (id: string) => void;
|
||||
rootRef: RefObject<HTMLDivElement>;
|
||||
_$rootPanelApi: WritableAtom<GridviewApi | null>;
|
||||
_$leftPanelApi: WritableAtom<GridviewApi | null>;
|
||||
_$centerPanelApi: WritableAtom<DockviewApi | null>;
|
||||
@@ -55,8 +56,10 @@ const activatePanel = (api: GridviewApi | DockviewApi, panelId: string) => {
|
||||
panel.api.setActive();
|
||||
};
|
||||
|
||||
export const AutoLayoutProvider = (props: PropsWithChildren<{ $rootApi: WritableAtom<GridviewApi | null> }>) => {
|
||||
const { $rootApi, children } = props;
|
||||
export const AutoLayoutProvider = (
|
||||
props: PropsWithChildren<{ $rootApi: WritableAtom<GridviewApi | null>; rootRef: RefObject<HTMLDivElement> }>
|
||||
) => {
|
||||
const { $rootApi, rootRef, children } = props;
|
||||
const $leftApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const $centerApi = useState(() => atom<DockviewApi | null>(null))[0];
|
||||
const $rightApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
@@ -128,6 +131,7 @@ export const AutoLayoutProvider = (props: PropsWithChildren<{ $rootApi: Writable
|
||||
toggleBothPanels,
|
||||
resetPanels,
|
||||
focusPanel,
|
||||
rootRef,
|
||||
_$rootPanelApi: $rootApi,
|
||||
_$leftPanelApi: $leftApi,
|
||||
_$centerPanelApi: $centerApi,
|
||||
@@ -140,6 +144,7 @@ export const AutoLayoutProvider = (props: PropsWithChildren<{ $rootApi: Writable
|
||||
$rootApi,
|
||||
focusPanel,
|
||||
resetPanels,
|
||||
rootRef,
|
||||
toggleBothPanels,
|
||||
toggleLeftPanel,
|
||||
toggleRightPanel,
|
||||
|
||||
@@ -247,7 +247,7 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
|
||||
};
|
||||
|
||||
export const CanvasTabAutoLayout = memo(() => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const $rootPanelApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
@@ -256,12 +256,12 @@ export const CanvasTabAutoLayout = memo(() => {
|
||||
},
|
||||
[$rootPanelApi]
|
||||
);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, ref);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, rootRef);
|
||||
|
||||
return (
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi}>
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi} rootRef={rootRef}>
|
||||
<GridviewReact
|
||||
ref={ref}
|
||||
ref={rootRef}
|
||||
className="dockview-theme-invoke"
|
||||
components={rootPanelComponents}
|
||||
onReady={onReady}
|
||||
|
||||
@@ -222,7 +222,7 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
|
||||
};
|
||||
|
||||
export const GenerateTabAutoLayout = memo(() => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const $rootPanelApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
@@ -231,12 +231,12 @@ export const GenerateTabAutoLayout = memo(() => {
|
||||
},
|
||||
[$rootPanelApi]
|
||||
);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, ref);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, rootRef);
|
||||
|
||||
return (
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi}>
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi} rootRef={rootRef}>
|
||||
<GridviewReact
|
||||
ref={ref}
|
||||
ref={rootRef}
|
||||
className="dockview-theme-invoke"
|
||||
components={rootPanelComponents}
|
||||
onReady={onReady}
|
||||
|
||||
@@ -217,7 +217,7 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
|
||||
};
|
||||
|
||||
export const UpscalingTabAutoLayout = memo(() => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const $rootPanelApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
@@ -226,12 +226,12 @@ export const UpscalingTabAutoLayout = memo(() => {
|
||||
},
|
||||
[$rootPanelApi]
|
||||
);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, ref);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, rootRef);
|
||||
|
||||
return (
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi}>
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi} rootRef={rootRef}>
|
||||
<GridviewReact
|
||||
ref={ref}
|
||||
ref={rootRef}
|
||||
className="dockview-theme-invoke"
|
||||
components={rootPanelComponents}
|
||||
onReady={onReady}
|
||||
|
||||
@@ -235,7 +235,7 @@ export const initializeRootPanelLayout = (api: GridviewApi) => {
|
||||
};
|
||||
|
||||
export const WorkflowsTabAutoLayout = memo(() => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const $rootPanelApi = useState(() => atom<GridviewApi | null>(null))[0];
|
||||
const onReady = useCallback<IGridviewReactProps['onReady']>(
|
||||
(event) => {
|
||||
@@ -244,12 +244,12 @@ export const WorkflowsTabAutoLayout = memo(() => {
|
||||
},
|
||||
[$rootPanelApi]
|
||||
);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, ref);
|
||||
useResizeMainPanelOnFirstVisit($rootPanelApi, rootRef);
|
||||
|
||||
return (
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi}>
|
||||
<AutoLayoutProvider $rootApi={$rootPanelApi} rootRef={rootRef}>
|
||||
<GridviewReact
|
||||
ref={ref}
|
||||
ref={rootRef}
|
||||
className="dockview-theme-invoke"
|
||||
components={rootPanelComponents}
|
||||
onReady={onReady}
|
||||
|
||||
Reference in New Issue
Block a user