feat: implement chroma key operation and related keybinds, update workspace panel management

This commit is contained in:
syntaxbullet
2026-07-11 00:09:41 +02:00
parent 51a54dbdb2
commit 556fd685a2
21 changed files with 134 additions and 50 deletions

View File

@@ -13,6 +13,14 @@ describe("tool commands", () => {
expect(next.editor.tools).toEqual({ activeTool: "brush", interactionMode: { type: "tool", tool: "brush" }, brush: defaultBrush, generate: defaultGenerate, chromaKey: defaultChromaKey, magicWand: defaultMagicWand });
});
test("selecting a persistent tool closes an open operation", () => {
const state = createInitialAppState("Test");
state.editor.workspace = { panel: "chromaKey" };
const next = toolSetActiveCommand.execute({ state }, { tool: "brush" });
expect(next.editor.tools.activeTool).toBe("brush");
expect(next.editor.workspace.panel).toBe("none");
});
test("sets brush settings", () => {
const next = toolSetBrushSettingsCommand.execute({ state: createInitialAppState("Test") }, { color: "#ff0000", size: 24, hardness: 50 });

View File

@@ -44,8 +44,6 @@ export const toolSetActiveCommand: Command<ToolSetActivePayload> = {
id: commandIds.toolSetActive,
name: "Set active tool",
execute({ state }, payload) {
const previousNonGenerateTool = payload.tool === "generate" ? state.editor.workspace.previousNonGenerateTool : payload.tool;
const panel = payload.tool === "generate" ? "generate" : state.editor.workspace.panel === "generate" ? "none" : state.editor.workspace.panel;
return {
...state,
editor: {
@@ -57,7 +55,9 @@ export const toolSetActiveCommand: Command<ToolSetActivePayload> = {
},
brushPreview: undefined,
brushStrokePreview: undefined,
workspace: { panel, previousNonGenerateTool },
workspace: state.editor.workspace.panel === "generate" || state.editor.workspace.panel === "chromaKey"
? { panel: "none" }
: state.editor.workspace,
},
};
},

View File

@@ -0,0 +1,13 @@
import { describe, expect, test } from "bun:test";
import { createInitialAppState } from "@editor/initial-state";
import { workspaceSetPanelCommand } from "./workspace";
describe("workspace commands", () => {
test.each(["generate", "chromaKey"] as const)("opens the %s operation without changing the persistent tool", (panel) => {
const state = createInitialAppState("Test");
const next = workspaceSetPanelCommand.execute({ state }, { panel });
expect(next.editor.workspace.panel).toBe(panel);
expect(next.editor.tools.activeTool).toBe("select");
expect(next.editor.tools.interactionMode).toEqual({ type: "tool", tool: "select" });
});
});

View File

@@ -10,23 +10,17 @@ export const workspaceSetPanelCommand: Command<WorkspaceSetPanelPayload> = {
history: { mode: "ignore" },
execute({ state }, payload) {
if (!workspacePanels.has(payload.panel)) return state;
const currentTool = state.editor.tools.activeTool;
const previousNonGenerateTool = currentTool === "generate" ? state.editor.workspace.previousNonGenerateTool : currentTool;
const nextTool = payload.panel === "generate" ? "generate" : currentTool === "generate" ? previousNonGenerateTool : currentTool;
if (state.editor.workspace.panel === payload.panel && currentTool === nextTool) return state;
if (state.editor.workspace.panel === payload.panel) return state;
return {
...state,
editor: {
...state.editor,
workspace: { panel: payload.panel, previousNonGenerateTool },
tools: nextTool === currentTool ? state.editor.tools : { ...state.editor.tools, activeTool: nextTool, interactionMode: { type: "tool", tool: nextTool } },
brushPreview: nextTool === currentTool ? state.editor.brushPreview : undefined,
brushStrokePreview: nextTool === currentTool ? state.editor.brushStrokePreview : undefined,
workspace: { panel: payload.panel },
},
};
},
};
const workspacePanels = new Set<WorkspacePanel>(["none", "generate", "layers"]);
const workspacePanels = new Set<WorkspacePanel>(["none", "generate", "chromaKey", "layers"]);
export const workspaceCommands = [workspaceSetPanelCommand] satisfies Command<unknown>[];