feat: add layer and mask thumbnails
This commit is contained in:
53
view/layers/thumbnailModel.test.ts
Normal file
53
view/layers/thumbnailModel.test.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import type { Asset } from "@core/asset";
|
||||
import type { Layer } from "@core/layer";
|
||||
import { createLayerThumbnailIndex, resolveLayerThumbnail } from "./thumbnailModel";
|
||||
|
||||
const assets = new Map([
|
||||
["asset-a", { id: "asset-a", name: "A", mimeType: "image/png", source: "data:image/png;base64,a", intrinsicSize: { w: 400, h: 200 } }],
|
||||
["asset-b", { id: "asset-b", name: "B", mimeType: "image/webp", source: "blob:b", intrinsicSize: { w: 80, h: 60 } }],
|
||||
] as const) as ReadonlyMap<string, Asset>;
|
||||
|
||||
describe("layer thumbnail read model", () => {
|
||||
test("preserves and clamps a layer source crop", () => {
|
||||
expect(resolveLayerThumbnail(raster("cropped", "asset-a", { x: 350, y: 20, w: 100, h: 240 }), assets)).toEqual({
|
||||
kind: "raster",
|
||||
source: "data:image/png;base64,a",
|
||||
viewBox: { x: 350, y: 20, w: 50, h: 180 },
|
||||
intrinsicWidth: 400,
|
||||
intrinsicHeight: 200,
|
||||
});
|
||||
});
|
||||
|
||||
test("returns a graceful empty model for invalid and missing assets", () => {
|
||||
expect(resolveLayerThumbnail(raster("missing", "unknown"), assets)).toEqual({ kind: "empty" });
|
||||
expect(resolveLayerThumbnail(raster("outside", "asset-a", { x: 500, y: 0, w: 20, h: 20 }), assets)).toEqual({ kind: "empty" });
|
||||
});
|
||||
|
||||
test("collects a bounded visible group preview and excludes attached masks", () => {
|
||||
const group = base("group", "group") as Layer & { type: "group"; children: Layer[] };
|
||||
group.type = "group";
|
||||
group.children = [raster("hidden", "asset-a", undefined, false), raster("mask", "asset-a"), raster("one", "asset-a"), raster("two", "asset-b"), raster("three", "asset-a"), raster("four", "asset-a")];
|
||||
const result = resolveLayerThumbnail(group, assets, new Set(["mask"]));
|
||||
expect(result.kind).toBe("group");
|
||||
if (result.kind === "group") expect(result.previews.map((preview) => preview.source)).toEqual(["data:image/png;base64,a", "blob:b", "data:image/png;base64,a"]);
|
||||
});
|
||||
|
||||
test("indexes nested group previews once for row lookup", () => {
|
||||
const child = { ...base("child-group", "Child"), type: "group" as const, children: [raster("nested", "asset-b")] };
|
||||
const group = { ...base("root", "Root"), type: "group" as const, children: [child] };
|
||||
const document = { id: "doc", version: 1, name: "Doc", assets: [...assets.values()], artboards: [{ id: "artboard", name: "Artboard", bounds: { x: 0, y: 0, w: 100, h: 100 }, backgroundColor: "#000000", visible: true, locked: false, layers: [group] }] };
|
||||
const index = createLayerThumbnailIndex(document, assets);
|
||||
expect(index.get("nested")?.kind).toBe("raster");
|
||||
expect(index.get("child-group")).toMatchObject({ kind: "group", previews: [{ source: "blob:b" }] });
|
||||
expect(index.get("root")).toMatchObject({ kind: "group", previews: [{ source: "blob:b" }] });
|
||||
});
|
||||
});
|
||||
|
||||
function raster(id: string, assetId: string, sourceRect?: { x: number; y: number; w: number; h: number }, visible = true): Layer {
|
||||
return { ...base(id, id), type: "raster", assetId, visible, ...(sourceRect ? { sourceRect } : {}) };
|
||||
}
|
||||
|
||||
function base(id: string, name: string) {
|
||||
return { id, name, visible: true, locked: false, opacity: 1, transform: { position: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, rotation: 0 } };
|
||||
}
|
||||
Reference in New Issue
Block a user