🎨 manage comics via yaml files

This commit is contained in:
mi
2025-11-15 19:20:21 +10:00
parent 91b6d4efeb
commit 13176c68d2
10 changed files with 419 additions and 80 deletions

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
Sunday Comics is a Flask-based webcomic website with server-side rendering and client-side navigation. Comics are stored as simple Python dictionaries in `comics_data.py`, making the system easy to manage without a database.
Sunday Comics is a Flask-based webcomic website with server-side rendering and client-side navigation. Comics are stored as individual YAML files in `data/comics/`, making them easy to manage without a database. Each comic gets its own file for clean organization and version control.
## Development Commands
@@ -24,7 +24,13 @@ python app.py
```bash
python scripts/add_comic.py
```
This creates a new entry in `comics_data.py` with defaults. Edit the file afterwards to customize title, alt_text, author_note, etc.
This creates a new YAML file in `data/comics/` with the next comic number and reasonable defaults. Edit the generated file to customize title, alt_text, author_note, etc.
**Add a new comic with markdown author note:**
```bash
python scripts/add_comic.py -m
```
This also creates a markdown file in `content/author_notes/` for the author note.
**Generate RSS feed:**
```bash
@@ -40,8 +46,23 @@ Run this after adding/updating comics to regenerate `static/sitemap.xml` for sea
## Architecture
### Data Layer: comics_data.py
Comics are stored as a Python list called `COMICS`. Each comic is a dictionary with:
### Data Layer: YAML Files in data/comics/
Comics are stored as individual YAML files in the `data/comics/` directory. The `data_loader.py` module automatically loads all `.yaml` files (except `TEMPLATE.yaml` and `README.yaml`), sorts them by comic number, and builds the `COMICS` list.
**File structure:**
- `data/comics/001.yaml` - Comic #1
- `data/comics/002.yaml` - Comic #2
- `data/comics/003.yaml` - Comic #3
- `data/comics/TEMPLATE.yaml` - Template for new comics (ignored by loader)
- `data/comics/README.md` - Documentation for comic files
**Adding a new comic:**
1. Use `python scripts/add_comic.py` to auto-generate the next comic file
2. OR manually copy `TEMPLATE.yaml` and rename it
3. Edit the YAML file to set comic properties
Each comic YAML file contains:
- `number` (required): Sequential comic number
- `filename` (required): Image filename in `static/images/comics/` OR list of filenames for multi-image comics (webtoon style)
- `date` (required): Publication date in YYYY-MM-DD format
@@ -53,11 +74,19 @@ Comics are stored as a Python list called `COMICS`. Each comic is a dictionary w
- `plain` (optional): Override global PLAIN_DEFAULT setting (hides header/border)
- `section` (optional): Section/chapter title (e.g., "Chapter 1: Origins"). Add to first comic of a new section.
**Multi-image comics (webtoon style):**
- Set `filename` to a list of image filenames: `['page1.png', 'page2.png', 'page3.png']`
- Set `alt_text` to either:
- A single string (applies to all images): `'A three-part vertical story'`
- A list matching each image: `['Description 1', 'Description 2', 'Description 3']`
**Multi-image comics (webtoon style) in YAML:**
```yaml
filename:
- page1.png
- page2.png
- page3.png
alt_text:
- "Description 1"
- "Description 2"
- "Description 3"
```
- Set `filename` to a list of image filenames
- Set `alt_text` to either a single string (applies to all images) or a list matching each image
- If `alt_text` is a list but doesn't match `filename` length, a warning is logged
- Images display vertically with seamless stacking (no gaps)
- First image loads immediately; subsequent images lazy-load as user scrolls
@@ -154,21 +183,25 @@ Global context variables injected into all templates:
## Important Implementation Details
1. **Comic ordering**: COMICS list order determines comic sequence. Last item is the "latest" comic.
1. **Comic loading**: The `data_loader.py` module scans `data/comics/` for `.yaml` files, loads them, validates required fields, and sorts by comic number. TEMPLATE.yaml and README.yaml are automatically ignored.
2. **Enrichment pattern**: Always use `enrich_comic()` before passing comics to templates or APIs. This adds computed properties like `full_width`, `plain`, and `formatted_date`.
2. **Comic ordering**: COMICS list order (determined by the `number` field in each YAML file) determines comic sequence. Last item is the "latest" comic.
3. **Date formatting**: The `format_comic_date()` function uses `%d` with lstrip('0') for cross-platform compatibility (not all systems support `%-d`).
3. **Enrichment pattern**: Always use `enrich_comic()` before passing comics to templates or APIs. This adds computed properties like `full_width`, `plain`, and `formatted_date`.
4. **Author notes hierarchy**: If `author_note_md` field is specified, the markdown file is loaded and rendered as HTML, taking precedence over the plain text `author_note` field. When markdown is used, `author_note_is_html` is set to True.
4. **Date formatting**: The `format_comic_date()` function uses `%d` with lstrip('0') for cross-platform compatibility (not all systems support `%-d`).
5. **Settings cascade**: Global settings (FULL_WIDTH_DEFAULT, PLAIN_DEFAULT) apply unless overridden per-comic with `full_width` or `plain` keys.
5. **Author notes hierarchy**: If `author_note_md` field is specified, the markdown file is loaded and rendered as HTML, taking precedence over the plain text `author_note` field. When markdown is used, `author_note_is_html` is set to True.
6. **Navigation state**: Client-side navigation reads `data-total-comics` and `data-comic-number` from the `.comic-container` element to manage button states.
6. **Settings cascade**: Global settings (FULL_WIDTH_DEFAULT, PLAIN_DEFAULT) apply unless overridden per-comic with `full_width` or `plain` keys in the YAML file.
7. **Comic icon navigation**: When `USE_COMIC_NAV_ICONS` is True, templates use `.btn-icon-nav` class with icon images instead of text buttons. JavaScript automatically detects icon mode and applies appropriate classes. Disabled icons have reduced opacity (0.3).
7. **Navigation state**: Client-side navigation reads `data-total-comics` and `data-comic-number` from the `.comic-container` element to manage button states.
8. **Archive sections**: When `SECTIONS_ENABLED` is True, comics with a `section` field will start a new section on the archive page. Only add the `section` field to the first comic of each new section. All subsequent comics belong to that section until a new `section` field is encountered.
8. **Comic icon navigation**: When `USE_COMIC_NAV_ICONS` is True, templates use `.btn-icon-nav` class with icon images instead of text buttons. JavaScript automatically detects icon mode and applies appropriate classes. Disabled icons have reduced opacity (0.3).
9. **Archive sections**: When `SECTIONS_ENABLED` is True, comics with a `section` field will start a new section on the archive page. Only add the `section` field to the first comic of each new section. All subsequent comics belong to that section until a new `section` field is encountered.
10. **YAML validation**: The data loader validates each comic file and logs warnings for missing required fields (`number`, `filename`, `date`, `alt_text`). Invalid files are skipped.
## Production Deployment