import { commandIds } from "@commands/ids"; import type { Vec2D } from "@core/geometry"; import type { AppStore } from "@editor/store"; import { mergeMaskSources } from "@platform/browser/maskRaster"; import { requestSemanticSelection } from "@platform/comfy/generationClient"; import { runGenerationJob } from "@operations/generation/generationJob"; import { documentPointToAssetPoint, resolveMaskSelectionTarget } from "./magic-wand"; export async function applySemanticSelectionAt(store: AppStore, point: Vec2D, mode: "replace" | "add" | "subtract") { const state = store.getState(); if (state.editor.tools.activeTool !== "semanticSelect") return false; const target = resolveMaskSelectionTarget(state.document, state.editor); if (!target?.inpaintRegion || !target.maskAsset) return true; const region = target.inpaintRegion; const maskAsset = target.maskAsset; const assetPoint = documentPointToAssetPoint(point, target.layer, target.asset.intrinsicSize); if (assetPoint.x < 0 || assetPoint.y < 0 || assetPoint.x >= target.asset.intrinsicSize.w || assetPoint.y >= target.asset.intrinsicSize.h) return true; const controller = new AbortController(); await runGenerationJob({ kind: "mask", label: "Selecting object", dispatch: store.dispatch, signal: controller.signal, task: async (signal, report) => { report(0.1, "Sending point to SAM3"); const result = await requestSemanticSelection({ inputImage: target.asset.source, x: assetPoint.x, y: assetPoint.y }, signal); report(0.85, "Merging object mask"); const source = await mergeMaskSources(maskAsset.source, result.source, Math.round(target.asset.intrinsicSize.w), Math.round(target.asset.intrinsicSize.h), mode); store.dispatch(commandIds.documentApplyInpaintRegionMaskOperation, { regionId: region.id, source, mimeType: "image/png", operation: { type: "magicWand" } }); report(1, "Object selected"); }, }); return true; }