mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
docs: correct composition offset rationale; pin nav fix; label-in-name
- html-schema: the previous clause claimed a composition host's data-media-start is never read. It is: readElementPlaybackStart (media.ts:16) resolves data-playback-start ?? data-media-start and timeline.ts calls it on composition clips. Rewrote to the accurate reason both reviewers gave — composition hosts are only inspected by the playback-start-first readers, media-start works as a fallback, but playback-start is what Studio writes/normalises to. - motion test: pin the HoverVideo click-suppression (preventDefault + stopPropagation). Removing it left the gate 12/12; now it fails. This is the bug that escaped static review and only surfaced by driving the live preview. - replica-compare: fold the visible 'Sound off/on' text into the aria-label so the accessible name contains it (WCAG 2.5.3, Rames). Round-5 findings from Magi and Rames.
This commit is contained in:
@@ -122,7 +122,7 @@ Audio has no visual lifecycle.
|
||||
|
||||
| Attribute | Applies to | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `data-media-start` / `data-playback-start` | Video, audio, nested composition | Offset into the source file, used by trim and split. Two groups of readers disagree, so the right name depends on the element. **Read only `data-media-start`:** the timing compiler, the HTML parser, `hyperframes validate` (which only inspects `<audio>`), and the engine's audio mixer (which feeds ffmpeg `-ss`). **Read `data-playback-start` first, falling back to `data-media-start`:** the runtime player, Studio (which also writes it), and `hyperframes snapshot`. Because the audio mixer reads only `data-media-start`, a `<video>` authored with just `data-playback-start` renders a trimmed picture over untrimmed audio. Set the name by kind: **`<video>` / `<audio>` → `data-media-start`**; **nested composition → `data-playback-start`** — none of the media-start-only readers inspect a composition host, so its offset is only ever read as `data-playback-start` (Studio writes it; it is the [child-timeline offset](/concepts/compositions)). |
|
||||
| `data-media-start` / `data-playback-start` | Video, audio, nested composition | Offset into the source file, used by trim and split. Two groups of readers disagree, so the right name depends on the element. **Read only `data-media-start`:** the timing compiler, the HTML parser, `hyperframes validate` (which only inspects `<audio>`), and the engine's audio mixer (which feeds ffmpeg `-ss`). **Read `data-playback-start` first, falling back to `data-media-start`:** the runtime player, Studio (which also writes it), and `hyperframes snapshot`. Because the audio mixer reads only `data-media-start`, a `<video>` authored with just `data-playback-start` renders a trimmed picture over untrimmed audio. Set the name by kind: **`<video>` / `<audio>` → `data-media-start`**; **nested composition → `data-playback-start`** — composition hosts are inspected only by the playback-start-first readers (the media-start-only ones are all `<video>`/`<audio>`-scoped). `data-media-start` still works there as a fallback, but `data-playback-start` is the canonical name Studio writes and normalises to for new composition hosts, so the other name works until an edit rewrites it (it is the [child-timeline offset](/concepts/compositions)). |
|
||||
| `data-playback-rate` | Video, audio, nested composition | Playback multiplier from `0.1` to `5` |
|
||||
| `data-volume` | Video and audio | Static volume from `0` to `1` |
|
||||
| `data-has-audio="true"` | Video | Declares that the video contributes audio |
|
||||
|
||||
@@ -158,11 +158,11 @@ export const ReplicaCompare = ({
|
||||
aria-label={
|
||||
muted
|
||||
? reduced
|
||||
? "Play the comparison with sound"
|
||||
: "Unmute the reference"
|
||||
? "Sound off — play the comparison with sound"
|
||||
: "Sound off — unmute the reference"
|
||||
: reduced
|
||||
? "Pause the comparison"
|
||||
: "Mute the reference"
|
||||
? "Sound on — pause the comparison"
|
||||
: "Sound on — mute the reference"
|
||||
}
|
||||
className="absolute left-3 top-3 z-10 flex items-center gap-1.5 rounded-full bg-black/60 px-3 py-1.5 text-xs font-medium text-white backdrop-blur transition hover:bg-black/75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
>
|
||||
|
||||
@@ -156,3 +156,16 @@ test("ReplicaCompare's control starts both films and does not gate sync on reduc
|
||||
assert.ok(teardown, "found the offscreen teardown");
|
||||
assert.match(teardown[1], /setMuted\(true\)/, "offscreen release resets muted state");
|
||||
});
|
||||
|
||||
// 40 of HoverVideo's 53 uses sit inside a link (11 of 23 examples cards are
|
||||
// <a href>, 29 of 30 thirty-days cards are <Card href>). Its control's click must
|
||||
// not bubble to that link, or pressing it — mouse or keyboard — navigates away
|
||||
// instead of toggling sound. This bug escaped static review and the gate; it only
|
||||
// surfaced by driving the live preview, so pin it.
|
||||
test("HoverVideo's control does not bubble its click to a wrapping link", () => {
|
||||
const source = readFileSync(join(here, "../docs/snippets/hover-video.jsx"), "utf8");
|
||||
const onClick = source.match(/onClick=\{\(e\) => \{([\s\S]*?)\n {8}\}\}/);
|
||||
assert.ok(onClick, "found the control's click handler");
|
||||
assert.match(onClick[1], /e\.preventDefault\(\)/, "default navigation is prevented");
|
||||
assert.match(onClick[1], /e\.stopPropagation\(\)/, "the click does not bubble to a wrapping link");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user