mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): restore invoke button (wip)
This commit is contained in:
parent
1f8f429d55
commit
c9bf00b80b
@ -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';
|
@ -4,10 +4,10 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
|
import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton';
|
||||||
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
|
import { BrushSize } from 'features/regionalPrompts/components/BrushSize';
|
||||||
|
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
|
||||||
import { StageComponent } from 'features/regionalPrompts/components/imperative/konvaApiDraft';
|
import { StageComponent } from 'features/regionalPrompts/components/imperative/konvaApiDraft';
|
||||||
import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem';
|
import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem';
|
||||||
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
|
import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity';
|
||||||
import { RegionalPromptsStage } from 'features/regionalPrompts/components/RegionalPromptsStage';
|
|
||||||
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
|
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
|
||||||
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
|
import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
|
||||||
@ -27,8 +27,11 @@ export const RegionalPromptsEditor = memo(() => {
|
|||||||
return (
|
return (
|
||||||
<Flex gap={4} w="full" h="full">
|
<Flex gap={4} w="full" h="full">
|
||||||
<Flex flexDir="column" gap={4} flexShrink={0}>
|
<Flex flexDir="column" gap={4} flexShrink={0}>
|
||||||
<Button onClick={debugBlobs}>DEBUG LAYERS</Button>
|
<Flex>
|
||||||
<AddLayerButton />
|
<Button onClick={debugBlobs}>DEBUG</Button>
|
||||||
|
<AddLayerButton />
|
||||||
|
<DeleteAllLayersButton />
|
||||||
|
</Flex>
|
||||||
<BrushSize />
|
<BrushSize />
|
||||||
<PromptLayerOpacity />
|
<PromptLayerOpacity />
|
||||||
<ImageSizeLinear />
|
<ImageSizeLinear />
|
||||||
|
@ -113,7 +113,7 @@ const renderBrushPreview = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderLayers = (
|
export const renderLayers = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
reduxLayers: Layer[],
|
reduxLayers: Layer[],
|
||||||
selectedLayerId: string | null,
|
selectedLayerId: string | null,
|
||||||
@ -369,7 +369,11 @@ export const StageComponent = () => {
|
|||||||
const container = useStore($container);
|
const container = useStore($container);
|
||||||
return (
|
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} />
|
<LogicalStage container={container} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -159,6 +159,10 @@ export const regionalPromptsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
layer.bbox = bbox;
|
layer.bbox = bbox;
|
||||||
},
|
},
|
||||||
|
allLayersDeleted: (state) => {
|
||||||
|
state.layers = [];
|
||||||
|
state.selectedLayer = null;
|
||||||
|
},
|
||||||
promptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string }>) => {
|
promptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string }>) => {
|
||||||
const { layerId, prompt } = action.payload;
|
const { layerId, prompt } = action.payload;
|
||||||
const layer = state.layers.find((l) => l.id === layerId);
|
const layer = state.layers.find((l) => l.id === layerId);
|
||||||
@ -258,6 +262,7 @@ export const {
|
|||||||
layerTranslated,
|
layerTranslated,
|
||||||
layerBboxChanged,
|
layerBboxChanged,
|
||||||
promptLayerOpacityChanged,
|
promptLayerOpacityChanged,
|
||||||
|
allLayersDeleted,
|
||||||
} = regionalPromptsSlice.actions;
|
} = regionalPromptsSlice.actions;
|
||||||
|
|
||||||
export const selectRegionalPromptsSlice = (state: RootState) => state.regionalPrompts;
|
export const selectRegionalPromptsSlice = (state: RootState) => state.regionalPrompts;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { getStore } from 'app/store/nanostores/store';
|
||||||
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
||||||
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||||
import { selectPromptLayerObjectGroup } from 'features/regionalPrompts/components/LayerComponent';
|
import { renderLayers } from 'features/regionalPrompts/components/imperative/konvaApiDraft';
|
||||||
import { getStage, REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
import { REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
@ -15,40 +16,27 @@ export const getRegionalPromptLayerBlobs = async (
|
|||||||
layerIds?: string[],
|
layerIds?: string[],
|
||||||
preview: boolean = false
|
preview: boolean = false
|
||||||
): Promise<Record<string, Blob>> => {
|
): Promise<Record<string, Blob>> => {
|
||||||
const stage = getStage();
|
const state = getStore().getState();
|
||||||
|
const container = document.createElement('div');
|
||||||
// This automatically omits layers that are not rendered. Rendering is controlled by the layer's `isVisible` flag in redux.
|
const stage = new Konva.Stage({ container, width: state.generation.width, height: state.generation.height });
|
||||||
const regionalPromptLayers = stage.getLayers().filter((l) => {
|
renderLayers(stage, state.regionalPrompts.layers, state.regionalPrompts.selectedLayer);
|
||||||
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 layers = stage.find<Konva.Layer>(`.${REGIONAL_PROMPT_LAYER_NAME}`);
|
||||||
const blobs: Record<string, Blob> = {};
|
const blobs: Record<string, Blob> = {};
|
||||||
|
|
||||||
for (const layer of regionalPromptLayers) {
|
// First remove all layers
|
||||||
const layerClone = layer.clone();
|
for (const layer of layers) {
|
||||||
for (const child of layerClone.getChildren()) {
|
layer.remove();
|
||||||
if (selectPromptLayerObjectGroup(child)) {
|
}
|
||||||
child.destroy();
|
|
||||||
} else {
|
// Next render each layer to a blob
|
||||||
// We need to re-cache to handle children with transparency and multiple objects - like prompt region layers.
|
for (const layer of layers) {
|
||||||
child.cache();
|
if (layerIds && !layerIds.includes(layer.id())) {
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
offscreenStage.add(layerClone);
|
stage.add(layer);
|
||||||
const blob = await new Promise<Blob>((resolve) => {
|
const blob = await new Promise<Blob>((resolve) => {
|
||||||
offscreenStage.toBlob({
|
stage.toBlob({
|
||||||
callback: (blob) => {
|
callback: (blob) => {
|
||||||
assert(blob, 'Blob is null');
|
assert(blob, 'Blob is null');
|
||||||
resolve(blob);
|
resolve(blob);
|
||||||
@ -60,7 +48,7 @@ export const getRegionalPromptLayerBlobs = async (
|
|||||||
const base64 = await blobToDataURL(blob);
|
const base64 = await blobToDataURL(blob);
|
||||||
openBase64ImageInTab([{ base64, caption: layer.id() }]);
|
openBase64ImageInTab([{ base64, caption: layer.id() }]);
|
||||||
}
|
}
|
||||||
layerClone.destroy();
|
layer.remove();
|
||||||
blobs[layer.id()] = blob;
|
blobs[layer.id()] = blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user