feat(ui): restore invoke button (wip)

This commit is contained in:
psychedelicious 2024-04-17 13:59:18 +10:00 committed by Kent Keirsey
parent 1f8f429d55
commit c9bf00b80b
5 changed files with 52 additions and 37 deletions

View File

@ -0,0 +1,15 @@
import { Button } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { allLayersDeleted } from 'features/regionalPrompts/store/regionalPromptsSlice';
import { memo, useCallback } from 'react';
export const DeleteAllLayersButton = memo(() => {
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
dispatch(allLayersDeleted());
}, [dispatch]);
return <Button onClick={onClick}>Delete All</Button>;
});
DeleteAllLayersButton.displayName = 'DeleteAllLayersButton';

View File

@ -4,10 +4,10 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
import { StageComponent } from 'features/regionalPrompts/components/imperative/konvaApiDraft';
import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem';
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
import { RegionalPromptsStage } from 'features/regionalPrompts/components/RegionalPromptsStage';
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
@ -27,8 +27,11 @@ export const RegionalPromptsEditor = memo(() => {
return (
<Flex gap={4} w="full" h="full">
<Flex flexDir="column" gap={4} flexShrink={0}>
<Button onClick={debugBlobs}>DEBUG LAYERS</Button>
<AddLayerButton />
<Flex>
<Button onClick={debugBlobs}>DEBUG</Button>
<AddLayerButton />
<DeleteAllLayersButton />
</Flex>
<BrushSize />
<PromptLayerOpacity />
<ImageSizeLinear />

View File

@ -113,7 +113,7 @@ const renderBrushPreview = (
});
};
const renderLayers = (
export const renderLayers = (
stage: Konva.Stage,
reduxLayers: Layer[],
selectedLayerId: string | null,
@ -369,7 +369,11 @@ export const StageComponent = () => {
const container = useStore($container);
return (
<>
<chakra.div ref={containerRef} tabIndex={-1} sx={{ borderWidth: 1, borderRadius: 'base' }} />
<chakra.div
ref={containerRef}
tabIndex={-1}
sx={{ borderWidth: 1, borderRadius: 'base', flexGrow: 0, h: 'min-content' }}
/>
<LogicalStage container={container} />
</>
);

View File

@ -159,6 +159,10 @@ export const regionalPromptsSlice = createSlice({
}
layer.bbox = bbox;
},
allLayersDeleted: (state) => {
state.layers = [];
state.selectedLayer = null;
},
promptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string }>) => {
const { layerId, prompt } = action.payload;
const layer = state.layers.find((l) => l.id === layerId);
@ -258,6 +262,7 @@ export const {
layerTranslated,
layerBboxChanged,
promptLayerOpacityChanged,
allLayersDeleted,
} = regionalPromptsSlice.actions;
export const selectRegionalPromptsSlice = (state: RootState) => state.regionalPrompts;

View File

@ -1,7 +1,8 @@
import { getStore } from 'app/store/nanostores/store';
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
import { selectPromptLayerObjectGroup } from 'features/regionalPrompts/components/LayerComponent';
import { getStage, REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/store/regionalPromptsSlice';
import { renderLayers } from 'features/regionalPrompts/components/imperative/konvaApiDraft';
import { REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/store/regionalPromptsSlice';
import Konva from 'konva';
import { assert } from 'tsafe';
@ -15,40 +16,27 @@ export const getRegionalPromptLayerBlobs = async (
layerIds?: string[],
preview: boolean = false
): Promise<Record<string, Blob>> => {
const stage = getStage();
// This automatically omits layers that are not rendered. Rendering is controlled by the layer's `isVisible` flag in redux.
const regionalPromptLayers = stage.getLayers().filter((l) => {
console.log(l.name(), l.id());
const isRegionalPromptLayer = l.name() === REGIONAL_PROMPT_LAYER_NAME;
const isRequestedLayerId = layerIds ? layerIds.includes(l.id()) : true;
return isRegionalPromptLayer && isRequestedLayerId;
});
// We need to reconstruct each layer to only output the desired data. This logic mirrors the logic in
// `getKonvaLayerBbox()` in `invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts`
const offscreenStageContainer = document.createElement('div');
const offscreenStage = new Konva.Stage({
container: offscreenStageContainer,
width: stage.width(),
height: stage.height(),
});
const state = getStore().getState();
const container = document.createElement('div');
const stage = new Konva.Stage({ container, width: state.generation.width, height: state.generation.height });
renderLayers(stage, state.regionalPrompts.layers, state.regionalPrompts.selectedLayer);
const layers = stage.find<Konva.Layer>(`.${REGIONAL_PROMPT_LAYER_NAME}`);
const blobs: Record<string, Blob> = {};
for (const layer of regionalPromptLayers) {
const layerClone = layer.clone();
for (const child of layerClone.getChildren()) {
if (selectPromptLayerObjectGroup(child)) {
child.destroy();
} else {
// We need to re-cache to handle children with transparency and multiple objects - like prompt region layers.
child.cache();
}
// First remove all layers
for (const layer of layers) {
layer.remove();
}
// Next render each layer to a blob
for (const layer of layers) {
if (layerIds && !layerIds.includes(layer.id())) {
continue;
}
offscreenStage.add(layerClone);
stage.add(layer);
const blob = await new Promise<Blob>((resolve) => {
offscreenStage.toBlob({
stage.toBlob({
callback: (blob) => {
assert(blob, 'Blob is null');
resolve(blob);
@ -60,7 +48,7 @@ export const getRegionalPromptLayerBlobs = async (
const base64 = await blobToDataURL(blob);
openBase64ImageInTab([{ base64, caption: layer.id() }]);
}
layerClone.destroy();
layer.remove();
blobs[layer.id()] = blob;
}