Cybrkyd's Git Repositories

page-with-py - commit: 0bd6581

commit 0bd658133c6a0e1a751736cab80f626f198568282f4aa0142c5e8e5e37ccd4d4
author cybrkyd <git@cybrkyd.com> 2026-03-12 09:16:54 +0000
committer cybrkyd <git@cybrkyd.com> 2026-03-12 09:16:54 +0000
v1.5

Commit Message

Version 1.5

- New template tag-single.html. This will provide more flexibility when styling individual tag pages.
- Archiving logic and display changes
- Lowercase titles on Tags pages

📊 Diffstat

pa-py/pa.py 87
1 files changed, 62 insertions(+), 25 deletions(-)

Diff

diff --git a/pa-py/pa.py b/pa-py/pa.py
index 0b21c81..b00a250 100644
--- a/pa-py/pa.py
+++ b/pa-py/pa.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
- PaPy (Page with Python): a Python static site generator
- v1.4
+ PaPy ([Pa]ge with [Py]thon): a Python static site generator
+ v1.5
"""
import re
@@ -24,7 +24,7 @@ PAGINATION = "numbers" # "numbers" or "direction"
ENABLE_SEARCH = False
GENERATE_ARCHIVE = "yes" # "yes" or "no"
ARCHIVE_URL = "archive" # the URL of the archive
- TAGS_PAGE_TITLE = "Tags and categories"
+ TAGS_PAGE_TITLE = "tags and categories"
# Global caches
CSS_VAR_PATTERN = re.compile(r'(var\(--[^)]+\))')
@@ -82,64 +82,60 @@ def parse_front_matter(content):
def load_template_parts(theme_dir: Path):
templates = {}
-
header_file = theme_dir / 'header.html'
footer_file = theme_dir / 'footer.html'
head_file = theme_dir / 'head.html'
main_file = theme_dir / 'main.html'
single_file = theme_dir / 'single.html'
tags_file = theme_dir / 'tags.html'
+ tag_single_file = theme_dir / 'tag-single.html'
page_file = theme_dir / 'page.html'
archive_file = theme_dir / 'archive.html'
-
if header_file.exists():
templates['header'] = header_file.read_text(encoding="utf-8")
else:
templates['header'] = '<!DOCTYPE html><html><body class="home">'
print("Warning: header.html not found in theme directory")
-
if footer_file.exists():
templates['footer'] = footer_file.read_text(encoding="utf-8")
else:
templates['footer'] = '</body></html>'
print("Warning: footer.html not found in theme directory")
-
if head_file.exists():
templates['head'] = head_file.read_text(encoding="utf-8")
else:
templates['head'] = '<head><meta charset="UTF-8"><title>{title}</title></head>'
print("Warning: head.html not found in theme directory")
-
if main_file.exists():
templates['main'] = main_file.read_text(encoding="utf-8")
else:
templates['main'] = '<main>\n{content}\n</main>'
print("Warning: main.html not found in theme directory")
-
if single_file.exists():
templates['single'] = single_file.read_text(encoding="utf-8")
else:
templates['single'] = '<main class="single-page">\n<article class="post-content">\n{content}\n</article>\n</main>'
print("Warning: single.html not found in theme directory")
-
if page_file.exists():
templates['page'] = page_file.read_text(encoding="utf-8")
else:
templates['page'] = templates['single']
print("Warning: page.html not found in theme directory, using single.html")
-
if archive_file.exists():
templates['archive'] = archive_file.read_text(encoding="utf-8")
else:
templates['archive'] = '<main>\n{content}\n</main>'
print("Warning: archive.html not found in theme directory")
-
if tags_file.exists():
templates['tags'] = tags_file.read_text(encoding="utf-8")
else:
templates['tags'] = templates['main']
print("Warning: tags.html not found in theme directory, using main.html")
-
+ if tag_single_file.exists():
+ templates['tag_single'] = tag_single_file.read_text(encoding="utf-8")
+ else:
+ templates['tag_single'] = templates['tags']
+ print("Warning: tag-single.html not found in theme directory, using tags.html")
return templates
@@ -371,7 +367,7 @@ def generate_index_page(templates, posts, output_dir: Path, page_num=1, is_pagin
title = SITE_TITLE
if tag_name:
- title = f"Posts tagged: #{tag_name}"
+ title = f"posts tagged: #{tag_name}"
elif is_pagination:
title = f"Page {page_num} - {SITE_TITLE}"
@@ -408,7 +404,7 @@ def generate_index_page(templates, posts, output_dir: Path, page_num=1, is_pagin
description = "Blog post archive" if is_pagination else SITE_DESC
if tag_name:
- description = f"Posts tagged {tag_name}"
+ description = f"posts tagged {tag_name}"
context = {
'title': title,
@@ -423,12 +419,12 @@ def generate_index_page(templates, posts, output_dir: Path, page_num=1, is_pagin
} for post in paginated_posts]
}
- template_to_use = templates['tags'] if tag_name else templates['main']
+ template_to_use = templates['tag_single'] if tag_name else templates['main']
content = render_template(template_to_use, context)
description = "Blog post archive" if is_pagination else SITE_DESC
if tag_name:
- description = f"Posts tagged {tag_name}"
+ description = f"posts tagged {tag_name}"
page_url = SITE_URL
if is_pagination and page_num > 1:
@@ -458,24 +454,65 @@ def generate_archive_page(templates, posts, output_dir: Path):
title = f"Archive - {SITE_TITLE}"
description = f"Complete post archive - {SITE_DESC}"
archive_template = templates['archive']
+ archive_posts = []
+ current_year = None
+
+ for post in posts:
+ post_year = post.get('date', '')[:4]
+
+ if post_year != current_year:
+ current_year = post_year
+ archive_posts.append({
+ 'title': f'<li class="year-heading">{post_year}</li>',
+ 'url': '',
+ 'date': '',
+ 'description': '',
+ 'content': '',
+ 'is_year_heading': True
+ })
+
+ archive_posts.append({
+ 'title': post['title'],
+ 'url': '/' + post['url'].replace(SITE_URL, '').lstrip('/'),
+ 'date': post.get('display_date', ''),
+ 'description': post.get('description', ''),
+ 'content': post.get('html_content', ''),
+ 'is_year_heading': False
+ })
+
context = {
'title': title,
'description': description,
'pagination': '',
- 'posts': [{
- 'title': post['title'],
- 'url': '/' + post['url'].replace(SITE_URL, '').lstrip('/'),
- 'date': post.get('date', ''),
- 'description': post.get('description', ''),
- 'content': post.get('html_content', '')
- } for post in posts]
+ 'posts': archive_posts
}
content = render_template(archive_template, context)
page_url = f"{SITE_URL}/{ARCHIVE_URL}/"
full_html = generate_html_page(templates, title, content, description, page_url, "", "website")
+
+ pattern = r'<li class="archive-item">\s*<span class="archive-title"><a href="[^"]*">([^<]*)</a></span>\s*<span class="archive-date">(\d+ \w+ \d+)</span>\s*</li>'
+ current_year = None
+ new_items = []
+ for match in re.finditer(pattern, full_html):
+ title, date_str = match.groups()
+ full_item = match.group(0)
+ try:
+ date_obj = datetime.strptime(date_str, '%d %b %Y')
+ year = date_obj.year
+ except:
+ continue
+ if year != current_year:
+ current_year = year
+ new_items.append(f'<span class="year-heading">{year}</span>')
+ new_items.append(full_item)
+
+ old_list_pattern = r'(<ul class="archive-list">).*?(</ul>)'
+ new_list = r'\1\n' + '\n'.join(new_items) + r'\n\2'
+ updated_html = re.sub(old_list_pattern, new_list, full_html, flags=re.DOTALL)
+
output_path = output_dir / ARCHIVE_URL / 'index.html'
- write_html_file(output_path, full_html)
+ write_html_file(output_path, updated_html)
return output_path