feat(onboarding): guided first-run tour with background FFmpeg provisioning

Phase 5 of the 0.1.0 release prep:
- FFmpeg no longer blocks startup (or crashes the app offline): provisioning
  runs in a background thread emitting throttled ffmpeg-progress events,
  with installed/downloading/failed state queryable via get_ffmpeg_status
  and a retry command
- video jobs are invisible to claiming and tier-priority checks until FFmpeg
  is ready, so they wait as pending without failing — and without starving
  image embeddings/tagging (include_videos gating in db.rs)
- 7-step show-don't-tell onboarding wizard: welcome + live FFmpeg progress,
  real first-folder picker, faked animating pipeline bar, placeholder
  gallery tiles, cycling search-syntax demo, views overview, and an AI
  features step with a real opt-in tagger download
- skippable at every point (Escape included), persisted via
  settings/onboarding_completed.txt, re-runnable from Settings > General,
  which also gains an FFmpeg status/retry row
This commit is contained in:
2026-06-12 23:09:05 +01:00
parent 1640e30330
commit 7403f0cfeb
17 changed files with 1139 additions and 30 deletions
+20
View File
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { CleanupOrphanedThumbnailsResult, DatabaseInfo, OrphanedThumbnailsInfo, TaggerAcceleration, TaggingQueueScope, VacuumResult, useGalleryStore } from "../store";
import { FfmpegStatusRow } from "./onboarding/StepWelcome";
type SettingsSection = "workspace" | "general";
@@ -184,6 +185,7 @@ export function SettingsModal() {
const updateError = useGalleryStore((state) => state.updateError);
const checkForUpdates = useGalleryStore((state) => state.checkForUpdates);
const installUpdate = useGalleryStore((state) => state.installUpdate);
const openOnboarding = useGalleryStore((state) => state.openOnboarding);
useEffect(() => {
if (!settingsOpen) return;
@@ -625,6 +627,24 @@ export function SettingsModal() {
</SettingsItem>
</SettingsGroup>
<SettingsGroup title="Setup">
<FfmpegStatusRow />
<SettingsItem
label="Welcome tour"
description="Replay the guided first-run tour — library setup, the background pipeline, search modes, and AI features."
>
<button
className="rounded-md border border-white/10 bg-white/[0.055] px-3 py-1.5 text-xs text-gray-300 transition-colors hover:bg-white/10 hover:text-white"
onClick={() => {
setSettingsOpen(false);
openOnboarding();
}}
>
Show welcome tour
</button>
</SettingsItem>
</SettingsGroup>
<SettingsGroup title="Storage & notifications">
<SettingsItem
label="App data folder"