Files
NibasaViewer/CLAUDE.md

176 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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('<USERNAME>', '<EMAIL>', '<PASSWORD>')
>>> 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`).