diff --git a/README.md b/README.md index 634e18b..549d0e8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A minimal static site generator for blogs built with Python 3 and Jinja2 -- Status: beta - expect many changes +- Status: alpha - expect many changes - [Issue Tracker](https://git.uphillsecurity.com/cf7/picopaper/issues) - Goals: keeping it simple and easy to understand - Demo: [picopaper.com](https://picopaper.com/) @@ -17,19 +17,21 @@ Show cases: ## Features **Available**: -- simple use, easy to understand and modify +- Simple use, easy to understand and modify - config file for settings - Themes -- long- and short form content -- pages -- static files +- 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) - HTML anchors for headers - list random posts at the bottom -- optional RSS feeds **Ideas**: +- RSS +- Dark mode +- logo - custom error pages (404, etc) **Not planned**: diff --git a/config.py b/config.py index 66cb25c..16f4d49 100644 --- a/config.py +++ b/config.py @@ -15,8 +15,7 @@ EXCLUDE_FEEDS_FROM_MAIN = ['draft','private'] # e.g., ['python', 'drafts'] NAVBAR_ITEMS = [ {'text': 'Home', 'url': '/'}, {'text': 'Feeds', 'url': '/feed/'}, - {'text': 'About', 'url': '/about/'}, - {'text': 'RSS', 'url': '/rss.xml'} + {'text': 'About', 'url': '/about/'} ] # Logo settings diff --git a/items/projects/2025-10-27_page_another-project.md b/items/projects/2025-10-27_page_another-project.md deleted file mode 100644 index cecf35f..0000000 --- a/items/projects/2025-10-27_page_another-project.md +++ /dev/null @@ -1,3 +0,0 @@ -# Another Project - -Just a placeholer. diff --git a/picopaper.py b/picopaper.py index bef764e..f06b157 100644 --- a/picopaper.py +++ b/picopaper.py @@ -41,13 +41,8 @@ class SSGGGenerator: # Setup markdown with toc extension for header anchors self.md = markdown.Markdown(extensions=['extra', 'toc']) - def parse_filename(self, filename, subpath=''): - """Parse filename format: YYYY-MM-DD_type_name[_feed].md - - Args: - filename: The markdown filename - subpath: Optional subdirectory path (e.g., 'notes' for items/notes/) - """ + def parse_filename(self, filename): + """Parse filename format: YYYY-MM-DD_type_name[_feed].md""" pattern = r'(\d{4}-\d{2}-\d{2})_(short|long|page)_(.+?)(?:_([a-z0-9-]+))?\.md' match = re.match(pattern, filename) @@ -63,8 +58,7 @@ class SSGGGenerator: 'type': post_type, 'name': name, 'feed': feed, - 'filename': filename, - 'subpath': subpath + 'filename': filename } def add_header_anchors(self, html_content): @@ -103,46 +97,32 @@ class SSGGGenerator: return title, html_content def collect_posts(self): - """Collect and parse all posts from items directory, including subdirectories""" + """Collect and parse all posts from items directory""" posts = [] if not self.items_dir.exists(): print(f"Warning: {self.items_dir} does not exist") return posts - # Use rglob to recursively find all .md files - for filepath in self.items_dir.rglob('*.md'): - # Calculate subpath relative to items_dir - relative_path = filepath.relative_to(self.items_dir) - subpath = str(relative_path.parent) if relative_path.parent != Path('.') else '' - - parsed = self.parse_filename(filepath.name, subpath) + for filepath in self.items_dir.glob('*.md'): + parsed = self.parse_filename(filepath.name) if not parsed: - print(f"Skipping {filepath}: doesn't match naming convention") + print(f"Skipping {filepath.name}: doesn't match naming convention") continue title, content = self.read_post(filepath) - # Build slug and URL with subpath - if parsed['subpath']: - slug = f"{parsed['subpath']}/{parsed['name']}" - url = f"/{parsed['subpath']}/{parsed['name']}/" - else: - slug = parsed['name'] - url = f"/{parsed['name']}/" - post = { 'date': parsed['date_str'], 'type': parsed['type'], 'name': parsed['name'], 'title': title, 'content': content, - 'slug': slug, - 'url': url, + 'slug': parsed['name'], + 'url': f"/{parsed['name']}/", 'feed': parsed['feed'], - 'source': str(relative_path), - 'subpath': parsed['subpath'] + 'source': filepath.name } posts.append(post) @@ -219,35 +199,6 @@ class SSGGGenerator: print(f"✓ Generated {output_path}") - def generate_subdir_index(self, subpath, posts, all_posts=None): - """Generate index page for a subdirectory (e.g., /projects/)""" - template = self.env.get_template('index.tmpl') - - # Use the subpath as the title (capitalize first letter) - subpath_title = subpath.replace('/', ' / ').title() - title = f"{subpath_title} - {self.blog_title}" - output_path = self.output_dir / subpath / 'index.html' - - html = template.render( - title=title, - blog_title=self.blog_title, - blog_description=self.blog_description, - navbar_items=self.navbar_items, - posts=posts, - all_posts=all_posts or posts, - hide_logo=self.hide_logo, - hide_title=self.hide_title, - logo_path=self.logo_path, - rss_feed_enabled=ENABLE_RSS_FEED, - rss_feed_path=RSS_FEED_PATH - ) - - output_path.parent.mkdir(parents=True, exist_ok=True) - with open(output_path, 'w', encoding='utf-8') as f: - f.write(html) - - print(f"✓ Generated {output_path}") - def generate_post_page(self, post, all_posts=None): """Generate individual post page for 'long' posts""" template = self.env.get_template('post.tmpl') @@ -266,9 +217,9 @@ class SSGGGenerator: rss_feed_path=RSS_FEED_PATH ) - # Create directory for the post slug (with parents for nested paths) + # Create directory for the post slug post_dir = self.output_dir / post['slug'] - post_dir.mkdir(parents=True, exist_ok=True) + post_dir.mkdir(exist_ok=True) # Generate index.html inside the slug directory output_path = post_dir / 'index.html' @@ -419,16 +370,6 @@ class SSGGGenerator: if feeds: self.generate_feeds_overview(feeds, all_posts=feed_posts) - # Group posts by subdirectory - subdirs = {} - for post in all_posts: - if post['subpath']: # Only posts in subdirectories - subdirs.setdefault(post['subpath'], []).append(post) - - # 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) - # Generate individual pages for long posts, short posts, and pages for post in all_posts: if post['type'] in ['long', 'short', 'page']: diff --git a/theme/default/templates/index.tmpl b/theme/default/templates/index.tmpl index f981319..f1c1e01 100644 --- a/theme/default/templates/index.tmpl +++ b/theme/default/templates/index.tmpl @@ -11,7 +11,11 @@
{{ post.date }}

+ {% if post.type in ['long', 'short'] %} {{ post.title }} + {% else %} + {{ post.title }} + {% endif %}

{% if post.type == 'short' %}