feat(core,studio,cli): add square + square-4k canvas resolutions

The four existing presets only cover 16:9 (landscape) and 9:16 (portrait)
aspect ratios. A 1080×1080 square comp had nowhere to land at any scale:
"Auto" rendered at the comp's authored 1080×1080, and picking 1080p or 4K
mapped to a landscape/portrait preset whose aspect ratio mismatched, which
the producer's resolveDeviceScaleFactor validator rejects with
"does not match the aspect ratio of the composition".

Add `square` (1080×1080) and `square-4k` (2160×2160) to CANVAS_DIMENSIONS
in core. The existing `keyof typeof CANVAS_DIMENSIONS` derivation
extends the `CanvasResolution` union and `VALID_CANVAS_RESOLUTIONS` array
automatically, so the producer's validator, the render API route, and
the CLI `--resolution` flag pick the new presets up without further
changes.

- core: extend CANVAS_DIMENSIONS, RESOLUTION_ALIASES, and the
  htmlParser to recognize `data-resolution="square|square-4k"` and to
  infer square from equal width/height (vs. the prior "square defaults
  to portrait" tie-breaker).
- studio: extend the local ResolutionPreset / CANVAS_DIMENSIONS mirrors;
  collapse isPortraitComp into a 3-way `compAspect` helper so
  resolveResolution returns the square preset for square comps.
- cli: update --resolution help text on `init` and `render` to mention
  the new presets.
- tests: add square cases to renderOrchestrator's resolveDeviceScaleFactor
  suite (returns 1 for square→square, 2 for square→square-4k, rejects
  landscape preset on square comp), update the htmlParser test that
  previously pinned the "square→portrait" tiebreaker.
This commit is contained in:
James
2026-05-11 16:48:54 +00:00
parent 976ceabedc
commit aa715ca8a0
9 changed files with 144 additions and 41 deletions
@@ -839,4 +839,37 @@ describe("resolveDeviceScaleFactor", () => {
}),
).toThrow(/aspect ratio|non-integer/);
});
it("returns 1 for a square comp matching the square preset", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "square",
}),
).toBe(1);
});
it("returns 2 for square 1080 → square-4k", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "square-4k",
}),
).toBe(2);
});
it("rejects landscape preset on a square composition", () => {
expect(() =>
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "landscape",
}),
).toThrow(/aspect ratio/);
});
});