diff --git a/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs b/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs index 50f53a1..36caae0 100644 --- a/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs +++ b/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs @@ -473,7 +473,7 @@ public class StudioService(StudioDbContext db) ); private static SavedSceneResponse MapSceneResponse(SavedScene s) => new( - s.Id, s.OriginalSceneId, s.Key, s.Title, s.Image, s.SceneType, + s.Id, s.OriginalSceneId, s.Key, s.Title, s.Image, s.Demo, s.SceneType, s.Sort, s.SceneLengthSec, s.MinDurationSec, s.MaxDurationSec, s.OverlapAtEndSec, s.CanHandleDuration, s.ManualColorSelection, s.SelectedColorPresetId, s.Contents.Select(MapContentResponse).ToList(), diff --git a/services/studio/FlatRender.StudioSvc/Models/Responses/Responses.cs b/services/studio/FlatRender.StudioSvc/Models/Responses/Responses.cs index 4587772..4c90d25 100644 --- a/services/studio/FlatRender.StudioSvc/Models/Responses/Responses.cs +++ b/services/studio/FlatRender.StudioSvc/Models/Responses/Responses.cs @@ -62,6 +62,7 @@ public record SavedSceneResponse( string Key, string? Title, string? Image, + string? Demo, string SceneType, int Sort, decimal SceneLengthSec, diff --git a/src/components/studio/timeline/SceneThumbnailBlock.tsx b/src/components/studio/timeline/SceneThumbnailBlock.tsx index 70a601b..5570739 100644 --- a/src/components/studio/timeline/SceneThumbnailBlock.tsx +++ b/src/components/studio/timeline/SceneThumbnailBlock.tsx @@ -52,6 +52,7 @@ export function SceneThumbnailBlock({ const [editName, setEditName] = useState(scene.name); const resizeRef = useRef({ startX: 0, startDuration: scene.duration }); const inputRef = useRef(null); + const videoRef = useRef(null); const duration = resizeDuration ?? scene.duration; const width = Math.max(80, duration * pxPerSecond); @@ -134,6 +135,16 @@ export function SceneThumbnailBlock({ onSelect(); } }} + onMouseEnter={() => { + void videoRef.current?.play().catch(() => {}); + }} + onMouseLeave={() => { + const v = videoRef.current; + if (v) { + v.pause(); + v.currentTime = 0; + } + }} className={cn( "group relative h-20 w-full cursor-pointer overflow-hidden rounded-lg", isActive @@ -141,10 +152,10 @@ export function SceneThumbnailBlock({ : "hover:ring-1 hover:ring-gray-300" )} > - {/* Background: real thumbnail or gradient placeholder */} - {scene.thumbnailUrl ? ( + {/* Background: template still, Konva preview, or gradient placeholder */} + {scene.image || scene.thumbnailUrl ? ( )} + {/* Hover: play the template's per-scene loop preview */} + {scene.demo ? ( +