import { useEffect } from "react"; import { TaggerModel, TaggerModelProgress, useGalleryStore } from "../../store"; import { TAGGER_MODELS } from "../../taggerModels"; import { FakeProgressBar, formatBytes } from "./fakes"; const FAKE_TAGS = ["landscape", "sunset", "outdoors", "no_humans", "ocean", "cloudy_sky"]; // Prefer the current file's byte fraction (the 1.3 GB model dominates); fall // back to the coarse step count; null renders an indeterminate bar. function taggerDownloadFraction(progress: TaggerModelProgress | null): number | null { if (!progress) return null; if (progress.downloaded_bytes != null && progress.total_bytes != null && progress.total_bytes > 0) { return progress.downloaded_bytes / progress.total_bytes; } if (progress.total_files > 0) return progress.completed_files / progress.total_files; return null; } function TaggerModelChoice({ model, current, disabled, onSelect }: { model: TaggerModel; current: TaggerModel; disabled: boolean; onSelect: (model: TaggerModel) => void; }) { const active = model === current; return ( ); } export function StepAiFeatures() { const taggerModelStatus = useGalleryStore((s) => s.taggerModelStatus); const taggerModelPreparing = useGalleryStore((s) => s.taggerModelPreparing); const taggerModelProgress = useGalleryStore((s) => s.taggerModelProgress); const taggerModelError = useGalleryStore((s) => s.taggerModelError); const taggerModel = useGalleryStore((s) => s.taggerModel); const prepareTaggerModel = useGalleryStore((s) => s.prepareTaggerModel); const loadTaggerModel = useGalleryStore((s) => s.loadTaggerModel); const loadTaggerModelStatus = useGalleryStore((s) => s.loadTaggerModelStatus); const setTaggerModel = useGalleryStore((s) => s.setTaggerModel); useEffect(() => { void loadTaggerModel(); void loadTaggerModelStatus(); }, [loadTaggerModel, loadTaggerModelStatus]); const taggerReady = taggerModelStatus?.ready ?? false; const taggerStatusLabel = taggerReady ? "Installed" : taggerModelPreparing ? "Preparing" : "Not installed"; return (
Phokus's AI runs entirely on this machine — nothing is sent anywhere. Semantic search sets itself up automatically; AI tagging is optional and only downloads if you want it.
Automatic tags for every image
The AI tagger model labels images so you can search with{" "}
/t — tags look like:
Current: {TAGGER_MODELS[taggerModel].name} · {taggerStatusLabel}
{TAGGER_MODELS[taggerModel].description}
{FAKE_TAGS.map((tag) => ( {tag} ))}Download failed: {taggerModelError}
) : null}Search by meaning, find look-alikes
Powers /s search,
"find similar", and the Explore view, so it's part of the standard pipeline: the CLIP model
(~580 MB) downloads automatically the first time embeddings run. Nothing to do — you'll see it
in the background-tasks bar.
Semantic search and similarity are part of the standard pipeline; AI tagging stays optional and can be downloaded any time from Settings.