# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Additional guidance is provided in @AGENTS.md ## Project Overview Nibasa Viewer is a lightweight, pure HTML+CSS gallery viewer designed for compatibility with older browsers. It uses Django as a backend framework but serves images directly from the filesystem rather than using database models. **Key Design Principles:** - Filesystem-first architecture (no image/folder models in database) - No JavaScript or modern frontend frameworks - Broad browser compatibility (uses HTML tables, not flexbox/grid) - Dark theme optimized for image viewing - Lazy thumbnail generation with on-disk caching ## Development Commands ```bash # Install dependencies pip install -r requirements.txt # Run development server python manage.py runserver # Create user for gallery access (required) python manage.py shell >>> from django.contrib.auth.models import User >>> user = User.objects.create_user('', '', '') >>> user.save() >>> exit() # Pre-generate all thumbnails (optional, done on-demand otherwise) python manage.py makethumbnails # Django admin commands python manage.py migrate python manage.py createsuperuser python manage.py collectstatic ``` ## Architecture ### Single App Structure The project has one Django app: **`viewer`**. All gallery functionality is consolidated here: - `views.py` - Gallery directory browsing and image viewing (viewer/views.py:65-189) - `utils.py` - Thumbnail generation helpers - `templates/` - Pure HTML templates (no JavaScript) - `static/` - CSS styling and navigation icons - `views.py` - Thin delegating entry points that route to `viewer/directory.py` and `viewer/image.py` (see `gallery_view` in `viewer/views.py`). - `directory.py` - Directory browsing, search, and list rendering helpers. - `image.py` - Single-image rendering and metadata helpers. - `common.py` - Shared helpers: URL/query builders, sorting, breadcrumbs, thumbnail utilities. - `utils.py` - Thumbnail generation helpers. - `templates/` - HTML templates (minimal JS via Bootstrap where needed). - `static/` - CSS styling and navigation icons ### Gallery Data Flow **Filesystem as Data Source:** - Images served from `GALLERY_ROOT` path (configured in `.env`) - Thumbnails cached in `THUMBNAILS_ROOT` path - No database models for images or directories - Directory structure navigated using Python's `pathlib` **Static File Routing:** - `/imgs/` → Original images from `GALLERY_ROOT` - `/thumbs/` → Cached thumbnails from `THUMBNAILS_ROOT` - `/static/` → CSS and navigation icons ### Authentication Uses Django's built-in `django.contrib.auth` system: - All gallery views protected with `@login_required` decorator - Users manually created via Django shell (no signup form) - Database only stores user accounts (SQLite) - Login/logout redirects configured in NibasaViewer/settings.py:108-110 ### View Logic **`gallery_view()` function** (viewer/views.py:65-189) handles both: 1. **Directory browsing** when path points to folder: - Lists subdirectories and images - Supports search with recursive directory scanning (`viewer/directory.py:do_recursive_search`) - Does not use server-side pagination; the gallery returns full image lists and templates render them in a grid - Converts image lists to structured context used by templates 2. **Image viewing** when path points to file: - Displays single image with prev/next navigation - Finds sibling images in same directory - Links to full-resolution image The image view logic now lives in `viewer/image.py` and exposes additional context keys used by templates and tests (see `viewer/test.py`): `prev_url`, `next_url`, `prev_thumb`, `next_thumb`, `back_url`, `back_thumb`, `home_url`, `home_thumb`, and `image_meta` (filename/width/height/filesize/created/modified). ### Thumbnail Generation **Lazy generation approach** (viewer/utils.py): - Thumbnails created on-demand when viewing gallery - 128×128 pixel size (`THUMB_SIZE`) and cached under `THUMBNAILS_ROOT` - Uses Pillow for image processing - Fast extension-based image detection (no MIME-type I/O) **Batch pre-generation:** - `python manage.py makethumbnails` command available - Useful for initial setup or after adding many images ### Performance Optimizations **Extension-based image filtering** (viewer/utils.py:17-27): - Uses `is_image_file()` helper that checks file extensions instead of reading file contents - Supported extensions defined in `IMAGE_EXTENSIONS` constant - Eliminates ~99% of I/O operations when listing directories - Significantly faster than MIME-type detection for large directories ## Configuration ### Required Environment Variables Create `.env` (not in git): ```env GALLERY_ROOT=/path/to/your/images THUMBNAILS_ROOT=/path/to/thumbnail/cache ``` `settings.py` loads `.env` with `python-dotenv` and parses both values as `pathlib.Path`. Each variable is optional and resolves to `None` when unset or empty. ### Pagination Constants Defined in viewer/views.py:23-25: - `CELLS_PER_ROW = 7` - Image grid columns - `ROWS_PER_PAGE = 4` - Image grid rows - `IMAGES_PER_PAGE = 28` - Total images per page ## Production Deployment Systemd service files included for Gunicorn deployment: - `nibasaviewer.service` - Service configuration - `nibasaviewer.socket` - Socket activation Default production location: `/var/lib/NibasaViewer` ## Dependencies - **Django 4.2.3** - Web framework - **Pillow 10.0.0** - Image processing (thumbnails) - **python-dotenv 1.1.1** - Loads local `.env` configuration - **gunicorn 21.2.0** - WSGI server (production) ## Code Patterns **Template rendering:** - All views use `render(request, template, context)` - Context includes pagination data, image paths, and navigation state **Path handling:** - Use `pathlib.Path` for all filesystem operations - Convert to relative paths for URLs: `image.relative_to(settings.GALLERY_ROOT)` - Gallery URLs follow pattern: `/gallery/{relative_path}` **Image filtering:** - Use `is_image_file(path)` from `viewer.utils` to validate image files - Fast extension-based detection (checks `IMAGE_EXTENSIONS` set) - Supports common formats: JPEG, PNG, GIF, WebP, BMP, TIFF, SVG - Accepts both `pathlib.Path` and string paths ### Recent changes notes - The image view was refactored to match the gallery layout and styling; `viewer/templates/image_view.html` now uses the shared `viewer/static/css/styles.css` file and Bootstrap/Font Awesome includes like `gallery_view.html`. - `_render_image` in `viewer/views.py` now exposes additional context keys used by the template and tests: `prev_url`, `next_url`, `prev_thumb`, `next_thumb`, `back_url`, `back_thumb`, `home_url`, `home_thumb`, and `image_meta` (dictionary with `filename`, `width`, `height`, `filesize`, `created`, `modified`). Agents modifying image-view behavior should update tests in `viewer/test.py` as well. - The top-bar sort button for the image view was replaced by an Info button (`fa-circle-info`) which shows a dropdown containing image metadata. The dropdown is vertically scrollable for long content. - The main displayed image now uses the same drop shadow and rounded corners as thumbnails (styles added to `viewer/static/css/styles.css`).