hyperframes 0.8.66 → 0.8.68

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.
Files changed (31) hide show
  1. package/dist/cli.js +1322 -667
  2. package/dist/fontLocalizeCli.js +407 -133
  3. package/dist/hyperframes-player.global.js +1 -1
  4. package/dist/renderSetupWorker.js +10625 -41829
  5. package/dist/skills/media-use/SKILL.md +10 -10
  6. package/dist/skills/media-use/references/meta.md +1 -1
  7. package/dist/skills/media-use/references/resolve.md +1 -1
  8. package/dist/skills/media-use/references/setup-providers.md +1 -1
  9. package/dist/skills/media-use/scripts/lib/coverage.test.mjs +5 -1
  10. package/dist/skills/media-use/scripts/lib/freeze.mjs +34 -24
  11. package/dist/skills/media-use/scripts/lib/freeze.test.mjs +93 -1
  12. package/dist/skills/media-use/scripts/lib/logo-provider.mjs +110 -89
  13. package/dist/skills/media-use/scripts/lib/logo-provider.test.mjs +204 -60
  14. package/dist/skills/media-use/scripts/lib/media-fetch.mjs +14 -0
  15. package/dist/skills/media-use/scripts/lib/media-fetch.test.mjs +29 -1
  16. package/dist/skills/media-use/scripts/lib/registry.mjs +6 -12
  17. package/dist/skills/media-use/scripts/lib/registry.test.mjs +1 -1
  18. package/dist/skills/media-use/scripts/lib/svg-sanitize.mjs +21 -0
  19. package/dist/studio/assets/{hyperframes-player-BRlwG-p9.js → hyperframes-player-RYzFlKye.js} +1 -1
  20. package/dist/studio/assets/index-BBOktYKC.css +1 -0
  21. package/dist/studio/assets/{index-D6Jy-2Zs.js → index-C8-wAsq3.js} +1 -1
  22. package/dist/studio/assets/{index-DJl2JKQj.js → index-CsnhFJIv.js} +1 -1
  23. package/dist/studio/assets/{index-BzGcmMBS.js → index-P0Rwat37.js} +200 -200
  24. package/dist/studio/index.css +147 -16
  25. package/dist/studio/index.css.map +1 -1
  26. package/dist/studio/index.html +2 -2
  27. package/dist/studio/index.js +4436 -4193
  28. package/dist/studio/index.js.map +1 -1
  29. package/dist/studio/styles.css +1 -1
  30. package/package.json +1 -1
  31. package/dist/studio/assets/index-BnsYeuxA.css +0 -1
@@ -12773,10 +12773,10 @@ function resolveSlideshow(manifest, scenes) {
12773
12773
  return { resolved: { slides, sequences }, errors };
12774
12774
  }
12775
12775
  function isSceneLikeCompositionId(compositionId) {
12776
- const normalized = compositionId.trim().toLowerCase();
12777
- if (!normalized || normalized === "main") return false;
12778
- if (normalized.includes("caption")) return false;
12779
- if (normalized.includes("ambient")) return false;
12776
+ const normalized2 = compositionId.trim().toLowerCase();
12777
+ if (!normalized2 || normalized2 === "main") return false;
12778
+ if (normalized2.includes("caption")) return false;
12779
+ if (normalized2.includes("ambient")) return false;
12780
12780
  return true;
12781
12781
  }
12782
12782
  var SLIDESHOW_ISLAND_TYPE;
@@ -31227,13 +31227,13 @@ var require_lib2 = __commonJS({
31227
31227
  DeclareClassFieldInitializer: "Initializers are not allowed in fields with the `declare` modifier.",
31228
31228
  DuplicateDeclareModuleExports: "Duplicate `declare module.exports` statement.",
31229
31229
  EnumBooleanMemberNotInitialized: ({
31230
- memberName: memberName2,
31230
+ memberName: memberName3,
31231
31231
  enumName
31232
- }) => `Boolean enum members need to be initialized. Use either \`${memberName2} = true,\` or \`${memberName2} = false,\` in enum \`${enumName}\`.`,
31232
+ }) => `Boolean enum members need to be initialized. Use either \`${memberName3} = true,\` or \`${memberName3} = false,\` in enum \`${enumName}\`.`,
31233
31233
  EnumDuplicateMemberName: ({
31234
- memberName: memberName2,
31234
+ memberName: memberName3,
31235
31235
  enumName
31236
- }) => `Enum member names need to be unique, but the name \`${memberName2}\` has already been used before in enum \`${enumName}\`.`,
31236
+ }) => `Enum member names need to be unique, but the name \`${memberName3}\` has already been used before in enum \`${enumName}\`.`,
31237
31237
  EnumInconsistentMemberValues: ({
31238
31238
  enumName
31239
31239
  }) => `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`,
@@ -31246,26 +31246,26 @@ var require_lib2 = __commonJS({
31246
31246
  }) => `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`,
31247
31247
  EnumInvalidMemberInitializerPrimaryType: ({
31248
31248
  enumName,
31249
- memberName: memberName2,
31249
+ memberName: memberName3,
31250
31250
  explicitType
31251
- }) => `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName2}\` needs to be a ${explicitType} literal.`,
31251
+ }) => `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName3}\` needs to be a ${explicitType} literal.`,
31252
31252
  EnumInvalidMemberInitializerSymbolType: ({
31253
31253
  enumName,
31254
- memberName: memberName2
31255
- }) => `Symbol enum members cannot be initialized. Use \`${memberName2},\` in enum \`${enumName}\`.`,
31254
+ memberName: memberName3
31255
+ }) => `Symbol enum members cannot be initialized. Use \`${memberName3},\` in enum \`${enumName}\`.`,
31256
31256
  EnumInvalidMemberInitializerUnknownType: ({
31257
31257
  enumName,
31258
- memberName: memberName2
31259
- }) => `The enum member initializer for \`${memberName2}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`,
31258
+ memberName: memberName3
31259
+ }) => `The enum member initializer for \`${memberName3}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`,
31260
31260
  EnumInvalidMemberName: ({
31261
31261
  enumName,
31262
- memberName: memberName2,
31262
+ memberName: memberName3,
31263
31263
  suggestion
31264
- }) => `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName2}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`,
31264
+ }) => `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName3}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`,
31265
31265
  EnumNumberMemberNotInitialized: ({
31266
31266
  enumName,
31267
- memberName: memberName2
31268
- }) => `Number enum members need to be initialized, e.g. \`${memberName2} = 1\` in enum \`${enumName}\`.`,
31267
+ memberName: memberName3
31268
+ }) => `Number enum members need to be initialized, e.g. \`${memberName3} = 1\` in enum \`${enumName}\`.`,
31269
31269
  EnumStringMemberInconsistentlyInitialized: ({
31270
31270
  enumName
31271
31271
  }) => `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`,
@@ -33262,10 +33262,10 @@ var require_lib2 = __commonJS({
33262
33262
  }
33263
33263
  flowEnumErrorBooleanMemberNotInitialized(loc, {
33264
33264
  enumName,
33265
- memberName: memberName2
33265
+ memberName: memberName3
33266
33266
  }) {
33267
33267
  this.raise(FlowErrors.EnumBooleanMemberNotInitialized, loc, {
33268
- memberName: memberName2,
33268
+ memberName: memberName3,
33269
33269
  enumName
33270
33270
  });
33271
33271
  }
@@ -33377,28 +33377,28 @@ var require_lib2 = __commonJS({
33377
33377
  id,
33378
33378
  init
33379
33379
  } = this.flowEnumMemberRaw();
33380
- const memberName2 = id.name;
33381
- if (memberName2 === "") {
33380
+ const memberName3 = id.name;
33381
+ if (memberName3 === "") {
33382
33382
  continue;
33383
33383
  }
33384
- if (/^[a-z]/.test(memberName2)) {
33384
+ if (/^[a-z]/.test(memberName3)) {
33385
33385
  this.raise(FlowErrors.EnumInvalidMemberName, id, {
33386
- memberName: memberName2,
33387
- suggestion: memberName2[0].toUpperCase() + memberName2.slice(1),
33386
+ memberName: memberName3,
33387
+ suggestion: memberName3[0].toUpperCase() + memberName3.slice(1),
33388
33388
  enumName
33389
33389
  });
33390
33390
  }
33391
- if (seenNames.has(memberName2)) {
33391
+ if (seenNames.has(memberName3)) {
33392
33392
  this.raise(FlowErrors.EnumDuplicateMemberName, id, {
33393
- memberName: memberName2,
33393
+ memberName: memberName3,
33394
33394
  enumName
33395
33395
  });
33396
33396
  }
33397
- seenNames.add(memberName2);
33397
+ seenNames.add(memberName3);
33398
33398
  const context = {
33399
33399
  enumName,
33400
33400
  explicitType,
33401
- memberName: memberName2
33401
+ memberName: memberName3
33402
33402
  };
33403
33403
  memberNode.id = id;
33404
33404
  switch (init.type) {
@@ -50287,7 +50287,7 @@ function resolveAliasDisplayName(alias) {
50287
50287
  if (!slug) return void 0;
50288
50288
  return CANONICAL_FONT_DISPLAY_NAMES[slug];
50289
50289
  }
50290
- var recast, import_parser2, CANVAS_DIMENSIONS, VALID_CANVAS_RESOLUTIONS, RESOLUTION_ALIASES, ASPECT_AGNOSTIC_RESOLUTION_ALIASES, PROPERTY_GROUPS, PROP_TO_GROUP, COMPOSITION_ATTRIBUTES, CANONICAL_AUTHORED_TIMING_ATTRIBUTES, DERIVED_TIMING_ATTRIBUTES, LEGACY_TIMING_ATTRIBUTES, OK, HTML_BODY_CSS_WIDTH_FIRST_RE, HTML_BODY_CSS_HEIGHT_FIRST_RE, VIEWPORT_META_SIZE_RE, FONT_ALIAS_MAP, FONT_ALIAS_KEYS, CANONICAL_FONT_DISPLAY_NAMES;
50290
+ var recast, import_parser2, CANVAS_DIMENSIONS, VALID_CANVAS_RESOLUTIONS, RESOLUTION_ALIASES, ASPECT_AGNOSTIC_RESOLUTION_ALIASES, PROPERTY_GROUPS, PROP_TO_GROUP, COMPOSITION_ATTRIBUTES, CANONICAL_AUTHORED_TIMING_ATTRIBUTES, DERIVED_TIMING_ATTRIBUTES, LEGACY_TIMING_ATTRIBUTES, OK, HTML_BODY_CSS_WIDTH_FIRST_RE, HTML_BODY_CSS_HEIGHT_FIRST_RE, FONT_ALIAS_MAP, FONT_ALIAS_KEYS, CANONICAL_FONT_DISPLAY_NAMES;
50291
50291
  var init_dist = __esm({
50292
50292
  "../parsers/dist/index.js"() {
50293
50293
  "use strict";
@@ -50367,7 +50367,6 @@ var init_dist = __esm({
50367
50367
  OK = { ok: true };
50368
50368
  HTML_BODY_CSS_WIDTH_FIRST_RE = /(html\s*,\s*body\s*\{[^}]*?width:\s*)(\d+)px([^}]*?height:\s*)(\d+)px/i;
50369
50369
  HTML_BODY_CSS_HEIGHT_FIRST_RE = /(html\s*,\s*body\s*\{[^}]*?height:\s*)(\d+)px([^}]*?width:\s*)(\d+)px/i;
50370
- VIEWPORT_META_SIZE_RE = /(<meta[^>]*name=["']viewport["'][^>]*content=["']width=)(\d+)(,\s*height=)(\d+)/i;
50371
50370
  FONT_ALIAS_MAP = {
50372
50371
  // ── Canonical bundled fonts (self-referencing) ────────────────────────
50373
50372
  inter: "inter",
@@ -50616,14 +50615,14 @@ function parseReferenceOffset(value) {
50616
50615
  return { refId, operator, magnitude };
50617
50616
  }
50618
50617
  function parseStartExpression(raw) {
50619
- const normalized = (raw ?? "").trim();
50620
- if (!normalized) return null;
50621
- const absolute = parseNumeric(normalized);
50618
+ const normalized2 = (raw ?? "").trim();
50619
+ if (!normalized2) return null;
50620
+ const absolute = parseNumeric(normalized2);
50622
50621
  if (absolute != null) return { kind: "absolute", value: absolute };
50623
- if (REFERENCE_ID_PATTERN.test(normalized)) {
50624
- return { kind: "reference", refId: normalized, offset: 0 };
50622
+ if (REFERENCE_ID_PATTERN.test(normalized2)) {
50623
+ return { kind: "reference", refId: normalized2, offset: 0 };
50625
50624
  }
50626
- const reference = parseReferenceOffset(normalized);
50625
+ const reference = parseReferenceOffset(normalized2);
50627
50626
  if (!reference) return null;
50628
50627
  return {
50629
50628
  kind: "reference",
@@ -52584,8 +52583,8 @@ function rewriteAssetPath(compSrcPath, relativePath, assetExists) {
52584
52583
  return assetExists(sibling2) ? sibling2 + suffix : relativePath;
52585
52584
  }
52586
52585
  const resolved = join(compDir, relativePath);
52587
- const normalized = resolve2("/", resolved).slice(1);
52588
- return normalized;
52586
+ const normalized2 = resolve2("/", resolved).slice(1);
52587
+ return normalized2;
52589
52588
  }
52590
52589
  function rewriteAssetPaths(elements, compSrcPath, getAttr2, setAttr2, assetExists) {
52591
52590
  for (const el of elements) {
@@ -55366,8 +55365,8 @@ function resolveExtractCacheDir(env = process.env) {
55366
55365
  if (raw === void 0) {
55367
55366
  return { dir: defaultExtractCacheDir(), disabled: false, source: "default" };
55368
55367
  }
55369
- const normalized = raw.trim().toLowerCase();
55370
- if (EXTRACT_CACHE_DIR_DISABLED_ALIASES.includes(normalized)) {
55368
+ const normalized2 = raw.trim().toLowerCase();
55369
+ if (EXTRACT_CACHE_DIR_DISABLED_ALIASES.includes(normalized2)) {
55371
55370
  return { dir: void 0, disabled: true, source: "env", rawValue: raw };
55372
55371
  }
55373
55372
  return { dir: raw, disabled: false, source: "env", rawValue: raw };
@@ -59354,8 +59353,8 @@ function candidateFileName(candidate) {
59354
59353
  return candidate.split(/[\\/]/).at(-1)?.toLowerCase() ?? candidate.toLowerCase();
59355
59354
  }
59356
59355
  function chooseBestPathCandidate(name, candidates) {
59357
- const normalized = candidates.map((candidate) => candidate.trim()).filter(Boolean);
59358
- return normalized.find((candidate) => candidateFileName(candidate) === `${name}.exe`) ?? normalized.find((candidate) => candidateFileName(candidate) === name) ?? normalized.find((candidate) => !candidateFileName(candidate).match(/\.(cmd|bat)$/i)) ?? normalized[0];
59356
+ const normalized2 = candidates.map((candidate) => candidate.trim()).filter(Boolean);
59357
+ return normalized2.find((candidate) => candidateFileName(candidate) === `${name}.exe`) ?? normalized2.find((candidate) => candidateFileName(candidate) === name) ?? normalized2.find((candidate) => !candidateFileName(candidate).match(/\.(cmd|bat)$/i)) ?? normalized2[0];
59359
59358
  }
59360
59359
  function isExecutablePathCandidate(candidate) {
59361
59360
  if (process.platform === "win32") return existsSync3(candidate);
@@ -59707,8 +59706,8 @@ function matchesAny(message, patterns) {
59707
59706
  return patterns.some((pattern) => pattern.test(message));
59708
59707
  }
59709
59708
  function hasIoOperationFailure(message) {
59710
- const normalized = message.toLowerCase();
59711
- return normalized.split(/[\r\n]/).some(
59709
+ const normalized2 = message.toLowerCase();
59710
+ return normalized2.split(/[\r\n]/).some(
59712
59711
  (line) => IO_OPERATION_TOKENS.some((operation) => {
59713
59712
  const operationIndex = line.indexOf(operation);
59714
59713
  if (operationIndex < 0) return false;
@@ -65300,15 +65299,15 @@ function looksLikeRemoteErrorDocument(prefix) {
65300
65299
  }
65301
65300
  function normalizeCallerSha256(value) {
65302
65301
  if (value === void 0) return void 0;
65303
- const normalized = value.trim().toLowerCase();
65304
- if (!/^[a-f0-9]{64}$/.test(normalized)) {
65302
+ const normalized2 = value.trim().toLowerCase();
65303
+ if (!/^[a-f0-9]{64}$/.test(normalized2)) {
65305
65304
  throw new UrlDownloadError(
65306
65305
  "hash_mismatch",
65307
65306
  false,
65308
65307
  "Caller-provided SHA-256 checksum is malformed"
65309
65308
  );
65310
65309
  }
65311
- return normalized;
65310
+ return normalized2;
65312
65311
  }
65313
65312
  function expectedResponseSha256(response, callerSha256) {
65314
65313
  if (callerSha256) return { value: callerSha256, encoding: "hex", source: "caller" };
@@ -65972,21 +65971,93 @@ function resolveAliasDisplayName2(alias) {
65972
65971
  return CANONICAL_FONT_DISPLAY_NAMES2[slug];
65973
65972
  }
65974
65973
  function isSafeMediaUrl(url) {
65975
- const normalized = url.replace(/[\u0000-\u0020]/g, "");
65976
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(normalized);
65974
+ const normalized2 = url.replace(/[\u0000-\u0020]/g, "");
65975
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(normalized2);
65977
65976
  if (!scheme) return true;
65978
65977
  const proto = scheme[1]?.toLowerCase();
65979
65978
  if (!proto) return false;
65980
65979
  if (proto === "https" || proto === "http" || proto === "blob") return true;
65981
- if (proto === "data") return /^data:image\//i.test(normalized);
65980
+ if (proto === "data") return /^data:image\//i.test(normalized2);
65982
65981
  return false;
65983
65982
  }
65984
- var CANVAS_DIMENSIONS2, VALID_CANVAS_RESOLUTIONS2, COMPOSITION_VARIABLE_TYPES2, FONT_ALIAS_MAP2, FONT_ALIAS_KEYS2, CANONICAL_FONT_DISPLAY_NAMES2, COMPOSITION_ATTRIBUTES4, CANONICAL_AUTHORED_TIMING_ATTRIBUTES4, DERIVED_TIMING_ATTRIBUTES4, LEGACY_TIMING_ATTRIBUTES4;
65983
+ function parseProgram(script) {
65984
+ try {
65985
+ return parse7(script, { ecmaVersion: "latest", sourceType: "script" });
65986
+ } catch {
65987
+ return parse7(script, { ecmaVersion: "latest", sourceType: "module" });
65988
+ }
65989
+ }
65990
+ function literalString2(node) {
65991
+ if (node?.type === "Literal" && typeof node.value === "string") return node.value;
65992
+ if (node?.type === "TemplateLiteral" && node.expressions?.length === 0) {
65993
+ return node.quasis?.[0]?.value?.cooked;
65994
+ }
65995
+ return void 0;
65996
+ }
65997
+ function memberName2(node) {
65998
+ if (node?.type !== "MemberExpression") return void 0;
65999
+ if (!node.computed && node.property?.type === "Identifier") return node.property.name;
66000
+ return literalString2(node.property);
66001
+ }
66002
+ function lookupSelector2(node, bindings) {
66003
+ if (node?.type === "Identifier") return bindings.get(node.name);
66004
+ if (node?.type !== "CallExpression" || node.callee?.type !== "MemberExpression") {
66005
+ return void 0;
66006
+ }
66007
+ const method = memberName2(node.callee);
66008
+ if (method !== "getElementById" && method !== "querySelector") return void 0;
66009
+ const value = literalString2(node.arguments?.[0]);
66010
+ if (!value) return void 0;
66011
+ return method === "getElementById" ? `#${value}` : value;
66012
+ }
66013
+ function extractMediaSrcMutations2(script) {
66014
+ try {
66015
+ const ast = parseProgram(script);
66016
+ const bindings = /* @__PURE__ */ new Map();
66017
+ simple(ast, {
66018
+ VariableDeclarator(node) {
66019
+ if (node.id?.type !== "Identifier") return;
66020
+ const selector = lookupSelector2(node.init, bindings);
66021
+ if (selector) bindings.set(node.id.name, selector);
66022
+ }
66023
+ });
66024
+ const mutations = [];
66025
+ simple(ast, {
66026
+ AssignmentExpression(node) {
66027
+ if (node.operator !== "=" || memberName2(node.left) !== "src") return;
66028
+ const selector = lookupSelector2(node.left.object, bindings);
66029
+ if (!selector) return;
66030
+ mutations.push({
66031
+ selector,
66032
+ operation: "src_assignment",
66033
+ raw: script.slice(node.start, node.end)
66034
+ });
66035
+ },
66036
+ CallExpression(node) {
66037
+ if (memberName2(node.callee) !== "setAttribute") return;
66038
+ if (literalString2(node.arguments?.[0])?.toLowerCase() !== "src") return;
66039
+ const selector = lookupSelector2(node.callee.object, bindings);
66040
+ if (!selector) return;
66041
+ mutations.push({
66042
+ selector,
66043
+ operation: "set_attribute",
66044
+ raw: script.slice(node.start, node.end)
66045
+ });
66046
+ }
66047
+ });
66048
+ return mutations;
66049
+ } catch {
66050
+ return [];
66051
+ }
66052
+ }
66053
+ var CANVAS_DIMENSIONS2, VALID_CANVAS_RESOLUTIONS2, COMPOSITION_VARIABLE_TYPES2, FONT_ALIAS_MAP2, FONT_ALIAS_KEYS2, CANONICAL_FONT_DISPLAY_NAMES2, COMPOSITION_ATTRIBUTES4, CANONICAL_AUTHORED_TIMING_ATTRIBUTES4, DERIVED_TIMING_ATTRIBUTES4, LEGACY_TIMING_ATTRIBUTES4, HTML_BODY_CSS_WIDTH_FIRST_RE2, HTML_BODY_CSS_HEIGHT_FIRST_RE2, VIEWPORT_META_SIZE_RE;
65985
66054
  var init_composition = __esm({
65986
66055
  "../parsers/dist/composition.js"() {
65987
66056
  "use strict";
65988
66057
  init_acorn();
65989
66058
  init_walk();
66059
+ init_acorn();
66060
+ init_walk();
65990
66061
  CANVAS_DIMENSIONS2 = {
65991
66062
  landscape: { width: 1920, height: 1080 },
65992
66063
  portrait: { width: 1080, height: 1920 },
@@ -66125,6 +66196,9 @@ var init_composition = __esm({
66125
66196
  COMPOSITION_ATTRIBUTES4.derivedEnd,
66126
66197
  COMPOSITION_ATTRIBUTES4.legacyTrack
66127
66198
  ]);
66199
+ HTML_BODY_CSS_WIDTH_FIRST_RE2 = /(html\s*,\s*body\s*\{[^}]*?width:\s*)(\d+)px([^}]*?height:\s*)(\d+)px/i;
66200
+ HTML_BODY_CSS_HEIGHT_FIRST_RE2 = /(html\s*,\s*body\s*\{[^}]*?height:\s*)(\d+)px([^}]*?width:\s*)(\d+)px/i;
66201
+ VIEWPORT_META_SIZE_RE = /(<meta[^>]*name=["']viewport["'][^>]*content=["']width=)(\d+)(,\s*height=)(\d+)/i;
66128
66202
  }
66129
66203
  });
66130
66204
 
@@ -70930,7 +71004,7 @@ function getObjectArrayKeyframeTiming(durations) {
70930
71004
  )
70931
71005
  };
70932
71006
  }
70933
- function parseProgram(script) {
71007
+ function parseProgram3(script) {
70934
71008
  try {
70935
71009
  return parse7(script, {
70936
71010
  ecmaVersion: "latest",
@@ -72087,7 +72161,7 @@ function parseGsapScriptAcornForWrite(script) {
72087
72161
  }
72088
72162
  function parseGsapScriptAcorn(script) {
72089
72163
  try {
72090
- const ast = parseProgram(script);
72164
+ const ast = parseProgram3(script);
72091
72165
  const scope = collectScopeBindings(ast);
72092
72166
  const detection = findTimelineVar(ast, scope);
72093
72167
  const ref2 = detection.ref ?? { kind: "identifier", name: "tl" };
@@ -72135,7 +72209,7 @@ function parseGsapScriptAcorn(script) {
72135
72209
  }
72136
72210
  function gsapScriptMotionPathFirstUseIndex(script) {
72137
72211
  try {
72138
- const ast = parseProgram(script);
72212
+ const ast = parseProgram3(script);
72139
72213
  const scope = collectScopeBindings(ast);
72140
72214
  const identifierBindings = collectIdentifierBindingIndex(ast);
72141
72215
  const timelineRef = findTimelineVar(ast, scope).ref;
@@ -72319,6 +72393,7 @@ var init_gsapParserAcorn = __esm({
72319
72393
  // ../lint/dist/index.js
72320
72394
  import postcss2 from "postcss";
72321
72395
  import postcss22 from "postcss";
72396
+ import postcss3 from "postcss";
72322
72397
  function parseHtmlStructure(source) {
72323
72398
  const tags = [];
72324
72399
  const blocks = { script: [], style: [] };
@@ -72363,6 +72438,7 @@ function parseHtmlStructure(source) {
72363
72438
  blocks[name].push({
72364
72439
  attrs: block.attrs,
72365
72440
  content: source.slice(block.contentStart, parser.startIndex),
72441
+ contentStart: block.contentStart,
72366
72442
  raw: source.slice(block.index, parser.endIndex + 1),
72367
72443
  index: block.index
72368
72444
  });
@@ -72512,11 +72588,17 @@ function readTimelineRegistryTopLevelKeys(source) {
72512
72588
  function getInlineScriptSyntaxError(source) {
72513
72589
  if (!source.trim()) return null;
72514
72590
  try {
72515
- new Function(source);
72591
+ parse7(source, {
72592
+ ecmaVersion: "latest",
72593
+ sourceType: "script",
72594
+ allowReturnOutsideFunction: true
72595
+ });
72516
72596
  return null;
72517
72597
  } catch (error) {
72518
- if (error instanceof Error) return error.message;
72519
- return String(error);
72598
+ return {
72599
+ message: error instanceof Error ? error.message : String(error),
72600
+ offset: error instanceof SyntaxError && "pos" in error && typeof error.pos === "number" ? error.pos : void 0
72601
+ };
72520
72602
  }
72521
72603
  }
72522
72604
  function stripStringLiterals(source) {
@@ -72788,7 +72870,7 @@ function stripCssComments(source) {
72788
72870
  }
72789
72871
  return out;
72790
72872
  }
72791
- function stripHtmlCommentsOnce(source) {
72873
+ function stripHtmlCommentsOnce(source, removed) {
72792
72874
  let out = "";
72793
72875
  let i = 0;
72794
72876
  for (; ; ) {
@@ -72797,14 +72879,17 @@ function stripHtmlCommentsOnce(source) {
72797
72879
  const end = source.indexOf("-->", start + 4);
72798
72880
  if (end < 0) return out + source.slice(i);
72799
72881
  out += source.slice(i, start);
72882
+ removed?.(start, end + 3);
72800
72883
  i = end + 3;
72801
72884
  }
72802
72885
  }
72803
- function stripHtmlComments(source) {
72886
+ function stripHtmlComments(source, pass) {
72804
72887
  let out = source;
72805
72888
  for (let prev = ""; prev !== out; ) {
72806
72889
  prev = out;
72807
- out = stripHtmlCommentsOnce(out);
72890
+ const ranges = [];
72891
+ out = stripHtmlCommentsOnce(out, pass ? (start, end) => ranges.push([start, end]) : void 0);
72892
+ if (ranges.length) pass?.(ranges);
72808
72893
  }
72809
72894
  return out;
72810
72895
  }
@@ -72820,14 +72905,94 @@ function hasCaptionStyles(styles) {
72820
72905
  return styles.some((s) => /\.caption[-_]?(?:group|word)/i.test(s.content));
72821
72906
  }
72822
72907
  function truncateSnippet(value, maxLength = 220) {
72823
- const normalized = value.replace(/\s+/g, " ").trim();
72824
- if (!normalized) return void 0;
72825
- if (normalized.length <= maxLength) return normalized;
72826
- return `${normalized.slice(0, maxLength - 3)}...`;
72908
+ const normalized2 = value.replace(/\s+/g, " ").trim();
72909
+ if (!normalized2) return void 0;
72910
+ if (normalized2.length <= maxLength) return normalized2;
72911
+ return `${normalized2.slice(0, maxLength - 3)}...`;
72912
+ }
72913
+ function normalized(source) {
72914
+ let text = "";
72915
+ const offsets = [];
72916
+ for (let i = 0; i < source.length; i++) {
72917
+ const char = source[i];
72918
+ if (/\s/.test(char)) {
72919
+ if (text.endsWith(" ")) continue;
72920
+ text += " ";
72921
+ } else text += char;
72922
+ offsets.push(i);
72923
+ }
72924
+ return { text, offsets };
72925
+ }
72926
+ function createSourceLocator(source) {
72927
+ const tags = parseHtmlStructure(source).tags;
72928
+ const searchable = source.split("");
72929
+ const parser = new Parser({
72930
+ oncomment() {
72931
+ searchable.fill("\0", parser.startIndex, parser.endIndex + 1);
72932
+ }
72933
+ });
72934
+ parser.write(source);
72935
+ parser.end();
72936
+ const normalizedSource = normalized(searchable.join(""));
72937
+ const at2 = (offset3) => {
72938
+ const before2 = source.slice(0, offset3);
72939
+ return {
72940
+ line: before2.split(/\r\n|\r|\n/).length,
72941
+ column: offset3 - Math.max(before2.lastIndexOf("\n"), before2.lastIndexOf("\r"))
72942
+ };
72943
+ };
72944
+ return (finding) => {
72945
+ if (finding.line !== void 0) return { line: finding.line, column: finding.column };
72946
+ if (finding.snippet) {
72947
+ const offset3 = snippetOffset(normalizedSource, finding.snippet);
72948
+ return offset3 === void 0 ? {} : at2(offset3);
72949
+ }
72950
+ const id = finding.elementId ?? /^#([\w-]+)$/.exec(finding.selector ?? "")?.[1];
72951
+ if (!id) return {};
72952
+ const matches2 = tags.filter((tag) => readDecodedAttr(tag.raw, "id") === id);
72953
+ return matches2.length === 1 ? at2(matches2[0].index) : {};
72954
+ };
72955
+ }
72956
+ function snippetOffset(source, snippet) {
72957
+ const needle = snippet.replace(/\s+/g, " ").trim().replace(/\.\.\.$/, "");
72958
+ if (!needle) return void 0;
72959
+ const index = source.text.indexOf(needle);
72960
+ if (index < 0 || source.text.indexOf(needle, index + 1) >= 0) return void 0;
72961
+ return source.offsets[index];
72962
+ }
72963
+ function mappedHtmlSource(original) {
72964
+ let offsets;
72965
+ const source = stripHtmlComments(original, (ranges) => {
72966
+ const previous = offsets;
72967
+ const length = (previous?.length ?? original.length + 1) - ranges.reduce((n, [a, b]) => n + b - a, 0);
72968
+ offsets = new Uint32Array(length);
72969
+ let target = 0;
72970
+ let start = 0;
72971
+ const spans = [
72972
+ ...ranges,
72973
+ [previous ? previous.length : original.length + 1, 0]
72974
+ ];
72975
+ for (const [a, b] of spans) {
72976
+ for (let i = start; i < a; i++) offsets[target++] = previous ? previous[i] : i;
72977
+ start = b;
72978
+ }
72979
+ });
72980
+ return { source, originalOffset: (index) => offsets ? offsets[index] : index };
72981
+ }
72982
+ function sourcePosition(source, offset3, file) {
72983
+ if (!Number.isInteger(offset3) || offset3 < 0 || offset3 > source.length) return {};
72984
+ const before2 = source.slice(0, offset3);
72985
+ return {
72986
+ ...file ? { file } : {},
72987
+ line: before2.split(/\r\n|\r|\n/).length,
72988
+ column: offset3 - Math.max(before2.lastIndexOf("\n"), before2.lastIndexOf("\r"))
72989
+ };
72827
72990
  }
72828
72991
  function buildLintContext(html, options = {}) {
72829
72992
  const rawSource = html || "";
72830
- let source = stripHtmlComments(rawSource);
72993
+ const mapped = mappedHtmlSource(rawSource);
72994
+ let source = mapped.source;
72995
+ let sourceStart = 0;
72831
72996
  const initialStructure = parseHtmlStructure(source);
72832
72997
  const templateTags = initialStructure.tags.filter(
72833
72998
  (tag) => tag.name === "template" && tag.closeIndex != null
@@ -72840,7 +73005,8 @@ function buildLintContext(html, options = {}) {
72840
73005
  const template = templateTags[0];
72841
73006
  let structure = initialStructure;
72842
73007
  if (template && !findRootTag(sourceWithoutTemplates)) {
72843
- source = source.slice(template.index + template.raw.length, template.closeIndex);
73008
+ sourceStart = template.index + template.raw.length;
73009
+ source = source.slice(sourceStart, template.closeIndex);
72844
73010
  structure = parseHtmlStructure(source);
72845
73011
  }
72846
73012
  const tags = structure.tags;
@@ -72850,14 +73016,23 @@ function buildLintContext(html, options = {}) {
72850
73016
  attrs: `href="${style.href}"`,
72851
73017
  content: style.content,
72852
73018
  raw: style.content,
72853
- index: -1
73019
+ index: -1,
73020
+ file: style.file ?? style.href
72854
73021
  }))
72855
73022
  ];
72856
73023
  const scripts = structure.scripts;
72857
73024
  const compositionIds = collectCompositionIds(tags);
72858
73025
  const rootTag = findRootTag(source, tags);
72859
73026
  const rootCompositionId = readDecodedAttr(rootTag?.raw || "", "data-composition-id");
73027
+ const locate = (block, offset3) => {
73028
+ if (offset3 === void 0 || offset3 < 0 || offset3 > block.content.length) return {};
73029
+ if (block.index === -1) return sourcePosition(block.content, offset3, block.file);
73030
+ if (block.contentStart === void 0) return {};
73031
+ const originalOffset = mapped.originalOffset(sourceStart + block.contentStart + offset3);
73032
+ return originalOffset === void 0 ? {} : sourcePosition(rawSource, originalOffset, options.filePath);
73033
+ };
72860
73034
  return {
73035
+ locate,
72861
73036
  source,
72862
73037
  rawSource,
72863
73038
  tags,
@@ -73040,12 +73215,12 @@ function findVisibleMarkupCommentLeak(source) {
73040
73215
  return null;
73041
73216
  }
73042
73217
  function readHtmlBodyCssSize(source) {
73043
- const widthFirst = source.match(HTML_BODY_CSS_WIDTH_FIRST_RE);
73218
+ const widthFirst = source.match(HTML_BODY_CSS_WIDTH_FIRST_RE2);
73044
73219
  if (widthFirst) {
73045
73220
  const [, , width = "", , height = ""] = widthFirst;
73046
73221
  return { width, height };
73047
73222
  }
73048
- const heightFirst = source.match(HTML_BODY_CSS_HEIGHT_FIRST_RE);
73223
+ const heightFirst = source.match(HTML_BODY_CSS_HEIGHT_FIRST_RE2);
73049
73224
  if (heightFirst) {
73050
73225
  const [, , height = "", , width = ""] = heightFirst;
73051
73226
  return { width, height };
@@ -73089,6 +73264,20 @@ function describeRootDimensionsDrift(source, dataWidth, dataHeight) {
73089
73264
  fixHint: "update html/body CSS and the meta viewport to match, or scaffold with `hyperframes init --resolution portrait`"
73090
73265
  };
73091
73266
  }
73267
+ function cssErrorOffset(source, error) {
73268
+ if (error.input && "offset" in error.input && typeof error.input.offset === "number")
73269
+ return error.input.offset;
73270
+ const { line, column } = error;
73271
+ if (line === void 0 || column === void 0) return void 0;
73272
+ let offset3 = 0;
73273
+ let currentLine = 1;
73274
+ for (const match of source.matchAll(/\n/g)) {
73275
+ if (currentLine >= line) break;
73276
+ offset3 = match.index + 1;
73277
+ currentLine++;
73278
+ }
73279
+ return currentLine === line ? offset3 + column - 1 : void 0;
73280
+ }
73092
73281
  function tweensVolumeInSameCall(script, id) {
73093
73282
  const selector = new RegExp(`#${escapeRegExp3(id)}(?![\\w-])`, "g");
73094
73283
  for (let hit = selector.exec(script); hit; hit = selector.exec(script)) {
@@ -73203,18 +73392,18 @@ function classNamesFromAttr(classAttr) {
73203
73392
  return classAttr.split(/\s+/).filter(Boolean);
73204
73393
  }
73205
73394
  function selectorTargetsManagedMedia(selector, mediaIndex) {
73206
- const normalized = selector.trim();
73207
- if (!normalized) return false;
73208
- if (mediaIndex.hasVideo && /\bvideo\b/i.test(normalized)) return true;
73209
- if (mediaIndex.hasAudio && /\baudio\b/i.test(normalized)) return true;
73395
+ const normalized2 = selector.trim();
73396
+ if (!normalized2) return false;
73397
+ if (mediaIndex.hasVideo && /\bvideo\b/i.test(normalized2)) return true;
73398
+ if (mediaIndex.hasAudio && /\baudio\b/i.test(normalized2)) return true;
73210
73399
  for (const mediaId of mediaIndex.ids) {
73211
73400
  const escapedId = escapeRegExp3(mediaId);
73212
- if (new RegExp(`#${escapedId}(?![\\w-])`).test(normalized) || normalized.includes(`[id="${mediaId}"]`) || normalized.includes(`[id='${mediaId}']`)) {
73401
+ if (new RegExp(`#${escapedId}(?![\\w-])`).test(normalized2) || normalized2.includes(`[id="${mediaId}"]`) || normalized2.includes(`[id='${mediaId}']`)) {
73213
73402
  return true;
73214
73403
  }
73215
73404
  }
73216
73405
  for (const className of mediaIndex.classes) {
73217
- if (new RegExp(`\\.${escapeRegExp3(className)}(?![\\w-])`).test(normalized)) {
73406
+ if (new RegExp(`\\.${escapeRegExp3(className)}(?![\\w-])`).test(normalized2)) {
73218
73407
  return true;
73219
73408
  }
73220
73409
  }
@@ -73348,7 +73537,7 @@ function findRuntimeMediaSrcMutationFindings(ctx) {
73348
73537
  const { document: document2 } = parseHTML(ctx.source);
73349
73538
  const findings = [];
73350
73539
  for (const script of ctx.scripts) {
73351
- for (const mutation of extractMediaSrcMutations(script.content)) {
73540
+ for (const mutation of extractMediaSrcMutations2(script.content)) {
73352
73541
  let targets;
73353
73542
  try {
73354
73543
  const id = /^#[A-Za-z_][\w-]*$/.test(mutation.selector) ? mutation.selector.slice(1) : null;
@@ -73527,13 +73716,47 @@ function findAudioGroupCarveAttrFindings(ctx) {
73527
73716
  }
73528
73717
  return findings;
73529
73718
  }
73719
+ function callsFor(script) {
73720
+ const cached2 = callIndexes.get(script);
73721
+ if (cached2) return cached2;
73722
+ const calls = [];
73723
+ try {
73724
+ const ast = parse7(script.content, {
73725
+ ecmaVersion: "latest",
73726
+ sourceType: "script",
73727
+ allowReturnOutsideFunction: true
73728
+ });
73729
+ simple(ast, {
73730
+ CallExpression: (node) => {
73731
+ calls.push(node);
73732
+ }
73733
+ });
73734
+ } catch {
73735
+ }
73736
+ calls.sort((a, b) => a.start - b.start);
73737
+ callIndexes.set(script, calls);
73738
+ return calls;
73739
+ }
73740
+ function gsapCallOffset(script, method) {
73741
+ return callsFor(script).find(
73742
+ ({ callee }) => callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "gsap" && callee.property.type === "Identifier" && callee.property.name === method
73743
+ )?.start;
73744
+ }
73745
+ function containingCallOffset(script, tokenOffset) {
73746
+ let start;
73747
+ for (const call of callsFor(script)) {
73748
+ if (call.start > tokenOffset) break;
73749
+ if (call.end > tokenOffset) start = call.start;
73750
+ }
73751
+ return start;
73752
+ }
73530
73753
  function readTagTiming(rawTag) {
73531
73754
  return readClipTiming({ getAttribute: (name) => readAttr(rawTag, name) });
73532
73755
  }
73533
73756
  function countPhysicalLines(source) {
73534
73757
  if (source.length === 0) return 0;
73535
- const normalized = source.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
73536
- const withoutFinalNewline = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
73758
+ const normalized2 = source.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
73759
+ const withoutFinalNewline = normalized2.endsWith("\n") ? normalized2.slice(0, -1) : normalized2;
73537
73760
  return withoutFinalNewline.split("\n").length;
73538
73761
  }
73539
73762
  function countStructuralLines(source) {
@@ -73546,8 +73769,8 @@ function isCaptionCue(tag) {
73546
73769
  }
73547
73770
  function isRegistrySourceFile(filePath) {
73548
73771
  if (!filePath) return false;
73549
- const normalized = filePath.replace(/\\/g, "/");
73550
- return /(?:^|\/)registry\/blocks\/([^/]+)\/\1\.html$/i.test(normalized);
73772
+ const normalized2 = filePath.replace(/\\/g, "/");
73773
+ return /(?:^|\/)registry\/blocks\/([^/]+)\/\1\.html$/i.test(normalized2);
73551
73774
  }
73552
73775
  function isRegistryInstalledFile(rawSource) {
73553
73776
  return /^\s*<!--\s*hyperframes-registry-item:[^>]*-->/i.test(rawSource.slice(0, 512));
@@ -73780,8 +74003,8 @@ function zeroValue(value) {
73780
74003
  return Number(value.trim()) === 0;
73781
74004
  }
73782
74005
  function isGsapColorProperty(property) {
73783
- const normalized = property.toLowerCase();
73784
- return !normalized.startsWith("--") && (normalized.endsWith("color") || normalized === "fill" || normalized === "stroke");
74006
+ const normalized2 = property.toLowerCase();
74007
+ return !normalized2.startsWith("--") && (normalized2.endsWith("color") || normalized2 === "fill" || normalized2 === "stroke");
73785
74008
  }
73786
74009
  function collectStaticCssVariableDefinitions(tags, styles) {
73787
74010
  const definitions = /* @__PURE__ */ new Set();
@@ -73908,10 +74131,10 @@ function styleHasHiddenInitialState(style) {
73908
74131
  function styleHasOpaqueBackground(style) {
73909
74132
  const background = readStyleProperty(style, "background") || readStyleProperty(style, "background-color");
73910
74133
  if (!background) return false;
73911
- const normalized = background.toLowerCase().replace(/\s+/g, "");
73912
- if (normalized === "transparent" || normalized === "none") return false;
73913
- if (/rgba?\([^)]*,0(?:\.0+)?\)$/.test(normalized)) return false;
73914
- if (/hsla?\([^)]*,0(?:\.0+)?\)$/.test(normalized)) return false;
74134
+ const normalized2 = background.toLowerCase().replace(/\s+/g, "");
74135
+ if (normalized2 === "transparent" || normalized2 === "none") return false;
74136
+ if (/rgba?\([^)]*,0(?:\.0+)?\)$/.test(normalized2)) return false;
74137
+ if (/hsla?\([^)]*,0(?:\.0+)?\)$/.test(normalized2)) return false;
73915
74138
  return true;
73916
74139
  }
73917
74140
  function styleLooksFullFrameOverlay(style) {
@@ -74024,7 +74247,7 @@ function scanScriptsForRegexMatches(scripts, pattern, options) {
74024
74247
  content.length,
74025
74248
  match.index + match[0].length + options.contextAfter
74026
74249
  );
74027
- hits.push({ match, snippet: content.slice(contextStart, contextEnd) });
74250
+ hits.push({ match, snippet: content.slice(contextStart, contextEnd), script });
74028
74251
  }
74029
74252
  }
74030
74253
  return hits;
@@ -74279,9 +74502,9 @@ function elementLevelTokens(tokens, tagsByToken) {
74279
74502
  return expanded;
74280
74503
  }
74281
74504
  function isMultiComponentDasharray(value) {
74282
- const normalized = value.replace(/!important\s*$/i, "").trim();
74283
- if (!normalized || /^none$/i.test(normalized)) return false;
74284
- return normalized.split(/[\s,]+/).filter(Boolean).length >= 2;
74505
+ const normalized2 = value.replace(/!important\s*$/i, "").trim();
74506
+ if (!normalized2 || /^none$/i.test(normalized2)) return false;
74507
+ return normalized2.split(/[\s,]+/).filter(Boolean).length >= 2;
74285
74508
  }
74286
74509
  function gsapDasharrayValueLooksMultiComponent(valueSource) {
74287
74510
  const literal2 = valueSource.trim().match(/^(["'`])([\s\S]*)\1$/)?.[2];
@@ -74531,6 +74754,26 @@ function collectGoogleFontFamilies(source, styles) {
74531
74754
  }
74532
74755
  return families;
74533
74756
  }
74757
+ function uniqueFontLocation(styles, families, locate) {
74758
+ const locations = [];
74759
+ for (const style of styles) {
74760
+ try {
74761
+ postcss3.parse(style.content).walkDecls(/^font-family$/i, (decl) => {
74762
+ let parent = decl.parent;
74763
+ while (parent) {
74764
+ if (parent.type === "atrule" && parent.name.toLowerCase() === "font-face") return;
74765
+ parent = parent.parent;
74766
+ }
74767
+ if (!decl.value.split(",").some((part) => families.includes(normalizeUsedFontName(part) ?? "")))
74768
+ return;
74769
+ locations.push(locate(style, decl.source?.start?.offset));
74770
+ });
74771
+ } catch {
74772
+ return {};
74773
+ }
74774
+ }
74775
+ return locations.length === 1 ? locations[0] : {};
74776
+ }
74534
74777
  function parseTiming(raw) {
74535
74778
  const startStr = readAttr(raw, "data-start");
74536
74779
  if (startStr === null) return null;
@@ -74684,13 +74927,27 @@ function collectFindings(produced, seen, filePath, into) {
74684
74927
  const dedupeKey = dedupeKeyFor(finding);
74685
74928
  if (seen.has(dedupeKey)) continue;
74686
74929
  seen.add(dedupeKey);
74687
- into.push(filePath ? { ...finding, file: filePath } : finding);
74930
+ into.push(filePath ? { ...finding, file: finding.file ?? filePath } : finding);
74688
74931
  }
74689
74932
  }
74690
74933
  async function lintHyperframeHtml(html, options = {}) {
74691
74934
  const startedAt = performance.now();
74692
74935
  const ctx = buildLintContext(html, options);
74693
- const { findings, timings } = await runRules(ctx, options.filePath);
74936
+ const { findings: rawFindings, timings } = await runRules(ctx, options.filePath);
74937
+ const locate = createSourceLocator(html);
74938
+ const findings = rawFindings.map((finding) => {
74939
+ if (finding.line !== void 0 || finding.code === "invalid_inline_script_syntax" || finding.code === "non_deterministic_code" || [
74940
+ "gsap_infinite_repeat",
74941
+ "gsap_repeat_ceil_overshoot",
74942
+ "gsap_repeat_floor_unclamped"
74943
+ ].includes(finding.code))
74944
+ return finding;
74945
+ if (finding.snippet && options.externalStyles?.some(
74946
+ (style) => style.content.replace(/\s+/g, " ").includes(finding.snippet.replace(/\.\.\.$/, ""))
74947
+ ))
74948
+ return finding;
74949
+ return { ...finding, ...locate(finding) };
74950
+ });
74694
74951
  const errorCount = findings.filter((f) => f.severity === "error").length;
74695
74952
  const warningCount = findings.filter((f) => f.severity === "warning").length;
74696
74953
  const infoCount = findings.filter((f) => f.severity === "info").length;
@@ -74703,16 +74960,20 @@ async function lintHyperframeHtml(html, options = {}) {
74703
74960
  timings: { totalMs: performance.now() - startedAt, ...timings }
74704
74961
  };
74705
74962
  }
74706
- var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, CLIP_MEDIA_TAGS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, INLINE_TEXT_TAGS, OPAQUE_TAGS, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
74963
+ var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, callIndexes, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, CLIP_MEDIA_TAGS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, INLINE_TEXT_TAGS, OPAQUE_TAGS, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
74707
74964
  var init_dist3 = __esm({
74708
74965
  "../lint/dist/index.js"() {
74709
74966
  "use strict";
74710
74967
  init_esm6();
74968
+ init_esm6();
74969
+ init_acorn();
74711
74970
  import_postcss_selector_parser = __toESM(require_dist(), 1);
74712
- init_dist();
74971
+ init_composition();
74713
74972
  init_colorGradingContract();
74714
- init_dist();
74973
+ init_composition();
74715
74974
  init_esm10();
74975
+ init_acorn();
74976
+ init_walk();
74716
74977
  init_composition();
74717
74978
  init_compositionContract();
74718
74979
  init_compositionDuration();
@@ -74935,7 +75196,7 @@ var init_dist3 = __esm({
74935
75196
  return findings;
74936
75197
  },
74937
75198
  // CSS selector safety
74938
- ({ styles }) => {
75199
+ ({ styles, locate }) => {
74939
75200
  const findings = [];
74940
75201
  const reportedRepeatedIds = /* @__PURE__ */ new Set();
74941
75202
  const reportedHiddenStyleSelectors = /* @__PURE__ */ new Set();
@@ -74945,6 +75206,10 @@ var init_dist3 = __esm({
74945
75206
  root = postcss2.parse(style.content);
74946
75207
  } catch (error) {
74947
75208
  findings.push({
75209
+ ...locate(
75210
+ style,
75211
+ error instanceof postcss2.CssSyntaxError ? cssErrorOffset(style.content, error) : void 0
75212
+ ),
74948
75213
  code: "css_parse_error",
74949
75214
  severity: "error",
74950
75215
  message: `CSS parse error: ${error instanceof Error ? error.message : "unknown"}`
@@ -75010,7 +75275,7 @@ var init_dist3 = __esm({
75010
75275
  ];
75011
75276
  },
75012
75277
  // invalid_inline_script_syntax (JS parse error)
75013
- ({ scripts }) => {
75278
+ ({ scripts, locate }) => {
75014
75279
  const findings = [];
75015
75280
  for (const script of scripts) {
75016
75281
  const attrs = script.attrs || "";
@@ -75021,9 +75286,10 @@ var init_dist3 = __esm({
75021
75286
  const syntaxError = getInlineScriptSyntaxError(script.content);
75022
75287
  if (!syntaxError) continue;
75023
75288
  findings.push({
75289
+ ...locate(script, syntaxError.offset),
75024
75290
  code: "invalid_inline_script_syntax",
75025
75291
  severity: "error",
75026
- message: `Inline script has invalid syntax: ${syntaxError}`,
75292
+ message: `Inline script has invalid syntax: ${syntaxError.message}`,
75027
75293
  fixHint: "Fix the inline script syntax before render verification.",
75028
75294
  snippet: truncateSnippet(script.content)
75029
75295
  });
@@ -75089,7 +75355,7 @@ var init_dist3 = __esm({
75089
75355
  return findings;
75090
75356
  },
75091
75357
  // non_deterministic_code
75092
- ({ scripts }) => {
75358
+ ({ scripts, locate }) => {
75093
75359
  const findings = [];
75094
75360
  const patterns = [
75095
75361
  {
@@ -75140,8 +75406,10 @@ var init_dist3 = __esm({
75140
75406
  const withoutComments = stripJsComments(script.content);
75141
75407
  const executable = stripStringLiterals(withoutComments);
75142
75408
  for (const { pattern, label, hint, scansStrings } of patterns) {
75143
- if (pattern.test(scansStrings ? withoutComments : executable)) {
75409
+ const match = pattern.exec(scansStrings ? withoutComments : executable);
75410
+ if (match) {
75144
75411
  findings.push({
75412
+ ...locate(script, match.index),
75145
75413
  code: "non_deterministic_code",
75146
75414
  severity: "error",
75147
75415
  message: `Script contains \`${label}\` which produces non-deterministic output. Renders may differ between frames or runs.`,
@@ -75572,6 +75840,7 @@ var init_dist3 = __esm({
75572
75840
  findAudioGroupCarveAttrFindings
75573
75841
  ];
75574
75842
  AUDIO_GROUP_TIMING_ATTRS = ["data-start", "data-duration", "data-track-index"];
75843
+ callIndexes = /* @__PURE__ */ new WeakMap();
75575
75844
  MAX_COMPOSITION_LINES = 300;
75576
75845
  MAX_TIMED_ELEMENTS_PER_TRACK = 3;
75577
75846
  TRACK_DENSITY_EXEMPT_TAGS = /* @__PURE__ */ new Set(["audio", "script", "style", "video"]);
@@ -76704,19 +76973,20 @@ ${right.raw}`)
76704
76973
  return findings;
76705
76974
  },
76706
76975
  // gsap_infinite_repeat
76707
- ({ scripts, rootTag }) => {
76976
+ ({ scripts, rootTag, locate }) => {
76708
76977
  const findings = [];
76709
76978
  const declaredDuration = Number.parseFloat(
76710
76979
  rootTag ? readAttr(rootTag.raw, "data-duration") ?? "" : ""
76711
76980
  );
76712
76981
  const hasFiniteCompositionWindow = Number.isFinite(declaredDuration) && declaredDuration > 0;
76713
76982
  const pattern = /repeat\s*:\s*-1(?!\d)/g;
76714
- for (const { snippet } of scanScriptsForRegexMatches(scripts, pattern, {
76983
+ for (const { snippet, match, script } of scanScriptsForRegexMatches(scripts, pattern, {
76715
76984
  stripComments: true,
76716
76985
  contextBefore: 60,
76717
76986
  contextAfter: 60
76718
76987
  })) {
76719
76988
  findings.push({
76989
+ ...locate(script, containingCallOffset(script, match.index)),
76720
76990
  code: "gsap_infinite_repeat",
76721
76991
  severity: hasFiniteCompositionWindow ? "warning" : "error",
76722
76992
  message: hasFiniteCompositionWindow ? `GSAP tween uses \`repeat: -1\` (infinite), but the composition declares a finite ${declaredDuration}s window. HyperFrames clips deterministic seeking and export to that explicit duration.` : "GSAP tween uses `repeat: -1` (infinite) without a finite composition `data-duration`. The timeline can report an unbounded duration and make render planning fail.",
@@ -76727,15 +76997,16 @@ ${right.raw}`)
76727
76997
  return findings;
76728
76998
  },
76729
76999
  // gsap_repeat_ceil_overshoot
76730
- ({ scripts }) => {
77000
+ ({ scripts, locate }) => {
76731
77001
  const findings = [];
76732
77002
  const pattern = /repeat\s*:\s*Math\.ceil\s*\([^)]+\)\s*-\s*1/g;
76733
- for (const { snippet } of scanScriptsForRegexMatches(scripts, pattern, {
77003
+ for (const { snippet, match, script } of scanScriptsForRegexMatches(scripts, pattern, {
76734
77004
  stripComments: false,
76735
77005
  contextBefore: 40,
76736
77006
  contextAfter: 40
76737
77007
  })) {
76738
77008
  findings.push({
77009
+ ...locate(script, containingCallOffset(script, match.index)),
76739
77010
  code: "gsap_repeat_ceil_overshoot",
76740
77011
  severity: "warning",
76741
77012
  message: "GSAP repeat calculation uses `Math.ceil` which can overshoot the composition duration. For example, Math.ceil(10.5 / 2) - 1 = 5 repeats \u2192 6 cycles \xD7 2s = 12s, exceeding 10.5s.",
@@ -76746,15 +77017,16 @@ ${right.raw}`)
76746
77017
  return findings;
76747
77018
  },
76748
77019
  // gsap_repeat_floor_unclamped
76749
- ({ scripts }) => {
77020
+ ({ scripts, locate }) => {
76750
77021
  const findings = [];
76751
77022
  const pattern = /repeat\s*:\s*Math\.floor\s*\([^)]+\)\s*-\s*1/g;
76752
- for (const { snippet } of scanScriptsForRegexMatches(scripts, pattern, {
77023
+ for (const { snippet, match, script } of scanScriptsForRegexMatches(scripts, pattern, {
76753
77024
  stripComments: false,
76754
77025
  contextBefore: 40,
76755
77026
  contextAfter: 40
76756
77027
  })) {
76757
77028
  findings.push({
77029
+ ...locate(script, containingCallOffset(script, match.index)),
76758
77030
  code: "gsap_repeat_floor_unclamped",
76759
77031
  severity: "warning",
76760
77032
  message: "GSAP repeat calculation can evaluate to -1 when the composition is shorter than one cycle, which GSAP interprets as an infinite repeat.",
@@ -76765,7 +77037,7 @@ ${right.raw}`)
76765
77037
  return findings;
76766
77038
  },
76767
77039
  // gsap_timeline_not_registered
76768
- ({ scripts, rawSource, options }) => {
77040
+ ({ scripts, rawSource, options, locate }) => {
76769
77041
  const findings = [];
76770
77042
  const canInheritFromHost = options.isSubComposition || rawSource.trimStart().toLowerCase().startsWith("<template");
76771
77043
  for (const script of scripts) {
@@ -76774,6 +77046,7 @@ ${right.raw}`)
76774
77046
  const hasRegistration = WINDOW_TIMELINE_ASSIGN_PATTERN.test(content) || TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN.test(content);
76775
77047
  if (hasRegistration || canInheritFromHost) continue;
76776
77048
  findings.push({
77049
+ ...locate(script, gsapCallOffset(script, "timeline")),
76777
77050
  code: "gsap_timeline_not_registered",
76778
77051
  severity: "error",
76779
77052
  message: "GSAP timeline is created but never registered in window.__timelines. The runtime discovers timelines from this registry \u2014 without registration, animations will not play during preview or render.",
@@ -77761,7 +78034,7 @@ ${other.raw}`)
77761
78034
  ];
77762
78035
  },
77763
78036
  // font_family_without_font_face
77764
- ({ styles, source, rawSource, options }) => {
78037
+ ({ styles, source, rawSource, options, locate }) => {
77765
78038
  if (isRegistrySourceFile(options.filePath) || isRegistryInstalledFile(rawSource)) return [];
77766
78039
  const findings = [];
77767
78040
  const declared = extractFontFaceFamilies(styles);
@@ -77772,6 +78045,7 @@ ${other.raw}`)
77772
78045
  );
77773
78046
  if (undeclared.length === 0) return findings;
77774
78047
  findings.push({
78048
+ ...uniqueFontLocation(styles, undeclared, locate),
77775
78049
  code: "font_family_without_font_face",
77776
78050
  severity: "error",
77777
78051
  message: `Font ${undeclared.length === 1 ? "family" : "families"} used without @font-face declaration: ${undeclared.join(", ")}. These are not in the auto-resolved font list, so the renderer cannot supply them automatically. Text will fall back to a generic font, producing incorrect typography in the video.`,
@@ -92921,17 +93195,17 @@ function getSystemProfilerIndex() {
92921
93195
  const filePath = typeface.path;
92922
93196
  if (typeof filePath !== "string" || !FONT_EXT_RE.test(filePath))
92923
93197
  continue;
92924
- const normalized = normalizeName(family);
93198
+ const normalized2 = normalizeName(family);
92925
93199
  const ext = filePath.match(FONT_EXT_RE)?.[1] ?? "ttf";
92926
93200
  const entry = {
92927
- family: normalized,
93201
+ family: normalized2,
92928
93202
  path: filePath,
92929
93203
  format: extensionToFormat(ext),
92930
93204
  isRegular: isRegularWeight(filePath)
92931
93205
  };
92932
- const list = profilerCache.get(normalized) ?? [];
93206
+ const list = profilerCache.get(normalized2) ?? [];
92933
93207
  list.push(entry);
92934
- profilerCache.set(normalized, list);
93208
+ profilerCache.set(normalized2, list);
92935
93209
  }
92936
93210
  }
92937
93211
  } catch {
@@ -92969,12 +93243,12 @@ function inferWeightAndStyle(fileName) {
92969
93243
  return { weight: "400", style };
92970
93244
  }
92971
93245
  function locateSystemFontVariants(family) {
92972
- const normalized = normalizeName(family);
92973
- if (!normalized)
93246
+ const normalized2 = normalizeName(family);
93247
+ if (!normalized2)
92974
93248
  return [];
92975
93249
  const variants = [];
92976
93250
  const profilerIndex = getSystemProfilerIndex();
92977
- const profilerEntries = profilerIndex.get(normalized);
93251
+ const profilerEntries = profilerIndex.get(normalized2);
92978
93252
  if (profilerEntries && profilerEntries.length > 0) {
92979
93253
  for (const e of profilerEntries) {
92980
93254
  if (!isRegularFile(e.path) || !isPathBounded(e.path))
@@ -92987,7 +93261,7 @@ function locateSystemFontVariants(family) {
92987
93261
  }
92988
93262
  const allCandidates = [];
92989
93263
  for (const dir of fontDirectories()) {
92990
- allCandidates.push(...collectCandidatesFromDir(dir, normalized));
93264
+ allCandidates.push(...collectCandidatesFromDir(dir, normalized2));
92991
93265
  }
92992
93266
  for (const c of allCandidates) {
92993
93267
  const { weight, style } = inferWeightAndStyle(c.path);
@@ -93009,7 +93283,7 @@ function dedupeVariants(variants) {
93009
93283
  init_esm10();
93010
93284
  init_fontData_generated();
93011
93285
  init_fontCompression();
93012
- import postcss3 from "postcss";
93286
+ import postcss4 from "postcss";
93013
93287
  var GENERIC_FAMILIES2 = /* @__PURE__ */ new Set([
93014
93288
  "sans-serif",
93015
93289
  "serif",
@@ -93068,7 +93342,7 @@ function systemPrimaryReplacement(value, deterministicPrimary) {
93068
93342
  }
93069
93343
  function parseCssRoot(css) {
93070
93344
  try {
93071
- return postcss3.parse(css);
93345
+ return postcss4.parse(css);
93072
93346
  } catch {
93073
93347
  return null;
93074
93348
  }
@@ -93330,8 +93604,8 @@ function extractExistingFontFaces(html) {
93330
93604
  if (semicolon < 0) break;
93331
93605
  const end = html.indexOf("}", semicolon + 1);
93332
93606
  if (end < 0) break;
93333
- const normalized = normalizeFamilyName(html.slice(valueStart, semicolon));
93334
- if (normalized) families.add(normalized);
93607
+ const normalized2 = normalizeFamilyName(html.slice(valueStart, semicolon));
93608
+ if (normalized2) families.add(normalized2);
93335
93609
  opening.lastIndex = end + 1;
93336
93610
  }
93337
93611
  return families;
@@ -93344,10 +93618,10 @@ function extractRequestedFontFamilies(html) {
93344
93618
  declaration,
93345
93619
  customProperties
93346
93620
  )) {
93347
- const normalized = originalCase.toLowerCase();
93348
- if (!normalized || GENERIC_FAMILIES2.has(normalized)) continue;
93349
- if (normalized.startsWith("var(")) continue;
93350
- if (!requested.has(normalized)) requested.set(normalized, originalCase);
93621
+ const normalized2 = originalCase.toLowerCase();
93622
+ if (!normalized2 || GENERIC_FAMILIES2.has(normalized2)) continue;
93623
+ if (normalized2.startsWith("var(")) continue;
93624
+ if (!requested.has(normalized2)) requested.set(normalized2, originalCase);
93351
93625
  }
93352
93626
  }
93353
93627
  return requested;
@@ -94025,11 +94299,11 @@ function hasGifExtension(src) {
94025
94299
  function readLoopOverride(el) {
94026
94300
  const raw = el.getAttribute("data-loop");
94027
94301
  if (raw == null) return null;
94028
- const normalized = raw.trim().toLowerCase();
94029
- if (normalized === "" || normalized === "true" || normalized === "1" || normalized === "yes") {
94302
+ const normalized2 = raw.trim().toLowerCase();
94303
+ if (normalized2 === "" || normalized2 === "true" || normalized2 === "1" || normalized2 === "yes") {
94030
94304
  return true;
94031
94305
  }
94032
- if (normalized === "false" || normalized === "0" || normalized === "no") {
94306
+ if (normalized2 === "false" || normalized2 === "0" || normalized2 === "no") {
94033
94307
  return false;
94034
94308
  }
94035
94309
  return null;
@@ -102770,11 +103044,11 @@ function validateOutputResolutionOverride(body) {
102770
103044
  if (typeof body.outputResolution !== "string") {
102771
103045
  return 'outputResolution must be a string preset (e.g. "4k", "landscape-4k")';
102772
103046
  }
102773
- const normalized = normalizeResolutionFlag(body.outputResolution);
102774
- if (body.outputResolution.trim().length > 0 && normalized === void 0) {
103047
+ const normalized2 = normalizeResolutionFlag(body.outputResolution);
103048
+ if (body.outputResolution.trim().length > 0 && normalized2 === void 0) {
102775
103049
  return `Invalid outputResolution "${body.outputResolution}". Must be one of: landscape, portrait, landscape-4k, portrait-4k, square, square-4k (aliases: 1080p, 4k, \u2026).`;
102776
103050
  }
102777
- if (normalized !== void 0 && (body.format === "webm" || body.format === "mov")) {
103051
+ if (normalized2 !== void 0 && (body.format === "webm" || body.format === "mov")) {
102778
103052
  return `outputResolution is not supported with format "${body.format}" \u2014 the alpha (webm/mov) capture path can't supersample. Use format "mp4", or omit outputResolution to render at the composition's native dimensions.`;
102779
103053
  }
102780
103054
  return void 0;
@@ -103448,8 +103722,8 @@ async function runFontLocalize(io, localize) {
103448
103722
  }
103449
103723
 
103450
103724
  // src/version.ts
103451
- var VERSION = true ? "0.8.66" : "0.0.0-dev";
103452
- var PRODUCER_VERSION = true ? "0.8.66" : "0.0.0-dev";
103725
+ var VERSION = true ? "0.8.68" : "0.0.0-dev";
103726
+ var PRODUCER_VERSION = true ? "0.8.68" : "0.0.0-dev";
103453
103727
 
103454
103728
  // src/fontLocalizeCli.ts
103455
103729
  async function readStdin() {