Compare commits
3 commits
e0a9427307
...
980392c519
| Author | SHA1 | Date | |
|---|---|---|---|
| 980392c519 | |||
| ae123ffe80 | |||
| a08a7f537e |
5 changed files with 36 additions and 5 deletions
|
|
@ -22,6 +22,7 @@ Show cases:
|
||||||
- Static files
|
- Static files
|
||||||
- separate feeds (used for categories, tagging, etc) `/feed/{tag}`
|
- separate feeds (used for categories, tagging, etc) `/feed/{tag}`
|
||||||
- exclusion of feeds from main feed (drafts or system notes)
|
- exclusion of feeds from main feed (drafts or system notes)
|
||||||
|
- HTML anchors for headers
|
||||||
|
|
||||||
**Ideas**:
|
**Ideas**:
|
||||||
- RSS
|
- RSS
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,15 @@
|
||||||
This is a `long` form articles. In the main feed, it is a simple link to the rest of the content.
|
This is a `long` form articles. In the main feed, it is a simple link to the rest of the content.
|
||||||
|
|
||||||
Please check the [official documentation](https://git.uphillsecurity.com/cf7/picopaper) for more details.
|
Please check the [official documentation](https://git.uphillsecurity.com/cf7/picopaper) for more details.
|
||||||
|
|
||||||
|
## Second Header
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Massa porta accumsan eros aliquet consequat elit tincidunt imperdiet in adipiscing diam integer risus. Dapibus bibendum adipiscing rhoncus parturient fusce ultrices in accumsan phasellus tempus est senectus felis. Donec placerat porta lobortis magna consectetur hac pretium eleifend tempor cum nulla fusce aliquet.
|
||||||
|
|
||||||
|
Primis amet nullam dictum ridiculus neque ipsum elit volutpat mauris posuere pellentesque euismod felis. Cum nunc ullamcorper dictum metus maecenas accumsan elit purus convallis vulputate mauris sollicitudin diam. Diam iaculis aliquet eleifend sociosqu nostra massa malesuada feugiat sit penatibus dolor montes purus. Porta vulputate dictumst sodales dolor ad euismod consequat interdum mus porttitor erat torquent consequat.
|
||||||
|
|
||||||
|
## Third Header
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Massa porta accumsan eros aliquet consequat elit tincidunt imperdiet in adipiscing diam integer risus. Dapibus bibendum adipiscing rhoncus parturient fusce ultrices in accumsan phasellus tempus est senectus felis. Donec placerat porta lobortis magna consectetur hac pretium eleifend tempor cum nulla fusce aliquet.
|
||||||
|
|
||||||
|
Primis amet nullam dictum ridiculus neque ipsum elit volutpat mauris posuere pellentesque euismod felis. Cum nunc ullamcorper dictum metus maecenas accumsan elit purus convallis vulputate mauris sollicitudin diam. Diam iaculis aliquet eleifend sociosqu nostra massa malesuada feugiat sit penatibus dolor montes purus. Porta vulputate dictumst sodales dolor ad euismod consequat interdum mus porttitor erat torquent consequat.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
- separate feeds like [/feed/tutorial/](/feed/tutorial/)
|
- separate feeds like [/feed/tutorial/](/feed/tutorial/)
|
||||||
- excluding feeds in config file like [/feed/draft/](/feed/draft/)
|
- excluding feeds in config file like [/feed/draft/](/feed/draft/)
|
||||||
- static files like [/LICENSE](/LICENSE)
|
- static files like [/LICENSE](/LICENSE)
|
||||||
|
- html anchors for headers like [#second-header](/your-first-article/#second-header)
|
||||||
|
|
||||||
Everything is **[open-source](https://git.uphillsecurity.com/cf7/picopaper)**!
|
Everything is **[open-source](https://git.uphillsecurity.com/cf7/picopaper)**!
|
||||||
|
|
||||||
|
|
|
||||||
25
picopaper.py
25
picopaper.py
|
|
@ -24,8 +24,8 @@ class SSGGGenerator:
|
||||||
# Setup Jinja2
|
# Setup Jinja2
|
||||||
self.env = Environment(loader=FileSystemLoader(self.templates_dir))
|
self.env = Environment(loader=FileSystemLoader(self.templates_dir))
|
||||||
|
|
||||||
# Setup markdown
|
# Setup markdown with toc extension for header anchors
|
||||||
self.md = markdown.Markdown(extensions=['extra'])
|
self.md = markdown.Markdown(extensions=['extra', 'toc'])
|
||||||
|
|
||||||
def parse_filename(self, filename):
|
def parse_filename(self, filename):
|
||||||
"""Parse filename format: YYYY-MM-DD_type_name[_feed].md"""
|
"""Parse filename format: YYYY-MM-DD_type_name[_feed].md"""
|
||||||
|
|
@ -47,6 +47,20 @@ class SSGGGenerator:
|
||||||
'filename': filename
|
'filename': filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def add_header_anchors(self, html_content):
|
||||||
|
"""Add anchor links to headers with IDs"""
|
||||||
|
# Pattern to match headers with id attributes: <h2 id="some-id">Text</h2>
|
||||||
|
def replace_header(match):
|
||||||
|
tag = match.group(1)
|
||||||
|
header_id = match.group(2)
|
||||||
|
text = match.group(3)
|
||||||
|
# Add anchor link with # symbol
|
||||||
|
return f'<{tag} id="{header_id}">{text} <a href="#{header_id}" class="header-anchor">#</a></{tag}>'
|
||||||
|
|
||||||
|
# Match h2-h6 tags with id attributes
|
||||||
|
pattern = r'<(h[2-6]) id="([^"]+)">([^<]+)<\/\1>'
|
||||||
|
return re.sub(pattern, replace_header, html_content)
|
||||||
|
|
||||||
def read_post(self, filepath):
|
def read_post(self, filepath):
|
||||||
"""Read markdown file and extract title and content"""
|
"""Read markdown file and extract title and content"""
|
||||||
with open(filepath, 'r', encoding='utf-8') as f:
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
|
@ -63,6 +77,9 @@ class SSGGGenerator:
|
||||||
# Convert markdown to HTML
|
# Convert markdown to HTML
|
||||||
html_content = self.md.convert(content)
|
html_content = self.md.convert(content)
|
||||||
|
|
||||||
|
# Add anchor links to headers
|
||||||
|
html_content = self.add_header_anchors(html_content)
|
||||||
|
|
||||||
return title, html_content
|
return title, html_content
|
||||||
|
|
||||||
def collect_posts(self):
|
def collect_posts(self):
|
||||||
|
|
@ -211,9 +228,9 @@ class SSGGGenerator:
|
||||||
for feed_name, posts in feeds.items():
|
for feed_name, posts in feeds.items():
|
||||||
self.generate_index(posts, feed_name)
|
self.generate_index(posts, feed_name)
|
||||||
|
|
||||||
# Generate individual pages for long posts and pages
|
# Generate individual pages for long posts, short posts, and pages
|
||||||
for post in all_posts:
|
for post in all_posts:
|
||||||
if post['type'] in ['long', 'page']:
|
if post['type'] in ['long', 'short', 'page']:
|
||||||
self.generate_post_page(post)
|
self.generate_post_page(post)
|
||||||
|
|
||||||
# Copy assets
|
# Copy assets
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<article class="post">
|
<article class="post">
|
||||||
<div class="post-meta">{{ post.date }}</div>
|
<div class="post-meta">{{ post.date }}</div>
|
||||||
<h2 class="post-title">
|
<h2 class="post-title">
|
||||||
{% if post.type == 'long' %}
|
{% if post.type in ['long', 'short'] %}
|
||||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue