From 09810cb868c278bdb17d5556028b0e89ccb60663 Mon Sep 17 00:00:00 2001 From: LyAhn Date: Sat, 13 Jun 2026 09:19:38 +0100 Subject: [PATCH] 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 --- src/assets/onboarding/sunset2.webp | Bin 0 -> 34680 bytes .../onboarding/StepGalleryPreview.tsx | 43 +++++----- src/components/onboarding/StepPipeline.tsx | 74 +++++++++++------- src/components/onboarding/StepSearchDemo.tsx | 41 +++++++--- src/components/onboarding/fakes.tsx | 27 ++++++- 5 files changed, 121 insertions(+), 64 deletions(-) create mode 100644 src/assets/onboarding/sunset2.webp diff --git a/src/assets/onboarding/sunset2.webp b/src/assets/onboarding/sunset2.webp new file mode 100644 index 0000000000000000000000000000000000000000..927828cb2ea905274c8c62857051b68b5e5f9a1d GIT binary patch literal 34680 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 { - 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 (
@@ -44,15 +46,18 @@ export function StepGalleryPreview() { ))}
-
-

- Click any tile to open the lightbox — keyboard navigation, - zoom, inline tag editing, ratings, and a full video player. -

-

- The toolbar filters by type, favorites, or rating, sorts by - date/name/size, and switches grid density. -

+
+
+

+ Click any tile to open the lightbox — keyboard navigation, + zoom, inline tag editing, ratings, and a full video player. +

+

+ The toolbar filters by type, favorites, or rating, sorts by + date/name/size, and switches grid density. +

+
+ {finished ? setRevealed(0)} /> : null}
); diff --git a/src/components/onboarding/StepPipeline.tsx b/src/components/onboarding/StepPipeline.tsx index 5cee730..c6c3550 100644 --- a/src/components/onboarding/StepPipeline.tsx +++ b/src/components/onboarding/StepPipeline.tsx @@ -1,29 +1,40 @@ import { useEffect, useState } from "react"; -import { FakeProgressBar, FakeStageTag } from "./fakes"; +import { FakeProgressBar, FakeStageTag, ReplayButton } from "./fakes"; const STAGES = ["Thumbnails", "Metadata", "Embeddings", "Tags"] as const; const STAGE_TOTAL = 128; -const TICK_MS = 90; -const STEP_PER_TICK = 6; +const TICK_MS = 80; +const STEP_PER_TICK = 8; -/// A looping, entirely fake render of the background-tasks bar: each stage -/// drains in order, then the cycle restarts. +/// A one-shot fake of the background-tasks bar: each stage drains in order, +/// then it stops at "all done" with a replay control. export function StepPipeline() { const [stageIndex, setStageIndex] = useState(0); - const [done, setDone] = useState(0); + const [filled, setFilled] = useState(0); + + const finished = stageIndex >= STAGES.length; useEffect(() => { - const timer = setInterval(() => { - setDone((current) => { - if (current + STEP_PER_TICK < STAGE_TOTAL) return current + STEP_PER_TICK; - setStageIndex((stage) => (stage + 1) % STAGES.length); - return 0; - }); + if (finished) return; + const timer = setTimeout(() => { + // Read from the closure and call setters directly — nesting one setter + // inside another's updater double-advances under React StrictMode. + if (filled + STEP_PER_TICK < STAGE_TOTAL) { + setFilled(filled + STEP_PER_TICK); + } else { + setStageIndex(stageIndex + 1); + setFilled(0); + } }, TICK_MS); - return () => clearInterval(timer); - }, []); + return () => clearTimeout(timer); + }, [filled, stageIndex, finished]); - const remaining = STAGE_TOTAL - done; + const replay = () => { + setStageIndex(0); + setFilled(0); + }; + + const remaining = STAGE_TOTAL - filled; return (
@@ -41,32 +52,37 @@ export function StepPipeline() {
- - + {!finished ? ( + + ) : null} + Holiday Photos
{STAGES.map((stage, i) => ( ))}
- +
-
-

- It's all interruptible. Close the app whenever you like — - the queue picks up where it left off next launch. -

-

- Per-folder control. Right-click a folder in the sidebar to - pause its background work, and click the bar itself to expand a detailed per-folder view. -

+
+
+

+ It's all interruptible. Close the app whenever you like — + the queue picks up where it left off next launch. +

+

+ Per-folder control. Right-click a folder in the sidebar to + pause its background work, and click the bar itself to expand a detailed per-folder view. +

+
+ {finished ? : null}
); diff --git a/src/components/onboarding/StepSearchDemo.tsx b/src/components/onboarding/StepSearchDemo.tsx index 926ba54..e2a4545 100644 --- a/src/components/onboarding/StepSearchDemo.tsx +++ b/src/components/onboarding/StepSearchDemo.tsx @@ -1,11 +1,11 @@ import { useEffect, useState } from "react"; -import { SEARCH_RESULTS } from "./fakes"; +import { ReplayButton, SEARCH_RESULTS } from "./fakes"; const DEMOS = [ { query: "beach-day_042.jpg", mode: "Filename", - description: "Plain text matches file names — the default, instant search.", + description: "Plain text matches file names — the default, instant search. One name, one file.", results: SEARCH_RESULTS.filename, }, { @@ -25,25 +25,39 @@ const DEMOS = [ const TYPE_MS = 55; const HOLD_MS = 2600; -/// A mock search bar that "types" each demo query, shows fake results, and -/// cycles to the next mode. +/// Types each demo query, shows matching results, advances through all three +/// modes once, then stops on the last with a replay control. export function StepSearchDemo() { const [demoIndex, setDemoIndex] = useState(0); const [typed, setTyped] = useState(0); + const [finished, setFinished] = useState(false); const demo = DEMOS[demoIndex]; + const isLastDemo = demoIndex === DEMOS.length - 1; useEffect(() => { + if (finished) return; if (typed < demo.query.length) { const timer = setTimeout(() => setTyped((n) => n + 1), TYPE_MS); return () => clearTimeout(timer); } + // Fully typed: hold, then advance — or finish on the last demo. const timer = setTimeout(() => { - setDemoIndex((i) => (i + 1) % DEMOS.length); - setTyped(0); + if (isLastDemo) { + setFinished(true); + } else { + setDemoIndex((i) => i + 1); + setTyped(0); + } }, HOLD_MS); return () => clearTimeout(timer); - }, [typed, demo.query.length]); + }, [typed, demo.query.length, isLastDemo, finished]); + + const replay = () => { + setDemoIndex(0); + setTyped(0); + setFinished(false); + }; const fullyTyped = typed >= demo.query.length; @@ -61,23 +75,26 @@ export function StepSearchDemo() { {demo.query.slice(0, typed)} - | + {!finished ? | : null} {demo.mode}
- {/* Fake results */} -
+ {/* Results — fixed-width tiles, centered, so 1/2/3 results all look right */} +
{demo.results.map((src, i) => ( -
+
))}
-

{demo.description}

+
+

{demo.description}

+ {finished ? : null} +
); } diff --git a/src/components/onboarding/fakes.tsx b/src/components/onboarding/fakes.tsx index c3e2896..ad43dba 100644 --- a/src/components/onboarding/fakes.tsx +++ b/src/components/onboarding/fakes.tsx @@ -3,6 +3,7 @@ // demo progress — so new users see the real UI's shapes before their own // library exists. import sunset from "../../assets/onboarding/sunset.webp"; +import sunset2 from "../../assets/onboarding/sunset2.webp"; import beach from "../../assets/onboarding/beach.webp"; import landscape1 from "../../assets/onboarding/landscape1.webp"; import landscape2 from "../../assets/onboarding/landscape2.webp"; @@ -37,11 +38,14 @@ export function fakeImage(index: number): string { return FAKE_IMAGES[index % FAKE_IMAGES.length]; } -// Curated result sets so each search demo returns thematically right images. +// Result sets matched to what each query would genuinely return. +// - filename: one exact file (a filename search hits a specific name) +// - semantic "golden sunset over water": only the ocean-sunset stills +// - tags "landscape": mountain valleys + the alpine lake (all landscapes) export const SEARCH_RESULTS = { - filename: [beach, dunes, sunset], // "beach-day_042.jpg" - semantic: [sunset, alpinelake, beach], // "golden sunset over water" - tags: [landscape1, landscape2, forest], // "landscape" + filename: [beach], + semantic: [sunset, sunset2], + tags: [landscape1, landscape2, alpinelake], } as const; // Abstract gradient blobs/ticks for the Explore & Timeline mini-previews, @@ -133,6 +137,21 @@ export function FakeProgressBar({ fraction, className = "" }: { fraction: number ); } +export function ReplayButton({ onClick, label = "Replay" }: { onClick: () => void; label?: string }) { + return ( + + ); +} + export function formatBytes(bytes: number): string { if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`; if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;