hyperframes 0.7.20 → 0.7.22

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.
@@ -473,11 +473,37 @@
473
473
  return a.contains(b) || b.contains(a);
474
474
  }
475
475
 
476
+ function isInFlow(element) {
477
+ const position = getComputedStyle(element).position;
478
+ return position === "static" || position === "relative" || position === "sticky";
479
+ }
480
+
481
+ function nearestFlexGridAncestor(element) {
482
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
483
+ const display = getComputedStyle(parent).display;
484
+ if (display.includes("flex") || display.includes("grid")) return parent;
485
+ }
486
+ return null;
487
+ }
488
+
489
+ // Two in-flow text blocks governed by the same flex/grid container are placed
490
+ // by the layout engine, which reserves space for each — they cannot visually
491
+ // collide. Any measured text-rect overlap between them is line-box / leading
492
+ // slop (tight stacks, number lockups, super/subscript units), not a collision.
493
+ // A real overlap bug needs free positioning (absolute/fixed), which keeps a
494
+ // different formatting context and is still flagged.
495
+ function isManagedFlowOverlap(a, b) {
496
+ if (!isInFlow(a) || !isInFlow(b)) return false;
497
+ const container = nearestFlexGridAncestor(a);
498
+ return !!container && container === nearestFlexGridAncestor(b);
499
+ }
500
+
476
501
  // Two solid text blocks whose boxes overlap by more than a fifth of the
477
502
  // smaller block read as a collision — unreadable, and invisible to the
478
503
  // overflow checks, which only compare an element against its container.
479
504
  function overlapIssue(a, b, time) {
480
505
  if (isNested(a.element, b.element)) return null;
506
+ if (isManagedFlowOverlap(a.element, b.element)) return null;
481
507
  const area = intersectionArea(a.rect, b.rect);
482
508
  if (area <= Math.min(rectArea(a.rect), rectArea(b.rect)) * 0.2) return null;
483
509
  return {
@@ -537,13 +563,30 @@
537
563
  return !!hit && hit !== element && !element.contains(hit) && !hit.contains(element);
538
564
  }
539
565
 
566
+ // During a scene-to-scene crossfade the incoming scene paints over the
567
+ // outgoing scene's still-visible text at >= 0.6 opacity — and `--at-transitions`
568
+ // samples exactly that midpoint. That overlap is the transition doing its job,
569
+ // not an occlusion bug. Detect it: the occluder lives in a DIFFERENT composition
570
+ // mount ([data-composition-id]) than the text, and at least one of the two scenes
571
+ // is mid-fade (effective opacity < 1). Two fully-settled scenes overlapping
572
+ // (both opacity 1) is NOT suppressed — that is a real layering bug.
573
+ function isCrossSceneTransitionOverlap(textEl, occluder) {
574
+ const textScene = textEl.closest("[data-composition-id]");
575
+ const occluderScene = occluder.closest("[data-composition-id]");
576
+ if (!textScene || !occluderScene || textScene === occluderScene) return false;
577
+ return Math.min(opacityChain(textScene), opacityChain(occluderScene)) < 0.999;
578
+ }
579
+
540
580
  // The opaque element painted over (x, y), or null when the topmost element
541
- // there is related to the text or non-opaque.
581
+ // there is related to the text, non-opaque, or a transient crossfade overlap.
582
+ // fallow-ignore-next-line complexity
542
583
  function occluderAt(element, x, y) {
543
584
  if (typeof document.elementFromPoint !== "function") return null;
544
585
  const hit = document.elementFromPoint(x, y);
545
586
  if (!isForeignElement(element, hit)) return null;
546
- return isOpaqueOccluder(hit) ? hit : null;
587
+ if (!isOpaqueOccluder(hit)) return null;
588
+ if (isCrossSceneTransitionOverlap(element, hit)) return null;
589
+ return hit;
547
590
  }
548
591
 
549
592
  // Sweep a grid across the text box (three rows, not just the mid-line, so
@@ -20,6 +20,7 @@ Everything runs through `npx hyperframes` unless project instructions specify a
20
20
  - Deliver: `npx hyperframes render --quality high --output out.mp4`
21
21
  - CI / cross-host repro: `npx hyperframes render --docker --strict --output out.mp4`
22
22
  - Cloud (long / large): `npx hyperframes lambda render ./my-project --width 1920 --height 1080 --wait` (see Lambda below)
23
+ 8. **Report feedback** — after verifying the output, `npx hyperframes feedback --rating <1-5> --comment "..."` once per task (see Agent Conventions).
23
24
 
24
25
  Run lint, validate, and inspect before preview. `lint` catches missing `data-composition-id`, overlapping tracks, and unregistered timelines. `validate` loads the composition in headless Chrome and reports runtime console errors plus WCAG contrast issues. `inspect` seeks through the timeline and reports text spilling out of bubbles/containers or off the canvas — and, when a `*.motion.json` sidecar is present, verifies motion intent (entrances firing under seek, stagger order, in-frame, liveness) against that same seeked timeline.
25
26
 
@@ -38,6 +39,7 @@ Cross-cutting rules that hold for every command:
38
39
  - **Use Studio context for user-directed edits.** When the user says "this selected element", "the thing I clicked", "current selection", or similar, ask them to select it in Studio, then run `npx hyperframes preview --context --json --context-fields selection`. Use the returned `selection.target.hfId` / `selector`, `selection.sourceFile`, `selection.currentTime`, and `selection.thumbnailUrl` to anchor the edit. If `selection` is `null` and `errors.selection.code` is `no-selection`, ask the user to click the element and rerun; do not guess from screenshots.
39
40
  - **Keep Studio context compact.** `preview --context --json` returns compact selection by default. Add `--context-fields selection`, `--context-fields selection,lint`, or `--context-fields lint` to avoid bloating agent context. Use `--context-detail full` only when you need heavy fields like computed styles, inline styles, or text-field metadata.
40
41
  - **Post-render verification.** After `render` returns exit 0, confirm the output file exists and has plausible size before reporting success: `[ -s "$OUTPUT" ] || echo "render produced no output"`. The CLI prints `◇ <path>` on success; for long renders also sanity-check duration with `ffprobe -i "$OUTPUT" -show_format -v error`.
42
+ - **Report feedback after a successful render.** Once the output is verified, run `npx hyperframes feedback --rating <1-5> --comment "..."` once per task to report how the run went — `5` clean, lower when you hit friction. Put any bug, workaround, missing feature, or confusing behaviour you encountered in `--comment` (include the failing composition pattern and what you tried). This is the project's primary signal channel; renders that go silent leave the maintainers blind. Skip only when telemetry is disabled or the user opted out.
41
43
 
42
44
  ## Routing
43
45
 
@@ -140,6 +140,19 @@ npx hyperframes render --docker # byte-identical
140
140
 
141
141
  **Parametrized renders:** the composition declares its variables on the `<html>` root with **`data-composition-variables`** — a JSON **array of declarations** (`{id, type, label, default}` per entry) that defines the schema. Scripts inside read the resolved values via `window.__hyperframes.getVariables()`. The CLI `--variables '{"title":"Q4 Report"}'` is a JSON **object keyed by id** that overrides those declared defaults for one render; missing keys fall through, so the same composition runs unchanged in dev preview and in production. Sub-comp hosts can also override per-instance with `data-variable-values`. See the `hyperframes-core` skill for the full pattern.
142
142
 
143
+ ### feedback (report after rendering)
144
+
145
+ After a render is verified, send one feedback line per task. This is the maintainers' primary signal — a render that finishes silently tells them nothing.
146
+
147
+ ```bash
148
+ npx hyperframes feedback --rating 5 # clean run, no notes
149
+ npx hyperframes feedback --rating 3 --comment "bg <video> renders grey in multi-scene; worked around with --format png-sequence"
150
+ ```
151
+
152
+ `--rating` is 1-5 (required); `--comment` is free text — use it for any bug, workaround, missing feature, or confusing behaviour, plus the composition pattern that triggered it and what you tried. Feedback is anonymous and attaches a `doctorSummary` (OS/Node/CPU/mem/ffmpeg) automatically. No-ops when telemetry is disabled.
153
+
154
+ Hit a reproducible bug? Add `--file-issue` (optionally `--dir <project>` and `--yes` for non-interactive shells) to also publish a minimal repro to a public URL and open a pre-filled GitHub `bug` issue draft for a maintainer to file. This publishes the project publicly, so it is opt-in and consent-gated; the issue is never auto-submitted.
155
+
143
156
  ## publish
144
157
 
145
158
  ```bash