✨ embed support
This commit is contained in:
18
CLAUDE.md
18
CLAUDE.md
@@ -214,7 +214,8 @@ Performance with caching (1000 comics):
|
||||
|
||||
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)
|
||||
- `filename` (required unless using html_embed): Image filename in `static/images/comics/` OR list of filenames for multi-image comics (webtoon style)
|
||||
- `html_embed` (optional): Custom HTML to embed instead of an image (e.g., video player, widget). Takes precedence over `filename`.
|
||||
- `date` (required): Publication date in YYYY-MM-DD format
|
||||
- `alt_text` (required): Accessibility text OR list of alt texts (one per image for multi-image comics)
|
||||
- `title` (optional): Comic title (defaults to "#X" if absent)
|
||||
@@ -224,6 +225,18 @@ Each comic YAML file contains:
|
||||
- `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.
|
||||
|
||||
**HTML embeds in YAML:**
|
||||
```yaml
|
||||
html_embed: '<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315" frameborder="0" allowfullscreen></iframe>'
|
||||
alt_text: "Video description for accessibility"
|
||||
```
|
||||
- Use `html_embed` to display custom HTML content instead of an image
|
||||
- Useful for embedding videos, interactive widgets, or special content
|
||||
- When `html_embed` is present, it takes precedence over `filename` and `mobile_filename`
|
||||
- The HTML is rendered as-is using the `| safe` filter in templates
|
||||
- No click-through navigation on HTML embeds (use navigation buttons instead)
|
||||
- Still provide `alt_text` for accessibility context
|
||||
|
||||
**Multi-image comics (webtoon style) in YAML:**
|
||||
```yaml
|
||||
filename:
|
||||
@@ -289,7 +302,8 @@ Global configuration in `comics_data.py`:
|
||||
Provides SPA-like navigation without page reloads:
|
||||
- Fetches comics from `/api/comics/<id>`
|
||||
- Updates DOM with `displayComic(comic)` function
|
||||
- Handles navigation buttons and image click-through
|
||||
- Handles navigation buttons and image click-through (disabled for HTML embeds and multi-image comics)
|
||||
- Renders HTML embeds, multi-image comics, and single-image comics dynamically
|
||||
- Uses History API to maintain proper URLs and browser back/forward
|
||||
- Shows/hides header based on plain mode
|
||||
- Adjusts container for full_width mode
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# REQUIRED: Sequential comic number
|
||||
number: 999
|
||||
|
||||
# REQUIRED: Image filename(s) in static/images/comics/
|
||||
# REQUIRED (unless using html_embed): Image filename(s) in static/images/comics/
|
||||
# Single image:
|
||||
filename: comic-999.jpg
|
||||
# OR multi-image (webtoon style):
|
||||
@@ -18,6 +18,12 @@ filename: comic-999.jpg
|
||||
# Optional: Mobile-optimized version of the comic
|
||||
# mobile_filename: comic-999-mobile.jpg
|
||||
|
||||
# Optional: HTML embed instead of image
|
||||
# Use this to embed videos, widgets, or other HTML content
|
||||
# When set, this takes precedence over filename/mobile_filename
|
||||
# Example: '<iframe src="https://www.youtube.com/embed/..." width="560" height="315"></iframe>'
|
||||
# html_embed: '<div>Your custom HTML here</div>'
|
||||
|
||||
# REQUIRED: Publication date (YYYY-MM-DD format)
|
||||
date: "2025-01-01"
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@
|
||||
const comicImageDiv = document.querySelector('.comic-image');
|
||||
updateComicImage(comicImageDiv, comic, title);
|
||||
|
||||
// Update or create/remove the link wrapper (only for single-image comics)
|
||||
if (!comic.is_multi_image) {
|
||||
// Update or create/remove the link wrapper (only for single-image comics, not HTML embeds)
|
||||
if (!comic.is_multi_image && !comic.html_embed) {
|
||||
updateComicImageLink(comic.number);
|
||||
}
|
||||
|
||||
@@ -181,15 +181,26 @@
|
||||
// Clear all existing content
|
||||
comicImageDiv.innerHTML = '';
|
||||
|
||||
// Update container class for multi-image
|
||||
if (comic.is_multi_image) {
|
||||
// Update container classes
|
||||
if (comic.html_embed) {
|
||||
comicImageDiv.classList.add('comic-image-embed');
|
||||
comicImageDiv.classList.remove('comic-image-multi');
|
||||
} else if (comic.is_multi_image) {
|
||||
comicImageDiv.classList.add('comic-image-multi');
|
||||
comicImageDiv.classList.remove('comic-image-embed');
|
||||
} else {
|
||||
comicImageDiv.classList.remove('comic-image-multi');
|
||||
comicImageDiv.classList.remove('comic-image-embed');
|
||||
}
|
||||
|
||||
// Create new image element(s)
|
||||
if (comic.is_multi_image) {
|
||||
// Create new content
|
||||
if (comic.html_embed) {
|
||||
// HTML embed (video, widget, etc.)
|
||||
const embedWrapper = document.createElement('div');
|
||||
embedWrapper.className = 'comic-embed-wrapper';
|
||||
embedWrapper.innerHTML = comic.html_embed;
|
||||
comicImageDiv.appendChild(embedWrapper);
|
||||
} else if (comic.is_multi_image) {
|
||||
// Multi-image comic (webtoon style)
|
||||
comic.filenames.forEach((filename, index) => {
|
||||
const img = document.createElement('img');
|
||||
@@ -447,13 +458,14 @@
|
||||
const formattedDate = dateDisplay ? dateDisplay.textContent : null;
|
||||
updateNavButtons(currentNumber, formattedDate);
|
||||
|
||||
// Check if current comic is multi-image
|
||||
// Check if current comic is multi-image or HTML embed
|
||||
const comicImageDiv = document.querySelector('.comic-image');
|
||||
const isMultiImage = comicImageDiv && comicImageDiv.classList.contains('comic-image-multi');
|
||||
const isHtmlEmbed = comicImageDiv && comicImageDiv.classList.contains('comic-image-embed');
|
||||
|
||||
if (!isMultiImage) {
|
||||
if (!isMultiImage && !isHtmlEmbed) {
|
||||
updateComicImageLink(currentNumber);
|
||||
} else {
|
||||
} else if (isMultiImage) {
|
||||
// Initialize lazy loading for multi-image comics on page load
|
||||
initLazyLoad();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,13 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="comic-image{% if comic.is_multi_image %} comic-image-multi{% endif %}" id="comic-image-focus" tabindex="-1">
|
||||
{% if comic.is_multi_image %}
|
||||
<div class="comic-image{% if comic.is_multi_image %} comic-image-multi{% endif %}{% if comic.html_embed %} comic-image-embed{% endif %}" id="comic-image-focus" tabindex="-1">
|
||||
{% if comic.html_embed %}
|
||||
{# HTML embed (video, widget, etc.) #}
|
||||
<div class="comic-embed-wrapper">
|
||||
{{ comic.html_embed | safe }}
|
||||
</div>
|
||||
{% elif comic.is_multi_image %}
|
||||
{# Multi-image layout (webtoon style) - no click-through on individual images #}
|
||||
{% for i in range(comic.filenames|length) %}
|
||||
<img src="{% if loop.first %}{{ ('images/comics/' + comic.filenames[i]) | cdn_static }}{% endif %}"
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
<p class="comic-date">{{ comic.date }}</p>
|
||||
</div>
|
||||
|
||||
<div class="comic-image{% if comic.is_multi_image %} comic-image-multi{% endif %}" id="comic-image-focus" tabindex="-1">
|
||||
{% if comic.is_multi_image %}
|
||||
<div class="comic-image{% if comic.is_multi_image %} comic-image-multi{% endif %}{% if comic.html_embed %} comic-image-embed{% endif %}" id="comic-image-focus" tabindex="-1">
|
||||
{% if comic.html_embed %}
|
||||
{# HTML embed (video, widget, etc.) #}
|
||||
<div class="comic-embed-wrapper">
|
||||
{{ comic.html_embed | safe }}
|
||||
</div>
|
||||
{% elif comic.is_multi_image %}
|
||||
{# Multi-image layout (webtoon style) - no click-through on individual images #}
|
||||
{% for i in range(comic.filenames|length) %}
|
||||
<img src="{% if loop.first %}{{ ('images/comics/' + comic.filenames[i]) | cdn_static }}{% endif %}"
|
||||
|
||||
Reference in New Issue
Block a user