From 98ab0ab419ea26680c199047673efcf2738467e1 Mon Sep 17 00:00:00 2001 From: CaffeineFueled Date: Mon, 27 Oct 2025 22:04:42 +0100 Subject: [PATCH] ADD subpaths for pages and create basic index #19 --- .../2025-10-27_page_another-project.md | 3 + picopaper.py | 83 ++++++++++++++++--- theme/default/templates/index.tmpl | 4 - 3 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 items/projects/2025-10-27_page_another-project.md diff --git a/items/projects/2025-10-27_page_another-project.md b/items/projects/2025-10-27_page_another-project.md new file mode 100644 index 0000000..cecf35f --- /dev/null +++ b/items/projects/2025-10-27_page_another-project.md @@ -0,0 +1,3 @@ +# Another Project + +Just a placeholer. diff --git a/picopaper.py b/picopaper.py index f06b157..bef764e 100644 --- a/picopaper.py +++ b/picopaper.py @@ -41,8 +41,13 @@ class SSGGGenerator: # Setup markdown with toc extension for header anchors self.md = markdown.Markdown(extensions=['extra', 'toc']) - def parse_filename(self, filename): - """Parse filename format: YYYY-MM-DD_type_name[_feed].md""" + 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/) + """ pattern = r'(\d{4}-\d{2}-\d{2})_(short|long|page)_(.+?)(?:_([a-z0-9-]+))?\.md' match = re.match(pattern, filename) @@ -58,7 +63,8 @@ class SSGGGenerator: 'type': post_type, 'name': name, 'feed': feed, - 'filename': filename + 'filename': filename, + 'subpath': subpath } def add_header_anchors(self, html_content): @@ -97,32 +103,46 @@ class SSGGGenerator: return title, html_content def collect_posts(self): - """Collect and parse all posts from items directory""" + """Collect and parse all posts from items directory, including subdirectories""" posts = [] if not self.items_dir.exists(): print(f"Warning: {self.items_dir} does not exist") return posts - for filepath in self.items_dir.glob('*.md'): - parsed = self.parse_filename(filepath.name) + # 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) if not parsed: - print(f"Skipping {filepath.name}: doesn't match naming convention") + print(f"Skipping {filepath}: 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': parsed['name'], - 'url': f"/{parsed['name']}/", + 'slug': slug, + 'url': url, 'feed': parsed['feed'], - 'source': filepath.name + 'source': str(relative_path), + 'subpath': parsed['subpath'] } posts.append(post) @@ -199,6 +219,35 @@ 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') @@ -217,9 +266,9 @@ class SSGGGenerator: rss_feed_path=RSS_FEED_PATH ) - # Create directory for the post slug + # Create directory for the post slug (with parents for nested paths) post_dir = self.output_dir / post['slug'] - post_dir.mkdir(exist_ok=True) + post_dir.mkdir(parents=True, exist_ok=True) # Generate index.html inside the slug directory output_path = post_dir / 'index.html' @@ -370,6 +419,16 @@ 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 f1c1e01..f981319 100644 --- a/theme/default/templates/index.tmpl +++ b/theme/default/templates/index.tmpl @@ -11,11 +11,7 @@
{{ post.date }}

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

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