refactor(onboarding): play animations once with replay, fix search demo results

Two refinements from testing:
- the gallery-reveal, pipeline-drain, and search-typing animations looped
  forever; they now play once and stop, with a Replay button to re-run
- the search demo's result tiles didn't match the queries. Now: a filename
  search returns one exact file, '/s golden sunset over water' shows only
  ocean-sunset stills (added a second), and '/t landscape' shows the three
  actual landscapes (valleys + alpine lake), not a foggy forest
This commit is contained in:
2026-06-13 09:19:38 +01:00
parent f8e981c6f6
commit 09810cb868
5 changed files with 121 additions and 64 deletions
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react";
import { FakeTile } from "./fakes";
import { FakeTile, ReplayButton } from "./fakes";
const TILE_COUNT = 12;
const REVEAL_MS = 350;
const REVEAL_MS = 280;
const TILE_PROPS: { favorite?: boolean; rating?: number; duration?: string }[] = [
{},
@@ -19,17 +18,20 @@ const TILE_PROPS: { favorite?: boolean; rating?: number; duration?: string }[] =
{},
];
/// Placeholder tiles "loading in" one by one, looping, to show how the grid
/// fills while thumbnails are generated.
const TILE_COUNT = TILE_PROPS.length;
/// Placeholder tiles "loading in" once (skeleton → image), then stopping fully
/// revealed with a replay control.
export function StepGalleryPreview() {
const [revealed, setRevealed] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setRevealed((current) => (current >= TILE_COUNT + 4 ? 0 : current + 1));
}, REVEAL_MS);
return () => clearInterval(timer);
}, []);
if (revealed >= TILE_COUNT) return;
const timer = setTimeout(() => setRevealed((n) => n + 1), REVEAL_MS);
return () => clearTimeout(timer);
}, [revealed]);
const finished = revealed >= TILE_COUNT;
return (
<div>
@@ -44,15 +46,18 @@ export function StepGalleryPreview() {
))}
</div>
<div className="mt-6 divide-y divide-white/[0.05] text-xs leading-relaxed text-gray-500">
<p className="py-2.5">
<span className="text-gray-300">Click any tile</span> to open the lightbox keyboard navigation,
zoom, inline tag editing, ratings, and a full video player.
</p>
<p className="py-2.5">
<span className="text-gray-300">The toolbar</span> filters by type, favorites, or rating, sorts by
date/name/size, and switches grid density.
</p>
<div className="mt-6 flex items-start justify-between gap-4">
<div className="divide-y divide-white/[0.05] text-xs leading-relaxed text-gray-500">
<p className="pb-2.5">
<span className="text-gray-300">Click any tile</span> to open the lightbox keyboard navigation,
zoom, inline tag editing, ratings, and a full video player.
</p>
<p className="pt-2.5">
<span className="text-gray-300">The toolbar</span> filters by type, favorites, or rating, sorts by
date/name/size, and switches grid density.
</p>
</div>
{finished ? <ReplayButton onClick={() => setRevealed(0)} /> : null}
</div>
</div>
);