📝 catch the docs up
This commit is contained in:
260
README.md
260
README.md
@@ -72,7 +72,7 @@ A Flask-based webcomic website with server-side rendering using Jinja2 templates
|
|||||||
- An archive page where readers can browse all your comics
|
- An archive page where readers can browse all your comics
|
||||||
- RSS feed so readers can subscribe to updates
|
- RSS feed so readers can subscribe to updates
|
||||||
- Mobile-friendly design that works on phones and tablets
|
- Mobile-friendly design that works on phones and tablets
|
||||||
- No database required - just upload images and edit a simple text file
|
- No database required - just upload images and edit simple YAML files
|
||||||
|
|
||||||
**Perfect for:**
|
**Perfect for:**
|
||||||
- Independent comic artists starting their first webcomic
|
- Independent comic artists starting their first webcomic
|
||||||
@@ -81,7 +81,7 @@ A Flask-based webcomic website with server-side rendering using Jinja2 templates
|
|||||||
- Anyone looking for a lightweight, customizable comic platform
|
- Anyone looking for a lightweight, customizable comic platform
|
||||||
|
|
||||||
**How it works:**
|
**How it works:**
|
||||||
You add your comics by uploading image files and adding basic information (title, date, description) to a configuration file. The website handles everything else - displaying comics with navigation, creating an archive, generating an RSS feed, and making your comics shareable on social media.
|
You add your comics by uploading image files and creating individual YAML files with basic information (title, date, description). The website handles everything else - displaying comics with navigation, creating an archive, generating an RSS feed, and making your comics shareable on social media.
|
||||||
|
|
||||||
No coding knowledge required for basic use - just follow the instructions below to add comics and customize your site's appearance.
|
No coding knowledge required for basic use - just follow the instructions below to add comics and customize your site's appearance.
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ No coding knowledge required for basic use - just follow the instructions below
|
|||||||
|
|
||||||
**Sunday Comics:**
|
**Sunday Comics:**
|
||||||
- Server-side application (Flask/Python) that runs on a web server
|
- Server-side application (Flask/Python) that runs on a web server
|
||||||
- Comics stored in a Python file - edit text to add comics
|
- Comics stored as individual YAML files - easy version control and management
|
||||||
- Includes an RSS feed generator and helper scripts
|
- Includes an RSS feed generator and helper scripts
|
||||||
- API endpoints for programmatic access
|
- API endpoints for programmatic access
|
||||||
- Markdown support for rich-formatted content
|
- Markdown support for rich-formatted content
|
||||||
@@ -100,7 +100,7 @@ No coding knowledge required for basic use - just follow the instructions below
|
|||||||
|
|
||||||
**Rarebit:**
|
**Rarebit:**
|
||||||
- Purely static HTML/CSS/JavaScript files
|
- Purely static HTML/CSS/JavaScript files
|
||||||
- Comics are inferred from static images upload - edit a JS to customize
|
- Comics are inferred from static images upload - edit a JS file to customize
|
||||||
- Can be hosted for free on GitHub Pages, Neocities, etc.
|
- Can be hosted for free on GitHub Pages, Neocities, etc.
|
||||||
- No server or programming language required
|
- No server or programming language required
|
||||||
- Simpler deployment - just upload files
|
- Simpler deployment - just upload files
|
||||||
@@ -233,13 +233,11 @@ To test keyboard navigation on your site:
|
|||||||
When adding comics to your site, follow these guidelines to maintain accessibility:
|
When adding comics to your site, follow these guidelines to maintain accessibility:
|
||||||
|
|
||||||
1. **Always provide alt text**
|
1. **Always provide alt text**
|
||||||
```python
|
```yaml
|
||||||
{
|
number: 1
|
||||||
'number': 1,
|
filename: comic-001.png
|
||||||
'filename': 'comic-001.png',
|
alt_text: A descriptive summary of what happens in the comic # Required!
|
||||||
'alt_text': 'A descriptive summary of what happens in the comic', # Required!
|
date: '2025-01-01'
|
||||||
# ...
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Write meaningful alt text**
|
2. **Write meaningful alt text**
|
||||||
@@ -632,16 +630,27 @@ Resources:
|
|||||||
```
|
```
|
||||||
sunday/
|
sunday/
|
||||||
├── app.py # Main Flask application
|
├── app.py # Main Flask application
|
||||||
├── comics_data.py # Comic data and configuration
|
├── comics_data.py # Global configuration (not comic data)
|
||||||
|
├── data_loader.py # YAML comic loader with caching
|
||||||
├── requirements.txt # Python dependencies
|
├── requirements.txt # Python dependencies
|
||||||
├── Dockerfile # Production Docker image
|
├── Dockerfile # Production Docker image
|
||||||
├── docker-compose.yml # Docker Compose configuration
|
├── docker-compose.yml # Docker Compose configuration
|
||||||
├── .dockerignore # Docker build exclusions
|
├── .dockerignore # Docker build exclusions
|
||||||
|
├── data/ # Comic data directory
|
||||||
|
│ └── comics/ # Individual comic YAML files
|
||||||
|
│ ├── 001.yaml # Comic #1
|
||||||
|
│ ├── 002.yaml # Comic #2
|
||||||
|
│ ├── TEMPLATE.yaml # Template for new comics
|
||||||
|
│ └── .comics_cache.pkl # Auto-generated cache file
|
||||||
├── scripts/ # Utility scripts
|
├── scripts/ # Utility scripts
|
||||||
│ ├── add_comic.py # Script to add new comic entries
|
│ ├── add_comic.py # Create new comic YAML files
|
||||||
│ └── generate_rss.py # Script to generate RSS feed
|
│ ├── generate_rss.py # Generate RSS feed
|
||||||
|
│ ├── generate_sitemap.py # Generate sitemap.xml
|
||||||
|
│ ├── rebuild_cache.py # Force rebuild comics cache
|
||||||
|
│ └── publish_comic.py # Rebuild cache + RSS + sitemap
|
||||||
├── content/ # Markdown content
|
├── content/ # Markdown content
|
||||||
│ ├── about.md # About page content
|
│ ├── about.md # About page content
|
||||||
|
│ ├── terms.md # Terms of Service
|
||||||
│ └── author_notes/ # Author notes for comics (by date)
|
│ └── author_notes/ # Author notes for comics (by date)
|
||||||
├── templates/ # Jinja2 templates
|
├── templates/ # Jinja2 templates
|
||||||
│ ├── base.html # Base template with navigation
|
│ ├── base.html # Base template with navigation
|
||||||
@@ -659,7 +668,8 @@ sunday/
|
|||||||
│ ├── comics/ # Comic images
|
│ ├── comics/ # Comic images
|
||||||
│ ├── thumbs/ # Thumbnail images for archive
|
│ ├── thumbs/ # Thumbnail images for archive
|
||||||
│ └── icons/ # Navigation and social icons (optional)
|
│ └── icons/ # Navigation and social icons (optional)
|
||||||
└── feed.rss # RSS feed (generated)
|
├── feed.rss # RSS feed (generated)
|
||||||
|
└── sitemap.xml # Sitemap (generated)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
@@ -683,6 +693,7 @@ The app can be configured via environment variables:
|
|||||||
- `SECRET_KEY` - Flask secret key (defaults to 'your-secret-key')
|
- `SECRET_KEY` - Flask secret key (defaults to 'your-secret-key')
|
||||||
- `PORT` - Port to run on (defaults to 3000)
|
- `PORT` - Port to run on (defaults to 3000)
|
||||||
- `DEBUG` - Enable debug mode (defaults to False)
|
- `DEBUG` - Enable debug mode (defaults to False)
|
||||||
|
- `DISABLE_COMIC_CACHE` - Set to 'true' to disable comic caching (useful for debugging)
|
||||||
|
|
||||||
**Development:**
|
**Development:**
|
||||||
```bash
|
```bash
|
||||||
@@ -697,9 +708,15 @@ export PORT=3000
|
|||||||
python app.py
|
python app.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Disable caching (debugging):**
|
||||||
|
```bash
|
||||||
|
export DISABLE_COMIC_CACHE=true
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The `comics_data.py` file contains both comic data and global configuration options:
|
The `comics_data.py` file contains global configuration options for your comic site. Comic data itself is stored in individual YAML files in the `data/comics/` directory.
|
||||||
|
|
||||||
### Global Settings
|
### Global Settings
|
||||||
|
|
||||||
@@ -716,10 +733,14 @@ FOOTER_IMAGE = None # Optional footer image path
|
|||||||
BANNER_IMAGE = 'banner.jpg' # Shareable banner for "Link to Me" section
|
BANNER_IMAGE = 'banner.jpg' # Shareable banner for "Link to Me" section
|
||||||
COMPACT_FOOTER = False # Display footer in compact mode
|
COMPACT_FOOTER = False # Display footer in compact mode
|
||||||
ARCHIVE_FULL_WIDTH = True # Full-width archive with 4 columns
|
ARCHIVE_FULL_WIDTH = True # Full-width archive with 4 columns
|
||||||
|
SECTIONS_ENABLED = True # Enable section headers on archive page
|
||||||
USE_COMIC_NAV_ICONS = True # Use icons for comic navigation buttons
|
USE_COMIC_NAV_ICONS = True # Use icons for comic navigation buttons
|
||||||
USE_HEADER_NAV_ICONS = True # Show icons in main header navigation
|
USE_HEADER_NAV_ICONS = True # Show icons in main header navigation
|
||||||
USE_FOOTER_SOCIAL_ICONS = True # Use icons for social links
|
USE_FOOTER_SOCIAL_ICONS = True # Use icons for social links
|
||||||
|
USE_SHARE_ICONS = True # Use icons in share buttons (permalink/embed)
|
||||||
NEWSLETTER_ENABLED = False # Show newsletter section in footer
|
NEWSLETTER_ENABLED = False # Show newsletter section in footer
|
||||||
|
EMBED_ENABLED = True # Enable comic embed functionality
|
||||||
|
PERMALINK_ENABLED = True # Enable permalink copy button
|
||||||
SOCIAL_INSTAGRAM = None # Instagram URL (or None)
|
SOCIAL_INSTAGRAM = None # Instagram URL (or None)
|
||||||
SOCIAL_YOUTUBE = None # YouTube URL (or None)
|
SOCIAL_YOUTUBE = None # YouTube URL (or None)
|
||||||
SOCIAL_EMAIL = None # Email mailto link (or None)
|
SOCIAL_EMAIL = None # Email mailto link (or None)
|
||||||
@@ -728,78 +749,107 @@ API_SPEC_LINK = None # API documentation link (or None)
|
|||||||
|
|
||||||
## Adding Comics
|
## Adding Comics
|
||||||
|
|
||||||
Comics are stored in the `COMICS` list in `comics_data.py`. Each comic entry:
|
Comics are stored as individual YAML files in the `data/comics/` directory. Each comic file contains:
|
||||||
|
|
||||||
```python
|
```yaml
|
||||||
{
|
number: 1 # Comic number (required, sequential)
|
||||||
'number': 1, # Comic number (required, sequential)
|
filename: comic-001.png # Image filename (required) OR list for multi-image
|
||||||
'filename': 'comic-001.png', # Image filename (required) OR list for multi-image
|
date: '2025-01-01' # Publication date (required)
|
||||||
'mobile_filename': 'comic-001-mobile.png', # Optional mobile version (single-image only)
|
alt_text: Alt text for comic # Accessibility text (required) OR list for multi-image
|
||||||
'date': '2025-01-01', # Publication date (required)
|
title: Comic Title # Title (optional, shows #X if absent)
|
||||||
'alt_text': 'Alt text for comic', # Accessibility text (required) OR list for multi-image
|
author_note: Optional note # Author note (optional, plain text)
|
||||||
'title': 'Comic Title', # Title (optional, shows #X if absent)
|
author_note_md: 2025-01-01.md # Optional markdown file for author note
|
||||||
'author_note': 'Optional note', # Author note (optional, plain text)
|
full_width: true # Optional: override FULL_WIDTH_DEFAULT
|
||||||
'author_note_md': '2025-01-01.md', # Optional markdown file for author note
|
plain: true # Optional: override PLAIN_DEFAULT
|
||||||
'full_width': True, # Optional: override FULL_WIDTH_DEFAULT
|
|
||||||
'plain': True # Optional: override PLAIN_DEFAULT
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**For multi-image comics (webtoon style):**
|
**For multi-image comics (webtoon style):**
|
||||||
```python
|
```yaml
|
||||||
{
|
number: 2
|
||||||
'number': 2,
|
filename:
|
||||||
'filename': ['page1.png', 'page2.png', 'page3.png'], # List of images
|
- page1.png
|
||||||
'alt_text': ['Panel 1 description', 'Panel 2', 'Panel 3'], # Individual alt texts
|
- page2.png
|
||||||
'date': '2025-01-08',
|
- page3.png
|
||||||
'full_width': True # Recommended for webtoons
|
alt_text:
|
||||||
}
|
- Panel 1 description
|
||||||
|
- Panel 2 description
|
||||||
|
- Panel 3 description
|
||||||
|
date: '2025-01-08'
|
||||||
|
full_width: true # Recommended for webtoons
|
||||||
```
|
```
|
||||||
|
|
||||||
### Adding a New Comic
|
### Adding a New Comic
|
||||||
|
|
||||||
**Option 1: Use the script (recommended)**
|
**Option 1: Use the script (recommended)**
|
||||||
```bash
|
```bash
|
||||||
# Add comic entry only
|
# Add comic YAML file with defaults
|
||||||
python scripts/add_comic.py
|
python scripts/add_comic.py
|
||||||
|
|
||||||
# Add comic entry AND create markdown file for author notes
|
# Add comic YAML file AND create markdown file for author notes
|
||||||
python scripts/add_comic.py -m
|
python scripts/add_comic.py -m
|
||||||
```
|
```
|
||||||
This will automatically add a new entry with defaults. The `-m` flag creates a markdown file in `content/author_notes/{date}.md` with a template and adds the `author_note_md` field to the comic entry. Then edit `comics_data.py` to customize.
|
This will create a new YAML file in `data/comics/` with the next comic number and reasonable defaults. The `-m` flag also creates a markdown file in `content/author_notes/{date}.md` with a template. Then:
|
||||||
|
1. Edit the generated YAML file to customize title, alt_text, author_note, etc.
|
||||||
|
2. Upload your comic image to `static/images/comics/`
|
||||||
|
3. Optionally create a thumbnail in `static/images/thumbs/` with the same filename
|
||||||
|
|
||||||
**Option 2: Manual**
|
**Option 2: Manual**
|
||||||
1. Save your comic image in `static/images/comics/` (e.g., `comic-001.png`)
|
1. Copy `data/comics/TEMPLATE.yaml` and rename it (e.g., `003.yaml`)
|
||||||
2. Optionally, create a thumbnail in `static/images/thumbs/` with the same filename
|
2. Edit the YAML file to set comic properties
|
||||||
3. Add the comic entry to the `COMICS` list in `comics_data.py`
|
3. Save your comic image in `static/images/comics/`
|
||||||
|
4. Optionally, create a thumbnail in `static/images/thumbs/` with the same filename
|
||||||
|
|
||||||
### Generating RSS Feed
|
### Publishing Comics
|
||||||
|
|
||||||
After adding comics, regenerate the RSS feed:
|
After adding or updating comics, use the publish script to update all generated files:
|
||||||
```bash
|
```bash
|
||||||
python scripts/generate_rss.py
|
python scripts/publish_comic.py
|
||||||
```
|
```
|
||||||
This creates/updates `static/feed.rss`
|
This convenience script rebuilds the cache and regenerates both RSS feed and sitemap in one command.
|
||||||
|
|
||||||
|
### Comic Caching System
|
||||||
|
|
||||||
|
Sunday Comics uses an automatic caching system to speed up comic loading:
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- **First load**: Parses all YAML files, saves to `data/comics/.comics_cache.pkl`
|
||||||
|
- **Subsequent loads**: Reads from cache (~100x faster)
|
||||||
|
- **Auto-invalidation**: Cache rebuilds automatically when any YAML file is modified
|
||||||
|
|
||||||
|
**Performance (1000 comics):**
|
||||||
|
- Initial load: ~2-3 seconds (builds cache)
|
||||||
|
- Subsequent loads: ~0.01 seconds (uses cache)
|
||||||
|
- Scripts share the same cache file on disk
|
||||||
|
|
||||||
|
**Manual cache management:**
|
||||||
|
```bash
|
||||||
|
# Force rebuild the cache (normally not needed)
|
||||||
|
python scripts/rebuild_cache.py
|
||||||
|
|
||||||
|
# Disable caching (for debugging)
|
||||||
|
export DISABLE_COMIC_CACHE=true
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The cache file is automatically excluded from git (listed in `.gitignore`).
|
||||||
|
|
||||||
### Markdown Support
|
### Markdown Support
|
||||||
|
|
||||||
**Author Notes:**
|
**Author Notes:**
|
||||||
Add the `author_note_md` field to your comic entry in `comics_data.py` to use markdown-formatted author notes. The field can be:
|
Add the `author_note_md` field to your comic YAML file to use markdown-formatted author notes. The field can be:
|
||||||
- Just a filename (e.g., `"2025-01-01.md"`) - looked up in `content/author_notes/`
|
- Just a filename (e.g., `2025-01-01.md`) - looked up in `content/author_notes/`
|
||||||
- A path relative to `content/` (e.g., `"special/intro.md"`)
|
- A path relative to `content/` (e.g., `special/intro.md`)
|
||||||
|
|
||||||
Markdown author notes take precedence over the plain text `author_note` field and render as HTML.
|
Markdown author notes take precedence over the plain text `author_note` field and render as HTML.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```yaml
|
||||||
# In comics_data.py
|
# In data/comics/001.yaml
|
||||||
{
|
number: 1
|
||||||
'number': 1,
|
filename: comic-001.png
|
||||||
'filename': 'comic-001.png',
|
date: '2025-01-01'
|
||||||
'date': '2025-01-01',
|
alt_text: First comic
|
||||||
'alt_text': 'First comic',
|
author_note_md: 2025-01-01.md # References content/author_notes/2025-01-01.md
|
||||||
'author_note_md': '2025-01-01.md' # References content/author_notes/2025-01-01.md
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -823,31 +873,34 @@ Sunday Comics supports vertical scrolling comics with multiple images stacked se
|
|||||||
- No click-through navigation on multi-image comics (use navigation buttons instead)
|
- No click-through navigation on multi-image comics (use navigation buttons instead)
|
||||||
|
|
||||||
**Basic Example:**
|
**Basic Example:**
|
||||||
```python
|
```yaml
|
||||||
# In comics_data.py
|
# In data/comics/004.yaml
|
||||||
{
|
number: 4
|
||||||
'number': 4,
|
title: Webtoon Episode 1
|
||||||
'title': 'Webtoon Episode 1',
|
filename:
|
||||||
'filename': ['page1.jpg', 'page2.jpg', 'page3.jpg'], # List of images
|
- page1.jpg
|
||||||
'alt_text': 'A three-part vertical story', # Single alt text for all images
|
- page2.jpg
|
||||||
'date': '2025-01-22',
|
- page3.jpg
|
||||||
}
|
alt_text: A three-part vertical story # Single alt text for all images
|
||||||
|
date: '2025-01-22'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Individual Alt Text (Recommended for Accessibility):**
|
**Individual Alt Text (Recommended for Accessibility):**
|
||||||
```python
|
```yaml
|
||||||
{
|
# In data/comics/005.yaml
|
||||||
'number': 5,
|
number: 5
|
||||||
'title': 'Long Scroll Episode',
|
title: Long Scroll Episode
|
||||||
'filename': ['scene1.png', 'scene2.png', 'scene3.png', 'scene4.png'],
|
filename:
|
||||||
'alt_text': [
|
- scene1.png
|
||||||
'Opening scene showing the city at dawn',
|
- scene2.png
|
||||||
'Character walking through the marketplace',
|
- scene3.png
|
||||||
'Close-up of the mysterious artifact',
|
- scene4.png
|
||||||
'Dramatic reveal of the antagonist'
|
alt_text:
|
||||||
], # List must match number of images (or use single string for all)
|
- Opening scene showing the city at dawn
|
||||||
'date': '2025-01-29',
|
- Character walking through the marketplace
|
||||||
}
|
- Close-up of the mysterious artifact
|
||||||
|
- Dramatic reveal of the antagonist
|
||||||
|
date: '2025-01-29'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important:** If you provide `alt_text` as a list, it should match the number of images in `filename`. If the counts don't match, you'll see a warning in the logs. To use the same alt text for all images, just provide a single string instead of a list.
|
**Important:** If you provide `alt_text` as a list, it should match the number of images in `filename`. If the counts don't match, you'll see a warning in the logs. To use the same alt text for all images, just provide a single string instead of a list.
|
||||||
@@ -866,29 +919,26 @@ Sunday Comics supports vertical scrolling comics with multiple images stacked se
|
|||||||
4. **Image optimization** - Compress images appropriately since readers will load multiple images per comic
|
4. **Image optimization** - Compress images appropriately since readers will load multiple images per comic
|
||||||
|
|
||||||
**Example with all options:**
|
**Example with all options:**
|
||||||
```python
|
```yaml
|
||||||
{
|
# In data/comics/006.yaml
|
||||||
'number': 6,
|
number: 6
|
||||||
'title': 'Chapter 2: The Journey Begins',
|
title: 'Chapter 2: The Journey Begins'
|
||||||
'filename': [
|
filename:
|
||||||
'ch2-001.png',
|
- ch2-001.png
|
||||||
'ch2-002.png',
|
- ch2-002.png
|
||||||
'ch2-003.png',
|
- ch2-003.png
|
||||||
'ch2-004.png',
|
- ch2-004.png
|
||||||
'ch2-005.png'
|
- ch2-005.png
|
||||||
],
|
alt_text:
|
||||||
'alt_text': [
|
- 'Panel 1: Hero packs their bag at sunrise'
|
||||||
'Panel 1: Hero packs their bag at sunrise',
|
- 'Panel 2: Saying goodbye to the village elder'
|
||||||
'Panel 2: Saying goodbye to the village elder',
|
- 'Panel 3: Walking along the forest path'
|
||||||
'Panel 3: Walking along the forest path',
|
- 'Panel 4: Encountering a mysterious stranger'
|
||||||
'Panel 4: Encountering a mysterious stranger',
|
- 'Panel 5: Accepting a map to the ancient ruins'
|
||||||
'Panel 5: Accepting a map to the ancient ruins'
|
date: '2025-02-05'
|
||||||
],
|
author_note: This was so much fun to draw! The journey arc begins.
|
||||||
'date': '2025-02-05',
|
full_width: true # Recommended for webtoon-style comics
|
||||||
'author_note': 'This was so much fun to draw! The journey arc begins.',
|
section: Chapter 2 # Optional: mark the start of a new chapter
|
||||||
'full_width': True, # Recommended for webtoon-style comics
|
|
||||||
'section': 'Chapter 2', # Optional: mark the start of a new chapter
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Technical Details:**
|
**Technical Details:**
|
||||||
|
|||||||
Reference in New Issue
Block a user