From 20f8318ee2c81afd41b20718373066fc06a3900c Mon Sep 17 00:00:00 2001 From: James Date: Thu, 7 May 2026 05:08:12 +0000 Subject: [PATCH] fix(core): tighten 4K threshold to 3840 and pin square=portrait convention --- packages/core/src/parsers/htmlParser.test.ts | 36 ++++++++++++++++++++ packages/core/src/parsers/htmlParser.ts | 11 +++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/core/src/parsers/htmlParser.test.ts b/packages/core/src/parsers/htmlParser.test.ts index 119284df9..b913deb5b 100644 --- a/packages/core/src/parsers/htmlParser.test.ts +++ b/packages/core/src/parsers/htmlParser.test.ts @@ -257,6 +257,42 @@ describe("parseHtml", () => { expect(result.resolution).toBe("portrait-4k"); }); + it("classifies 1440p (QHD) as landscape, not landscape-4k", () => { + // Regression: an earlier `>= 2560` cutoff misclassified QHD compositions + // as 4K. The current rule uses the canonical 4K long-side (3840) so + // 2560×1440 stays in the landscape preset. + const html = ` + + +
+
Hello
+
+ + + `; + const result = parseHtml(html); + + expect(result.resolution).toBe("landscape"); + }); + + it("classifies square compositions as portrait by convention", () => { + // 1080×1080 has no obvious orientation. The parser collapses the tie to + // portrait — same bias the prior `w > h ? landscape : portrait` ternary + // had. Pinning so a future refactor doesn't silently flip it. + const html = ` + + +
+
Hello
+
+ + + `; + const result = parseHtml(html); + + expect(result.resolution).toBe("portrait"); + }); + it("extracts x, y, scale, opacity from data attributes", () => { const html = ` diff --git a/packages/core/src/parsers/htmlParser.ts b/packages/core/src/parsers/htmlParser.ts index a8eb9b7ae..ba4c65ffb 100644 --- a/packages/core/src/parsers/htmlParser.ts +++ b/packages/core/src/parsers/htmlParser.ts @@ -143,9 +143,18 @@ function parseResolutionFromHtml(doc: Document): CanvasResolution | null { } function resolveResolutionFromDimensions(width: number, height: number): CanvasResolution { + // `width === height` (square) falls into the portrait branch by convention — + // the same bias the previous `w > h ? landscape : portrait` ternary used. + // Square compositions are rare; pick portrait-as-default so we don't surprise + // the existing call sites that depend on this behavior. const isLandscape = width > height; const longSide = Math.max(width, height); - const isUhd = longSide >= 2560; + // UHD cutoff is the long side of `landscape-4k` / `portrait-4k` (3840). A + // looser threshold (e.g. ≥ 2560) would silently misclassify QHD/1440p + // (2560×1440) as 4K, which is the wrong default for a common authoring + // resolution closer to 1080p than to UHD. Authors who genuinely want the + // 4K preset can still set `data-resolution="landscape-4k"` explicitly. + const isUhd = longSide >= 3840; if (isLandscape) return isUhd ? "landscape-4k" : "landscape"; return isUhd ? "portrait-4k" : "portrait"; }