Skip to content

fix(cli): media-treatment applies one treatment to every matched image or video - #5443

Merged
miguel-heygen merged 6 commits into
mainfrom
fix/media-treatment-all-matches
Oct 11, 2026
Merged

miguel-heygen merged 6 commits into
mainfrom
fix/media-treatment-all-matches

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

What changes for the user

hyperframes media-treatment --selector video --grading '<patch>' --apply now applies the patch to every matched image or video, instead of failing with "Selector matched N elements". Grading every segment of a cut alike is one command. The text output says how many of the matched elements changed, and --json reports count plus each target (selectorIndex, tag, changed, before, after, value). Each match gets the patch merged into its own current grading, so segments that already differ keep their other settings; run --clear with the same selector first for an identical look. --selector-index still treats only one match, and --dry-run previews all of them without writing. The reported action (Applied/Cleared, JSON action) reflects every target, not just the first.

Why this default

The command's own help example (--selector 'video' --grading ... --dry-run) only worked on a project with exactly one video, and the reported case wanted one consistent look on every segment. The change is previewable (--dry-run) and reversible (--clear with the same selector). Every match must be an <img> or <video>: a selector that also matches anything else fails the whole command, naming the element it matched, and so does a match that is not an HTML element (a <video> inside an SVG foreignObject). The previous path refused that only when a write was needed; it is now refused on every run, including --analyze and no-op runs. Matches come from the main document or, when it has none, from the first <template> that has any, the same scope rule as Studio's source edits.

How

selectMediaElements in media-treatment.ts returns every match (or the one --selector-index picks), each checked to be an <img> or <video>. applyMediaTreatmentToHtml parses the file once (the same parseSourceDocument the studio-server patch uses), finds every match in that one document, sets or removes data-color-grading on each matched element directly, and serializes once with ensureHfIds, exactly as patchElementInHtml does. Nothing re-finds an element after a write, so a write that changes which elements the selector matches (video[data-color-grading], video:has(+ video[data-color-grading])), duplicated data-hf-ids in the file, or matches inside a <template> cannot redirect another write. A single-element apply produces the same bytes as before. --analyze still needs a single element, since it inspects one media file. The JSON keeps the old single-target fields (selectorIndex, tag, before, after, value) when one element is treated, and reports targets instead when several are. The flag help, the help summary, the CLI docs page and the media-use treatment reference now describe the behaviour.

Verification

  • New media-treatment.test.ts cases: two videos matched by video both get the same grading; clearing with video[data-color-grading] clears both; a mixed match (.seg on a video and a div) is rejected naming <div>; two images are both graded unless --selector-index picks one; --apply --json reports count: 2 and both targets and writes both. Each fails on the previous code except the --selector-index case, which the previous code already supported.
  • Also: a video:has(+ video[data-color-grading]) clear over four graded videos clears exactly the three it matched, in the main document and inside a <template>; with an <img> and a <video> sharing one data-hf-id, --selector video grades the video, not the image; a later match's change is written when the first match is already graded; two matches starting from different gradings each keep their own settings under the merged patch; a patch that leaves one match ungraded and another graded reports apply (fails on the previous head, which reported clear from the first match).
  • A <video> inside an SVG foreignObject is refused rather than written (writing it would serialize the following content inside the video tag); fails on the previous head.
  • The media-treatment tests (34) pass 3 runs in a row; typecheck, oxlint, oxfmt and the comment ratchet are clean.

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 2061 (base branch 2061), smooth 1635 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@mintlify

mintlify Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 11, 2026, 1:30 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at 0a7c9f6b. No blockers.

What I checked:

  • applyMediaTreatmentToHtml now parses once with the studio-server parseSourceDocument (which runs ensureHfIds first), selects from that same document, writes each match in place, and serializes once with ensureHfIds. That is the same parse → write → serialize sequence patchElementInHtml uses (sourceMutation.ts:246-328), so a single-element apply gives the same bytes as before. Nothing is looked up again after a write, so the video:has(+ video[data-color-grading]) and duplicate-data-hf-id cases can't send one write to another element.
  • selectMediaElements checks every target before any write: tag is img/video and isHTMLElement. A mixed selector or a <video> inside an SVG foreignObject fails before the document changes, and if serializeGradingPatch throws partway through the map, nothing is written either.
  • --analyze still goes through the single-target selectMediaElement, which keeps the "matched N elements" error.
  • action comes from every target (every(value === null)). The JSON keeps the old single-target fields when count === 1 and reports targets otherwise. Nothing in the repo reads the old multi-match shape (that case used to fail), so no caller breaks.
  • Docs, help.ts and the media-use reference describe the new default and the per-element merge.

Local run at this head (no real media files or render involved):

  • media-treatment.test.ts: 34/34 pass.
  • Mutation probe: I changed action back to "from the first target", and reports apply when any match keeps a grading after the patch failed. I reverted the change afterwards.

All 11 required checks are green at this head.

— Rames

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at 99343c71. I'm re-pinning my earlier approval of 0a7c9f6b.

The new head fast-forwards the one I approved by a single commit. That commit changes one line in skills-manifest.json: the media-use hash goes from 206565a9b0cc6ac2 to 1a841e4288d862af, and the file count stays at 110. No code changed, so my review of 0a7c9f6b still holds. Skills: manifest in sync passes at this head, which confirms the new hash, and all 11 required checks are green.

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 44e5a47 Oct 11, 2026
83 checks passed
@miguel-heygen
miguel-heygen deleted the fix/media-treatment-all-matches branch October 11, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants