Skip to content

fix(studio): capture the storyboard frame hero at full resolution - #3338

Open
miguel-heygen wants to merge 1 commit into
mainfrom
fix/storyboard-hero-poster-resolution
Open

fix(studio): capture the storyboard frame hero at full resolution#3338
miguel-heygen wants to merge 1 commit into
mainfrom
fix/storyboard-hero-poster-resolution

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Closes #3271.

What

The storyboard's frame detail hero now captures at the composition's own dimensions. The contact-sheet tile and the timeline are unchanged.

Why

The thumbnail route bounds every preview capture to 240x135 (THUMBNAIL_MAX_OUTPUT_WIDTH / _HEIGHT, fed into thumbnailDeviceScaleFactor). That bound arrived with #2720, a timeline change: timeline thumbnails are small and numerous and their decoded bytes are budgeted.

The storyboard reuses the same route for its frame detail hero, which is up to 900px wide. Measured in a running Studio on a 1920x1080 composition:

surface asset displayed upscale on a 2x display
contact-sheet tile 240x135 314 CSS px 2.62x
frame detail hero 240x135 898 CSS px 7.48x

Headlines survive that. Body copy, table labels and captions do not.

That is the surface where it costs the most. references/review-loop.md sends the user to the poster to confirm layout and real copy, and tells them to run no CLI in that pass: "the poster is the only picture this pass needs". So there is no sharp image available at the moment the workflow asks for a judgement.

One correction to #3271, which points at posterMaxPhysicalWidth: 240 in timelineViewportBudgets.ts. That constant is not what sizes the poster: its three uses are a decoded-byte weight for the timeline thumbnail cache and the video-decoder bounds. Same two numbers, different file. Changing it would perturb the timeline cache budget and leave the storyboard exactly as blurry.

How

buildCompositionThumbnailUrl gains an optional output param. The route already accepts output=source, so this is client-side only.

FramePoster's fit prop becomes surface: "tile" | "hero". Whether a poster is a tile or the hero decides both the crop and the capture density, so one prop owns both rather than two that can disagree.

The bound is not raised, so nothing the perf work covered regresses:

surface after
timeline untouched, still bounded
contact-sheet tile untouched, still bounded (many tiles, and it is a contact sheet)
frame detail hero source density, one image at a time

Not covered: the contact-sheet tile still upscales 2.62x on a 2x display. #3271 explicitly scopes the fix to the detail view and leaves the grid as is, so this PR does too.

Test plan

New FramePoster.test.tsx pins the wiring: the hero asks for source density and letterboxes, the tile does not and fills its cell, and the default is the tile. Verified the two behavioural cases fail on the parent commit and pass here. CompositionThumbnail.test.ts covers the URL param in both directions.

Full suites green: packages/studio (1062 tests) and packages/studio-server (446 tests).

Manual, in a running Studio against a frame carrying a headline, a small table and a 14px paragraph:

  • Before: hero asset 240x135, 7.48x upscale, table and paragraph unreadable

  • After: hero asset 1920x1080, 0.94x, both legible

  • Tile in the same run before and after: 240x135, unchanged

  • Unit tests added/updated

  • Manual testing performed

  • Documentation updated (if applicable)

The thumbnail route bounds every preview capture to 240x135. That bound came
from the timeline, where thumbnails are small and numerous and their decoded
bytes are budgeted. The storyboard reuses the same route for its frame detail
hero, which is up to 900px wide, so the poster arrived at 240x135 and upscaled
past 7x on a retina display. Headlines survived it; body copy, table labels and
captions did not.

That is the surface where it costs the most. references/review-loop.md sends the
user here to confirm layout and real copy, and tells them to run no CLI in that
pass: "the poster is the only picture this pass needs".

Give the caller a way to ask for the composition's own dimensions, which the
route already supports as `output=source`, and fold the choice into a single
`surface` prop. Whether a poster is a tile or the hero decides both the crop and
the capture density, so one prop owns both rather than two that can disagree.

The contact sheet keeps the bounded capture: many tiles, and it is a contact
sheet. The timeline is untouched.

Reported with a reproduction and a correct read of the consequences in #3271.

Co-Authored-By: anikam13 <22992075+anikam13@users.noreply.github.com>
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.

Studio storyboard posters are capped at 240×135, including the single-frame detail view

1 participant