153 lines
4.7 KiB
Markdown
153 lines
4.7 KiB
Markdown
|
|
# CLAUDE.md
|
|||
|
|
|
|||
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|||
|
|
|
|||
|
|
## 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
|
|||
|
|
|
|||
|
|
### Gallery Data Flow
|
|||
|
|
|
|||
|
|
**Filesystem as Data Source:**
|
|||
|
|
- Images served from `GALLERY_ROOT` path (configured in `local_settings.py`)
|
|||
|
|
- 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/views.py:32-52)
|
|||
|
|
- Paginates images at 28 per page (7 columns × 4 rows)
|
|||
|
|
- Converts flat image lists to table rows for HTML rendering
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
### Thumbnail Generation
|
|||
|
|
|
|||
|
|
**Lazy generation approach** (viewer/utils.py):
|
|||
|
|
- Thumbnails created on-demand when viewing gallery
|
|||
|
|
- 128×128 pixel size (hardcoded)
|
|||
|
|
- Uses Pillow for image processing
|
|||
|
|
- MIME-type validation via `filetype` library
|
|||
|
|
|
|||
|
|
**Batch pre-generation:**
|
|||
|
|
- `python manage.py makethumbnails` command available
|
|||
|
|
- Useful for initial setup or after adding many images
|
|||
|
|
|
|||
|
|
## Configuration
|
|||
|
|
|
|||
|
|
### Required Local Settings
|
|||
|
|
|
|||
|
|
Create `NibasaViewer/local_settings.py` (not in git):
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
GALLERY_ROOT = Path('/path/to/your/images')
|
|||
|
|
THUMBNAILS_ROOT = Path('/path/to/thumbnail/cache')
|
|||
|
|
|
|||
|
|
# Production settings
|
|||
|
|
DEBUG = False
|
|||
|
|
ALLOWED_HOSTS = ['yourdomain.com']
|
|||
|
|
SECRET_KEY = 'your-secret-key'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 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)
|
|||
|
|
- **filetype 1.2.0** - MIME-type detection
|
|||
|
|
- **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 `filetype.is_image(str(path))` to validate image files
|
|||
|
|
- Supports common formats (JPEG, PNG, GIF, WebP, etc.)
|