feat: add inpaint region functionality and related tools

- Enhanced cursor behavior for new tools: semantic select, mask lasso, and mask rectangle.
- Updated mask edit state to include mask asset ID and kind.
- Implemented inpaint region commands for adding, applying, and removing inpaint regions.
- Introduced new operations for lasso and semantic selection tools.
- Created UI components for candidate review and inpaint region management.
- Added tests for inpaint region commands to ensure functionality.
- Updated various components to support new inpaint features and improve user experience.
This commit is contained in:
syntaxbullet
2026-07-11 16:41:22 +02:00
parent f4e13b80e7
commit ff762b8f17
78 changed files with 1632 additions and 301 deletions

View File

@@ -15,6 +15,8 @@ import {
} from "@input/index";
import { beginBrushSession, canPreviewBrush, cancelBrushSession, commitBrushSession, updateBrushSession, type BrushSession } from "@operations/paint/brush";
import { applyMagicWandAt } from "@operations/masks/magic-wand";
import { commitInpaintLasso } from "@operations/masks/lasso";
import { applySemanticSelectionAt } from "@operations/masks/semantic-select";
export type CanvasInputOptions = {
globalKeybindConsumer: GlobalKeybindConsumer;
@@ -122,6 +124,12 @@ export function useCanvasInput(
const state = store.getState();
const documentPoint = viewportPointToDocumentPoint(inputEvent.position, state.editor.viewport);
if (!isOperationWorkspacePanel(state.editor.workspace.panel) && (state.editor.tools.activeTool === "maskLasso" || state.editor.tools.activeTool === "maskRectangle") && state.editor.maskEdit?.kind === "inpaintRegion" && (inputEvent.buttons & 1) === 1) {
store.dispatch(commandIds.toolBeginMaskShape, { point: documentPoint, mode: inputEvent.shiftKey ? "add" : inputEvent.altKey ? "subtract" : "replace" });
canvas.setPointerCapture(event.pointerId);
event.preventDefault();
return;
}
const brush = !isOperationWorkspacePanel(state.editor.workspace.panel) && (inputEvent.buttons & 1) === 1 && !isPanInteractionMode(state.editor.tools.interactionMode)
? beginBrushSession(state.document, state.editor, documentPoint)
: undefined;
@@ -139,6 +147,11 @@ export function useCanvasInput(
event.preventDefault();
return;
}
if (!isOperationWorkspacePanel(state.editor.workspace.panel) && state.editor.tools.activeTool === "semanticSelect") {
void applySemanticSelectionAt(store, documentPoint, inputEvent.shiftKey ? "add" : inputEvent.altKey ? "subtract" : "replace");
event.preventDefault();
return;
}
const currentState = store.getState();
const selectionToolActive = currentState.editor.tools.activeTool === "select";
@@ -153,6 +166,12 @@ export function useCanvasInput(
const handlePointerMove = (event: PointerEvent) => {
const inputEvent = pointerInputEventFromPointerEvent(event);
if (store.getState().editor.maskShapeSession) {
const point = viewportPointToDocumentPoint(inputEvent.position, store.getState().editor.viewport);
store.dispatch(commandIds.toolAppendMaskShape, { point });
event.preventDefault();
return;
}
if (brushSession.current) {
if ((inputEvent.buttons & 1) !== 1 || isPanInteractionMode(store.getState().editor.tools.interactionMode)) {
commitActiveBrushSession(inputEvent.position);
@@ -163,7 +182,7 @@ export function useCanvasInput(
const point = viewportPointToDocumentPoint(inputEvent.position, store.getState().editor.viewport);
store.dispatch(commandIds.toolSetBrushPreview, { position: point });
const settings = store.getState().editor.tools.brush;
brushSession.current = updateBrushSession({ store, session: brushSession.current, point, color: settings.color, size: settings.size, hardness: settings.hardness });
brushSession.current = updateBrushSession({ store, session: brushSession.current, point, color: settings.color, size: settings.size, hardness: settings.hardness, opacity: settings.opacity, flow: settings.flow, smoothing: settings.smoothing, pressure: inputEvent.pressure ?? 1, pressureSize: settings.pressureSize });
event.preventDefault();
return;
}
@@ -187,6 +206,13 @@ export function useCanvasInput(
const handlePointerUp = (event: PointerEvent) => {
const inputEvent = pointerInputEventFromPointerEvent(event);
if (store.getState().editor.maskShapeSession) {
const point = viewportPointToDocumentPoint(inputEvent.position, store.getState().editor.viewport);
store.dispatch(commandIds.toolAppendMaskShape, { point });
void commitInpaintLasso(store);
event.preventDefault();
return;
}
if (brushSession.current) {
commitActiveBrushSession(inputEvent.position);
event.preventDefault();