dtoro 5f11698907 feat: sorting + Google-Photos-style date-grouped timeline
Two related changes:

1. Sorting controls
   - filterStore gains sortBy (taken_at | added_at | filename | file_size
     | rating) and sortOrder (asc | desc), defaults taken_at desc.
   - filtersToParams sends sort + order to the backend list endpoint.
   - usePhotosQuery drops the hardcoded sort/order and reads from the
     store.
   - useFilterUrlSync round-trips ?sort= and ?order= so the choice
     persists in the URL.
   - FilterBar gets a Sort group with a field <select> and an asc/desc
     toggle button (ArrowDown / ArrowUp icons).

2. Date-grouped timeline (Google Photos style)
   - When sorted by a date field (taken_at or added_at), the Timeline
     now groups photos by month label ("April 2026") with a small
     header row between groups.
   - Refactored the virtualizer items from "rows of photos" to a flat
     mixed array of header | row items, with per-item heights via the
     virtualizer's estimateSize callback. Headers are 36px, photo rows
     are THUMBNAIL_SIZE + GAP.
   - buildItems() walks photos in order, breaks groups when the month
     label changes, and chunks each group into rows of `columns` cells.
     Photos with no taken_at fall back to "Unknown date".
   - For non-date sorts (filename / file_size / rating) the timeline
     reverts to a single un-headered stream — grouping by month
     wouldn't be meaningful.
   - Range selection and arrow-key nav still operate on the flat
     photos array, so grouping is purely a visual layer.
   - Also fixes a small bug: photo nav arrow-key handler now ignores
     events fired while focus is in an INPUT or TEXTAREA.

Sticky header overlay (the header that stays at the top while you
scroll past photos in its group) is intentionally deferred — inline
headers already give the visual grouping; the sticky behaviour is
polish for a follow-up.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:28:16 +02:00
2026-04-06 23:30:19 +02:00
2026-04-06 23:30:19 +02:00
2026-04-06 23:30:19 +02:00
2026-04-06 23:30:19 +02:00
2026-04-06 23:30:19 +02:00

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

  1. Clone the repository:
git clone <repository-url>
cd muleimage
  1. Configure your photo directories in .env:
# Edit .env file
PHOTO_DIRS=/path/to/your/photos
  1. Start the application:
docker-compose up -d
  1. 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
No description provided
Readme 13 MiB
Languages
Svelte 54.6%
TypeScript 26.5%
Go 18.2%
CSS 0.4%
Dockerfile 0.1%
Other 0.1%