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 { 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 />
|
||||
|
@ -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} />
|
||||
</>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user