feat: layer effects, bucket fill, simple brush

This commit is contained in:
2026-05-21 00:02:12 +07:00
parent bcf4650c70
commit e7dd9420e7
47 changed files with 1675 additions and 1038 deletions
+23 -15
View File
@@ -72,7 +72,7 @@ function makeMalformedProject(id: string): Project {
layers: [
{
id: "layer-1",
type: "image",
type: "raster",
x: 10,
y: 10,
width: 10.4,
@@ -80,24 +80,25 @@ function makeMalformedProject(id: string): Project {
scale: 0,
rotation: 720.4,
opacity: 0.75,
effects: [],
},
],
};
}
function makeLegacyFaceBlurProject(id: string): Project {
function makeFaceBlurProject(id: string): Project {
const now = new Date().toISOString();
return {
id,
title: "legacy",
title: "faceblur",
createdAt: now,
updatedAt: now,
aspectRatio: "4:5",
canvas: { width: 1200, height: 1500, unit: "px" },
layers: [
{
id: "image-legacy",
type: "image",
id: "image-faceblur",
type: "raster",
x: 30,
y: 40,
width: 600,
@@ -105,11 +106,16 @@ function makeLegacyFaceBlurProject(id: string): Project {
scale: 1,
rotation: 0,
opacity: 1,
faceBlur: {
method: "gaussian",
amount: 14,
regions: [{ x: 120, y: 160, width: 180, height: 200 }],
},
visible: true,
effects: [
{
kind: "face-blur",
enabled: true,
method: "gaussian",
amount: 14,
regions: [{ x: 120, y: 160, width: 180, height: 200 }],
},
],
},
],
};
@@ -148,19 +154,21 @@ describe("storage read flows", () => {
expect(raw?.layers[0]?.height).toBe(11.6);
});
it("loads legacy projects without source dimensions through storage and normalize flow", async () => {
const source = makeLegacyFaceBlurProject("project-legacy-faceblur");
it("loads projects with face-blur effect through storage and normalize flow", async () => {
const source = makeFaceBlurProject("project-faceblur");
await seedProject(source);
const loaded = await getProjectById(source.id);
const region = loaded?.layers[0]?.faceBlur?.regions[0];
expect(region).toBeDefined();
const effect = loaded?.layers[0]?.effects.find((e) => e.kind === "face-blur");
expect(effect).toBeDefined();
const region = effect?.kind === "face-blur" ? effect.regions[0] : undefined;
expect(region?.x).toBe(120);
expect(region?.sourceWidth).toBeUndefined();
expect(region?.sourceHeight).toBeUndefined();
const listed = await loadProjects();
const listedRegion = listed[0]?.layers[0]?.faceBlur?.regions[0];
const listedEffect = listed.find((p) => p.id === source.id)?.layers[0]?.effects.find((e) => e.kind === "face-blur");
const listedRegion = listedEffect?.kind === "face-blur" ? listedEffect.regions[0] : undefined;
expect(listedRegion?.width).toBe(180);
expect(listedRegion?.sourceWidth).toBeUndefined();
expect(listedRegion?.sourceHeight).toBeUndefined();
+1 -1
View File
@@ -100,7 +100,7 @@ function makeLinkId(projectId: string, layerId: string): string {
}
function isBinaryLayer(layer: Layer): boolean {
return layer.type === "image" || layer.type === "sticker";
return layer.type === "raster" || layer.type === "sticker";
}
function inferMimeType(layer: Layer): string {