From 2e60005137ee60d6b5ec25295e20e486658fb5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 30 Mar 2026 19:33:09 +0200 Subject: [PATCH] feat(studio): hover-to-autoplay on render rows using shared ExpandedVideoPreview Extracted shared ExpandedVideoPreview component (src/components/ui/) used by both AssetsTab (video assets) and RenderQueueItem (rendered videos). Wraps completed render rows in ExpandOnHover with the same spring-expand + video autoplay pattern as the Compositions and Assets tabs. No code duplication: AssetsTab video case delegates to the shared component, RenderQueueItem reuses it with an Open action button. --- .../components/renders/RenderQueueItem.tsx | 53 ++++++++++++++++--- .../src/components/sidebar/AssetsTab.tsx | 32 +++++++---- .../components/ui/ExpandedVideoPreview.tsx | 37 +++++++++++++ 3 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 packages/studio/src/components/ui/ExpandedVideoPreview.tsx diff --git a/packages/studio/src/components/renders/RenderQueueItem.tsx b/packages/studio/src/components/renders/RenderQueueItem.tsx index d3d11198a..220a24523 100644 --- a/packages/studio/src/components/renders/RenderQueueItem.tsx +++ b/packages/studio/src/components/renders/RenderQueueItem.tsx @@ -1,4 +1,6 @@ import { memo, useCallback, useState, useEffect, useRef } from "react"; +import { ExpandOnHover } from "../ui/ExpandOnHover"; +import { ExpandedVideoPreview } from "../ui/ExpandedVideoPreview"; import type { RenderJob } from "./useRenderQueue"; interface RenderQueueItemProps { @@ -91,16 +93,15 @@ export const RenderQueueItem = memo(function RenderQueueItem({ [job.id, job.filename], ); - const isClickable = job.status === "complete"; + const viewSrc = `/api/render/${job.id}/view`; - return ( + const row = (
setHovered(true)} onPointerLeave={() => setHovered(false)} - onClick={isClickable ? handleOpen : undefined} className={[ "px-3 py-2.5 border-b border-neutral-800/30 last:border-0 transition-colors duration-150", - isClickable ? "cursor-pointer hover:bg-neutral-800/30" : "", + job.status === "complete" ? "cursor-pointer hover:bg-neutral-800/30" : "", ] .filter(Boolean) .join(" ")} @@ -108,7 +109,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
{/* Thumbnail — matches CompCard sizing (w-20 h-[45px]) */}
- {job.status === "complete" && } + {job.status === "complete" && } {job.status === "rendering" && (
@@ -139,7 +140,6 @@ export const RenderQueueItem = memo(function RenderQueueItem({ )}
- {/* Progress bar + percentage */} {job.status === "rendering" && (
@@ -214,4 +214,45 @@ export const RenderQueueItem = memo(function RenderQueueItem({
); + + // Completed renders: wrap in ExpandOnHover with shared ExpandedVideoPreview + // (same spring-expand pattern as AssetsTab video assets and CompositionsTab). + if (job.status !== "complete") { + return row; + } + + const subtitle = [ + job.durationMs ? formatDuration(job.durationMs) : null, + formatTimeAgo(job.createdAt), + ] + .filter(Boolean) + .join(" · "); + + return ( + { + e.stopPropagation(); + handleOpen(); + }} + className="px-4 py-1.5 text-xs font-semibold text-[#09090B] bg-[#3CE6AC] rounded-lg hover:brightness-110 transition-colors flex-shrink-0" + > + Open + + } + /> + } + onClick={handleOpen} + expandScale={0.5} + delay={500} + > + {row} + + ); }); diff --git a/packages/studio/src/components/sidebar/AssetsTab.tsx b/packages/studio/src/components/sidebar/AssetsTab.tsx index 7584b284c..70d7e53a7 100644 --- a/packages/studio/src/components/sidebar/AssetsTab.tsx +++ b/packages/studio/src/components/sidebar/AssetsTab.tsx @@ -1,5 +1,6 @@ import { memo, useState, useCallback, useRef } from "react"; import { ExpandOnHover } from "../ui/ExpandOnHover"; +import { ExpandedVideoPreview } from "../ui/ExpandedVideoPreview"; interface AssetsTabProps { projectId: string; @@ -112,22 +113,33 @@ function ExpandedAssetPreview({ isAudio: boolean; onCopy: () => void; }) { + if (isVideo) { + return ( + { + e.stopPropagation(); + onCopy(); + }} + className="px-4 py-1.5 text-xs font-semibold text-[#09090B] bg-[#3CE6AC] rounded-lg hover:brightness-110 transition-colors flex-shrink-0" + > + Copy Path + + } + /> + ); + } + return (
{isImage && ( {name} )} - {isVideo && ( -