hyperframes 0.7.78 → 0.7.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ var VERSION;
50
50
  var init_version = __esm({
51
51
  "src/version.ts"() {
52
52
  "use strict";
53
- VERSION = true ? "0.7.78" : "0.0.0-dev";
53
+ VERSION = true ? "0.7.79" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -62373,6 +62373,30 @@ var init_feedbackRating = __esm({
62373
62373
  }
62374
62374
  });
62375
62375
 
62376
+ // src/utils/errorMessage.ts
62377
+ function normalizeErrorMessage(error) {
62378
+ if (error instanceof Error) return error.message;
62379
+ if (typeof error === "string") return error;
62380
+ if (typeof error === "object" && error !== null) {
62381
+ const msg = error.message;
62382
+ if (typeof msg === "string") return msg;
62383
+ try {
62384
+ return JSON.stringify(error);
62385
+ } catch {
62386
+ try {
62387
+ return `{${Object.keys(error).join(", ")}}`;
62388
+ } catch {
62389
+ }
62390
+ }
62391
+ }
62392
+ return String(error ?? "unknown error");
62393
+ }
62394
+ var init_errorMessage = __esm({
62395
+ "src/utils/errorMessage.ts"() {
62396
+ "use strict";
62397
+ }
62398
+ });
62399
+
62376
62400
  // src/telemetry/config.ts
62377
62401
  import { existsSync as existsSync2, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
62378
62402
  import { join as join3 } from "path";
@@ -62425,7 +62449,11 @@ function readConfig() {
62425
62449
  cachedConfig = config;
62426
62450
  return { ...config };
62427
62451
  } catch {
62428
- const config = { ...DEFAULT_CONFIG, anonymousId: randomUUID() };
62452
+ const config = {
62453
+ ...DEFAULT_CONFIG,
62454
+ telemetryEnabled: false,
62455
+ anonymousId: randomUUID()
62456
+ };
62429
62457
  writeConfig(config);
62430
62458
  return config;
62431
62459
  }
@@ -62435,15 +62463,18 @@ function readConfigFresh() {
62435
62463
  return readConfig();
62436
62464
  }
62437
62465
  function writeConfig(config) {
62466
+ return writeConfigWithResult(config).ok;
62467
+ }
62468
+ function writeConfigWithResult(config) {
62438
62469
  try {
62439
62470
  mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
62440
62471
  const tmpFile = `${CONFIG_FILE}.${process.pid}.tmp`;
62441
62472
  writeFileSync(tmpFile, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
62442
62473
  renameSync(tmpFile, CONFIG_FILE);
62443
62474
  cachedConfig = { ...config };
62444
- return true;
62445
- } catch {
62446
- return false;
62475
+ return { ok: true };
62476
+ } catch (error) {
62477
+ return { ok: false, error: normalizeErrorMessage(error) };
62447
62478
  }
62448
62479
  }
62449
62480
  function incrementCommandCount() {
@@ -62456,6 +62487,7 @@ var CONFIG_DIR, CONFIG_FILE, MAX_RECENT_RENDERS, DEFAULT_CONFIG, cachedConfig, C
62456
62487
  var init_config = __esm({
62457
62488
  "src/telemetry/config.ts"() {
62458
62489
  "use strict";
62490
+ init_errorMessage();
62459
62491
  CONFIG_DIR = join3(homedir(), ".hyperframes");
62460
62492
  CONFIG_FILE = join3(CONFIG_DIR, "config.json");
62461
62493
  MAX_RECENT_RENDERS = 5;
@@ -62594,21 +62626,6 @@ var init_diagnostics2 = __esm({
62594
62626
  }
62595
62627
  });
62596
62628
 
62597
- // src/utils/env.ts
62598
- function isDevMode() {
62599
- try {
62600
- const url = new URL(import.meta.url);
62601
- return url.pathname.endsWith(".ts");
62602
- } catch {
62603
- return false;
62604
- }
62605
- }
62606
- var init_env = __esm({
62607
- "src/utils/env.ts"() {
62608
- "use strict";
62609
- }
62610
- });
62611
-
62612
62629
  // ../engine/src/services/systemMemory.ts
62613
62630
  import { readFileSync as readFileSync2 } from "fs";
62614
62631
  import { totalmem } from "os";
@@ -88795,18 +88812,59 @@ var init_transport = __esm({
88795
88812
  }
88796
88813
  });
88797
88814
 
88798
- // src/telemetry/client.ts
88799
- function shouldTrack() {
88800
- if (telemetryEnabled !== null) return telemetryEnabled;
88801
- if (process.env["HYPERFRAMES_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1") {
88802
- telemetryEnabled = false;
88815
+ // src/utils/env.ts
88816
+ function isDevMode() {
88817
+ try {
88818
+ const url = new URL(import.meta.url);
88819
+ return url.pathname.endsWith(".ts");
88820
+ } catch {
88803
88821
  return false;
88804
88822
  }
88823
+ }
88824
+ var init_env = __esm({
88825
+ "src/utils/env.ts"() {
88826
+ "use strict";
88827
+ }
88828
+ });
88829
+
88830
+ // src/telemetry/policy.ts
88831
+ function isEnvOptOutValue(value) {
88832
+ return value !== void 0 && ENV_OPT_OUT_VALUES.has(value.trim().toLowerCase());
88833
+ }
88834
+ function telemetryRuntimeOverride() {
88835
+ if (isEnvOptOutValue(process.env["HYPERFRAMES_NO_TELEMETRY"])) {
88836
+ return "HYPERFRAMES_NO_TELEMETRY";
88837
+ }
88838
+ if (isEnvOptOutValue(process.env["DO_NOT_TRACK"])) {
88839
+ return "DO_NOT_TRACK";
88840
+ }
88805
88841
  if (isDevMode()) {
88806
- telemetryEnabled = false;
88807
- return false;
88842
+ return "dev_mode";
88808
88843
  }
88809
88844
  if (!POSTHOG_API_KEY.startsWith("phc_")) {
88845
+ return "telemetry_disabled_build";
88846
+ }
88847
+ return null;
88848
+ }
88849
+ function effectiveTelemetryStatus(configEnabled) {
88850
+ const override = telemetryRuntimeOverride();
88851
+ if (override !== null) return { enabled: false, source: override };
88852
+ return { enabled: configEnabled, source: "config" };
88853
+ }
88854
+ var ENV_OPT_OUT_VALUES;
88855
+ var init_policy = __esm({
88856
+ "src/telemetry/policy.ts"() {
88857
+ "use strict";
88858
+ init_env();
88859
+ init_transport();
88860
+ ENV_OPT_OUT_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
88861
+ }
88862
+ });
88863
+
88864
+ // src/telemetry/client.ts
88865
+ function shouldTrack() {
88866
+ if (telemetryEnabled !== null) return telemetryEnabled;
88867
+ if (telemetryRuntimeOverride() !== null) {
88810
88868
  telemetryEnabled = false;
88811
88869
  return false;
88812
88870
  }
@@ -88870,9 +88928,9 @@ var init_client = __esm({
88870
88928
  init_version();
88871
88929
  init_colors();
88872
88930
  init_diagnostics2();
88873
- init_env();
88874
88931
  init_system();
88875
88932
  init_transport();
88933
+ init_policy();
88876
88934
  init_transport();
88877
88935
  telemetryEnabled = null;
88878
88936
  }
@@ -93480,9 +93538,10 @@ async function checkForUpdate(force) {
93480
93538
  return fallbackResult(config.latestVersion);
93481
93539
  }
93482
93540
  const latest = data2.version;
93483
- config.lastUpdateCheck = (/* @__PURE__ */ new Date()).toISOString();
93484
- config.latestVersion = latest;
93485
- writeConfig(config);
93541
+ const freshConfig = readConfigFresh();
93542
+ freshConfig.lastUpdateCheck = (/* @__PURE__ */ new Date()).toISOString();
93543
+ freshConfig.latestVersion = latest;
93544
+ writeConfig(freshConfig);
93486
93545
  return { current: VERSION, latest, updateAvailable: isNewerSemver(latest, VERSION) };
93487
93546
  } catch {
93488
93547
  return fallbackResult(config.latestVersion);
@@ -94875,30 +94934,6 @@ var init_storyboard = __esm({
94875
94934
  }
94876
94935
  });
94877
94936
 
94878
- // src/utils/errorMessage.ts
94879
- function normalizeErrorMessage(error) {
94880
- if (error instanceof Error) return error.message;
94881
- if (typeof error === "string") return error;
94882
- if (typeof error === "object" && error !== null) {
94883
- const msg = error.message;
94884
- if (typeof msg === "string") return msg;
94885
- try {
94886
- return JSON.stringify(error);
94887
- } catch {
94888
- try {
94889
- return `{${Object.keys(error).join(", ")}}`;
94890
- } catch {
94891
- }
94892
- }
94893
- }
94894
- return String(error ?? "unknown error");
94895
- }
94896
- var init_errorMessage = __esm({
94897
- "src/utils/errorMessage.ts"() {
94898
- "use strict";
94899
- }
94900
- });
94901
-
94902
94937
  // src/utils/openBrowser.ts
94903
94938
  import { spawn as spawn7 } from "child_process";
94904
94939
  function parseRemoteDebuggingPort(value) {
@@ -139816,12 +139851,17 @@ function geometryIssueAnchor(candidate, time) {
139816
139851
  rect: candidate.rect
139817
139852
  };
139818
139853
  }
139854
+ function captionCenterInZone(rect, zone, canvas) {
139855
+ const cx = rect.left + rect.width / 2;
139856
+ const cy = rect.top + rect.height / 2;
139857
+ const inside = cx >= zone.x0 * canvas.width && cx <= zone.x1 * canvas.width && cy >= zone.y0 * canvas.height && cy <= zone.y1 * canvas.height;
139858
+ return { inside, cy };
139859
+ }
139819
139860
  function captionFinding(candidate, options, canvas, time) {
139820
139861
  const zone = options.captionZone;
139821
139862
  if (!zone || candidate.kind !== "text" || !candidateIsSized(candidate, canvas)) return null;
139822
- const cx = candidate.rect.left + candidate.rect.width / 2;
139823
- const cy = candidate.rect.top + candidate.rect.height / 2;
139824
- const inside = cx >= zone.x0 * canvas.width && cx <= zone.x1 * canvas.width && cy >= zone.y0 * canvas.height && cy <= zone.y1 * canvas.height;
139863
+ if ("data-layout-allow-caption-zone" in candidate.dataAttributes) return null;
139864
+ const { inside, cy } = captionCenterInZone(candidate.rect, zone, canvas);
139825
139865
  if (!inside) return null;
139826
139866
  const text2 = candidate.text.slice(0, 48);
139827
139867
  const pctFromBottom = Math.round((canvas.height - cy) / canvas.height * 100);
@@ -139833,7 +139873,7 @@ function captionFinding(candidate, options, canvas, time) {
139833
139873
  severity: zone.severity === "error" ? "error" : "warning",
139834
139874
  text: text2,
139835
139875
  message: `<${candidate.tag}> "${text2}" is centred in the reserved caption band (~${pctFromBottom}% up from the bottom).`,
139836
- fixHint: "Keep main content outside the configured caption band."
139876
+ fixHint: "Keep main content outside the configured caption band, or mark intentional lower-third copy with data-layout-allow-caption-zone."
139837
139877
  }
139838
139878
  };
139839
139879
  }
@@ -147313,42 +147353,67 @@ __export(telemetry_exports, {
147313
147353
  default: () => telemetry_default,
147314
147354
  examples: () => examples27
147315
147355
  });
147316
- function runEnable() {
147317
- const config = readConfig();
147318
- config.telemetryEnabled = true;
147319
- writeConfig(config);
147320
- console.log(`
147321
- ${c.success("\u2713")} Telemetry ${c.success("enabled")}
147322
- `);
147356
+ function describeOverride(source) {
147357
+ switch (source) {
147358
+ case "HYPERFRAMES_NO_TELEMETRY":
147359
+ case "DO_NOT_TRACK":
147360
+ return `${source} is set`;
147361
+ case "dev_mode":
147362
+ return "this is a development build";
147363
+ case "telemetry_disabled_build":
147364
+ return "this build has no telemetry key";
147365
+ }
147323
147366
  }
147324
- function runDisable() {
147325
- const config = readConfig();
147326
- config.telemetryEnabled = false;
147327
- writeConfig(config);
147367
+ function setTelemetryEnabled(enabled) {
147368
+ const config = readConfigFresh();
147369
+ config.telemetryEnabled = enabled;
147370
+ const result = writeConfigWithResult(config);
147371
+ if (!result.ok) {
147372
+ console.error(
147373
+ `
147374
+ ${c.error("\u2717")} Could not persist telemetry preference to ${c.accent(CONFIG_PATH)}
147375
+ ${c.dim("Reason:")} ${result.error}
147376
+ `
147377
+ );
147378
+ failCommand();
147379
+ }
147380
+ const effective = effectiveTelemetryStatus(enabled);
147381
+ const preference = enabled ? c.success("enabled") : c.bold("disabled");
147382
+ const noun = enabled && !effective.enabled ? "Telemetry preference" : "Telemetry";
147328
147383
  console.log(`
147329
- ${c.success("\u2713")} Telemetry ${c.bold("disabled")}
147330
- `);
147384
+ ${c.success("\u2713")} ${noun} ${preference}`);
147385
+ if (effective.source !== "config") {
147386
+ console.log(
147387
+ ` ${c.dim("Note:")} Telemetry remains disabled because ${describeOverride(effective.source)}.`
147388
+ );
147389
+ }
147390
+ console.log();
147331
147391
  }
147332
147392
  function runStatus() {
147333
- const config = readConfig();
147334
- const status = config.telemetryEnabled ? c.success("enabled") : c.dim("disabled");
147393
+ const config = readConfigFresh();
147394
+ const effective = effectiveTelemetryStatus(config.telemetryEnabled);
147395
+ const status = effective.enabled ? c.success("enabled") : c.dim("disabled");
147335
147396
  console.log();
147336
147397
  console.log(` ${c.dim("Status:")} ${status}`);
147398
+ console.log(` ${c.dim("Source:")} ${effective.source}`);
147337
147399
  console.log(` ${c.dim("Config:")} ${c.accent(CONFIG_PATH)}`);
147338
- console.log(` ${c.dim("Commands:")} ${c.bold(String(config.commandCount))}`);
147400
+ console.log(` ${c.dim("Tracked commands:")} ${c.bold(String(config.commandCount))}`);
147339
147401
  console.log();
147340
147402
  console.log(` ${c.dim("Disable:")} ${c.accent("hyperframes telemetry disable")}`);
147341
- console.log(` ${c.dim("Env var:")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")}`);
147403
+ console.log(
147404
+ ` ${c.dim("Env var:")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("or")} ${c.accent("DO_NOT_TRACK=1")}`
147405
+ );
147342
147406
  console.log();
147343
147407
  }
147344
147408
  var examples27, telemetry_default;
147345
147409
  var init_telemetry = __esm({
147346
147410
  "src/commands/telemetry.ts"() {
147347
147411
  "use strict";
147348
- init_commandResult();
147349
147412
  init_dist();
147350
- init_colors();
147351
147413
  init_config();
147414
+ init_policy();
147415
+ init_colors();
147416
+ init_commandResult();
147352
147417
  examples27 = [
147353
147418
  ["Check current telemetry status", "hyperframes telemetry status"],
147354
147419
  ["Disable telemetry", "hyperframes telemetry disable"],
@@ -147387,15 +147452,15 @@ ${c.bold("WHAT WE DON'T COLLECT:")}
147387
147452
  ${c.dim("\u2022")} IP addresses (discarded by our analytics provider)
147388
147453
  ${c.dim("\u2022")} Any personally identifiable information
147389
147454
 
147390
- ${c.dim("You can also set")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("to disable.")}
147455
+ ${c.dim("You can also set")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("or")} ${c.accent("DO_NOT_TRACK=1")} ${c.dim("to disable.")}
147391
147456
  `);
147392
147457
  return;
147393
147458
  }
147394
147459
  switch (subcommand) {
147395
147460
  case "enable":
147396
- return runEnable();
147461
+ return setTelemetryEnabled(true);
147397
147462
  case "disable":
147398
- return runDisable();
147463
+ return setTelemetryEnabled(false);
147399
147464
  case "status":
147400
147465
  return runStatus();
147401
147466
  default:
@@ -200970,7 +201035,7 @@ if (!isHelp && command !== "telemetry" && command !== "events" && command !== "u
200970
201035
  if (mod.shouldTrack()) mod.incrementCommandCount();
200971
201036
  });
200972
201037
  }
200973
- if (!isHelp && !hasJsonFlag && command !== "upgrade" && command !== "events" && command !== "skills") {
201038
+ if (!isHelp && !hasJsonFlag && command !== "upgrade" && command !== "events" && command !== "telemetry" && command !== "skills") {
200974
201039
  Promise.resolve().then(() => (init_autoUpdate(), autoUpdate_exports)).then((mod) => mod.reportCompletedUpdate()).catch(() => {
200975
201040
  });
200976
201041
  Promise.resolve().then(() => (init_updateCheck(), updateCheck_exports)).then(async (mod) => {
@@ -105,6 +105,10 @@
105
105
  return !!element.closest("[data-layout-allow-overflow]");
106
106
  }
107
107
 
108
+ function hasAllowCaptionZoneFlag(element) {
109
+ return !!element.closest("[data-layout-allow-caption-zone]");
110
+ }
111
+
108
112
  function hasTextClipOptOut(element) {
109
113
  return hasAllowOverflowFlag(element) || element.hasAttribute("data-layout-bleed");
110
114
  }
@@ -1396,7 +1400,7 @@
1396
1400
  }
1397
1401
  if (!isVisibleElement(element, 0.05, false)) continue;
1398
1402
  const elementRect = toRect(element.getBoundingClientRect());
1399
- if (includeText && hasOwnTextCandidate(element, true)) {
1403
+ if (includeText && hasOwnTextCandidate(element, true) && !hasAllowCaptionZoneFlag(element)) {
1400
1404
  const rect = textRectFor(element, true);
1401
1405
  if (rect) {
1402
1406
  candidates.push(
@@ -1,4 +1,4 @@
1
- "use strict";var HyperframesPlayer=(()=>{var J=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Ye=Object.prototype.hasOwnProperty;var Xe=(i,e)=>{for(var t in e)J(i,t,{get:e[t],enumerable:!0})},Ze=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of qe(e))!Ye.call(i,n)&&n!==t&&J(i,n,{get:()=>e[n],enumerable:!(r=Ge(e,n))||r.enumerable});return i};var Qe=i=>Ze(J({},"__esModule",{value:!0}),i);var Lt={};Xe(Lt,{HyperframesPlayer:()=>Q,SPEED_PRESETS:()=>te,formatSpeed:()=>N,formatTime:()=>$});function ye(i){return i.hasRuntime||i.runtimeInjected?!1:!!(i.hasNestedCompositions||i.hasTimelines&&i.attempts>=5)}function I(i){return typeof i=="object"&&i!==null}function Ee(i){return I(i)&&typeof i.getDuration=="function"}function Se(i){return I(i)&&typeof i.duration=="function"&&typeof i.time=="function"&&typeof i.seek=="function"&&typeof i.play=="function"&&typeof i.pause=="function"}var Je="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.7.78/dist/hyperframe.runtime.iife.js";function D(i){if(i===null)return null;let e=Number.parseInt(i,10);return Number.isFinite(e)&&e>0?e:null}function Ke(i){let e=i?.querySelector("[data-composition-id][data-width][data-height]")??i?.querySelector("[data-width][data-height]");if(!e)return null;let t=D(e.getAttribute("data-width")),r=D(e.getAttribute("data-height"));return t!==null&&r!==null?{width:t,height:r}:null}var j=class{constructor(e,t){this._iframe=e;this._callbacks=t}_iframe;_callbacks;_interval=null;_runtimeInjected=!1;get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let e=0;this._interval=setInterval(()=>{e++;try{let t=this._iframe.contentWindow;if(!t)return;let r=!!(t.__hf||t.__player),n=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!this._iframe.contentDocument?.querySelector("[data-composition-src]");if(ye({hasRuntime:r,hasTimelines:n,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;let s=this._resolvePlaybackDurationAdapter(t);if(s&&s.getDuration()>0){this.stop();let l=Ke(this._iframe.contentDocument);this._callbacks.onReady({duration:s.getDuration(),adapter:s,compositionSize:l});return}}catch{}e>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{let e=this._iframe.contentWindow;return e?this._resolveDirectTimelineAdapterFromWindow(e):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(e){return this._resolveDirectTimelineAdapterFromWindow(e)}hasRuntimeBridge(e){return Reflect.get(e,"__hf")!==void 0||I(Reflect.get(e,"__player"))}_injectRuntime(){this._runtimeInjected=!0;try{let e=this._iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=Je,(e.head||e.documentElement).appendChild(t),this._callbacks.onRuntimeInjected?.()}catch{}}_resolveDirectTimelineAdapterFromWindow(e){if(this.hasRuntimeBridge(e))return null;let t=Reflect.get(e,"__timelines");if(!I(t))return null;let r=Object.keys(t);if(r.length===0)return null;let n=this._iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),o=n&&n in t?n:r[r.length-1],s=t[o];return Se(s)?s:null}_resolvePlaybackDurationAdapter(e){let t=Reflect.get(e,"__player");if(Ee(t))return{kind:"runtime",getDuration:()=>t.getDuration()};let r=this._resolveDirectTimelineAdapterFromWindow(e);return r?{kind:"direct-timeline",timeline:r,getDuration:()=>r.duration()}:null}};var Te=`
1
+ "use strict";var HyperframesPlayer=(()=>{var J=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Ye=Object.prototype.hasOwnProperty;var Xe=(i,e)=>{for(var t in e)J(i,t,{get:e[t],enumerable:!0})},Ze=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of qe(e))!Ye.call(i,n)&&n!==t&&J(i,n,{get:()=>e[n],enumerable:!(r=Ge(e,n))||r.enumerable});return i};var Qe=i=>Ze(J({},"__esModule",{value:!0}),i);var Lt={};Xe(Lt,{HyperframesPlayer:()=>Q,SPEED_PRESETS:()=>te,formatSpeed:()=>N,formatTime:()=>$});function ye(i){return i.hasRuntime||i.runtimeInjected?!1:!!(i.hasNestedCompositions||i.hasTimelines&&i.attempts>=5)}function I(i){return typeof i=="object"&&i!==null}function Ee(i){return I(i)&&typeof i.getDuration=="function"}function Se(i){return I(i)&&typeof i.duration=="function"&&typeof i.time=="function"&&typeof i.seek=="function"&&typeof i.play=="function"&&typeof i.pause=="function"}var Je="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.7.79/dist/hyperframe.runtime.iife.js";function D(i){if(i===null)return null;let e=Number.parseInt(i,10);return Number.isFinite(e)&&e>0?e:null}function Ke(i){let e=i?.querySelector("[data-composition-id][data-width][data-height]")??i?.querySelector("[data-width][data-height]");if(!e)return null;let t=D(e.getAttribute("data-width")),r=D(e.getAttribute("data-height"));return t!==null&&r!==null?{width:t,height:r}:null}var j=class{constructor(e,t){this._iframe=e;this._callbacks=t}_iframe;_callbacks;_interval=null;_runtimeInjected=!1;get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let e=0;this._interval=setInterval(()=>{e++;try{let t=this._iframe.contentWindow;if(!t)return;let r=!!(t.__hf||t.__player),n=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!this._iframe.contentDocument?.querySelector("[data-composition-src]");if(ye({hasRuntime:r,hasTimelines:n,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;let s=this._resolvePlaybackDurationAdapter(t);if(s&&s.getDuration()>0){this.stop();let l=Ke(this._iframe.contentDocument);this._callbacks.onReady({duration:s.getDuration(),adapter:s,compositionSize:l});return}}catch{}e>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{let e=this._iframe.contentWindow;return e?this._resolveDirectTimelineAdapterFromWindow(e):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(e){return this._resolveDirectTimelineAdapterFromWindow(e)}hasRuntimeBridge(e){return Reflect.get(e,"__hf")!==void 0||I(Reflect.get(e,"__player"))}_injectRuntime(){this._runtimeInjected=!0;try{let e=this._iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=Je,(e.head||e.documentElement).appendChild(t),this._callbacks.onRuntimeInjected?.()}catch{}}_resolveDirectTimelineAdapterFromWindow(e){if(this.hasRuntimeBridge(e))return null;let t=Reflect.get(e,"__timelines");if(!I(t))return null;let r=Object.keys(t);if(r.length===0)return null;let n=this._iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),o=n&&n in t?n:r[r.length-1],s=t[o];return Se(s)?s:null}_resolvePlaybackDurationAdapter(e){let t=Reflect.get(e,"__player");if(Ee(t))return{kind:"runtime",getDuration:()=>t.getDuration()};let r=this._resolveDirectTimelineAdapterFromWindow(e);return r?{kind:"direct-timeline",timeline:r,getDuration:()=>r.duration()}:null}};var Te=`
2
2
  :host {
3
3
  display: block;
4
4
  position: relative;
@@ -9,7 +9,7 @@ When the composition is animation-driven, run the checks before you reach for `p
9
9
  - Run `lint` after the first HTML pass for early feedback. It is an iteration aid, not a separate final gate.
10
10
  - Run `check --snapshots` at the first full pass: the overview frames and per-finding crops show you what the auditor saw.
11
11
  - Look at the PNGs before tuning automated warnings: your eye catches what the auditor misses, and the auditor catches what your eye misses.
12
- - Treat layout errors as defects unless a snapshot proves the layering is intentional, in which case mark it with `data-layout-allow-overflow` / `data-layout-allow-overlap` / `data-layout-allow-occlusion`.
12
+ - Treat layout errors as defects unless a snapshot proves the layering is intentional, in which case mark it with `data-layout-allow-overflow` / `data-layout-allow-overlap` / `data-layout-allow-occlusion` / `data-layout-allow-caption-zone` (caption band only).
13
13
  - State motion intent in a `*.motion.json` sidecar so `check` verifies it automatically (entrances firing under seek, stagger order, in-frame, liveness). This is the closest automated proxy for "watch the MP4" and catches render-vs-preview bugs the eye misses (see **Motion verification** below).
14
14
 
15
15
  ## lint
@@ -57,6 +57,7 @@ Every finding carries a selector, the element's `data-*` identity, the compositi
57
57
  - `data-layout-allow-overflow` — overflow is intentional (entrance/exit travel).
58
58
  - `data-layout-allow-overlap` — deliberate text layering (e.g. a demo cursor label over a heading).
59
59
  - `data-layout-allow-occlusion` — an element is meant to cover text.
60
+ - `data-layout-allow-caption-zone` — intentional lower-third / caption-band copy under `--caption-zone`. Applies to the marked element and every descendant (`closest`); silences only `caption_zone_collision` (not overflow/overlap/occlusion). Prefer the narrowest wrapper that owns the intentional band copy.
60
61
  - `data-layout-ignore` — decorative element that should never be audited.
61
62
 
62
63
  **Opt-in pipeline gates** (used by orchestrators; off by default):
@@ -66,7 +67,7 @@ npx hyperframes check --caption-zone "x0=0;y0=.82;x1=1;y1=1;severity=error;seek=
66
67
  npx hyperframes check --frame-check # media (img/svg/video/canvas) out-of-frame detection
67
68
  ```
68
69
 
69
- `--caption-zone` takes fractional band geometry (`x0/y0/x1/y1` required, 0-1 fractions of the composition's own canvas, portrait included) with optional `severity` and comma-separated `seek` fractions; it flags content whose center sits inside the band. `--frame-check` reports media elements breaching the canvas beyond `max(120px, 6% of the min canvas dimension)`.
70
+ `--caption-zone` takes fractional band geometry (`x0/y0/x1/y1` required, 0-1 fractions of the composition's own canvas, portrait included) with optional `severity` and comma-separated `seek` fractions; it flags content whose center sits inside the band. Waive intentional lower-third copy with `data-layout-allow-caption-zone` on the element or its nearest wrapper (see Escape hatches). `--frame-check` reports media elements breaching the canvas beyond `max(120px, 6% of the min canvas dimension)`.
70
71
 
71
72
  **Fixing contrast errors** — thresholds are 4.5:1 for normal text, 3:1 for large text (24px+, or 19px+ bold). The finding's `suggestedColor` already picks the nearest compliant color in the right direction (brighten on dark backgrounds, darken on light); apply it or adjust within the palette family, then re-run `check`.
72
73
 
@@ -1,4 +1,4 @@
1
- var ce=Object.defineProperty;var pe=(r,t,e)=>t in r?ce(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var c=(r,t,e)=>pe(r,typeof t!="symbol"?t+"":t,e);import{r as ne,i as me,a as fe}from"./index-BSZrK0bx.js";function _e(r){return r.hasRuntime||r.runtimeInjected?!1:!!(r.hasNestedCompositions||r.hasTimelines&&r.attempts>=5)}function F(r){return typeof r=="object"&&r!==null}function ge(r){return F(r)&&typeof r.getDuration=="function"}function ye(r){return F(r)&&typeof r.duration=="function"&&typeof r.time=="function"&&typeof r.seek=="function"&&typeof r.play=="function"&&typeof r.pause=="function"}function ve(r){if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r))throw new Error(`Invalid HyperFrames runtime version: ${r}`);return`https://cdn.jsdelivr.net/npm/@hyperframes/core@${r}/dist/hyperframe.runtime.iife.js`}const be=typeof __HYPERFRAMES_RUNTIME_CDN_URL__=="string"?__HYPERFRAMES_RUNTIME_CDN_URL__:ve("0.0.0-dev");function H(r){if(r===null)return null;const t=Number.parseInt(r,10);return Number.isFinite(t)&&t>0?t:null}function we(r){const t=(r==null?void 0:r.querySelector("[data-composition-id][data-width][data-height]"))??(r==null?void 0:r.querySelector("[data-width][data-height]"));if(!t)return null;const e=H(t.getAttribute("data-width")),i=H(t.getAttribute("data-height"));return e!==null&&i!==null?{width:e,height:i}:null}class Ae{constructor(t,e){c(this,"_interval",null);c(this,"_runtimeInjected",!1);this._iframe=t,this._callbacks=e}get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let t=0;this._interval=setInterval(()=>{var e;t++;try{const i=this._iframe.contentWindow;if(!i)return;const s=!!(i.__hf||i.__player),o=!!(i.__timelines&&Object.keys(i.__timelines).length>0),d=!!((e=this._iframe.contentDocument)!=null&&e.querySelector("[data-composition-src]"));if(_e({hasRuntime:s,hasTimelines:o,hasNestedCompositions:d,runtimeInjected:this._runtimeInjected,attempts:t})){this._injectRuntime();return}if(this._runtimeInjected&&!s)return;const a=this._resolvePlaybackDurationAdapter(i);if(a&&a.getDuration()>0){this.stop();const h=we(this._iframe.contentDocument);this._callbacks.onReady({duration:a.getDuration(),adapter:a,compositionSize:h});return}}catch{}t>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{const t=this._iframe.contentWindow;return t?this._resolveDirectTimelineAdapterFromWindow(t):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(t){return this._resolveDirectTimelineAdapterFromWindow(t)}hasRuntimeBridge(t){return Reflect.get(t,"__hf")!==void 0||F(Reflect.get(t,"__player"))}_injectRuntime(){var t,e;this._runtimeInjected=!0;try{const i=this._iframe.contentDocument;if(!i)return;const s=i.createElement("script");s.src=be,(i.head||i.documentElement).appendChild(s),(e=(t=this._callbacks).onRuntimeInjected)==null||e.call(t)}catch{}}_resolveDirectTimelineAdapterFromWindow(t){var a,h;if(this.hasRuntimeBridge(t))return null;const e=Reflect.get(t,"__timelines");if(!F(e))return null;const i=Object.keys(e);if(i.length===0)return null;const s=(h=(a=this._iframe.contentDocument)==null?void 0:a.querySelector("[data-composition-id]"))==null?void 0:h.getAttribute("data-composition-id"),o=s&&s in e?s:i[i.length-1],d=e[o];return ye(d)?d:null}_resolvePlaybackDurationAdapter(t){const e=Reflect.get(t,"__player");if(ge(e))return{kind:"runtime",getDuration:()=>e.getDuration()};const i=this._resolveDirectTimelineAdapterFromWindow(t);return i?{kind:"direct-timeline",timeline:i,getDuration:()=>i.duration()}:null}}const Ee=`
1
+ var ce=Object.defineProperty;var pe=(r,t,e)=>t in r?ce(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var c=(r,t,e)=>pe(r,typeof t!="symbol"?t+"":t,e);import{r as ne,i as me,a as fe}from"./index-DlZMDyYs.js";function _e(r){return r.hasRuntime||r.runtimeInjected?!1:!!(r.hasNestedCompositions||r.hasTimelines&&r.attempts>=5)}function F(r){return typeof r=="object"&&r!==null}function ge(r){return F(r)&&typeof r.getDuration=="function"}function ye(r){return F(r)&&typeof r.duration=="function"&&typeof r.time=="function"&&typeof r.seek=="function"&&typeof r.play=="function"&&typeof r.pause=="function"}function ve(r){if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r))throw new Error(`Invalid HyperFrames runtime version: ${r}`);return`https://cdn.jsdelivr.net/npm/@hyperframes/core@${r}/dist/hyperframe.runtime.iife.js`}const be=typeof __HYPERFRAMES_RUNTIME_CDN_URL__=="string"?__HYPERFRAMES_RUNTIME_CDN_URL__:ve("0.0.0-dev");function H(r){if(r===null)return null;const t=Number.parseInt(r,10);return Number.isFinite(t)&&t>0?t:null}function we(r){const t=(r==null?void 0:r.querySelector("[data-composition-id][data-width][data-height]"))??(r==null?void 0:r.querySelector("[data-width][data-height]"));if(!t)return null;const e=H(t.getAttribute("data-width")),i=H(t.getAttribute("data-height"));return e!==null&&i!==null?{width:e,height:i}:null}class Ae{constructor(t,e){c(this,"_interval",null);c(this,"_runtimeInjected",!1);this._iframe=t,this._callbacks=e}get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let t=0;this._interval=setInterval(()=>{var e;t++;try{const i=this._iframe.contentWindow;if(!i)return;const s=!!(i.__hf||i.__player),o=!!(i.__timelines&&Object.keys(i.__timelines).length>0),d=!!((e=this._iframe.contentDocument)!=null&&e.querySelector("[data-composition-src]"));if(_e({hasRuntime:s,hasTimelines:o,hasNestedCompositions:d,runtimeInjected:this._runtimeInjected,attempts:t})){this._injectRuntime();return}if(this._runtimeInjected&&!s)return;const a=this._resolvePlaybackDurationAdapter(i);if(a&&a.getDuration()>0){this.stop();const h=we(this._iframe.contentDocument);this._callbacks.onReady({duration:a.getDuration(),adapter:a,compositionSize:h});return}}catch{}t>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{const t=this._iframe.contentWindow;return t?this._resolveDirectTimelineAdapterFromWindow(t):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(t){return this._resolveDirectTimelineAdapterFromWindow(t)}hasRuntimeBridge(t){return Reflect.get(t,"__hf")!==void 0||F(Reflect.get(t,"__player"))}_injectRuntime(){var t,e;this._runtimeInjected=!0;try{const i=this._iframe.contentDocument;if(!i)return;const s=i.createElement("script");s.src=be,(i.head||i.documentElement).appendChild(s),(e=(t=this._callbacks).onRuntimeInjected)==null||e.call(t)}catch{}}_resolveDirectTimelineAdapterFromWindow(t){var a,h;if(this.hasRuntimeBridge(t))return null;const e=Reflect.get(t,"__timelines");if(!F(e))return null;const i=Object.keys(e);if(i.length===0)return null;const s=(h=(a=this._iframe.contentDocument)==null?void 0:a.querySelector("[data-composition-id]"))==null?void 0:h.getAttribute("data-composition-id"),o=s&&s in e?s:i[i.length-1],d=e[o];return ye(d)?d:null}_resolvePlaybackDurationAdapter(t){const e=Reflect.get(t,"__player");if(ge(e))return{kind:"runtime",getDuration:()=>e.getDuration()};const i=this._resolveDirectTimelineAdapterFromWindow(t);return i?{kind:"direct-timeline",timeline:i,getDuration:()=>i.duration()}:null}}const Ee=`
2
2
  :host {
3
3
  display: block;
4
4
  position: relative;
@@ -1,4 +1,4 @@
1
- import{n as Qi}from"./index-BSZrK0bx.js";/*!
1
+ import{n as Qi}from"./index-DlZMDyYs.js";/*!
2
2
  * Copyright (c) 2026-present, Vanilagy and contributors
3
3
  *
4
4
  * This Source Code Form is subject to the terms of the Mozilla Public