feat(erase-object): add freeform lasso selection mode (#503)

Adds a Brush | Lasso toggle to the object eraser. Lasso lets the user drag a freeform loop that auto-closes and fills into the mask, so they select around a subject instead of painting every pixel. Frontend-only; the mask contract is unchanged. Also un-skips the erase-object e2e suite via a shared mockAiFeaturesInstalled helper (7 tests now run; 2 multi-file tests fixme'd for a pre-existing tool-page remount bug). Closes #492.
This commit is contained in:
SnapOtter
2026-07-11 22:27:51 +08:00
committed by GitHub
parent 380419dd06
commit 601557edae
27 changed files with 408 additions and 124 deletions
+4
View File
@@ -361,6 +361,7 @@ export function ToolPage() {
const eraserRef = useRef<EraserCanvasRef | null>(null);
const [eraserHasStrokes, setEraserHasStrokes] = useState(false);
const [eraserBrushSize, setEraserBrushSize] = useState(30);
const [eraserMode, setEraserMode] = useState<"brush" | "lasso">("brush");
const [eraserMaskedCount, setEraserMaskedCount] = useState(0);
// Center of the painted mask as a 0-100 percentage, used to init the slider at the erased spot
const [eraserSliderInitPos, setEraserSliderInitPos] = useState<number | null>(null);
@@ -660,6 +661,8 @@ export function ToolPage() {
hasStrokes: eraserHasStrokes,
brushSize: eraserBrushSize,
onBrushSizeChange: setEraserBrushSize,
mode: eraserMode,
onModeChange: setEraserMode,
onMaskCenter: setEraserSliderInitPos,
maskedFileCount: eraserMaskedCount,
}
@@ -851,6 +854,7 @@ export function ToolPage() {
ref={eraserRef}
imageSrc={originalBlobUrl}
brushSize={eraserBrushSize}
mode={eraserMode}
onStrokeChange={setEraserHasStrokes}
onMaskedCountChange={setEraserMaskedCount}
/>