✨ sections
This commit is contained in:
42
app.py
42
app.py
@@ -7,9 +7,9 @@ from datetime import datetime
|
|||||||
from flask import Flask, render_template, abort, jsonify, request
|
from flask import Flask, render_template, abort, jsonify, request
|
||||||
from comics_data import (
|
from comics_data import (
|
||||||
COMICS, COMIC_NAME, SITE_URL, FULL_WIDTH_DEFAULT, PLAIN_DEFAULT, LOGO_IMAGE, LOGO_MODE,
|
COMICS, COMIC_NAME, SITE_URL, FULL_WIDTH_DEFAULT, PLAIN_DEFAULT, LOGO_IMAGE, LOGO_MODE,
|
||||||
HEADER_IMAGE, FOOTER_IMAGE, COMPACT_FOOTER, ARCHIVE_FULL_WIDTH, USE_COMIC_NAV_ICONS,
|
HEADER_IMAGE, FOOTER_IMAGE, COMPACT_FOOTER, ARCHIVE_FULL_WIDTH, SECTIONS_ENABLED,
|
||||||
USE_HEADER_NAV_ICONS, USE_FOOTER_SOCIAL_ICONS, SOCIAL_INSTAGRAM, SOCIAL_YOUTUBE,
|
USE_COMIC_NAV_ICONS, USE_HEADER_NAV_ICONS, USE_FOOTER_SOCIAL_ICONS, SOCIAL_INSTAGRAM,
|
||||||
SOCIAL_EMAIL, API_SPEC_LINK
|
SOCIAL_YOUTUBE, SOCIAL_EMAIL, API_SPEC_LINK
|
||||||
)
|
)
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ def inject_global_settings():
|
|||||||
'footer_image': FOOTER_IMAGE,
|
'footer_image': FOOTER_IMAGE,
|
||||||
'compact_footer': COMPACT_FOOTER,
|
'compact_footer': COMPACT_FOOTER,
|
||||||
'archive_full_width': ARCHIVE_FULL_WIDTH,
|
'archive_full_width': ARCHIVE_FULL_WIDTH,
|
||||||
|
'sections_enabled': SECTIONS_ENABLED,
|
||||||
'use_comic_nav_icons': USE_COMIC_NAV_ICONS,
|
'use_comic_nav_icons': USE_COMIC_NAV_ICONS,
|
||||||
'use_header_nav_icons': USE_HEADER_NAV_ICONS,
|
'use_header_nav_icons': USE_HEADER_NAV_ICONS,
|
||||||
'use_footer_social_icons': USE_FOOTER_SOCIAL_ICONS,
|
'use_footer_social_icons': USE_FOOTER_SOCIAL_ICONS,
|
||||||
@@ -141,13 +142,46 @@ def comic(comic_id):
|
|||||||
comic=comic, total_comics=len(COMICS))
|
comic=comic, total_comics=len(COMICS))
|
||||||
|
|
||||||
|
|
||||||
|
def group_comics_by_section(comics_list):
|
||||||
|
"""Group comics by section. Returns list of (section_title, comics) tuples"""
|
||||||
|
if not SECTIONS_ENABLED:
|
||||||
|
return [(None, comics_list)]
|
||||||
|
|
||||||
|
sections = []
|
||||||
|
current_section = None
|
||||||
|
current_comics = []
|
||||||
|
|
||||||
|
for comic in comics_list:
|
||||||
|
# Check if this comic starts a new section
|
||||||
|
if 'section' in comic:
|
||||||
|
# Save previous section if it has comics
|
||||||
|
if current_comics:
|
||||||
|
sections.append((current_section, current_comics))
|
||||||
|
# Start new section
|
||||||
|
current_section = comic['section']
|
||||||
|
current_comics = [comic]
|
||||||
|
else:
|
||||||
|
# Add to current section
|
||||||
|
current_comics.append(comic)
|
||||||
|
|
||||||
|
# Don't forget the last section
|
||||||
|
if current_comics:
|
||||||
|
sections.append((current_section, current_comics))
|
||||||
|
|
||||||
|
return sections
|
||||||
|
|
||||||
|
|
||||||
@app.route('/archive')
|
@app.route('/archive')
|
||||||
def archive():
|
def archive():
|
||||||
"""Archive page showing all comics"""
|
"""Archive page showing all comics"""
|
||||||
# Reverse order to show newest first
|
# Reverse order to show newest first
|
||||||
comics = [enrich_comic(comic) for comic in reversed(COMICS)]
|
comics = [enrich_comic(comic) for comic in reversed(COMICS)]
|
||||||
|
|
||||||
|
# Group by section if enabled
|
||||||
|
sections = group_comics_by_section(comics)
|
||||||
|
|
||||||
return render_template('archive.html', title='Archive',
|
return render_template('archive.html', title='Archive',
|
||||||
comics=comics)
|
sections=sections)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/about')
|
@app.route('/about')
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ COMPACT_FOOTER = False
|
|||||||
# Full-width archive shows square thumbnails with only dates, no titles
|
# Full-width archive shows square thumbnails with only dates, no titles
|
||||||
ARCHIVE_FULL_WIDTH = True
|
ARCHIVE_FULL_WIDTH = True
|
||||||
|
|
||||||
|
# Global setting: Set to True to enable sections/chapters on the archive page
|
||||||
|
# Add 'section': 'Chapter Title' to comics where a new section starts
|
||||||
|
SECTIONS_ENABLED = True
|
||||||
|
|
||||||
# Global setting: Set to True to use icon images for comic navigation buttons
|
# Global setting: Set to True to use icon images for comic navigation buttons
|
||||||
# Icons should be in static/images/icons/ (first.png, previous.png, next.png, latest.png)
|
# Icons should be in static/images/icons/ (first.png, previous.png, next.png, latest.png)
|
||||||
USE_COMIC_NAV_ICONS = True
|
USE_COMIC_NAV_ICONS = True
|
||||||
@@ -80,6 +84,7 @@ COMICS = [
|
|||||||
'author_note': 'This is where your comic journey begins!',
|
'author_note': 'This is where your comic journey begins!',
|
||||||
'full_width': True, # Optional: override FULL_WIDTH_DEFAULT for this comic
|
'full_width': True, # Optional: override FULL_WIDTH_DEFAULT for this comic
|
||||||
'plain': True, # Optional: override PLAIN_DEFAULT for this comic
|
'plain': True, # Optional: override PLAIN_DEFAULT for this comic
|
||||||
|
'section': 'Chapter 1: The Beginning', # Optional: start a new section on archive page
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'number': 2,
|
'number': 2,
|
||||||
|
|||||||
@@ -468,6 +468,25 @@ main {
|
|||||||
margin-top: var(--space-md);
|
margin-top: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Section Headers for Archive */
|
||||||
|
.section-header {
|
||||||
|
margin-top: var(--space-2xl);
|
||||||
|
margin-bottom: var(--space-lg);
|
||||||
|
padding-bottom: var(--space-sm);
|
||||||
|
border-bottom: var(--border-width-thin) solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h2 {
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: var(--font-size-xl);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: var(--letter-spacing-tight);
|
||||||
|
}
|
||||||
|
|
||||||
.archive-grid {
|
.archive-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(var(--archive-grid-min), 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(var(--archive-grid-min), 1fr));
|
||||||
|
|||||||
@@ -7,27 +7,35 @@
|
|||||||
|
|
||||||
<div class="page-header{% if archive_full_width %} page-header-fullwidth{% endif %}">
|
<div class="page-header{% if archive_full_width %} page-header-fullwidth{% endif %}">
|
||||||
<h1>Comic Archive</h1>
|
<h1>Comic Archive</h1>
|
||||||
<p>Browse all {{ comics|length }} comics</p>
|
<p>Browse all {% set total = namespace(count=0) %}{% for section_title, section_comics in sections %}{% set total.count = total.count + section_comics|length %}{% endfor %}{{ total.count }} comics</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="archive-content{% if archive_full_width %} archive-content-fullwidth{% endif %}">
|
<section class="archive-content{% if archive_full_width %} archive-content-fullwidth{% endif %}">
|
||||||
<div class="archive-grid{% if archive_full_width %} archive-grid-fullwidth{% endif %}">
|
{% for section_title, section_comics in sections %}
|
||||||
{% for comic in comics %}
|
{% if section_title and sections_enabled %}
|
||||||
<div class="archive-item{% if archive_full_width %} archive-item-fullwidth{% endif %}">
|
<div class="section-header">
|
||||||
<a href="{{ url_for('comic', comic_id=comic.number) }}">
|
<h2>{{ section_title }}</h2>
|
||||||
<img src="{{ url_for('static', filename='images/thumbs/' + comic.filename) }}"
|
|
||||||
onerror="this.onerror=null; this.src='{{ url_for('static', filename='images/thumbs/default.jpg') }}';"
|
|
||||||
alt="{{ comic.title if comic.title else '#' ~ comic.number }}">
|
|
||||||
<div class="archive-info">
|
|
||||||
{% if not archive_full_width %}
|
|
||||||
<h3>#{{ comic.number }}{% if comic.title %}: {{ comic.title }}{% endif %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
<p class="archive-date">{{ comic.date }}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
<div class="archive-grid{% if archive_full_width %} archive-grid-fullwidth{% endif %}">
|
||||||
|
{% for comic in section_comics %}
|
||||||
|
<div class="archive-item{% if archive_full_width %} archive-item-fullwidth{% endif %}">
|
||||||
|
<a href="{{ url_for('comic', comic_id=comic.number) }}">
|
||||||
|
<img src="{{ url_for('static', filename='images/thumbs/' + comic.filename) }}"
|
||||||
|
onerror="this.onerror=null; this.src='{{ url_for('static', filename='images/thumbs/default.jpg') }}';"
|
||||||
|
alt="{{ comic.title if comic.title else '#' ~ comic.number }}">
|
||||||
|
<div class="archive-info">
|
||||||
|
{% if not archive_full_width %}
|
||||||
|
<h3>#{{ comic.number }}{% if comic.title %}: {{ comic.title }}{% endif %}</h3>
|
||||||
|
{% endif %}
|
||||||
|
<p class="archive-date">{{ comic.date }}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if archive_full_width %}
|
{% if archive_full_width %}
|
||||||
|
|||||||
Reference in New Issue
Block a user