feat: add first-class text layers

This commit is contained in:
syntaxbullet
2026-07-11 12:38:34 +02:00
parent 606426c885
commit 37aa719047
33 changed files with 315 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
import type { ImageDocument } from "@core/document";
import type { Layer } from "@core/layer";
import type { Rect } from "@core/geometry";
import { isValidTextStyle, type TextStyle } from "@core/text-layer";
export const PROJECT_FORMAT = "image-studio-project";
export const CURRENT_PROJECT_VERSION = 1;
@@ -115,11 +116,14 @@ function assertLayers(value: unknown[], nested: boolean): asserts value is Layer
if (nested) throw new Error("Adjustment layers must stay at artboard level.");
if (!isAdjustment(layer.adjustment)) throw new Error("A project adjustment layer contains invalid settings.");
if (layer.layerMask !== undefined || layer.clippingMask !== undefined) throw new Error("Adjustment layers do not support masks.");
} else if (layer.type === "text") {
if (typeof layer.content !== "string" || !layer.content.trim() || !isRecord(layer.style) || !isValidTextStyle(layer.style as TextStyle)) throw new Error("A project text layer contains invalid typography.");
if (layer.layerMask !== undefined || layer.clippingMask !== undefined) throw new Error("Text layers do not support masks. Rasterize text before masking.");
} else if ((layer.type === "image" || layer.type === "raster") && typeof layer.assetId !== "string") {
throw new Error("A project layer is missing its asset reference.");
} else if ((layer.type === "image" || layer.type === "raster") && layer.sourceRect !== undefined && (!isRect(layer.sourceRect) || layer.sourceRect.w < 1 || layer.sourceRect.h < 1)) {
throw new Error("A project layer contains an invalid source crop.");
} else if (layer.type !== "image" && layer.type !== "raster") {
} else if (layer.type !== "image" && layer.type !== "raster" && layer.type !== "text") {
throw new Error(`Unsupported layer type: ${layer.type}.`);
}
}