hyperframes 0.8.81 → 0.8.82

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 (42) hide show
  1. package/dist/backgroundChecksWorker.js +890 -0
  2. package/dist/cli.js +2928 -1809
  3. package/dist/commands/layout-audit.browser.js +55 -25
  4. package/dist/fontLocalizeCli.js +417 -134
  5. package/dist/hyperframe-runtime.js +53 -53
  6. package/dist/hyperframe.manifest.json +1 -1
  7. package/dist/hyperframe.runtime.iife.js +53 -53
  8. package/dist/hyperframes-player.global.js +3 -3
  9. package/dist/registry/localSemantic.js +7 -3
  10. package/dist/renderSetupWorker.js +62 -32
  11. package/dist/sherpaWorker.js +179 -0
  12. package/dist/skills/hyperframes/SKILL.md +2 -0
  13. package/dist/skills/hyperframes/references/plugin-installation.md +43 -0
  14. package/dist/skills/hyperframes/references/skill-lifecycle.md +2 -0
  15. package/dist/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
  16. package/dist/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
  17. package/dist/skills/hyperframes-cli/SKILL.md +2 -0
  18. package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +1 -0
  19. package/dist/skills/media-use/SKILL.md +2 -0
  20. package/dist/skills/media-use/references/operations.md +7 -5
  21. package/dist/skills/media-use/references/setup-providers.md +9 -9
  22. package/dist/skills/media-use/registry/localSemantic.js +7 -3
  23. package/dist/skills/media-use/scripts/lib/ff-binaries.mjs +37 -0
  24. package/dist/skills/media-use/scripts/lib/probe.mjs +3 -1
  25. package/dist/skills/media-use/scripts/lib/probe.test.mjs +118 -1
  26. package/dist/skills/media-use/scripts/lib/tts-local-provider.mjs +5 -3
  27. package/dist/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
  28. package/dist/skills/media-use/scripts/resolve.mjs +45 -19
  29. package/dist/skills/media-use/scripts/transcribe.mjs +39 -25
  30. package/dist/skills/media-use/scripts/transcribe.test.mjs +112 -0
  31. package/dist/skills/registry/localSemantic.js +7 -3
  32. package/dist/studio/assets/{hyperframes-player-C68UawR3.js → hyperframes-player-zGIbZhLJ.js} +1 -1
  33. package/dist/studio/assets/{index-CMzPGv9P.js → index-BiLCTunu.js} +1 -1
  34. package/dist/studio/assets/{index-hNVM9Pd2.js → index-DSazIt7R.js} +189 -189
  35. package/dist/studio/assets/{index-CS5SImRT.js → index-DStmfnpZ.js} +1 -1
  36. package/dist/studio/index.d.ts +172 -7
  37. package/dist/studio/index.html +1 -1
  38. package/dist/studio/index.js +2414 -2172
  39. package/dist/studio/index.js.map +1 -1
  40. package/dist/templates/_shared/AGENTS.md +5 -1
  41. package/dist/templates/_shared/CLAUDE.md +5 -1
  42. package/package.json +1 -1
@@ -471,19 +471,55 @@
471
471
  };
472
472
  }
473
473
 
474
- // An ancestor (up to and including `stopAt`) that clips its overflow makes any
475
- // text spilling past it invisible — that clipping IS the layout mechanism
476
- // (odometer/ticker reels, masked windows), not a defect to report.
477
- function clippedByAncestor(element, stopAt) {
478
- for (let current = element; current; current = current.parentElement) {
479
- if (current !== element && clipsOverflow(getComputedStyle(current))) return true;
480
- if (current === stopAt) break;
481
- }
482
- return false;
474
+ function horizontalTextMetrics(element, style) {
475
+ if (style.writingMode && style.writingMode !== "horizontal-tb") return null;
476
+ const context = document.createElement("canvas").getContext("2d");
477
+ if (!context) return null;
478
+ context.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
479
+ let text = textContentFor(element, true);
480
+ if (style.textTransform === "uppercase") text = text.toUpperCase();
481
+ if (style.textTransform === "lowercase") text = text.toLowerCase();
482
+ const metrics = context.measureText(text);
483
+ return metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent > 0 ? metrics : null;
484
+ }
485
+
486
+ function intersectsTextWindow(rect, clip, tolerance) {
487
+ return (
488
+ Math.min(rect.right, clip.right) - Math.max(rect.left, clip.left) > tolerance &&
489
+ Math.min(rect.bottom, clip.bottom) - Math.max(rect.top, clip.top) > tolerance
490
+ );
491
+ }
492
+
493
+ function visibleTextLineRects(element, rects, style, clip, tolerance) {
494
+ const metrics = horizontalTextMetrics(element, style);
495
+ const fontHeight = metrics ? metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent : 0;
496
+ const lineHeight = parsePx(style.lineHeight) || fontHeight;
497
+ return rects.flatMap((rect) => {
498
+ if (!metrics) return intersectsTextWindow(rect, clip, tolerance) ? [rect] : [];
499
+ const scale = rect.height / fontHeight;
500
+ const inkTop =
501
+ rect.top + (metrics.fontBoundingBoxAscent - metrics.actualBoundingBoxAscent) * scale;
502
+ const inkBottom =
503
+ rect.bottom - (metrics.fontBoundingBoxDescent - metrics.actualBoundingBoxDescent) * scale;
504
+ const ink = { ...rect, top: inkTop, bottom: inkBottom };
505
+ if (!intersectsTextWindow(ink, clip, tolerance)) return [];
506
+ // Negative leading belongs outside the used line box. Font metrics scale
507
+ // with the Range rect, so zoomed cards retain the same clipping decision.
508
+ const inset = Math.max(0, (rect.height - lineHeight * scale) / 2);
509
+ return [
510
+ toRect({
511
+ ...rect,
512
+ top: rect.top + inset,
513
+ bottom: rect.bottom - inset,
514
+ height: rect.height - 2 * inset,
515
+ }),
516
+ ];
517
+ });
483
518
  }
484
519
 
485
520
  function textOverflowIssues(element, root, rootRect, time, tolerance, clippedIssue) {
486
- const textRect = textRectFor(element, true);
521
+ const lineRects = textClientRects(element, true).map(toRect);
522
+ const textRect = unionRects(lineRects);
487
523
  if (!textRect) return [];
488
524
  const text = textContentFor(element, true);
489
525
  const selector = selectorFor(element);
@@ -491,32 +527,26 @@
491
527
 
492
528
  const container = nearestConstraint(element, root, rootRect);
493
529
  const containerRect = container === root ? rootRect : toRect(container.getBoundingClientRect());
494
- // Glyph ink (ascenders / descenders / accents / heavy display faces) routinely exceeds a
495
- // snug line-height box by a few px, proportional to font size. When the constraining box
496
- // does NOT clip, that vertical spill is normal typography — it shows in the padding, nothing
497
- // is hidden — not a layout defect (it false-flagged caption words). Allow a font-metric
498
- // vertical tolerance there; keep it tight when the box actually clips (a real cut-off) and
499
- // always tight horizontally (too-wide text is a real wrap/legibility issue).
500
530
  const elementStyle = getComputedStyle(element);
501
531
  const containerClips = clipsOverflow(
502
532
  container === root ? getComputedStyle(root) : getComputedStyle(container),
503
533
  );
534
+ const visibleTextRect = containerClips
535
+ ? unionRects(visibleTextLineRects(element, lineRects, elementStyle, containerRect, tolerance))
536
+ : textRect;
504
537
  const verticalTolerance = containerClips
505
538
  ? tolerance
506
539
  : Math.max(tolerance, parsePx(elementStyle.fontSize) * 0.2);
507
- const containerOverflow = overflowFor(textRect, containerRect, tolerance, verticalTolerance);
540
+ const containerOverflow = visibleTextRect
541
+ ? overflowFor(visibleTextRect, containerRect, tolerance, verticalTolerance)
542
+ : null;
508
543
  const billedAsClippedText =
509
544
  container === element &&
510
545
  clippedIssue != null &&
511
546
  containerOverflow != null &&
512
547
  containerOverflow.left == null &&
513
548
  containerOverflow.top == null;
514
- if (
515
- containerOverflow &&
516
- !billedAsClippedText &&
517
- !hasTextClipOptOut(element) &&
518
- !clippedByAncestor(element, container)
519
- ) {
549
+ if (containerOverflow && !billedAsClippedText && !hasTextClipOptOut(element)) {
520
550
  const style = elementStyle;
521
551
  issues.push({
522
552
  code: "text_box_overflow",
@@ -526,11 +556,11 @@
526
556
  containerSelector: selectorFor(container),
527
557
  text,
528
558
  message: "Text extends outside its nearest visual/container box.",
529
- rect: textRect,
559
+ rect: visibleTextRect,
530
560
  containerRect,
531
561
  overflow: containerOverflow,
532
562
  fixHint: textOverflowFixHint(
533
- textRect,
563
+ visibleTextRect,
534
564
  containerRect,
535
565
  containerOverflow,
536
566
  parsePx(style.fontSize),