feat(onboarding): add a closing 'Staying current' step on updates + the app mark

- new StepUpdates: explains the title-bar update indicator with a mini
  app-window mockup, and introduces the aperture mark as the app's identity
- move the tour-closing copy off the AI features step onto this final step
- changelog: note the title-bar update indicator + one-click install, and the
  new onboarding step
This commit is contained in:
2026-06-14 18:26:35 +01:00
parent 2785b7d5e6
commit 0144526a3d
4 changed files with 95 additions and 5 deletions
+5 -3
View File
@@ -38,9 +38,11 @@ installer with a built-in updater.
the background-tasks bar. the background-tasks bar.
- **Guided first-run onboarding** — background FFmpeg provisioning with live - **Guided first-run onboarding** — background FFmpeg provisioning with live
progress and retry, a walkthrough of the library, pipeline, search modes, progress and retry, a walkthrough of the library, pipeline, search modes,
and views, and an optional AI-tagger download. Re-runnable from Settings. views, and updates, plus an optional AI-tagger download. Re-runnable from
- **Updater** — checks GitHub Releases on launch and from Settings; downloads Settings.
and installs signed update artifacts. - **Updater** — checks GitHub Releases on launch and from Settings; a title-bar
indicator lights up when a new version is ready, and one click downloads,
installs the signed update, and relaunches.
- **Maintenance** — database compaction and orphaned-thumbnail cleanup from - **Maintenance** — database compaction and orphaned-thumbnail cleanup from
Settings, with live size/reclaimable stats. Settings, with live size/reclaimable stats.
- **Window state** persistence and single-instance handling. - **Window state** persistence and single-instance handling.
@@ -8,6 +8,7 @@ import { StepGalleryPreview } from "./StepGalleryPreview";
import { StepSearchDemo } from "./StepSearchDemo"; import { StepSearchDemo } from "./StepSearchDemo";
import { StepViews } from "./StepViews"; import { StepViews } from "./StepViews";
import { StepAiFeatures } from "./StepAiFeatures"; import { StepAiFeatures } from "./StepAiFeatures";
import { StepUpdates } from "./StepUpdates";
const STEPS: { id: string; title: string; component: () => React.ReactNode }[] = [ const STEPS: { id: string; title: string; component: () => React.ReactNode }[] = [
{ id: "welcome", title: "Welcome", component: () => <StepWelcome /> }, { id: "welcome", title: "Welcome", component: () => <StepWelcome /> },
@@ -17,6 +18,7 @@ const STEPS: { id: string; title: string; component: () => React.ReactNode }[] =
{ id: "search", title: "Search", component: () => <StepSearchDemo /> }, { id: "search", title: "Search", component: () => <StepSearchDemo /> },
{ id: "views", title: "Views", component: () => <StepViews /> }, { id: "views", title: "Views", component: () => <StepViews /> },
{ id: "ai", title: "AI features", component: () => <StepAiFeatures /> }, { id: "ai", title: "AI features", component: () => <StepAiFeatures /> },
{ id: "updates", title: "Staying current", component: () => <StepUpdates /> },
]; ];
export function OnboardingOverlay() { export function OnboardingOverlay() {
+2 -2
View File
@@ -105,8 +105,8 @@ export function StepAiFeatures() {
</div> </div>
<p className="mt-5 text-xs leading-relaxed text-gray-500"> <p className="mt-5 text-xs leading-relaxed text-gray-500">
That's the tour. Add folders from the sidebar, and revisit any of this from Settings including Semantic search and similarity are part of the standard pipeline; AI tagging stays optional and
re-running this tour. can be downloaded any time from Settings.
</p> </p>
</div> </div>
); );
+86
View File
@@ -0,0 +1,86 @@
import { PhokusMark } from "../PhokusMark";
// Closing step. Introduces the app mark (the aperture in the title bar) and,
// since that same mark doubles as the update indicator, explains how updates
// work in one place. The mini app-window mockup shows the lit mark exactly
// where it appears, so there's nothing abstract to decode.
export function StepUpdates() {
return (
<div>
<p className="text-sm leading-relaxed text-gray-300">
Phokus keeps itself up to date it quietly checks for new versions on startup, so you don't
have to go looking for one.
</p>
<h4 className="mt-6 text-[12px] font-semibold uppercase tracking-[0.08em] text-gray-400">
When an update is ready
</h4>
<div className="mt-1 py-4">
<p className="text-sm text-white">The mark in the title bar lights up</p>
<p className="mt-1 text-xs leading-relaxed text-gray-500">
That aperture in the top-left corner is Phokus. When a new version is waiting, its focal point
glows amber — click it to update and relaunch. Nothing installs on its own; you're always in
control.
</p>
{/* Mini app-window mockup: the lit mark shown in a real title bar. */}
<div className="mt-3 overflow-hidden rounded-lg border border-white/10 bg-gray-950 shadow-lg">
<div className="flex items-center gap-2 border-b border-white/[0.06] px-3 py-2">
<div className="relative flex h-6 w-6 items-center justify-center rounded-md bg-white/[0.08] text-gray-300">
<span className="pointer-events-none absolute left-1/2 top-1/2 h-3.5 w-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-amber-400/30 blur-[3px]" />
<span className="pointer-events-none absolute left-1/2 top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-amber-400/55 animate-ping" />
<PhokusMark className="relative h-[18px] w-[18px]" dotClassName="fill-amber-400" />
</div>
<span className="text-xs font-semibold tracking-wide text-gray-300">Phokus</span>
<div className="flex-1" />
<div className="flex items-center gap-3.5 text-gray-600">
<svg width="9" height="9" viewBox="0 0 10 10" fill="none">
<rect y="4.5" width="10" height="1" rx="0.5" fill="currentColor" />
</svg>
<svg width="9" height="9" viewBox="0 0 10 10" fill="none">
<rect x="0.5" y="0.5" width="9" height="9" rx="1.5" stroke="currentColor" />
</svg>
<svg width="9" height="9" viewBox="0 0 10 10" fill="none">
<path d="M1 1L9 9M9 1L1 9" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" />
</svg>
</div>
</div>
{/* Ghosted window body, just enough to read as the app. */}
<div className="flex h-[72px] bg-gray-900/30">
<div className="w-14 shrink-0 border-r border-white/[0.05] p-2.5">
<div className="h-1.5 w-full rounded bg-white/[0.07]" />
<div className="mt-2 h-1.5 w-3/4 rounded bg-white/[0.04]" />
<div className="mt-2 h-1.5 w-3/4 rounded bg-white/[0.04]" />
</div>
<div className="flex-1 p-2.5">
<div className="h-2 w-20 rounded bg-white/[0.06]" />
<div className="mt-2.5 grid grid-cols-5 gap-1.5">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="h-7 rounded bg-white/[0.04]" />
))}
</div>
</div>
</div>
</div>
</div>
<h4 className="mt-6 text-[12px] font-semibold uppercase tracking-[0.08em] text-gray-400">
Prefer to manage it yourself?
</h4>
<div className="mt-1 py-4">
<p className="text-sm text-white">Check any time from Settings</p>
<p className="mt-1 text-xs leading-relaxed text-gray-500">
Settings General shows your current version with a manual{" "}
<span className="text-gray-300">Check for updates</span> button, so you can update on your own
schedule.
</p>
</div>
<p className="mt-5 text-xs leading-relaxed text-gray-500">
That's the tour. Add folders from the sidebar, and revisit any of this from Settings including
re-running this tour.
</p>
</div>
);
}