Compare commits

..

9 Commits

8 changed files with 274 additions and 13 deletions

View File

@ -104,6 +104,12 @@ mjx-container {
p.metadata { margin: 0 } p.metadata { margin: 0 }
.blog_inline_post {
border-left: 1em solid var(--fg-lc);
padding: 0 1em 0 1em;
margin-bottom: 5em;
}
@media (max-width: 80em) { @media (max-width: 80em) {
/* CSS that should be displayed if width is equal to or less than 60em goes here */ /* CSS that should be displayed if width is equal to or less than 60em goes here */
#contentWrapper { flex-direction: column } #contentWrapper { flex-direction: column }

View File

@ -14,6 +14,7 @@ import time
import magic import magic
import regex as re import regex as re
import pprint import pprint
from datetime import datetime as dt
import frontmatter import frontmatter
import jinja2 import jinja2
@ -38,6 +39,10 @@ JINJA_TEMPLATE_INDEX = JINJA_ENV.get_template("index.html")
JINJA_TEMPLATE_ARTICLE = JINJA_ENV.get_template("article.html") JINJA_TEMPLATE_ARTICLE = JINJA_ENV.get_template("article.html")
JINJA_TEMPLATE_PERMALINK = JINJA_ENV.get_template("permalink.html") JINJA_TEMPLATE_PERMALINK = JINJA_ENV.get_template("permalink.html")
JINJA_TEMPLATE_BLOGINDEX = JINJA_ENV.get_template("blog_index.html")
JINJA_TEMPLATE_BLOG_INLINE_POST = JINJA_ENV.get_template("blog_inline_post.html")
JINJA_TEMPLATE_BLOG_FEED = JINJA_ENV.get_template("rss.xml")
LICENSE = None LICENSE = None
FILEMAP = None FILEMAP = None
@ -53,6 +58,10 @@ class FileMap:
self.input_dir = Path(input_dir) self.input_dir = Path(input_dir)
self.output_dir = Path(output_dir) self.output_dir = Path(output_dir)
def get_base_url(self):
props = self.get(self.input_dir.joinpath('readme.md'))
return props['base_url']
@staticmethod @staticmethod
def _path_to_key(path): def _path_to_key(path):
return str(path) return str(path)
@ -112,7 +121,8 @@ class FileMap:
include_index_entries=True): include_index_entries=True):
post = { post = {
'title': filepath.name, 'title': filepath.name,
'content_after_search': False, 'blog': False,
'content_after_search': None,
'automatic_index': True, 'automatic_index': True,
'search_bar': True, 'search_bar': True,
'tags': [], 'tags': [],
@ -125,6 +135,9 @@ class FileMap:
file_pointer).to_dict().items(): file_pointer).to_dict().items():
post[key] = val post[key] = val
if post['content_after_search'] is None:
post['content_after_search'] = post['blog']
if 'content' in post.keys(): if 'content' in post.keys():
post['content'] = render_markdown(post['content']) post['content'] = render_markdown(post['content'])
@ -160,7 +173,7 @@ class FileMap:
return entries return entries
def _get_file_properties(self, filepath): def _get_file_properties(self, filepath):
post = {'title': filepath.name} post = {'title': filepath.name, 'pub_date': False}
if filepath.suffix == '.md': if filepath.suffix == '.md':
with open(filepath, encoding='utf-8') as file_pointer: with open(filepath, encoding='utf-8') as file_pointer:
@ -233,6 +246,15 @@ class FileMap:
return d return d
def rfc822_date_sorter_key(date):
if date is None:
ret = 0
else:
ret = int(dt.strptime(date, '%a, %d %b %Y %H:%M:%S %z').timestamp())
return ret
def update_required(src_filepath, output_filepath): def update_required(src_filepath, output_filepath):
""" """
check if file requires an update, check if file requires an update,
@ -257,6 +279,34 @@ def get_args():
) )
return parser.parse_args() return parser.parse_args()
def render_inline_blog_post(input_filepath):
"""
render markdown file as blog post for inlinining into blog index
returns html
"""
with open(input_filepath, encoding='utf-8') as file_pointer:
content = frontmatter.load(file_pointer).content
properties = FILEMAP.get(input_filepath)
html = render_markdown(content)
html = JINJA_TEMPLATE_BLOG_INLINE_POST.render(
license=LICENSE,
content=html,
lecture_slides=properties.get("lecture_slides"),
lecture_notes=properties.get("lecture_notes"),
uuid=properties.get("uuid"),
tags=properties.get("tags"),
author=properties.get("author"),
title=properties.get("title"),
published=properties.get("pub_date"),
base_url=FILEMAP.get_base_url(),
)
properties['dst_path']['html'].write_text(html)
return html
def render_markdown_file(input_filepath): def render_markdown_file(input_filepath):
""" """
@ -278,7 +328,9 @@ def render_markdown_file(input_filepath):
uuid=properties.get("uuid"), uuid=properties.get("uuid"),
tags=properties.get("tags"), tags=properties.get("tags"),
author=properties.get("author"), author=properties.get("author"),
title=properties.get("title")) title=properties.get("title"),
published=properties.get("pub_date")
)
properties['dst_path']['html'].write_text(html) properties['dst_path']['html'].write_text(html)
@ -476,22 +528,51 @@ def main(args):
root_properties = FILEMAP.get(root) root_properties = FILEMAP.get(root)
root_properties['dst_path']['raw'].mkdir(parents=True, exist_ok=True) root_properties['dst_path']['raw'].mkdir(parents=True, exist_ok=True)
posts = []
if root_properties['blog']:
for file in files:
props = FILEMAP.get(root.joinpath(file))
post = {
'title': props['title'],
'link': props['dst_path']['web'],
'pub_date': props.get('pub_date'),
'description': render_inline_blog_post(root.joinpath(file)),
}
posts.append(post)
posts.sort(
key=lambda p: rfc822_date_sorter_key(p.get('pub_date')),
reverse=True
)
# render rss feed
rss = JINJA_TEMPLATE_BLOG_FEED.render(
title=root_properties.get('title', ''),
description=root_properties.get('content', ''),
base_url=FILEMAP.get_base_url(),
link=f"{FILEMAP.get_base_url()}{root_properties['dst_path']['web']}",
language='en-GB',
posts=posts,
)
root_properties['dst_path']['raw'].joinpath('feed.xml').write_text(rss)
#pprint.pprint(root_properties) #pprint.pprint(root_properties)
html = JINJA_TEMPLATE_INDEX.render( # render index
html = (JINJA_TEMPLATE_BLOGINDEX if root_properties['blog'] else JINJA_TEMPLATE_INDEX).render(
gronk_commit=GRONK_COMMIT, gronk_commit=GRONK_COMMIT,
title=root_properties.get('title', ''), title=root_properties.get('title', ''),
content=root_properties.get('content', ''), content=root_properties.get('content', ''),
content_after_search=root_properties['content_after_search'], content_after_search=root_properties['content_after_search'],
automatic_index=root_properties['automatic_index'], automatic_index=root_properties['automatic_index'],
search_bar=root_properties['search_bar'], search_bar=root_properties['search_bar'],
posts=posts,
index_entries=[{ index_entries=[{
'title': entry.get('title', ''), 'title': entry.get('title', ''),
'is_dir': entry.get('is_dir', False), 'is_dir': entry.get('is_dir', False),
'path': str(entry.get('path', Path(''))), 'path': str(entry.get('path', Path(''))),
} for entry in root_properties.get('index_entries', '')], } for entry in root_properties.get('index_entries', '')],
) )
root_properties['dst_path']['raw'].joinpath('index.html').write_text( root_properties['dst_path']['raw'].joinpath('index.html').write_text(html)
html)
# render each file # render each file
for file in files: for file in files:

View File

@ -57,13 +57,14 @@ To add custom content to a directory index, put it in a file called `readme.md`
You can set the following frontmatter variables to customise the directory index of a directory: You can set the following frontmatter variables to customise the directory index of a directory:
| variable | default value | description | | variable | default value | description |
|------------------------|-------------------|--------------------------------------------------------------------------------------------| |------------------------|----------------|--------------------------------------------------------------------------------------------|
| `tags` | `[]` | list of tags, used by search and inherited by any notes and subdirectories | | `blog` | `false` | enable [blog mode](#blog-mode) for this directory |
| `uuid` | none | unique id to reference directory, used for permalinking | | `tags` | `[]` | list of tags, used by search and inherited by any notes and subdirectories |
| `content_after_search` | `false` | show custom content in `readme.md` after search bar and directory index | | `uuid` | none | unique id to reference directory, used for permalinking |
| `automatic_index` | `true` | show the automatically generated directory index. required for search bar to function. | | `content_after_search` | same as `blog` | show custom content in `readme.md` after search bar and directory index |
| `search_bar` | `true` | show search bar to search directory items. requires `automatic_index` (enabled by default) | | `automatic_index` | `true` | show the automatically generated directory index. required for search bar to function. |
| `search_bar` | `true` | show search bar to search directory items. requires `automatic_index` (enabled by default) |
## Notes Metadata ## Notes Metadata
@ -73,12 +74,31 @@ gronk reads the following YAML [frontmatter](https://jekyllrb.com/docs/front-mat
| variable | description | | variable | description |
|------------------|---------------------------------------------------------------------------------------| |------------------|---------------------------------------------------------------------------------------|
| `author` | The person(s) who wrote the article | | `author` | The person(s) who wrote the article |
| `pub_date` | (for blog mode) set the publish date of an article/post/note (MUST be RFC822 format) |
| `tags` | A YAML list of tags which the article relates to - this is used for browsing and also | | `tags` | A YAML list of tags which the article relates to - this is used for browsing and also |
| `title` | The title of the article | | `title` | The title of the article |
| `uuid` | A unique identifier used for permalinks. | | `uuid` | A unique identifier used for permalinks. |
| `lecture_slides` | a list of paths pointing to lecture slides used while taking notes | | `lecture_slides` | a list of paths pointing to lecture slides used while taking notes |
| `lecture_notes` | a list of paths pointing to other notes used while taking notes | | `lecture_notes` | a list of paths pointing to other notes used while taking notes |
## Blog Mode
A directory can be turned into a blog by enabling blog mode.
This can be done by setting the `blog` variable to `true` in the `readme.md` [custom directory metadata](#custom-directory-index-and-metadata).
Notes under this directory will be published to a blog, whose feed is accesible at `https://notes.alv.cx/notes/<directory..>/feed.xml`.
When blog mode is enabled, it is required that the `base_url` property is set in the top level `readme.md` file.
Note that there should be no trailing slash.
If a `readme.md` file does not exist, then an empty one can be created:
```md
---
base_url: https://notes.alv.cx
---
```
## Permalinks ## Permalinks
Permalinks are currently rather basic and requires JavaScript to be enabled on the local computer. Permalinks are currently rather basic and requires JavaScript to be enabled on the local computer.

View File

@ -32,6 +32,10 @@
<p class="smallText metadata"> uuid: {{ uuid }} (<a href="/permalink?uuid={{ uuid }}">permalink</a>) </p> <p class="smallText metadata"> uuid: {{ uuid }} (<a href="/permalink?uuid={{ uuid }}">permalink</a>) </p>
{% endif %} {% endif %}
{% if published %}
<p class="smallText metadata"> published: {{ published }} </p>
{% endif %}
<p class="smallText metadata"> tags: [ <p class="smallText metadata"> tags: [
{% for tag in tags %} {% for tag in tags %}
<a href="/tags/{{ tag }}">{{ tag }}</a>{% if loop.nextitem %},{% endif %} <a href="/tags/{{ tag }}">{{ tag }}</a>{% if loop.nextitem %},{% endif %}

83
templates/blog_index.html Normal file
View File

@ -0,0 +1,83 @@
{% extends "base.html" %}
{% block head %}
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
tex: {
tags: 'ams'
}
}
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
{% endblock %}
{% block content %}
<p class="smallText metadata">
syntax highlighting based on <a href="https://pygments.org/">Pygments'</a> default
colors
</p>
<p class="smallText metadata">
page generated by <a href="https://git.alv.cx/alvierahman90/gronk">gronk</a>
</p>
<p class="smallText metadata">
<a href="./feed.xml">rss feed</a>
</p>
{% if license %}
<details id="license">
<summary class="smallText">
License
</summary>
<pre>{{ license }}</pre>
</details>
{% endif %}
{% if not content %}
<h1>{{ title }}</h1>
{% endif %}
{% if not content_after_search %}
{{ content|safe }}
{% for post in posts %}
{{ post['description']|safe }}
{% endfor %}
{% endif %}
{% if automatic_index %}
<details>
<summary>
<h4> List of posts </h4>
</summary>
{% if search_bar %}
<div id="searchWrapper">
<input id="search" placeholder="search" autocomplete="off" autofocus>
</div>
<p class="searchSmallText" style="margin-top: 0; text-align: center">
Press (<kbd>Shift</kbd>+)<kbd>Enter</kbd> to open first result (in new tab)
</p>
{% endif %}
<ul id="searchResults" class="buttonlist">
<li class="article"><a href=".."><p>../</p></a></li>
{% for entry in index_entries %}
<li class="article"><a href="{{ entry['path'] }}"><p>{{ entry['title'] }}{% if entry['is_dir'] %}/{% endif %}</p></a></li>
{% endfor %}
</ul>
</details>
{% endif %}
{% if content_after_search %}
{{ content|safe }}
{% for post in posts %}
<div class="blog_inline_post">
{{ post['description']|safe }}
</div>
{% endfor %}
{% endif %}
<script src="/js/fuse.js"> </script>
<script> const search_data = {{ index_entries|tojson }} </script>
<script src="/js/indexsearch.js"> </script>
{% endblock %}

View File

@ -0,0 +1,42 @@
{% block content %}
<p class="smallText metadata"> title: {{ title }} </p>
{% if lecture_slides %}
<p class="smallText metadata"> lecture_slides: [
{% for slide in lecture_slides %}
<a href="{{ base_url}}{{ slide }}">{{ slide }}</a>{% if loop.nextitem %},{% endif %}
{% endfor %}
]</p>
{% endif %}
{% if lecture_notes %}
<p class="smallText metadata"> lecture_notes: [
{% for note in lecture_notes %}
<a href="{{ base_url }}{{ note }}">{{ note }}</a>{% if loop.nextitem %},{% endif %}
{% endfor %}
]</p>
{% endif %}
{% if uuid %}
<p class="smallText metadata"> uuid: {{ uuid }} (<a href="{{ base_url }}/permalink?uuid={{ uuid }}">permalink</a>) </p>
{% endif %}
{% if published %}
<p class="smallText metadata"> published: {{ published }} </p>
{% endif %}
<p class="smallText metadata"> tags: [
{% for tag in tags %}
<a href="{{ base_url }}/tags/{{ tag }}">{{ tag }}</a>{% if loop.nextitem %},{% endif %}
{% endfor %}
]</p>
<p class="smallText metadata">
{% if author is string %}
written by: {{ author }}
{% elif author is iterable %}
written by: [ {% for auth in author %}{{ auth }}{% if loop.nextitem %}, {% endif %}{% endfor %} ]
{% endif %}
</p>
{% block body_content %}
{{ content|safe }}
{% endblock %}
{% endblock %}

View File

@ -1,6 +1,8 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% if not content %}
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
{% endif %}
{% if not content_after_search %} {% if not content_after_search %}
{{ content|safe }} {{ content|safe }}

23
templates/rss.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ title }}</title>
<description>{{ description }}</description>
<language>{{ language }}</language>
<link>{{ link }}</link>
<atom:link href="{{ link }}/feed.xml" rel="self" type="application/rss+xml" />
{% for post in posts %}
<item>
<title>{{ post['title']}}</title>
<guid>{{ base_url }}{{ post['link'] }}</guid>
{% if post['pub_date'] %}
<pubDate>{{ post['pub_date'] }}</pubDate>
{% endif %}
<description><![CDATA[{{ post['description']|safe }}]]></description>
</item>
{% endfor %}
</channel>
</rss>