Skip to content

fix(studio): capture storyboard tiles at review density - #3371

Merged
jrusso1020 merged 2 commits into
mainfrom
fix/storyboard-tile-source-density
Aug 20, 2026
Merged

fix(studio): capture storyboard tiles at review density#3371
jrusso1020 merged 2 commits into
mainfrom
fix/storyboard-tile-source-density

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

What

  • Capture storyboard contact-sheet posters at a bounded high-density review size.
  • Preserve object-cover for tiles and object-contain for the focus hero.
  • Keep timeline thumbnails on the bounded preview path.
  • Add component and server-route regression coverage for preview, storyboard, and source JPEG output.

Why

Storyboard tiles currently omit output=source, so the thumbnail route caps them at 240x135. A square 1080x1080 composition therefore arrives as only 135x135 and is stretched across a much larger card. On high-density displays this makes typography, thin lines, and sprite detail visibly blurry, which undermines the storyboard review surface.

How

FramePoster now requests output: "storyboard" for tiles and retains output: "source" for the single focus hero. The storyboard mode preserves authored aspect ratio while capping the longest side at 1080px. Generic timeline thumbnails remain unchanged and continue to omit an output parameter.

The tests pin the complete contract: a 1080x1080 composition maps to 135x135 preview and 1080x1080 storyboard/source output, while an 8K composition is capped to 1080x608 in storyboard mode.

Test plan

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

Commands run:

  • bun run --cwd packages/studio test src/components/storyboard/FramePoster.test.tsx src/player/components/CompositionThumbnail.test.ts
  • bun run --cwd packages/studio-server test src/routes/thumbnail.test.ts
  • bun run --cwd packages/studio typecheck
  • bun run --cwd packages/studio-server typecheck
  • bunx oxfmt --check on all four changed files
  • bunx oxlint on all four changed files

Manual verification used the same six 1080x1080 Framey storyboard compositions at a 1280x720, DPR-2 viewport. The same 387x217 CSS tile changed from a 135x135 poster response to 1080x1080, an 8x increase per dimension and 64x increase in pixel area. The follow-up route coverage verifies that storyboard output remains bounded for 8K sources.

@miguel-heygen miguel-heygen 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.

The blur diagnosis is right, and the scope is mostly disciplined, but I would not ship unbounded output=source for every contact-sheet tile.

What holds

  • FramePoster.tsx:53-62 is the only production caller changed to request source output. Generic CompositionThumbnail / timeline callers still omit output, so the bounded timeline path is preserved.
  • FramePoster.tsx:72 keeps the intended fit split: tile=object-cover, hero=object-contain. FramePoster.test.tsx:40-58 pins both that split and the default tile surface.
  • The JPEG route already supports source output and keeps source/preview in separate cache keys. Focused local verification passed all 22 component/URL/route tests.

Blocker — contact-sheet cost is now unbounded by both composition size and frame count

FramePoster.tsx:61 sends output=source for every tile, and thumbnail.ts:160-165 maps that directly to the authored dimensions with no ceiling. StoryboardGrid.tsx:38-52 maps the full manifest and the storyboard schema has no frame-count cap. loading="lazy" and the server's concurrency-1 coordinator soften the burst, but they do not bound it: HyperFrames supports 7680×4320 compositions, where one decoded tile is ~126.6 MiB; six near-viewport tiles can approach ~760 MiB of decoded image memory, plus six serialized 8K captures. The existing hero risk was one image at a time—this multiplies it across the board.

The measured 1080×1080 Framey case is reasonable, but “source” is the wrong invariant for a ~387 CSS-pixel tile. Add a storyboard-specific bounded high-density mode (for example, preserve authored aspect/layout while capping output near the tile's maximum DPR-2 physical size or a fixed ~1080 longest side). Keep source for the single focus hero if desired. Then pin a 4K/8K source fixture to the cap. This fixes the blur without making arbitrary source resolution the contact sheet's resource budget.

Important — the new route test does not test the square authored-size contract that motivated the PR

thumbnail.test.ts:71-89 creates no composition HTML, so it only proves that output=source returns the route defaults, 1920×1080. A mutation that hard-codes 1920×1080 for source JPEGs still passes it. The motivating regression is a 1080×1080 composition becoming 135×135. Write a square data-width="1080" data-height="1080" fixture and assert source JPEG output is 1080×1080; ideally assert the same fixture remains 135×135 on output=preview. That pins both sides of the contract rather than the default case.

CI is green except the still-running Windows test at review time; the PR is a draft. The code and tests I ran locally are otherwise clean.

Verdict: REQUEST CHANGES
Reasoning: The visual fix is correct and well scoped, but it replaces a severe quality problem with an unbounded multi-tile capture/decode cost, and the route regression test does not exercise the square authored-dimension case it claims to protect.

— Magi

@jrusso1020 jrusso1020 changed the title fix(studio): capture storyboard tiles at source resolution fix(studio): capture storyboard tiles at review density Aug 20, 2026
@jrusso1020

Copy link
Copy Markdown
Collaborator Author

Addressed the requested changes in 045cccb40:\n\n- Contact-sheet tiles now use a storyboard-specific mode capped to a 1080px longest side; only the single focus hero retains true source output, and timeline thumbnails remain unchanged.\n- The route test now writes a real 1080x1080 fixture and asserts 135x135 preview plus 1080x1080 storyboard/source output.\n- A separate 7680x4320 fixture asserts storyboard output is capped to 1080x608.\n\nFocused Studio/server tests are 23/23 green, both package typechecks pass, lint/format pass, and the Studio production build passes. Please re-review exact head 045cccb40 when ready.

@miguel-heygen miguel-heygen 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.

Re-reviewed exact head 045cccb40865a808dd6df94f061912469a0d2e1c.

Both prior blockers are closed.

  • FramePoster.tsx:53-62 now routes tiles to a distinct storyboard density while the single focus hero retains source; generic CompositionThumbnail callers still omit the parameter and stay on preview output.
  • thumbnail.ts:160-173 bounds storyboard output to a 1080px longest side, preserves aspect ratio, and never upscales sources already below the cap. The cache key includes the output mode and resolved dimensions, so preview/storyboard/source artifacts cannot collide.
  • The route coverage now exercises the actual motivating shape: thumbnail.test.ts:78-120 writes a 1080×1080 composition and pins preview=135×135, storyboard=1080×1080, source=1080×1080. :122-142 writes 7680×4320 and pins the cap at 1080×608. A hard-coded default-size source branch no longer survives the suite.
  • FramePoster.test.tsx:40-59 pins tile=storyboard, hero=source, default surface=tile, tile=object-cover, and hero=object-contain.

Focused local verification: 23/23 component, URL-builder, and route tests pass. Required GitHub CI is green at this reviewed head.

Verdict: APPROVE
Reasoning: The blur fix now has a bounded resource contract for multi-tile boards, preserves the single-hero/source and timeline/preview paths, and the regression suite directly protects both the square bug and 8K cap.

— Magi

@miguel-heygen miguel-heygen 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.

Re-reviewed exact head 045cccb40865a808dd6df94f061912469a0d2e1c.

Both prior blockers are closed.

  • FramePoster.tsx:53-62 now routes tiles to a distinct storyboard density while the single focus hero retains source; generic CompositionThumbnail callers still omit the parameter and stay on preview output.
  • thumbnail.ts:160-173 bounds storyboard output to a 1080px longest side, preserves aspect ratio, and never upscales sources already below the cap. The cache key includes the output mode and resolved dimensions, so preview/storyboard/source artifacts cannot collide.
  • The route coverage now exercises the actual motivating shape: thumbnail.test.ts:78-120 writes a 1080×1080 composition and pins preview=135×135, storyboard=1080×1080, source=1080×1080. :122-142 writes 7680×4320 and pins the cap at 1080×608. A hard-coded default-size source branch no longer survives the suite.
  • FramePoster.test.tsx:40-59 pins tile=storyboard, hero=source, default surface=tile, tile=object-cover, and hero=object-contain.

Focused local verification: 23/23 component, URL-builder, and route tests pass. Required GitHub CI is green at this reviewed head.

Verdict: APPROVE
Reasoning: The blur fix now has a bounded resource contract for multi-tile boards, preserves the single-hero/source and timeline/preview paths, and the regression suite directly protects both the square bug and 8K cap.

— Magi

@jrusso1020
jrusso1020 marked this pull request as ready for review August 20, 2026 22:45
@jrusso1020
jrusso1020 merged commit b4d5abd into main Aug 20, 2026
49 of 79 checks passed
@jrusso1020
jrusso1020 deleted the fix/storyboard-tile-source-density branch August 20, 2026 22:46
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