ux: CHANGE change 'feeds' to 'sections' - BREAKING - short guide in release notes
This commit is contained in:
parent
98ab0ab419
commit
a99d1db1cb
4 changed files with 59 additions and 59 deletions
24
README.md
24
README.md
|
|
@ -23,8 +23,8 @@ Show cases:
|
|||
- long- and short form content
|
||||
- pages
|
||||
- static files
|
||||
- separate feeds (used for categories, tagging, etc) `/feed/{tag}`
|
||||
- exclusion of feeds from main feed (drafts or system notes)
|
||||
- separate sections (used for categories, tagging, etc) `/section/{tag}`
|
||||
- exclusion of sections from main index (drafts or system notes)
|
||||
- HTML anchors for headers
|
||||
- list random posts at the bottom
|
||||
- optional RSS feeds
|
||||
|
|
@ -47,33 +47,33 @@ Put markdown file into `items` dir. **Important naming convention**:
|
|||
2025-10-05_short_quick-update_draft.md
|
||||
```
|
||||
|
||||
Format: `YYYY-MM-DD_type_slug[_feed].md`
|
||||
Format: `YYYY-MM-DD_type_slug[_section].md`
|
||||
|
||||
- `2025-10-03` - date of the article
|
||||
- `_long_` - type of content: `long`, `short`, or `page`
|
||||
- `building-a-static-site-generator` - slug/path for the URL
|
||||
- `_draft` (optional) - feed tag for categorization
|
||||
- `_draft` (optional) - section tag for categorization
|
||||
|
||||
The first `#` header is the title of the article - no frontmatter needed.
|
||||
|
||||
**Types of content**:
|
||||
- `long` - only title with link to articles will be displayed in feed
|
||||
- `long` - only title with link to articles will be displayed in the main index
|
||||
- `short` - title and all content will be displayed
|
||||
- `page` - won't be displayed in feed at all
|
||||
- `page` - won't be displayed in the main index at all
|
||||
|
||||
### Feeds
|
||||
### Sections
|
||||
|
||||
Posts can be tagged with an optional feed category (e.g., `_python`, `_webdev`). Posts with feed tags:
|
||||
Posts can be tagged with an optional section (e.g., `_python`, `_webdev`). Posts with section tags:
|
||||
- Appear on the main page (unless excluded in config)
|
||||
- Have their own feed page at `/feed/{tag}/`
|
||||
- Have their own section page at `/section/{tag}/`
|
||||
|
||||
**Configuration in `config.py`:**
|
||||
```python
|
||||
# Exclude specific feeds from main page (they'll still have /feed/name/ pages)
|
||||
EXCLUDE_FEEDS_FROM_MAIN = ['draft', 'private']
|
||||
# Exclude specific sections from main page (they'll still have /section/name/ pages)
|
||||
EXCLUDE_SECTIONS_FROM_MAIN = ['draft', 'private']
|
||||
```
|
||||
|
||||
This is useful for draft posts or topic-specific content you want separated from the main feed.
|
||||
This is useful for draft posts or topic-specific content you want separated from the main index.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ AUTHOR_NAME = "PicoPaper"
|
|||
AUTHOR_EMAIL = "hello@picopaper.com" # Optional
|
||||
THEME = "default"
|
||||
|
||||
# Exclude specific feeds from the main page (they'll still have their own /feed/name/ pages)
|
||||
EXCLUDE_FEEDS_FROM_MAIN = ['draft','private'] # e.g., ['python', 'drafts']
|
||||
# Exclude specific sections from the main page (they'll still have their own /section/name/ pages)
|
||||
EXCLUDE_SECTIONS_FROM_MAIN = ['draft','private'] # e.g., ['python', 'drafts']
|
||||
|
||||
# Navigation bar items - list of dictionaries with 'text' and 'url' keys
|
||||
NAVBAR_ITEMS = [
|
||||
{'text': 'Home', 'url': '/'},
|
||||
{'text': 'Feeds', 'url': '/feed/'},
|
||||
{'text': 'Sections', 'url': '/section/'},
|
||||
{'text': 'About', 'url': '/about/'},
|
||||
{'text': 'RSS', 'url': '/rss.xml'}
|
||||
]
|
||||
|
|
|
|||
82
picopaper.py
82
picopaper.py
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
|||
from pathlib import Path
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
import markdown
|
||||
from config import (BLOG_TITLE, BLOG_DESCRIPTION, THEME, EXCLUDE_FEEDS_FROM_MAIN,
|
||||
from config import (BLOG_TITLE, BLOG_DESCRIPTION, THEME, EXCLUDE_SECTIONS_FROM_MAIN,
|
||||
NAVBAR_ITEMS, HIDE_LOGO, HIDE_TITLE, LOGO_PATH,
|
||||
ENABLE_RSS_FEED, RSS_FEED_PATH,
|
||||
BASE_URL, AUTHOR_NAME, AUTHOR_EMAIL, FEED_MAX_ITEMS)
|
||||
|
|
@ -21,7 +21,7 @@ class SSGGGenerator:
|
|||
self.assets_dir = self.theme_dir / 'assets'
|
||||
self.blog_title = blog_title or BLOG_TITLE
|
||||
self.blog_description = blog_description or BLOG_DESCRIPTION
|
||||
self.exclude_feeds = EXCLUDE_FEEDS_FROM_MAIN
|
||||
self.exclude_sections = EXCLUDE_SECTIONS_FROM_MAIN
|
||||
self.navbar_items = NAVBAR_ITEMS
|
||||
self.hide_logo = HIDE_LOGO
|
||||
self.hide_title = HIDE_TITLE
|
||||
|
|
@ -42,7 +42,7 @@ class SSGGGenerator:
|
|||
self.md = markdown.Markdown(extensions=['extra', 'toc'])
|
||||
|
||||
def parse_filename(self, filename, subpath=''):
|
||||
"""Parse filename format: YYYY-MM-DD_type_name[_feed].md
|
||||
"""Parse filename format: YYYY-MM-DD_type_name[_section].md
|
||||
|
||||
Args:
|
||||
filename: The markdown filename
|
||||
|
|
@ -54,7 +54,7 @@ class SSGGGenerator:
|
|||
if not match:
|
||||
return None
|
||||
|
||||
date_str, post_type, name, feed = match.groups()
|
||||
date_str, post_type, name, section = match.groups()
|
||||
date = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
|
||||
return {
|
||||
|
|
@ -62,7 +62,7 @@ class SSGGGenerator:
|
|||
'date_str': date.strftime('%Y-%m-%d'),
|
||||
'type': post_type,
|
||||
'name': name,
|
||||
'feed': feed,
|
||||
'section': section,
|
||||
'filename': filename,
|
||||
'subpath': subpath
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ class SSGGGenerator:
|
|||
'content': content,
|
||||
'slug': slug,
|
||||
'url': url,
|
||||
'feed': parsed['feed'],
|
||||
'section': parsed['section'],
|
||||
'source': str(relative_path),
|
||||
'subpath': parsed['subpath']
|
||||
}
|
||||
|
|
@ -152,13 +152,13 @@ class SSGGGenerator:
|
|||
|
||||
return posts
|
||||
|
||||
def generate_index(self, posts, feed_name=None, all_posts=None):
|
||||
"""Generate index.html with all posts (or feed-specific index)"""
|
||||
def generate_index(self, posts, section_name=None, all_posts=None):
|
||||
"""Generate index.html with all posts (or section-specific index)"""
|
||||
template = self.env.get_template('index.tmpl')
|
||||
|
||||
if feed_name:
|
||||
title = f"{feed_name} - {self.blog_title}"
|
||||
output_path = self.output_dir / 'feed' / feed_name / 'index.html'
|
||||
if section_name:
|
||||
title = f"{section_name} - {self.blog_title}"
|
||||
output_path = self.output_dir / 'section' / section_name / 'index.html'
|
||||
else:
|
||||
title = self.blog_title
|
||||
output_path = self.output_dir / 'index.html'
|
||||
|
|
@ -183,28 +183,28 @@ class SSGGGenerator:
|
|||
|
||||
print(f"✓ Generated {output_path}")
|
||||
|
||||
def generate_feeds_overview(self, feeds, all_posts=None):
|
||||
"""Generate /feed/index.html with list of all non-excluded feeds"""
|
||||
template = self.env.get_template('feeds.tmpl')
|
||||
def generate_sections_overview(self, sections, all_posts=None):
|
||||
"""Generate /section/index.html with list of all non-excluded sections"""
|
||||
template = self.env.get_template('sections.tmpl')
|
||||
|
||||
# Prepare feed data with counts, excluding feeds in EXCLUDE_FEEDS_FROM_MAIN
|
||||
feed_list = []
|
||||
for feed_name, posts in sorted(feeds.items()):
|
||||
if feed_name not in self.exclude_feeds:
|
||||
feed_list.append({
|
||||
'name': feed_name,
|
||||
# Prepare section data with counts, excluding sections in EXCLUDE_SECTIONS_FROM_MAIN
|
||||
section_list = []
|
||||
for section_name, posts in sorted(sections.items()):
|
||||
if section_name not in self.exclude_sections:
|
||||
section_list.append({
|
||||
'name': section_name,
|
||||
'count': len(posts)
|
||||
})
|
||||
|
||||
title = f"Feeds - {self.blog_title}"
|
||||
output_path = self.output_dir / 'feed' / 'index.html'
|
||||
title = f"Sections - {self.blog_title}"
|
||||
output_path = self.output_dir / 'section' / 'index.html'
|
||||
|
||||
html = template.render(
|
||||
title=title,
|
||||
blog_title=self.blog_title,
|
||||
blog_description=self.blog_description,
|
||||
navbar_items=self.navbar_items,
|
||||
feeds=feed_list,
|
||||
sections=section_list,
|
||||
all_posts=all_posts or [],
|
||||
hide_logo=self.hide_logo,
|
||||
hide_title=self.hide_title,
|
||||
|
|
@ -397,27 +397,27 @@ class SSGGGenerator:
|
|||
all_posts = self.collect_posts()
|
||||
print(f"Found {len(all_posts)} posts")
|
||||
|
||||
# Filter out pages and excluded feeds from main feed
|
||||
feed_posts = [p for p in all_posts
|
||||
# Filter out pages and excluded sections from main index
|
||||
main_posts = [p for p in all_posts
|
||||
if p['type'] != 'page'
|
||||
and p['feed'] not in self.exclude_feeds]
|
||||
and p['section'] not in self.exclude_sections]
|
||||
|
||||
# Generate main index with filtered feed posts
|
||||
self.generate_index(feed_posts, all_posts=feed_posts)
|
||||
# Generate main index with filtered posts
|
||||
self.generate_index(main_posts, all_posts=main_posts)
|
||||
|
||||
# Group posts by feed (include all posts, not just those in main feed)
|
||||
feeds = {}
|
||||
# Group posts by section (include all posts, not just those in main index)
|
||||
sections = {}
|
||||
for post in all_posts:
|
||||
if post['feed'] and post['type'] != 'page':
|
||||
feeds.setdefault(post['feed'], []).append(post)
|
||||
if post['section'] and post['type'] != 'page':
|
||||
sections.setdefault(post['section'], []).append(post)
|
||||
|
||||
# Generate feed-specific pages
|
||||
for feed_name, posts in feeds.items():
|
||||
self.generate_index(posts, feed_name, all_posts=feed_posts)
|
||||
# Generate section-specific pages
|
||||
for section_name, posts in sections.items():
|
||||
self.generate_index(posts, section_name, all_posts=main_posts)
|
||||
|
||||
# Generate feeds overview page
|
||||
if feeds:
|
||||
self.generate_feeds_overview(feeds, all_posts=feed_posts)
|
||||
# Generate sections overview page
|
||||
if sections:
|
||||
self.generate_sections_overview(sections, all_posts=main_posts)
|
||||
|
||||
# Group posts by subdirectory
|
||||
subdirs = {}
|
||||
|
|
@ -427,16 +427,16 @@ class SSGGGenerator:
|
|||
|
||||
# Generate subdirectory index pages (e.g., /projects/)
|
||||
for subpath, subdir_posts in subdirs.items():
|
||||
self.generate_subdir_index(subpath, subdir_posts, all_posts=feed_posts)
|
||||
self.generate_subdir_index(subpath, subdir_posts, all_posts=main_posts)
|
||||
|
||||
# Generate individual pages for long posts, short posts, and pages
|
||||
for post in all_posts:
|
||||
if post['type'] in ['long', 'short', 'page']:
|
||||
self.generate_post_page(post, all_posts=feed_posts)
|
||||
self.generate_post_page(post, all_posts=main_posts)
|
||||
|
||||
# Generate RSS feed
|
||||
if ENABLE_RSS_FEED:
|
||||
self.generate_rss_feed(feed_posts)
|
||||
self.generate_rss_feed(main_posts)
|
||||
|
||||
# Copy assets
|
||||
self.copy_assets()
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
{% include 'header.tmpl' %}
|
||||
|
||||
<main>
|
||||
<h2>Feeds</h2>
|
||||
<h2>Sections</h2>
|
||||
<ul>
|
||||
{% for feed in feeds %}
|
||||
<li><a href="/feed/{{ feed.name }}/">{{ feed.name }}</a> ({{ feed.count }} posts)</li>
|
||||
{% for section in sections %}
|
||||
<li><a href="/section/{{ section.name }}/">{{ section.name }}</a> ({{ section.count }} posts)</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</main>
|
||||
Loading…
Add table
Add a link
Reference in a new issue