From 1a85c92881910e1a3001f859c6f3751babe133c7 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 20 Jul 2026 21:44:01 +0200 Subject: [PATCH] perf(studio): schedule adaptive timeline thumbnails --- .../src/components/TimelineToolbar.test.tsx | 21 +- .../studio/src/components/TimelineToolbar.tsx | 30 +- .../src/hooks/useRenderClipContent.test.ts | 5 +- .../studio/src/hooks/useRenderClipContent.ts | 13 +- .../src/player/lib/thumbnailPolicy.test.ts | 14 + .../studio/src/player/lib/thumbnailPolicy.ts | 21 + .../src/player/lib/thumbnailScheduler.test.ts | 281 ++++++++++++ .../src/player/lib/thumbnailScheduler.ts | 425 ++++++++++++++++++ .../studio/src/player/store/playerStore.ts | 8 + .../src/utils/studioUiPreferences.test.ts | 14 + .../studio/src/utils/studioUiPreferences.ts | 6 + 11 files changed, 834 insertions(+), 4 deletions(-) create mode 100644 packages/studio/src/player/lib/thumbnailPolicy.test.ts create mode 100644 packages/studio/src/player/lib/thumbnailPolicy.ts create mode 100644 packages/studio/src/player/lib/thumbnailScheduler.test.ts create mode 100644 packages/studio/src/player/lib/thumbnailScheduler.ts diff --git a/packages/studio/src/components/TimelineToolbar.test.tsx b/packages/studio/src/components/TimelineToolbar.test.tsx index 0050dcfc95..2e7900e826 100644 --- a/packages/studio/src/components/TimelineToolbar.test.tsx +++ b/packages/studio/src/components/TimelineToolbar.test.tsx @@ -12,7 +12,7 @@ import { TimelineToolbar } from "./TimelineToolbar"; afterEach(() => { document.body.innerHTML = ""; - usePlayerStore.setState({ autoKeyframeEnabled: true }); + usePlayerStore.setState({ autoKeyframeEnabled: true, thumbnailMode: "adaptive" }); }); function renderToolbar( @@ -58,6 +58,25 @@ describe("TimelineToolbar — auto-keyframe toggle (#1808)", () => { act(() => root.unmount()); }); }); + +describe("TimelineToolbar — adaptive thumbnails", () => { + it("keeps a user-controlled hidden mode as the rollback path", () => { + const { host, root } = renderToolbar(); + const button = host.querySelector( + 'button[aria-label="Hide thumbnails — labels only"]', + ); + if (!button) throw new Error("thumbnail toggle not rendered"); + + act(() => button.click()); + + expect(usePlayerStore.getState().thumbnailMode).toBe("hidden"); + expect(button.getAttribute("aria-label")).toBe( + "Show thumbnails — posters stay visible; richer previews appear on interaction", + ); + act(() => root.unmount()); + }); +}); + describe("TimelineToolbar — motion path endpoints", () => { it("does not advertise a destructive keyframe toggle for a required endpoint", () => { usePlayerStore.setState({ currentTime: 10 }); diff --git a/packages/studio/src/components/TimelineToolbar.tsx b/packages/studio/src/components/TimelineToolbar.tsx index 43416414d5..99928935da 100644 --- a/packages/studio/src/components/TimelineToolbar.tsx +++ b/packages/studio/src/components/TimelineToolbar.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from "react"; -import { Magnet, MagnifyingGlassMinus, MagnifyingGlassPlus } from "@phosphor-icons/react"; +import { Image, Magnet, MagnifyingGlassMinus, MagnifyingGlassPlus } from "@phosphor-icons/react"; import { useEnableKeyframes, isPlayheadWithinTween, @@ -115,6 +115,9 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool const setTimelineSnapEnabled = usePlayerStore((s) => s.setTimelineSnapEnabled); const autoKeyframeEnabled = usePlayerStore((s) => s.autoKeyframeEnabled); const setAutoKeyframeEnabled = usePlayerStore((s) => s.setAutoKeyframeEnabled); + const thumbnailMode = usePlayerStore((s) => s.thumbnailMode); + const setThumbnailMode = usePlayerStore((s) => s.setThumbnailMode); + const thumbnailsVisible = thumbnailMode === "adaptive"; // Subscribe so the add-beat button reacts to playhead movement and analysis load. const currentTime = usePlayerStore((s) => s.currentTime); const beatAnalysisReady = usePlayerStore((s) => s.beatAnalysis !== null); @@ -410,6 +413,31 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool })()}
+ + +