fix(cli): read actual composition dimensions in info and fix semver update check (#194)

## Summary

- **`info`** **command** now reads actual `data-width`/`data-height` from the root composition element instead of hardcoding 1920x1080 or 1080x1920 based on a parser heuristic that defaults to "portrait"
- **Update check** now uses proper semver comparison instead of string inequality (`!== VERSION`). Previously reported "update available" when installed `0.2.1` and npm had `0.2.0`

**Part 2 of 5** in a stacked PR series fixing E2E test findings.

## Test plan

- [x] `npx hyperframes info` on a 1920x1080 project shows "1920x1080" (not "1080x1920")
- [x] `npx hyperframes info --json` returns correct width/height fields
- [x] Version check no longer reports downgrade as available update
This commit is contained in:
Miguel Ángel
2026-04-02 14:12:48 -07:00
committed by GitHub
parent 5e8ff36675
commit 7f4e4333e5
4 changed files with 43 additions and 12 deletions
+8 -5
View File
@@ -19,7 +19,7 @@
}, },
"packages/cli": { "packages/cli": {
"name": "@hyperframes/cli", "name": "@hyperframes/cli",
"version": "0.1.15", "version": "0.2.1",
"bin": { "bin": {
"hyperframes": "./dist/cli.js", "hyperframes": "./dist/cli.js",
}, },
@@ -29,6 +29,7 @@
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"cheerio": "^1.2.0", "cheerio": "^1.2.0",
"citty": "^0.2.1", "citty": "^0.2.1",
"compare-versions": "^6.1.1",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"giget": "^3.2.0", "giget": "^3.2.0",
"hono": "^4.0.0", "hono": "^4.0.0",
@@ -59,7 +60,7 @@
}, },
"packages/core": { "packages/core": {
"name": "@hyperframes/core", "name": "@hyperframes/core",
"version": "0.1.15", "version": "0.2.1",
"dependencies": { "dependencies": {
"@chenglou/pretext": "^0.0.3", "@chenglou/pretext": "^0.0.3",
}, },
@@ -85,7 +86,7 @@
}, },
"packages/engine": { "packages/engine": {
"name": "@hyperframes/engine", "name": "@hyperframes/engine",
"version": "0.1.15", "version": "0.2.1",
"dependencies": { "dependencies": {
"@hono/node-server": "^1.13.0", "@hono/node-server": "^1.13.0",
"@hyperframes/core": "workspace:^", "@hyperframes/core": "workspace:^",
@@ -102,7 +103,7 @@
}, },
"packages/producer": { "packages/producer": {
"name": "@hyperframes/producer", "name": "@hyperframes/producer",
"version": "0.1.15", "version": "0.2.1",
"dependencies": { "dependencies": {
"@fontsource/archivo-black": "^5.2.8", "@fontsource/archivo-black": "^5.2.8",
"@fontsource/eb-garamond": "^5.2.7", "@fontsource/eb-garamond": "^5.2.7",
@@ -133,7 +134,7 @@
}, },
"packages/studio": { "packages/studio": {
"name": "@hyperframes/studio", "name": "@hyperframes/studio",
"version": "0.1.15", "version": "0.2.1",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.20.1", "@codemirror/autocomplete": "^6.20.1",
"@codemirror/commands": "^6.10.3", "@codemirror/commands": "^6.10.3",
@@ -807,6 +808,8 @@
"compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "1.0.0", "dot-prop": "5.3.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "1.0.0", "dot-prop": "5.3.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="],
"compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="],
"concurrently": ["concurrently@8.2.2", "", { "dependencies": { "chalk": "4.1.2", "date-fns": "2.30.0", "lodash": "4.17.23", "rxjs": "7.8.2", "shell-quote": "1.8.3", "spawn-command": "0.0.2", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg=="], "concurrently": ["concurrently@8.2.2", "", { "dependencies": { "chalk": "4.1.2", "date-fns": "2.30.0", "lodash": "4.17.23", "rxjs": "7.8.2", "shell-quote": "1.8.3", "spawn-command": "0.0.2", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg=="],
"confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+1
View File
@@ -30,6 +30,7 @@
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"cheerio": "^1.2.0", "cheerio": "^1.2.0",
"citty": "^0.2.1", "citty": "^0.2.1",
"compare-versions": "^6.1.1",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"giget": "^3.2.0", "giget": "^3.2.0",
"hono": "^4.0.0", "hono": "^4.0.0",
+20 -3
View File
@@ -45,7 +45,24 @@ export default defineCommand({
(max, el) => Math.max(max, el.startTime + el.duration), (max, el) => Math.max(max, el.startTime + el.duration),
0, 0,
); );
const resolution = parsed.resolution === "portrait" ? "1080x1920" : "1920x1080"; // Read actual dimensions from root composition element
const widthMatch =
html.match(/data-composition-id[^>]*data-width=["'](\d+)["']/) ||
html.match(/data-width=["'](\d+)["'][^>]*data-composition-id/);
const heightMatch =
html.match(/data-composition-id[^>]*data-height=["'](\d+)["']/) ||
html.match(/data-height=["'](\d+)["'][^>]*data-composition-id/);
const width = widthMatch?.[1]
? parseInt(widthMatch[1], 10)
: parsed.resolution === "portrait"
? 1080
: 1920;
const height = heightMatch?.[1]
? parseInt(heightMatch[1], 10)
: parsed.resolution === "portrait"
? 1920
: 1080;
const resolution = `${width}x${height}`;
const size = totalSize(project.dir); const size = totalSize(project.dir);
const typeCounts: Record<string, number> = {}; const typeCounts: Record<string, number> = {};
@@ -62,8 +79,8 @@ export default defineCommand({
withMeta({ withMeta({
name: project.name, name: project.name,
resolution: parsed.resolution, resolution: parsed.resolution,
width: parsed.resolution === "portrait" ? 1080 : 1920, width,
height: parsed.resolution === "portrait" ? 1920 : 1080, height,
duration: maxEnd, duration: maxEnd,
elements: parsed.elements.length, elements: parsed.elements.length,
tracks: tracks.size, tracks: tracks.size,
+14 -4
View File
@@ -1,3 +1,4 @@
import { compareVersions } from "compare-versions";
import { readConfig, writeConfig } from "../telemetry/config.js"; import { readConfig, writeConfig } from "../telemetry/config.js";
import { VERSION } from "../version.js"; import { VERSION } from "../version.js";
import { isDevMode } from "./env.js"; import { isDevMode } from "./env.js";
@@ -6,6 +7,15 @@ const NPM_REGISTRY_URL = "https://registry.npmjs.org/hyperframes/latest";
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
const FETCH_TIMEOUT_MS = 3000; const FETCH_TIMEOUT_MS = 3000;
/** Returns true if `a` is newer than `b` per semver (handles alpha, beta, rc). */
function isNewerSemver(a: string, b: string): boolean {
try {
return compareVersions(a, b) > 0;
} catch {
return a !== b;
}
}
export interface UpdateCheckResult { export interface UpdateCheckResult {
current: string; current: string;
latest: string; latest: string;
@@ -34,7 +44,7 @@ export async function checkForUpdate(force?: boolean): Promise<UpdateCheckResult
return { return {
current: VERSION, current: VERSION,
latest: config.latestVersion, latest: config.latestVersion,
updateAvailable: config.latestVersion !== VERSION, updateAvailable: isNewerSemver(config.latestVersion, VERSION),
}; };
} }
} }
@@ -57,7 +67,7 @@ export async function checkForUpdate(force?: boolean): Promise<UpdateCheckResult
config.latestVersion = latest; config.latestVersion = latest;
writeConfig(config); writeConfig(config);
return { current: VERSION, latest, updateAvailable: latest !== VERSION }; return { current: VERSION, latest, updateAvailable: isNewerSemver(latest, VERSION) };
} catch { } catch {
return fallbackResult(config.latestVersion); return fallbackResult(config.latestVersion);
} }
@@ -67,7 +77,7 @@ function fallbackResult(cachedLatest?: string): UpdateCheckResult {
return { return {
current: VERSION, current: VERSION,
latest: cachedLatest ?? VERSION, latest: cachedLatest ?? VERSION,
updateAvailable: cachedLatest ? cachedLatest !== VERSION : false, updateAvailable: cachedLatest ? isNewerSemver(cachedLatest, VERSION) : false,
}; };
} }
@@ -80,7 +90,7 @@ export function getUpdateMeta(): UpdateMeta {
return { return {
version: VERSION, version: VERSION,
latestVersion: config.latestVersion, latestVersion: config.latestVersion,
updateAvailable: config.latestVersion ? config.latestVersion !== VERSION : false, updateAvailable: config.latestVersion ? isNewerSemver(config.latestVersion, VERSION) : false,
}; };
} }