2679214cb9c2c2d4201f60af2c4d2d491503d941
The loupe view is now called "preview" everywhere — file paths, type
names, store actions, and the contextual hint pill. There's a single
preview action bound to E and Space (Enter is gone); double-click on a
thumbnail still works. Both shortcuts toggle: open from grid, close
from preview.
This commit also folds in the fix for the "preview shows nothing" bug
the user just hit:
- Extract usePhotosQuery into frontend/src/hooks/usePhotosQuery.ts so
Timeline, PreviewView, and App.tsx all share one query — and one
cache entry. Previously PreviewView and App.tsx looked the cache up
by ['photos'], but the Timeline query key gained the filter params
(['photos', filterParams]) when the filter bar shipped, so the
lookup returned undefined and the preview rendered "No photo to
display". App.tsx's getFirstPhotoId callback had the same bug.
- Harden PreviewImage: render the <img> immediately and overlay the
spinner with absolute positioning, instead of toggling opacity-0 →
opacity-100 on load. The previous opacity-toggle could leave the
image stuck invisible if the load event raced with a key change.
- Add { preventDefault: true } to every useHotkeys call so single
letter shortcuts (1-5, P, X, U) no longer leak into Firefox quick-
find, and Cmd/Ctrl+F no longer triggers the browser find toolbar.
Files renamed:
components/loupe/LoupeView.tsx -> components/preview/PreviewView.tsx
components/loupe/LoupeImage.tsx -> components/preview/PreviewImage.tsx
components/loupe/LoupeFilmstrip.tsx -> components/preview/PreviewFilmstrip.tsx
components/loupe/loupeSrc.ts -> components/preview/previewSrc.ts
Symbol renames: openLoupe→openPreview, closeLoupe→closePreview, the
viewMode 'loupe' tag → 'preview', and all the LoupeXxx component and
helper exports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mulita - Self-Hosted Photo Management Application
A self-hosted, Docker-deployed photo management application inspired by Lightroom's workflow. Mulita provides a fast, keyboard-driven interface to browse, organize, tag, and manage your photo library.
Features
- Photo Organization: Browse photos in a timeline view with virtual scrolling for performance
- Thumbnail Generation: Automatic thumbnail generation for all photo formats including RAW
- Metadata Extraction: Full EXIF/XMP metadata extraction and search
- Keyboard Shortcuts: Lightroom-style keyboard navigation and actions
- File Support: JPEG, PNG, RAW formats (CR2, CR3, NEF, ARW, etc.), HEIC/HEIF, and videos
- Heaps: Temporary collections for organizing photos
- Tags & Ratings: Organize with tags, star ratings, and color labels
- Dark Mode: Photography-optimized dark interface
Tech Stack
Backend
- Python 3.12 with FastAPI
- SQLite with SQLAlchemy (async)
- Celery + Redis for background tasks
- pyvips for fast thumbnail generation
- ExifTool for metadata extraction
Frontend
- React 18 with TypeScript
- Vite for fast development
- TanStack Query for data fetching
- TanStack Virtual for virtualized scrolling
- Tailwind CSS for styling
- Zustand for state management
Quick Start
Prerequisites
- Docker and Docker Compose
- Photo directories to mount
Setup
- Clone the repository:
git clone <repository-url>
cd muleimage
- Configure your photo directories in
.env:
# Edit .env file
PHOTO_DIRS=/path/to/your/photos
- Start the application:
docker-compose up -d
- Access the application at
http://localhost:3000
Architecture
The application consists of 5 Docker services:
- frontend: React SPA served by Nginx
- backend: FastAPI REST API
- worker: Celery workers for background tasks
- redis: Message broker for Celery
- db: SQLite database (file-based)
Keyboard Shortcuts
| Key | Action |
|---|---|
← → ↑ ↓ |
Navigate photos |
Space |
Quick preview |
Enter |
Open loupe view |
P |
Pick photo |
X |
Reject photo |
1-5 |
Set star rating |
Tab |
Toggle left sidebar |
I |
Toggle metadata panel |
G |
Grid view |
E |
Loupe view |
Delete |
Move to trash |
Development
Backend Development
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload
Frontend Development
cd frontend
npm install
npm run dev
Configuration
Edit mulita.yml to configure:
- Source photo directories
- Thumbnail sizes and quality
- Scanner settings
- Performance tuning
Performance
- Handles 100,000+ photos efficiently
- Virtual scrolling for smooth timeline navigation
- Thumbnail generation at 10+ photos/second
- SQLite FTS5 for fast full-text search
Future Features (Phase 2)
- AI-powered scene classification
- Face detection and clustering
- Smart albums
- Duplicate detection
- Export presets
- Multi-user support
License
MIT
Description
Languages
Svelte
54.6%
TypeScript
26.5%
Go
18.2%
CSS
0.4%
Dockerfile
0.1%
Other
0.1%