Files
phokus/CLAUDE.md
T
LyAhn 665c315f56 feat(duplicates): phased scan progress with skipped-file reporting
Replace the [scanned, total] tuple progress event with a structured
{phase, processed, total, skipped} payload covering all three scan
phases (checking, hashing, confirming). find_duplicates now returns a
DuplicateScanResult with scanned/candidate/skipped counts, and the UI
surfaces phase labels plus a warning when unreadable files were
skipped. Also adds a clean:app script for cargo clean.
2026-06-12 08:12:49 +01:00

4.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project overview

Phokus — a Tauri v2 desktop image gallery app with a React/TypeScript frontend and a Rust backend. The app indexes local media folders, generates thumbnails via FFmpeg, computes visual embeddings for semantic search and similarity, and AI-tags images using the WD tagger (ONNX via ort). AI captioning code exists in the backend but the UI surface has been removed; the worker is commented out in lib.rs.

Commands

# Development (Vite hot-reload + Rust auto-rebuild)
pnpm dev:app

# Frontend only (no Tauri window)
pnpm dev:vite

# Production build
pnpm build:app

# Type-check frontend
pnpm build:vite

Use pnpm — never npm.

There are no test suites configured.

Architecture

Frontend (src/)

  • store.ts — single Zustand store (useGalleryStore) that owns all app state and all invoke() calls to the Tauri backend. Every feature (folders, images, search, similar images, tags, captions, tagger, duplicates) is implemented as store actions here. React components are thin consumers.
  • App.tsx — sets up Tauri event listeners (subscribeToProgress) and renders the top-level layout (sidebar + active view).
  • src/components/ — UI components: Gallery, Lightbox, Sidebar, Toolbar, TagCloud, DuplicateFinder, BackgroundTasks, SettingsModal, MenuBar, TitleBar.
  • State management: Zustand v5, no selectors library — components call useGalleryStore(s => s.field) directly.
  • Styling: Tailwind CSS v4 (Vite plugin, no config file).
  • Virtualized gallery grid: @tanstack/react-virtual.
  • Animation: framer-motion.

Search modes

The search bar supports prefix syntax parsed by parseSearchValue in store.ts:

  • No prefix / f: — filename search (paginated, DB-backed)
  • /s <query> or s: <query> — semantic (embedding) search
  • /t <tag> or t: <tag> — tag search

Backend (src-tauri/src/)

Workers are started in lib.rs and run as background threads throughout the app lifetime:

  • thumbnail worker (multiple threads, count from StorageProfile::Balanced)
  • metadata worker — FFmpeg probe for video files
  • embedding worker — generates CLIP-style visual embeddings (candle, HuggingFace hub)
  • tagging worker — WD tagger via ONNX Runtime (ort), DirectML/CPU acceleration

Key modules:

File Purpose
db.rs SQLite pool (r2d2 + rusqlite), schema migrations, all query functions
commands.rs All #[tauri::command] handlers — one-to-one with frontend invoke() calls
indexer.rs Worker thread launchers and job dispatch
embedder.rs Visual embedding generation (candle + HF hub models)
vector.rs sqlite-vec integration + HNSW index for ANN search
hnsw_index.rs In-memory HNSW index wrapper (hnsw_rs)
tagger.rs WD tagger: ONNX model download, inference, CSV tag loading
captioner.rs AI captioning (ONNX, disabled in workers but code intact)
thumbnail.rs Thumbnail generation (image crate + fast_image_resize, FFmpeg for video)
media.rs FFmpeg sidecar provisioning and probing
storage.rs StorageProfile for tuning worker counts

Database: SQLite with WAL mode, stored in the Tauri app data directory as gallery.db. Thumbnails stored alongside as thumbnails/.

Tauri events (backend → frontend)

Event Payload
index-progress IndexProgress
media-job-progress MediaJobProgressEvent
indexed-images IndexedImagesBatch
media-updated ThumbnailBatch
caption-model-progress CaptionModelProgress
tagger-model-progress TaggerModelProgress
duplicate_scan_progress { phase, processed, total, skipped }

Key types

ImageRecord (mirrored in store.ts and db.rs) is the central data type. It carries embedding status, tagging status, caption data, and media metadata. The frontend type must stay in sync with the Rust struct serialization.

Development notes

  • Hot Reload is active during dev:app — do not restart the server for frontend changes. Restart only when adding Rust crates or changing Vite config.
  • ML inference crates (candle-*, ort, image, rayon, tokenizers, xxhash-rust, rusqlite) use opt-level = 3 in dev profile to keep inference performance acceptable.
  • The caption worker is intentionally disabled (lib.rs:73) — the backend code is intact for future re-enabling.
  • Never use any type in TypeScript — look up correct types.