Skip to content

feat(studio): sidebar asset card (unwired)#2210

Merged
miguel-heygen merged 1 commit into
studio-dnd/pr18-nle-shell-assemblyfrom
studio-dnd/pr19-asset-card
Jul 12, 2026
Merged

feat(studio): sidebar asset card (unwired)#2210
miguel-heygen merged 1 commit into
studio-dnd/pr18-nle-shell-assemblyfrom
studio-dnd/pr19-asset-card

Conversation

@ukimsanov

@ukimsanov ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

AssetCard — the sidebar asset tile (thumbnail, hover preview, click policy, context menu incl. add-at-playhead), unwired.

Why

standalone sidebar concept; AssetsTab adopts it in the app-shell swap.

How

new file against the coexistence layer (assetHelpers, AssetContextMenu, assetClickBehavior, assetPreviewStore); one TEMP(studio-dnd) entry until AssetsTab wires it.

Test plan

tsc --noEmit; fallow audit clean.


Stack position 19/25 — studio NLE overhaul (CapCut-parity timeline + canvas). Graphite manages bases; merge from #2192 upward. Temporary TEMP(studio-dnd) fallow entries (unwired-component windows) are all removed by #2213 (app-shell swap), whose tree is byte-identical to the fully-verified integration branch.

Merge invariant: #2206#2213 land as ONE ordered unit — no cherry-picks, no deploys between #2211#2213 (review-fix foliation concentrates engine wiring at #2213; seam evidence in the #2205 thread).

ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed as part of the 25-PR NLE overhaul stack. Full stack review on #2205 (the keystone). No blockers on this PR. — Miga

@vanceingalls vanceingalls 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.

R1 — Batch C (unwired chrome / assembly)

Verdict: 🟢 approve

Reviewed at c87f8d54. Focus per Phase-2 recon: sidebar AssetCard is presentational, verify unwired, sanity-check prop shape / consumer contract.

Unwired assertion — 🟢

Grepped packages/studio/src/**/*.{ts,tsx} at #2210 (top-of-Batch-C) for AssetCard imports. Zero consumers. The pre-existing AssetsTab.tsx still owns its inline asset-tile implementation; this file is a parallel extraction that will be swapped in at #2213.

The header comment is explicit about the extraction motivation: "Extracted from AssetsTab.tsx to keep that file under the 600-line CI gate."

Prop / store contract sanity — 🟢

AssetCard imports useAssetPreviewStore from @/utils/assetPreviewStore (added in #2204). Verified at #2210 head that:

  • assetPreviewStore.ts is present with previewAsset, previewProjectId, clearPreviewAsset, setPreviewAsset — the shape AssetCard reads.
  • AssetPreviewOverlay (from #2204) reads the same store, so store⇄reader contract is symmetric. When #2213 wires AssetsTab → AssetCard, the sidebar-writer / preview-reader pair activates atomically.

Other imports (VideoFrameThumbnail, AssetContextMenu, assetHelpers, mediaTypes, timelineAssetDrop, playerStore, assetClickBehavior) are all pre-existing utilities that AssetsTab already consumes today. No new dependency introduced.

Fallow lifecycle — 🟢

Added to .fallowrc.jsonc:

  • unusedExports block: sidebar/AssetCard.tsx — 1 entry.
  • complexity ignore block: sidebar/AssetCard.tsx — 1 entry.

Both share the TEMP(studio-dnd) marker pointing at studio-dnd/pr22; removable in one motion.

Notes for Phase 3 cross-check

None. Clean unwired extraction.

R1 by Via

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

Re-review — URL encoding gap: silent 404 for asset paths with special chars

Blocker-adjacent: AssetCard.tsx:174 builds the serve URL raw:

const serveUrl = `/api/projects/${projectId}/preview/${asset}`;

No encodeURIComponent on any segment. The sibling AssetPreviewOverlay in the same PR (PR #2206 baseline, AssetPreviewOverlay.tsx:82) explicitly encodes:

previewAsset.split("/").map(encodeURIComponent).join("/")

Impact: any asset path containing space, #, ?, %, non-ASCII, or + will 404 in:

  • The card thumbnail (<VideoFrameThumbnail src={serveUrl}> and <img src={serveUrl}>)
  • useProbedDuration (which uses the same unencoded serveUrl at line 130)

While the overlay opens the same asset correctly. Same file, inconsistent encoding — will silently break for filenames like My Video.mp4 or non-ASCII asset names.

Fix: pipe asset through the same split("/").map(encodeURIComponent).join("/") helper that the overlay uses. Or extract to a shared encodePreviewPath() helper (the tail batch already has one at thumbnailUtils.ts:37-54 — reuse it).

Additional confirmed nit: useProbedDuration orphaned network fetch

RDJ's finding stands. AssetCard.tsx:40-63: cleanup only sets cancelled = true; the <video> element created via document.createElement("video") with preload="metadata" fires a network request when vid.src = src, and the cleanup never nulls vid.src or calls vid.load() to abort. On rapid scroll/filter in the sidebar, orphaned fetches queue up. Not corruption, real waste at scale.

Fix: in cleanup, call vid.removeAttribute("src") (or vid.src = "") + vid.load() before setting cancelled = true.

— Rames Jusso

@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch from ddb8f80 to 49c25f9 Compare July 11, 2026 10:49
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch 2 times, most recently from 3e255a1 to 84523e1 Compare July 11, 2026 12:51
@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch 2 times, most recently from 6fec739 to 31681da Compare July 11, 2026 21:08
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch from 84523e1 to 0dcdd15 Compare July 11, 2026 21:08
@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch from 31681da to dec2b9b Compare July 11, 2026 22:38
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch from 0dcdd15 to 2508687 Compare July 11, 2026 22:38

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

Final rebase review: current head inspected, latest required checks pass, no unresolved blocking findings. Approving; stack order/invariants remain as specified.

@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch from dec2b9b to b54a1b6 Compare July 11, 2026 23:26
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch from 2508687 to 5b7aa8c Compare July 11, 2026 23:26
@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch from b54a1b6 to e2f4782 Compare July 12, 2026 01:03
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch from 5b7aa8c to 5032b2d Compare July 12, 2026 01:04
@ukimsanov ukimsanov force-pushed the studio-dnd/pr18-nle-shell-assembly branch from e2f4782 to c39b84f Compare July 12, 2026 01:23
@ukimsanov ukimsanov force-pushed the studio-dnd/pr19-asset-card branch from 5032b2d to 2b47ce6 Compare July 12, 2026 01:23

@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-audit current head 2b47ce6: required checks pass; no new actionable threads. Approving current head.

@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 current head and verified the addressed feedback and required behavior. Approval is for this exact head; Graphite mergeability may still be pending.

What: AssetCard — the sidebar asset tile (thumbnail, hover preview, click
policy, context menu incl. add-at-playhead), unwired.

Why: standalone sidebar concept; AssetsTab adopts it in the app-shell swap.

How: new file against the coexistence layer (assetHelpers, AssetContextMenu,
assetClickBehavior, assetPreviewStore); one TEMP(studio-dnd) entry until
AssetsTab wires it.

Test plan: tsc --noEmit; fallow audit clean.
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.

5 participants