mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): group lines together in undo history
This commit is contained in:
parent
d9dd00ea20
commit
0d8edd67ab
@ -150,6 +150,7 @@ const serialize: SerializeFunction = (data, key) => {
|
|||||||
if (!persistConfig) {
|
if (!persistConfig) {
|
||||||
throw new Error(`No persist config for slice "${key}"`);
|
throw new Error(`No persist config for slice "${key}"`);
|
||||||
}
|
}
|
||||||
|
// Heuristic to determine if the slice is undoable - could just hardcode it in the persistConfig
|
||||||
const isUndoable = 'present' in data && 'past' in data && 'future' in data && '_latestUnfiltered' in data;
|
const isUndoable = 'present' in data && 'past' in data && 'future' in data && '_latestUnfiltered' in data;
|
||||||
const result = omit(isUndoable ? data.present : data, persistConfig.persistDenylist);
|
const result = omit(isUndoable ? data.present : data, persistConfig.persistDenylist);
|
||||||
return JSON.stringify(result);
|
return JSON.stringify(result);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction, UnknownAction } from '@reduxjs/toolkit';
|
||||||
import { createAction, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
import { createAction, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils';
|
import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils';
|
||||||
@ -6,7 +6,7 @@ import type { ParameterAutoNegative } from 'features/parameters/types/parameterS
|
|||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
import type { RgbColor } from 'react-colorful';
|
import type { RgbColor } from 'react-colorful';
|
||||||
import type { StateWithHistory, UndoableOptions } from 'redux-undo';
|
import type { UndoableOptions } from 'redux-undo';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
@ -68,7 +68,6 @@ type RegionalPromptsState = {
|
|||||||
brushSize: number;
|
brushSize: number;
|
||||||
promptLayerOpacity: number;
|
promptLayerOpacity: number;
|
||||||
autoNegative: ParameterAutoNegative;
|
autoNegative: ParameterAutoNegative;
|
||||||
lastActionType: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialRegionalPromptsState: RegionalPromptsState = {
|
export const initialRegionalPromptsState: RegionalPromptsState = {
|
||||||
@ -79,7 +78,6 @@ export const initialRegionalPromptsState: RegionalPromptsState = {
|
|||||||
layers: [],
|
layers: [],
|
||||||
promptLayerOpacity: 0.5, // This currently doesn't work
|
promptLayerOpacity: 0.5, // This currently doesn't work
|
||||||
autoNegative: 'off',
|
autoNegative: 'off',
|
||||||
lastActionType: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLine = (obj: LayerObject): obj is LineObject => obj.kind === 'line';
|
const isLine = (obj: LayerObject): obj is LineObject => obj.kind === 'line';
|
||||||
@ -327,7 +325,7 @@ export const regionalPromptsPersistConfig: PersistConfig<RegionalPromptsState> =
|
|||||||
name: regionalPromptsSlice.name,
|
name: regionalPromptsSlice.name,
|
||||||
initialState: initialRegionalPromptsState,
|
initialState: initialRegionalPromptsState,
|
||||||
migrate: migrateRegionalPromptsState,
|
migrate: migrateRegionalPromptsState,
|
||||||
persistDenylist: ['tool', 'lastActionType'],
|
persistDenylist: ['tool'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Payload-less actions for `redux-undo`
|
// Payload-less actions for `redux-undo`
|
||||||
@ -335,9 +333,8 @@ export const undoRegionalPrompts = createAction(`${regionalPromptsSlice.name}/un
|
|||||||
export const redoRegionalPrompts = createAction(`${regionalPromptsSlice.name}/redo`);
|
export const redoRegionalPrompts = createAction(`${regionalPromptsSlice.name}/redo`);
|
||||||
export const clearHistoryRegionalPrompts = createAction(`${regionalPromptsSlice.name}/clearHistory`);
|
export const clearHistoryRegionalPrompts = createAction(`${regionalPromptsSlice.name}/clearHistory`);
|
||||||
|
|
||||||
// These actions are grouped together as single undoable actions
|
// These actions are _individually_ grouped together as single undoable actions
|
||||||
const undoableGroupByMatcher = isAnyOf(
|
const undoableGroupByMatcher = isAnyOf(
|
||||||
pointsAdded,
|
|
||||||
positivePromptChanged,
|
positivePromptChanged,
|
||||||
negativePromptChanged,
|
negativePromptChanged,
|
||||||
brushSizeChanged,
|
brushSizeChanged,
|
||||||
@ -345,30 +342,43 @@ const undoableGroupByMatcher = isAnyOf(
|
|||||||
promptLayerOpacityChanged
|
promptLayerOpacityChanged
|
||||||
);
|
);
|
||||||
|
|
||||||
export const regionalPromptsUndoableConfig: UndoableOptions = {
|
const LINE_1 = 'LINE_1';
|
||||||
|
const LINE_2 = 'LINE_2';
|
||||||
|
|
||||||
|
export const regionalPromptsUndoableConfig: UndoableOptions<RegionalPromptsState, UnknownAction> = {
|
||||||
limit: 64,
|
limit: 64,
|
||||||
undoType: undoRegionalPrompts.type,
|
undoType: undoRegionalPrompts.type,
|
||||||
redoType: redoRegionalPrompts.type,
|
redoType: redoRegionalPrompts.type,
|
||||||
clearHistoryType: clearHistoryRegionalPrompts.type,
|
clearHistoryType: clearHistoryRegionalPrompts.type,
|
||||||
groupBy: (action, _currentState: RegionalPromptsState, _previousHistory: StateWithHistory<RegionalPromptsState>) => {
|
groupBy: (action, state, history) => {
|
||||||
|
// Lines are started with `lineAdded` and may have any number of subsequent `pointsAdded` events. We can use a
|
||||||
|
// double-buffering-ish trick to group each logical line as a single undoable action, without grouping separate
|
||||||
|
// logical lines as a single undo action.
|
||||||
|
if (lineAdded.match(action)) {
|
||||||
|
return history.group === LINE_1 ? LINE_2 : LINE_1;
|
||||||
|
}
|
||||||
|
if (pointsAdded.match(action)) {
|
||||||
|
if (history.group === LINE_1 || history.group === LINE_2) {
|
||||||
|
return history.group;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (undoableGroupByMatcher(action)) {
|
if (undoableGroupByMatcher(action)) {
|
||||||
return action.type;
|
return action.type;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
filter: (action, _currentState: RegionalPromptsState, _previousHistory: StateWithHistory<RegionalPromptsState>) => {
|
filter: (action, _state, _history) => {
|
||||||
// Return `true` if we should record state in history, `false` if not
|
// Ignore all actions from other slices
|
||||||
if (layerBboxChanged.match(action)) {
|
|
||||||
// This action is triggered on state changes, including when we undo. If we do not ignore this action, when we
|
|
||||||
// undo, this action triggers and empties the future states array. Therefore, we must ignore this action.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (toolChanged.match(action)) {
|
|
||||||
// We don't want to record tool changes in the undo history
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!action.type.startsWith('regionalPrompts/')) {
|
if (!action.type.startsWith('regionalPrompts/')) {
|
||||||
// Ignore all actions from other slices
|
return false;
|
||||||
|
}
|
||||||
|
// This action is triggered on state changes, including when we undo. If we do not ignore this action, when we
|
||||||
|
// undo, this action triggers and empties the future states array. Therefore, we must ignore this action.
|
||||||
|
if (layerBboxChanged.match(action)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We don't want to record tool changes in the undo history
|
||||||
|
if (toolChanged.match(action)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user