- Enhanced cursor behavior for new tools: semantic select, mask lasso, and mask rectangle. - Updated mask edit state to include mask asset ID and kind. - Implemented inpaint region commands for adding, applying, and removing inpaint regions. - Introduced new operations for lasso and semantic selection tools. - Created UI components for candidate review and inpaint region management. - Added tests for inpaint region commands to ensure functionality. - Updated various components to support new inpaint features and improve user experience.
469 lines
21 KiB
TypeScript
469 lines
21 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import type { Layer } from "@core/layer";
|
|
import { createInitialAppState } from "@editor/initial-state";
|
|
import {
|
|
documentAddArtboardCommand,
|
|
documentAddAssetCommand,
|
|
documentAddGroupLayerCommand,
|
|
documentAddImageLayerCommand,
|
|
documentAddLayerMaskCommand,
|
|
documentAddRasterLayerCommand,
|
|
documentApplyLayerMaskOperationCommand,
|
|
documentGroupLayersCommand,
|
|
documentDuplicateLayerCommand,
|
|
documentMoveLayerCommand,
|
|
documentRemoveArtboardCommand,
|
|
documentRemoveLayerCommand,
|
|
documentRemoveLayerMaskCommand,
|
|
documentRenameArtboardCommand,
|
|
documentRenameLayerCommand,
|
|
documentSetArtboardBoundsCommand,
|
|
documentResizeArtboardCommand,
|
|
documentSetArtboardLockedCommand,
|
|
documentSetArtboardVisibleCommand,
|
|
documentSetLayerClippingMaskCommand,
|
|
documentSetLayerLockedCommand,
|
|
documentSetLayerOpacityCommand,
|
|
documentSetLayerSourceRectCommand,
|
|
documentSetLayerVisibleCommand,
|
|
documentUpdateAssetSourceCommand,
|
|
documentUngroupLayerCommand,
|
|
} from "./document";
|
|
|
|
describe("document commands", () => {
|
|
test("adds transparent artboard", () => {
|
|
const next = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
expect(next.document.artboards).toEqual([
|
|
{
|
|
id: "a1",
|
|
name: "Artboard 1",
|
|
bounds: { x: 0, y: 0, w: 320, h: 240 },
|
|
backgroundColor: "transparent",
|
|
visible: true,
|
|
locked: false,
|
|
layers: [],
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("adds assets", () => {
|
|
const next = documentAddAssetCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ asset: { id: "asset-1", name: "Image", mimeType: "image/png", source: "blob:test", intrinsicSize: { w: 100, h: 50 } } },
|
|
);
|
|
|
|
expect(next.document.assets).toEqual([
|
|
{ id: "asset-1", name: "Image", mimeType: "image/png", source: "blob:test", intrinsicSize: { w: 100, h: 50 } },
|
|
]);
|
|
});
|
|
|
|
test("crops a raster layer non-destructively and clamps the crop to its asset", () => {
|
|
const state = documentWithRaster();
|
|
const next = documentSetLayerSourceRectCommand.execute({ state }, { layerId: "r1", sourceRect: { x: 20, y: 10, w: 200, h: 100 } });
|
|
expect(next.document.artboards[0]?.layers[0]).toMatchObject({ sourceRect: { x: 20, y: 10, w: 80, h: 40 } });
|
|
expect(next.document.assets[0]?.intrinsicSize).toEqual({ w: 100, h: 50 });
|
|
|
|
const reset = documentSetLayerSourceRectCommand.execute({ state: next }, { layerId: "r1" });
|
|
expect(reset.document.artboards[0]?.layers[0]).not.toHaveProperty("sourceRect");
|
|
});
|
|
|
|
test("resizes artboard bounds without scaling contents", () => {
|
|
const state = documentWithRaster();
|
|
const next = documentResizeArtboardCommand.execute({ state }, { id: "a1", bounds: { x: 5, y: 10, w: 640, h: 480 }, scaleContents: false });
|
|
expect(next.document.artboards[0]?.bounds).toEqual({ x: 5, y: 10, w: 640, h: 480 });
|
|
expect(next.document.artboards[0]?.layers[0]?.transform).toEqual(state.document.artboards[0]?.layers[0]?.transform);
|
|
});
|
|
|
|
test("resizes an artboard and scales nested contents including masks", () => {
|
|
const state = documentWithRaster();
|
|
const leaf = state.document.artboards[0]!.layers[0]!;
|
|
state.document.artboards[0]!.layers = [{ ...group("g", "Group"), children: [leaf] }];
|
|
const next = documentResizeArtboardCommand.execute({ state }, { id: "a1", bounds: { x: 10, y: 20, w: 640, h: 120 }, scaleContents: true });
|
|
const nested = next.document.artboards[0]?.layers[0];
|
|
expect(nested?.type === "group" ? nested.children[0]?.transform : undefined).toEqual({ position: { x: 30, y: 30 }, scale: { x: 2, y: 0.5 }, rotation: 0 });
|
|
});
|
|
|
|
test("updates asset sources", () => {
|
|
const state = documentAddAssetCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ asset: { id: "asset-1", name: "Image", mimeType: "image/png", source: "old", intrinsicSize: { w: 100, h: 50 } } },
|
|
);
|
|
|
|
const next = documentUpdateAssetSourceCommand.execute({ state }, { assetId: "asset-1", source: "new" });
|
|
|
|
expect(next.document.assets[0]?.source).toBe("new");
|
|
});
|
|
|
|
test("adds image layers to artboards", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
const next = documentAddImageLayerCommand.execute(
|
|
{ state },
|
|
{
|
|
artboardId: "a1",
|
|
layer: {
|
|
id: "l1",
|
|
type: "image",
|
|
name: "Image",
|
|
visible: true,
|
|
locked: false,
|
|
opacity: 1,
|
|
assetId: "asset-1",
|
|
transform: { position: { x: 10, y: 20 }, scale: { x: 1, y: 1 }, rotation: 0 },
|
|
},
|
|
},
|
|
);
|
|
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["l1"]);
|
|
});
|
|
|
|
test("adds raster layers to artboards", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
const next = documentAddRasterLayerCommand.execute(
|
|
{ state },
|
|
{
|
|
artboardId: "a1",
|
|
layer: {
|
|
id: "r1",
|
|
type: "raster",
|
|
name: "Raster",
|
|
visible: true,
|
|
locked: false,
|
|
opacity: 1,
|
|
assetId: "asset-1",
|
|
transform: { position: { x: 10, y: 20 }, scale: { x: 1, y: 1 }, rotation: 0 },
|
|
},
|
|
},
|
|
);
|
|
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["r1"]);
|
|
});
|
|
|
|
test("adds group layers", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
const next = documentAddGroupLayerCommand.execute({ state }, { artboardId: "a1", group: group("g1", "Group") });
|
|
|
|
expect(next.document.artboards[0]?.layers).toEqual([group("g1", "Group")]);
|
|
expect(next.editor.selection.layerIds).toEqual(["g1"]);
|
|
});
|
|
|
|
test("moves layers", () => {
|
|
const state = documentWithLayers([group("a", "A"), group("b", "B"), group("c", "C")]);
|
|
|
|
const next = documentMoveLayerCommand.execute({ state }, { layerId: "a", toArtboardId: "a1", toIndex: 2 });
|
|
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["b", "c", "a"]);
|
|
});
|
|
|
|
test("groups and ungroups top-level layers", () => {
|
|
const state = documentWithLayers([group("a", "A"), group("b", "B"), group("c", "C")]);
|
|
const grouped = documentGroupLayersCommand.execute({ state }, { artboardId: "a1", layerIds: ["a", "b"], group: group("g", "Group") });
|
|
|
|
expect(grouped.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["g", "c"]);
|
|
expect((grouped.document.artboards[0]?.layers[0] as { children: { id: string }[] }).children.map((layer) => layer.id)).toEqual(["a", "b"]);
|
|
expect(grouped.editor.selection.layerIds).toEqual(["g"]);
|
|
|
|
const ungrouped = documentUngroupLayerCommand.execute({ state: grouped }, { groupId: "g" });
|
|
|
|
expect(ungrouped.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["a", "b", "c"]);
|
|
expect(ungrouped.editor.selection.layerIds).toEqual(["a", "b"]);
|
|
});
|
|
|
|
test("groups nested sibling layers in place", () => {
|
|
const state = documentWithLayers([{ ...group("parent", "Parent"), children: [group("a", "A"), group("b", "B"), group("c", "C")] }]);
|
|
|
|
const grouped = documentGroupLayersCommand.execute({ state }, { artboardId: "a1", layerIds: ["a", "b"], group: group("g", "Group") });
|
|
const parent = grouped.document.artboards[0]?.layers[0];
|
|
const nestedGroup = parent?.type === "group" ? parent.children[0] : undefined;
|
|
|
|
expect(parent?.type).toBe("group");
|
|
expect(parent?.type === "group" ? parent.children.map((layer) => layer.id) : []).toEqual(["g", "c"]);
|
|
expect(nestedGroup?.type === "group" ? nestedGroup.children.map((layer) => layer.id) : []).toEqual(["a", "b"]);
|
|
expect(grouped.editor.selection).toEqual({ artboardId: "a1", layerIds: ["g"] });
|
|
});
|
|
|
|
test("does not group layers from different parents", () => {
|
|
const state = documentWithLayers([group("a", "A"), { ...group("parent", "Parent"), children: [group("b", "B")] }]);
|
|
|
|
const grouped = documentGroupLayersCommand.execute({ state }, { artboardId: "a1", layerIds: ["a", "b"], group: group("g", "Group") });
|
|
|
|
expect(grouped).toBe(state);
|
|
});
|
|
|
|
test("does not move groups into their own descendants", () => {
|
|
const state = documentWithLayers([{ ...group("parent", "Parent"), children: [group("child", "Child")] }]);
|
|
|
|
const moved = documentMoveLayerCommand.execute({ state }, { layerId: "parent", toArtboardId: "a1", toParentGroupId: "child", toIndex: 0 });
|
|
|
|
expect(moved).toBe(state);
|
|
});
|
|
|
|
test("removes layers", () => {
|
|
const state = documentWithLayers([group("a", "A"), group("b", "B")]);
|
|
const selectedState = { ...state, editor: { ...state.editor, selection: { artboardId: "a1", layerIds: ["a"] } } };
|
|
const next = documentRemoveLayerCommand.execute({ state: selectedState }, { layerId: "a" });
|
|
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["b"]);
|
|
expect(next.editor.selection.layerIds).toEqual([]);
|
|
});
|
|
|
|
test("removes artboards and clears artboard selection", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
const selectedState = { ...state, editor: { ...state.editor, selection: { artboardId: "a1", layerIds: [] } } };
|
|
|
|
const next = documentRemoveArtboardCommand.execute({ state: selectedState }, { id: "a1" });
|
|
|
|
expect(next.document.artboards).toEqual([]);
|
|
expect(next.editor.selection).toEqual({ layerIds: [] });
|
|
});
|
|
|
|
test("renames artboards and layers", () => {
|
|
const state = documentWithLayers([group("a", "A")]);
|
|
const renamedArtboard = documentRenameArtboardCommand.execute({ state }, { id: "a1", name: "New Artboard" });
|
|
const renamedLayer = documentRenameLayerCommand.execute({ state: renamedArtboard }, { layerId: "a", name: "New Layer" });
|
|
|
|
expect(renamedLayer.document.artboards[0]?.name).toBe("New Artboard");
|
|
expect(renamedLayer.document.artboards[0]?.layers[0]?.name).toBe("New Layer");
|
|
});
|
|
|
|
test("sets and clears layer clipping masks", () => {
|
|
const state = documentWithLayers([group("mask", "Mask"), group("target", "Target")]);
|
|
const masked = documentSetLayerClippingMaskCommand.execute({ state }, { layerId: "target", maskLayerId: "mask" });
|
|
|
|
expect(masked.document.artboards[0]?.layers[1]?.clippingMask).toEqual({ maskLayerId: "mask" });
|
|
|
|
const cleared = documentSetLayerClippingMaskCommand.execute({ state: masked }, { layerId: "target" });
|
|
|
|
expect(cleared.document.artboards[0]?.layers[1]?.clippingMask).toBeUndefined();
|
|
});
|
|
|
|
test("moves masked layers directly after their mask", () => {
|
|
const state = documentWithLayers([group("target", "Target"), group("other", "Other"), group("mask", "Mask")]);
|
|
|
|
const masked = documentSetLayerClippingMaskCommand.execute({ state }, { layerId: "target", maskLayerId: "mask" });
|
|
|
|
expect(masked.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["other", "mask", "target"]);
|
|
expect(masked.document.artboards[0]?.layers[2]?.clippingMask).toEqual({ maskLayerId: "mask" });
|
|
});
|
|
|
|
test("adds editable raster masks directly before the target layer", () => {
|
|
const state = documentWithLayers([raster("target", "Target")]);
|
|
|
|
const masked = documentAddLayerMaskCommand.execute(
|
|
{ state },
|
|
{
|
|
layerId: "target",
|
|
asset: maskAsset(),
|
|
maskLayer: raster("mask", "Target Mask", "mask-asset"),
|
|
},
|
|
);
|
|
|
|
expect(masked.document.assets).toContainEqual(maskAsset());
|
|
expect(masked.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["mask", "target"]);
|
|
expect(masked.document.artboards[0]?.layers[1]?.layerMask).toEqual({
|
|
kind: "raster",
|
|
maskLayerId: "mask",
|
|
enabled: true,
|
|
inverted: false,
|
|
});
|
|
expect(masked.document.artboards[0]?.layers[1]?.clippingMask).toEqual({ maskLayerId: "mask" });
|
|
expect(masked.editor.maskEdit).toEqual({ kind: "layerMask", targetLayerId: "target", maskLayerId: "mask", maskAssetId: "mask-asset" });
|
|
expect(masked.editor.tools.activeTool).toBe("brush");
|
|
});
|
|
|
|
test("removes attached layer masks and exits mask editing", () => {
|
|
const state = documentAddLayerMaskCommand.execute(
|
|
{ state: documentWithLayers([raster("target", "Target")]) },
|
|
{ layerId: "target", asset: maskAsset(), maskLayer: raster("mask", "Target Mask", "mask-asset") },
|
|
);
|
|
|
|
const unmasked = documentRemoveLayerMaskCommand.execute({ state }, { layerId: "target" });
|
|
|
|
expect(unmasked.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["target"]);
|
|
expect(unmasked.document.artboards[0]?.layers[0]?.layerMask).toBeUndefined();
|
|
expect(unmasked.document.artboards[0]?.layers[0]?.clippingMask).toBeUndefined();
|
|
expect(unmasked.editor.maskEdit).toBeUndefined();
|
|
});
|
|
|
|
test("applies operations only to attached layer mask assets", () => {
|
|
const state = documentAddLayerMaskCommand.execute(
|
|
{ state: documentWithLayers([raster("target", "Target"), raster("loose", "Loose", "loose-asset")]) },
|
|
{ layerId: "target", asset: maskAsset(), maskLayer: raster("mask", "Target Mask", "mask-asset") },
|
|
);
|
|
const withLooseAsset = documentAddAssetCommand.execute(
|
|
{ state },
|
|
{ asset: { id: "loose-asset", name: "Loose", mimeType: "image/png", source: "loose-source", intrinsicSize: { w: 100, h: 100 } } },
|
|
);
|
|
|
|
const updated = documentApplyLayerMaskOperationCommand.execute(
|
|
{ state: withLooseAsset },
|
|
{ maskLayerId: "mask", source: "updated-mask", mimeType: "image/png", operation: { type: "invert" } },
|
|
);
|
|
const ignored = documentApplyLayerMaskOperationCommand.execute(
|
|
{ state: updated },
|
|
{ maskLayerId: "loose", source: "wrong", operation: { type: "invert" } },
|
|
);
|
|
|
|
expect(updated.document.assets.find((asset) => asset.id === "mask-asset")?.source).toBe("updated-mask");
|
|
expect(updated.document.assets.find((asset) => asset.id === "mask-asset")?.mimeType).toBe("image/png");
|
|
expect(ignored.document.assets.find((asset) => asset.id === "loose-asset")?.source).toBe("loose-source");
|
|
});
|
|
|
|
test("cleans mask references when deleting targets or mask layers", () => {
|
|
const state = documentAddLayerMaskCommand.execute(
|
|
{ state: documentWithLayers([raster("target", "Target")]) },
|
|
{ layerId: "target", asset: maskAsset(), maskLayer: raster("mask", "Target Mask", "mask-asset") },
|
|
);
|
|
|
|
const removedTarget = documentRemoveLayerCommand.execute({ state }, { layerId: "target" });
|
|
const removedMask = documentRemoveLayerCommand.execute({ state }, { layerId: "mask" });
|
|
|
|
expect(removedTarget.document.artboards[0]?.layers).toEqual([]);
|
|
expect(removedMask.document.artboards[0]?.layers[0]?.id).toBe("target");
|
|
expect(removedMask.document.artboards[0]?.layers[0]?.clippingMask).toBeUndefined();
|
|
expect(removedMask.editor.maskEdit).toBeUndefined();
|
|
});
|
|
|
|
test("sets artboard visibility and lock state", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
const hidden = documentSetArtboardVisibleCommand.execute({ state }, { id: "a1", visible: false });
|
|
const locked = documentSetArtboardLockedCommand.execute({ state: hidden }, { id: "a1", locked: true });
|
|
|
|
expect(locked.document.artboards[0]?.visible).toBe(false);
|
|
expect(locked.document.artboards[0]?.locked).toBe(true);
|
|
});
|
|
|
|
test("sets layer visibility and lock state", () => {
|
|
const state = documentWithLayers([group("a", "A")]);
|
|
|
|
const hidden = documentSetLayerVisibleCommand.execute({ state }, { layerId: "a", visible: false });
|
|
const locked = documentSetLayerLockedCommand.execute({ state: hidden }, { layerId: "a", locked: true });
|
|
|
|
expect(locked.document.artboards[0]?.layers[0]?.visible).toBe(false);
|
|
expect(locked.document.artboards[0]?.layers[0]?.locked).toBe(true);
|
|
});
|
|
|
|
test("sets normalized opacity and refuses locked layer edits", () => {
|
|
const state = documentWithLayers([raster("a", "A")]);
|
|
const translucent = documentSetLayerOpacityCommand.execute({ state }, { layerId: "a", opacity: 0.35 });
|
|
const clamped = documentSetLayerOpacityCommand.execute({ state: translucent }, { layerId: "a", opacity: 2 });
|
|
const lockedState = documentSetLayerLockedCommand.execute({ state: clamped }, { layerId: "a", locked: true });
|
|
const ignored = documentSetLayerOpacityCommand.execute({ state: lockedState }, { layerId: "a", opacity: 0 });
|
|
|
|
expect(translucent.document.artboards[0]?.layers[0]?.opacity).toBe(0.35);
|
|
expect(clamped.document.artboards[0]?.layers[0]?.opacity).toBe(1);
|
|
expect(ignored).toBe(lockedState);
|
|
});
|
|
|
|
test("duplicates a nested group with remapped child mask references", () => {
|
|
const maskedTarget = { ...raster("target", "Target"), layerMask: { kind: "raster" as const, maskLayerId: "mask", enabled: true, inverted: false } };
|
|
const parent = { ...group("parent", "Parent"), children: [raster("mask", "Mask"), maskedTarget] };
|
|
const state = documentWithLayers([parent]);
|
|
const next = documentDuplicateLayerCommand.execute(
|
|
{ state },
|
|
{ layerId: "parent", idByLayerId: { parent: "parent-copy", mask: "mask-copy", target: "target-copy" } },
|
|
);
|
|
|
|
const duplicate = next.document.artboards[0]?.layers[1];
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["parent", "parent-copy"]);
|
|
expect(duplicate?.type === "group" ? duplicate.children.map((layer) => layer.id) : []).toEqual(["mask-copy", "target-copy"]);
|
|
expect(duplicate?.type === "group" ? duplicate.children[1]?.layerMask?.maskLayerId : undefined).toBe("mask-copy");
|
|
expect(next.editor.selection.layerIds).toEqual(["parent-copy"]);
|
|
});
|
|
|
|
test("duplicates an attached top-level mask beside its target", () => {
|
|
const target = { ...raster("target", "Target"), layerMask: { kind: "raster" as const, maskLayerId: "mask", enabled: true, inverted: false } };
|
|
const state = documentWithLayers([raster("mask", "Mask"), target]);
|
|
const next = documentDuplicateLayerCommand.execute(
|
|
{ state },
|
|
{ layerId: "target", idByLayerId: { mask: "mask-copy", target: "target-copy" } },
|
|
);
|
|
|
|
expect(next.document.artboards[0]?.layers.map((layer) => layer.id)).toEqual(["mask", "target", "mask-copy", "target-copy"]);
|
|
expect(next.document.artboards[0]?.layers[3]?.layerMask?.maskLayerId).toBe("mask-copy");
|
|
});
|
|
|
|
test("sets artboard bounds", () => {
|
|
const state = documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 } },
|
|
);
|
|
|
|
const next = documentSetArtboardBoundsCommand.execute({ state }, { id: "a1", bounds: { x: 10, y: 20, w: 640, h: 480 } });
|
|
|
|
expect(next.document.artboards[0]?.bounds).toEqual({ x: 10, y: 20, w: 640, h: 480 });
|
|
});
|
|
});
|
|
|
|
function documentWithLayers(layers: Layer[]) {
|
|
return {
|
|
...createInitialAppState("Test"),
|
|
document: {
|
|
...createInitialAppState("Test").document,
|
|
artboards: [{ id: "a1", name: "Artboard 1", bounds: { x: 0, y: 0, w: 320, h: 240 }, backgroundColor: "transparent", visible: true, locked: false, layers }],
|
|
},
|
|
editor: {
|
|
...createInitialAppState("Test").editor,
|
|
selection: { artboardId: "a1", layerIds: [] },
|
|
},
|
|
};
|
|
}
|
|
|
|
function documentWithRaster() {
|
|
const state = documentWithLayers([{ ...raster("r1", "Raster", "asset-1"), transform: { position: { x: 10, y: 20 }, scale: { x: 1, y: 1 }, rotation: 0 } }]);
|
|
state.document.assets = [{ id: "asset-1", name: "Raster", mimeType: "image/png", source: "asset://raster", intrinsicSize: { w: 100, h: 50 } }];
|
|
return state;
|
|
}
|
|
|
|
function group(id: string, name: string) {
|
|
return {
|
|
id,
|
|
type: "group" as const,
|
|
name,
|
|
visible: true,
|
|
locked: false,
|
|
opacity: 1,
|
|
transform: { position: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, rotation: 0 },
|
|
children: [],
|
|
};
|
|
}
|
|
|
|
function raster(id: string, name: string, assetId = `${id}-asset`) {
|
|
return {
|
|
id,
|
|
type: "raster" as const,
|
|
name,
|
|
visible: true,
|
|
locked: false,
|
|
opacity: 1,
|
|
assetId,
|
|
transform: { position: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, rotation: 0 },
|
|
};
|
|
}
|
|
|
|
function maskAsset() {
|
|
return { id: "mask-asset", name: "Target Mask", mimeType: "image/svg+xml", source: "mask", intrinsicSize: { w: 100, h: 100 } };
|
|
}
|