Skip to content

fix(cli): keep scaffolded video sources playable - #4839

Open
AKnassa wants to merge 2 commits into
facebook:mainfrom
AKnassa:rocky/issue-4780-lightbox-video-scaffold
Open

fix(cli): keep scaffolded video sources playable#4839
AKnassa wants to merge 2 commits into
facebook:mainfrom
AKnassa:rocky/issue-4780-lightbox-video-scaffold

Conversation

@AKnassa

@AKnassa AKnassa commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What this does

Scaffolding a template that shows a video now gives you a video that actually plays. Before, the copied file pointed the video at a picture, so the player came up empty.

Why

astryx template LightboxVideo ./out --type block is meant to hand you a working starting point, and it handed you a broken one. The copied file kept type: 'video' but its source was swapped for the image placeholder, so the Lightbox asked a <video> element to play SVG picture data. Nothing plays, and it is not obvious why.

Only the copy path was affected. astryx template LightboxVideo on its own, and the docsite example, read the original file and were always fine.

What changed

  • On copy, the placeholder is now chosen for each reference from the file type. Video files (.mp4, .webm, .mov, .m4v, .ogv) get a video placeholder; everything else keeps the image placeholder it has always had.
  • Added that video placeholder: 640x360, about 1.6 seconds over 3 frames, silent, 2.8 kB, carried inside the file itself. It draws the same grey mark as the image placeholder with a play symbol in it. Like the image placeholder it needs no network and no asset folder, so a freshly scaffolded project just works.
  • Added placeholder-video.svg alongside the existing placeholder.svg so the still frame is viewable in the diff rather than hidden inside an encoded string.
  • The file type is read from the last part of the filename. clip.min.mp4 used to be judged on min and treated as a picture, which is the same bug in a different shape, and @2x filenames slipped through untouched and would go missing in a scaffolded project. Both are latent rather than live: replaying the old and new rules over all 1328 template files gives byte-identical results.

How it was checked

  • Tests cover the five video file types, an upper-case extension, a file mixing a picture and a video, multi-part filenames, @2x names, and a full copy of the LightboxVideo block. The layout expand path is covered too, since it runs the same swap.
  • The inline video cannot be reviewed by reading it, so its size, frame count, length, track layout and codec are all asserted against the decoded bytes, along with agreement between its internal duration headers. That set of tests caught two real defects in the first version of this PR: the clip was a single frame declaring 33ms while the notes claimed two seconds, and the encoder had written the media header duration in the wrong timescale.
  • Every one of those assertions was mutation-checked: swapping the two placeholders fails 23 tests, forcing everything down the image branch fails 18, and restoring the old filename rule fails 3.

How to see it

node packages/cli/clients/cli/bin/astryx.mjs template LightboxVideo ./out --type block

Open out/LightboxVideo.tsx. The src now begins with data:video/mp4;base64, instead of data:image/svg+xml,. Paste that whole value into a browser address bar to play it, or render the block and open the Lightbox.

The frame it shows is in the diff at apps/docsite/public/template-assets/placeholder-video.svg.

Known and deliberately left alone

  • A reference carrying a query string (clip.mp4?v=2) would leave the query glued to the end of the data URI. No template does this, and widening the pattern to swallow trailing characters is riskier than the bug.
  • astryx template <name> (and its --json form) still prints the raw /template-assets/... path, because it shows the template as authored rather than as scaffolded. That is existing behaviour and a separate question from this fix.

Fixes #4780

`stripTemplateAssetRefs` swapped every `/template-assets/*` reference for the
inline image placeholder, whatever the file type. Scaffolding the LightboxVideo
block therefore produced `src: 'data:image/svg+xml,...'` sitting next to
`type: 'video'` — SVG image data handed to a <video> element, which can never
play. Only the copy path was affected; `astryx template LightboxVideo` and the
docsite example read the raw source and stayed valid.

Capture the extension and choose the placeholder per reference: `.mp4`,
`.webm`, `.mov`, `.m4v`, and `.ogv` take a new inline video placeholder,
everything else keeps the image one. Both remain self-contained, so a
scaffolded project still needs no `/template-assets/` dir and no network.

The video placeholder is 640x360, 2 seconds, no audio track, H.264 constrained
baseline in MP4 (2,269 bytes) drawing the same mark as the image placeholder
with a play glyph. placeholder-video.svg is committed beside placeholder.svg
as the reviewable still of what those bytes encode.

Tests cover the five video extensions, case-insensitive matching, per-reference
selection in a file that mixes image and video, and an end-to-end scaffold of
LightboxVideo. The blob is opaque to review, so one test also walks its
top-level MP4 boxes and asserts they tile the buffer exactly — a single dropped
byte fails it.

Fixes facebook#4780
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

@AKnassa is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 9, 2026
@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge labels Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

No new or modified components detected.

Bundle Size Summary

No component packages changed.

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 9, 2026
Self-review of the first commit found the placeholder was not what it said
it was, and that the classifier had a latent hole.

The blob was a single frame declaring 33ms, while the JSDoc, the changeset,
and the PR body all promised two seconds. Its duration headers also
contradicted each other: mvhd said 33ms, mdhd said 1.1ms, and the sample
table said 33.3ms. Re-encoded to a real clip — 3 frames, 1.625s — and
corrected mdhd, which the encoder had written in the movie timescale instead
of the media timescale. Docs and changeset now state the measured values.

Every documented property is now asserted against the decoded bytes:
dimensions, frame count, duration, one video track with no audio, the codec
profile, and agreement between mvhd, tkhd, mdhd and the sample table. The
old structural test could not fail on any of this — a one-frame movie
satisfied it. The box walk is lazy so a broken transform fails these tests
instead of collapsing the whole file at collection time.

The extension is now read from the last dotted segment of the filename.
`clip.min.mp4` was matched as `clip.min`, classified from `min` as an image,
and left a stray `.mp4` welded to the data URI — the same image-behind-video
bug this PR exists to fix. `@` joins the name class because the demo-media
dir ships 16 `@2x` variants that fell through the pattern entirely and would
404 in a scaffolded project. Both shapes are latent: replaying the old and
new patterns over all 1328 shipped template sources produces byte-identical
output.

Adds a layout-expand test, since splicing runs block sources through the
same transform and had no media coverage.

Verified: the five video extensions, case-insensitivity, per-reference
selection, multi-dot, `@2x`, and the whole placeholder property set survive
mutation testing — swapping the placeholders fails 23 tests, forcing the
image branch fails 18, dropping the lowercase fails 1, removing an extension
fails 1, restoring the old pattern fails 3. Scaffolding through the real CLI
and decoding the result gives a 2,788-byte, 3-frame, 1.625s clip that
AVFoundation renders as the placeholder frame.
github-actions Bot added a commit that referenced this pull request Aug 9, 2026
@AKnassa
AKnassa marked this pull request as ready for review August 9, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI: LightboxVideo scaffold replaces MP4 source with an SVG image data URI

1 participant