Files
image-studio/input/selection.ts
syntaxbullet 5e4b548ad4 feat: add ComfyUI integration for image generation and management
- Implemented ComfyGenerateRequest type and associated functions for generating images using various architectures and modes.
- Added functions for listing generation options and handling image uploads.
- Created workflows for different generation modes including SDXL, Z-Image, Z-Image Turbo, and Anima.
- Introduced GenerationJobStatus component to display the status of ongoing generation jobs.
- Developed MaskControls for managing mask operations and displaying mask analysis.
- Created palette items for tool selection, layer management, and generation settings.
2026-07-10 23:15:02 +02:00

73 lines
3.0 KiB
TypeScript

import { commandIds } from "@commands/ids";
import type { Dispatch } from "@commands/dispatcher";
import type { InputDocument, InputLayer } from "./read-model";
import { resolveTransformTargetBounds, viewportPointToDocumentPoint, type InputViewportState } from "./document-geometry";
import type { PointerInputEvent } from "./pointer";
export function handleArtboardSelection(options: {
event: PointerInputEvent;
document: InputDocument;
viewport: InputViewportState;
dispatch: Dispatch;
}): boolean {
if (options.event.pointerType !== "mouse" || (options.event.buttons & 1) !== 1) return false;
const point = viewportPointToDocumentPoint(options.event.position, options.viewport);
const layerHit = findTopmostLayerAtPoint(options.document, point);
if (layerHit) {
options.dispatch(commandIds.selectionSet, { artboardId: layerHit.artboardId, layerIds: [layerHit.layerId] });
return true;
}
const artboard = [...options.document.artboards].reverse().find((candidate) => {
if (!candidate.visible || candidate.locked) return false;
const bounds = candidate.bounds;
return point.x >= bounds.x && point.x <= bounds.x + bounds.w && point.y >= bounds.y && point.y <= bounds.y + bounds.h;
});
if (!artboard) {
options.dispatch(commandIds.selectionClear, undefined);
return true;
}
options.dispatch(commandIds.selectionSet, { artboardId: artboard.id, layerIds: [] });
return true;
}
function findTopmostLayerAtPoint(document: InputDocument, point: { x: number; y: number }) {
const maskLayerIds = collectMaskLayerIds(document.artboards.flatMap((artboard) => artboard.layers));
for (const artboard of [...document.artboards].reverse()) {
if (!artboard.visible || artboard.locked) continue;
const layerId = findTopmostLayerInTreeAtPoint(document, [...artboard.layers].reverse(), point, maskLayerIds);
if (layerId) return { artboardId: artboard.id, layerId };
}
return undefined;
}
function findTopmostLayerInTreeAtPoint(document: InputDocument, layers: InputLayer[], point: { x: number; y: number }, maskLayerIds: ReadonlySet<string>): string | undefined {
for (const layer of layers) {
if (!layer.visible || layer.locked || maskLayerIds.has(layer.id)) continue;
if (layer.type === "group") {
const childId = findTopmostLayerInTreeAtPoint(document, [...layer.children].reverse(), point, maskLayerIds);
if (childId) return childId;
}
const bounds = resolveTransformTargetBounds(document, { type: "layer", id: layer.id });
if (bounds && point.x >= bounds.x && point.x <= bounds.x + bounds.w && point.y >= bounds.y && point.y <= bounds.y + bounds.h) {
return layer.id;
}
}
return undefined;
}
function collectMaskLayerIds(layers: readonly InputLayer[], ids = new Set<string>()): Set<string> {
for (const layer of layers) {
const layerMask = layer.layerMask ?? layer.clippingMask;
if (layerMask) ids.add(layerMask.maskLayerId);
if (layer.type === "group") collectMaskLayerIds(layer.children, ids);
}
return ids;
}