refactor: dedupe resolution presets and clean up 4k stack

This commit is contained in:
James
2026-05-07 16:58:26 +00:00
parent bc02b8810c
commit 2b2281d490
12 changed files with 116 additions and 121 deletions
+28
View File
@@ -28,6 +28,34 @@ export const CANVAS_DIMENSIONS = {
"portrait-4k": { width: 2160, height: 3840 },
} as const;
export const VALID_CANVAS_RESOLUTIONS = Object.keys(
CANVAS_DIMENSIONS,
) as readonly CanvasResolution[];
const RESOLUTION_ALIASES: Record<string, CanvasResolution> = {
"1080p": "landscape",
hd: "landscape",
"1080p-portrait": "portrait",
"portrait-1080p": "portrait",
"4k": "landscape-4k",
uhd: "landscape-4k",
"4k-portrait": "portrait-4k",
};
/**
* Map a user-facing resolution string (canonical name or alias) to a
* `CanvasResolution`. Returns undefined for unknown values so callers
* can produce their own "invalid" UX (CLI exit, route validation, etc.).
*/
export function normalizeResolutionFlag(input: string | undefined): CanvasResolution | undefined {
if (!input) return undefined;
const lowered = input.toLowerCase();
if ((VALID_CANVAS_RESOLUTIONS as readonly string[]).includes(lowered)) {
return lowered as CanvasResolution;
}
return RESOLUTION_ALIASES[lowered];
}
export interface TimelineElementBase {
id: string;
type: TimelineElementType;
+19
View File
@@ -12,6 +12,25 @@ describe("@hyperframes/core public API exports", () => {
expect(core.CANVAS_DIMENSIONS["portrait-4k"]).toEqual({ width: 2160, height: 3840 });
});
it("exports VALID_CANVAS_RESOLUTIONS derived from CANVAS_DIMENSIONS", () => {
expect(core.VALID_CANVAS_RESOLUTIONS).toEqual([
"landscape",
"portrait",
"landscape-4k",
"portrait-4k",
]);
});
it("exports normalizeResolutionFlag with alias support", () => {
expect(core.normalizeResolutionFlag("4k")).toBe("landscape-4k");
expect(core.normalizeResolutionFlag("uhd")).toBe("landscape-4k");
expect(core.normalizeResolutionFlag("1080p")).toBe("landscape");
expect(core.normalizeResolutionFlag("landscape-4k")).toBe("landscape-4k");
expect(core.normalizeResolutionFlag("UHD")).toBe("landscape-4k");
expect(core.normalizeResolutionFlag("8k")).toBeUndefined();
expect(core.normalizeResolutionFlag(undefined)).toBeUndefined();
});
it("exports TIMELINE_COLORS", () => {
expect(core.TIMELINE_COLORS).toBeDefined();
expect(core.TIMELINE_COLORS.video).toBeDefined();
+2
View File
@@ -36,6 +36,8 @@ export type {
export {
CANVAS_DIMENSIONS,
VALID_CANVAS_RESOLUTIONS,
normalizeResolutionFlag,
TIMELINE_COLORS,
DEFAULT_DURATIONS,
COMPOSITION_VARIABLE_TYPES,
@@ -3,6 +3,9 @@ import { streamSSE } from "hono/streaming";
import { existsSync, readFileSync, mkdirSync, unlinkSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import type { StudioApiAdapter, RenderJobState } from "../types.js";
import { VALID_CANVAS_RESOLUTIONS, type CanvasResolution } from "../../core.types.js";
const VALID_RESOLUTIONS = new Set<string>(VALID_CANVAS_RESOLUTIONS);
export function registerRenderRoutes(api: Hono, adapter: StudioApiAdapter): void {
// Scoped job store — not shared across createStudioApi() calls
@@ -59,9 +62,8 @@ export function registerRenderRoutes(api: Hono, adapter: StudioApiAdapter): void
const quality = ["draft", "standard", "high"].includes(body.quality ?? "")
? (body.quality as string)
: "standard";
const VALID_RESOLUTIONS = new Set(["landscape", "portrait", "landscape-4k", "portrait-4k"]);
const outputResolution = VALID_RESOLUTIONS.has(body.resolution ?? "")
? (body.resolution as "landscape" | "portrait" | "landscape-4k" | "portrait-4k")
? (body.resolution as CanvasResolution)
: undefined;
const now = new Date();
+5 -5
View File
@@ -1,3 +1,5 @@
import type { CanvasResolution } from "../core.types.js";
/** Resolved info about a single project. */
export interface ResolvedProject {
id: string;
@@ -65,12 +67,10 @@ export interface StudioApiAdapter {
quality: string;
jobId: string;
/**
* Optional output resolution preset (e.g. "landscape-4k"). When set, the
* producer supersamples the composition via Chrome `deviceScaleFactor`.
* The composition's authored dimensions are unchanged. See the
* `resolveDeviceScaleFactor` constraints in the producer.
* Optional output resolution preset. See `resolveDeviceScaleFactor` in
* the producer for the integer-scale + aspect + HDR constraints.
*/
outputResolution?: "landscape" | "portrait" | "landscape-4k" | "portrait-4k";
outputResolution?: CanvasResolution;
}): RenderJobState;
/** Optional: generate a JPEG thumbnail via Puppeteer or similar. */