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.
This commit is contained in:
syntaxbullet
2026-07-10 23:15:02 +02:00
parent 41bbd1e16b
commit 5e4b548ad4
80 changed files with 2114 additions and 1954 deletions

View File

@@ -7,6 +7,9 @@ import {
generationRemoveCandidateCommand,
generationReplaceCandidatePixelsCommand,
generationSetCompareModeCommand,
generationFailJobCommand,
generationStartJobCommand,
generationSucceedJobCommand,
} from "./generation";
describe("generation commands", () => {
@@ -64,6 +67,8 @@ describe("generation commands", () => {
candidates: [generationCandidate("candidate-2")],
selectedCandidateId: "candidate-2",
compareMode: "split",
jobs: [],
resources: { status: "idle" },
});
});
@@ -99,8 +104,27 @@ describe("generation commands", () => {
candidates: [generationCandidate("candidate-2", true)],
selectedCandidateId: "candidate-2",
compareMode: "before",
jobs: [],
resources: { status: "idle" },
});
});
test("tracks one durable generation job through completion", () => {
const state = createInitialAppState("Test");
const running = generationStartJobCommand.execute({ state }, { jobId: "job-1", kind: "generate", label: "Generating", startedAt: 100 });
const duplicate = generationStartJobCommand.execute({ state: running }, { jobId: "job-2", kind: "regenerate", label: "Regenerate", startedAt: 101 });
const completed = generationSucceedJobCommand.execute({ state: duplicate }, { jobId: "job-1", finishedAt: 150 });
expect(duplicate).toBe(running);
expect(completed.editor.generation.jobs[0]).toEqual({ id: "job-1", kind: "generate", label: "Generating", status: "succeeded", startedAt: 100, finishedAt: 150, error: undefined });
});
test("preserves generation errors in authoritative state", () => {
const running = generationStartJobCommand.execute({ state: createInitialAppState("Test") }, { jobId: "job-1", kind: "replace", label: "Replacing pixels", startedAt: 100 });
const failed = generationFailJobCommand.execute({ state: running }, { jobId: "job-1", finishedAt: 125, error: "Backend unavailable" });
expect(failed.editor.generation.jobs[0]).toMatchObject({ id: "job-1", status: "failed", error: "Backend unavailable", finishedAt: 125 });
});
});
function documentWithSourceLayer() {