- 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.
94 lines
3.8 KiB
TypeScript
94 lines
3.8 KiB
TypeScript
import { useMemo, useRef } from "react";
|
|
import type { ImageDocument } from "@core/document";
|
|
import type { AppState, MaskEditState } from "@editor/state";
|
|
import type { AppStore } from "@editor/store";
|
|
import type { InteractionMode } from "@editor/tools";
|
|
import type { GlobalKeybindConsumer, GlobalPointerConsumer, GlobalWheelConsumer } from "@input/index";
|
|
import { brushUnavailableHint, canPreviewBrush, type BrushTargetEditorState } from "@operations/paint/brush";
|
|
import { canvasCursorClass } from "./canvas/cursor";
|
|
import { useCanvasInput } from "./canvas/useCanvasInput";
|
|
import { useCanvasRenderer } from "./canvas/useCanvasRenderer";
|
|
import { useCanvasResize } from "./canvas/useCanvasResize";
|
|
import { useAppState } from "./useAppState";
|
|
|
|
const ignoreGlobalKeybind: GlobalKeybindConsumer = () => false;
|
|
const ignoreGlobalPointer: GlobalPointerConsumer = () => false;
|
|
const ignoreGlobalWheel: GlobalWheelConsumer = () => false;
|
|
|
|
export type CanvasViewportProps = {
|
|
store: AppStore;
|
|
globalKeybindConsumer?: GlobalKeybindConsumer;
|
|
globalPointerConsumer?: GlobalPointerConsumer;
|
|
globalWheelConsumer?: GlobalWheelConsumer;
|
|
};
|
|
|
|
export function CanvasViewport({
|
|
store,
|
|
globalKeybindConsumer = ignoreGlobalKeybind,
|
|
globalPointerConsumer = ignoreGlobalPointer,
|
|
globalWheelConsumer = ignoreGlobalWheel,
|
|
}: CanvasViewportProps) {
|
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
const cursorState = useAppState(store, selectCanvasCursorState, canvasCursorStatesEqual);
|
|
const inputOptions = useMemo(
|
|
() => ({ globalKeybindConsumer, globalPointerConsumer, globalWheelConsumer }),
|
|
[globalKeybindConsumer, globalPointerConsumer, globalWheelConsumer],
|
|
);
|
|
|
|
useCanvasRenderer(canvasRef, store);
|
|
useCanvasResize(canvasRef, store.dispatch);
|
|
useCanvasInput(canvasRef, store, inputOptions);
|
|
const brushHint = brushUnavailableHint(cursorState.document, cursorState.editor);
|
|
const hasBrushPreview = Boolean(cursorState.hasBrushPreview && !brushHint && canPreviewBrush(cursorState.document, cursorState.editor));
|
|
const cursorClass = canvasCursorClass(cursorState.editor.tools.interactionMode, cursorState.isPanning, hasBrushPreview, !brushHint);
|
|
|
|
return <canvas ref={canvasRef} className={`h-full w-full ${cursorClass}`} />;
|
|
}
|
|
|
|
type CanvasCursorState = {
|
|
document: ImageDocument;
|
|
editor: BrushTargetEditorState;
|
|
hasBrushPreview: boolean;
|
|
isPanning: boolean;
|
|
};
|
|
|
|
function selectCanvasCursorState(state: AppState): CanvasCursorState {
|
|
return {
|
|
document: state.document,
|
|
editor: {
|
|
selection: state.editor.selection,
|
|
tools: {
|
|
activeTool: state.editor.tools.activeTool,
|
|
interactionMode: state.editor.tools.interactionMode,
|
|
},
|
|
maskEdit: state.editor.maskEdit,
|
|
},
|
|
hasBrushPreview: Boolean(state.editor.brushPreview),
|
|
isPanning: state.editor.pointerSession?.type === "pan",
|
|
};
|
|
}
|
|
|
|
function canvasCursorStatesEqual(a: CanvasCursorState, b: CanvasCursorState): boolean {
|
|
return (
|
|
a.document === b.document &&
|
|
a.hasBrushPreview === b.hasBrushPreview &&
|
|
a.isPanning === b.isPanning &&
|
|
a.editor.selection === b.editor.selection &&
|
|
a.editor.tools.activeTool === b.editor.tools.activeTool &&
|
|
interactionModesEqual(a.editor.tools.interactionMode, b.editor.tools.interactionMode) &&
|
|
maskEditStatesEqual(a.editor.maskEdit, b.editor.maskEdit)
|
|
);
|
|
}
|
|
|
|
function interactionModesEqual(a: InteractionMode, b: InteractionMode): boolean {
|
|
if (a.type !== b.type) return false;
|
|
if (a.type === "temporary-pan") return b.type === "temporary-pan" && a.previousTool === b.previousTool;
|
|
return b.type === "tool" && a.tool === b.tool;
|
|
}
|
|
|
|
function maskEditStatesEqual(a: MaskEditState | undefined, b: MaskEditState | undefined): boolean {
|
|
if (a === b) return true;
|
|
if (!a || !b) return false;
|
|
return a.targetLayerId === b.targetLayerId && a.maskLayerId === b.maskLayerId && a.viewMode === b.viewMode;
|
|
}
|