roundabout,
created on Saturday, 20 April 2024, 08:45:31 (1713602731),
received on Monday, 29 April 2024, 06:47:46 (1714373266)
Author identity: vlad <vlad.muntoiu@gmail.com>
237c006c97957e907ed3ecaaeb8dad51cb908f7f
.idea/misc.xml
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.10 (blog)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (blog)" project-jdk-type="Python SDK" />
</project>
.idea/workspace.xml
@@ -1,14 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="">
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="Font attribution">
<change afterPath="$PROJECT_DIR$/templates/project.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/projects.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/articles/ampoule.md" beforeDir="false" afterPath="$PROJECT_DIR$/projects/ampoule.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/static/style.css" beforeDir="false" afterPath="$PROJECT_DIR$/static/style.css" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 7
}]]></component>
@@ -18,6 +30,24 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.ShowReadmeOnStart": "true",
"git-widget-placeholder": "master",
"last_opened_file_path": "/home/vlad/blog/static/fonts"
}
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/static/fonts" />
<recent name="$PROJECT_DIR$" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/projects" />
<recent name="$PROJECT_DIR$/articles" />
<recent name="$PROJECT_DIR$" />
</key>
</component>
<component name="RunManager">
<configuration name="main" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="blog" />
@@ -58,17 +88,48 @@
<option name="presentableId" value="Default" />
<updated>1713536817770</updated>
</task>
<task id="LOCAL-00001" summary="Blog">
<option name="closed" value="true" />
<created>1713552319159</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1713552319159</updated>
</task>
<task id="LOCAL-00002" summary="Add missing files">
<option name="closed" value="true" />
<created>1713553990582</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1713553990582</updated>
</task>
<task id="LOCAL-00003" summary="Font attribution">
<option name="closed" value="true" />
<created>1713554601045</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1713554601045</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/main.py</url>
<line>8</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Blog" />
<MESSAGE value="Add missing files" />
<MESSAGE value="Font attribution" />
<option name="LAST_COMMIT_MESSAGE" value="Font attribution" />
</component>
</project>
articles/ampoule.md
@@ -1,14 +0,0 @@
---
title: Ampoule
---
Ampoule is a lightweight, simple yet flexible, static site generator written in Python.
It uses Jinja2 for templating.
Header 2
--------
### Header 3
#### Header 4
##### Header 5
###### Header 6
main.py
@@ -5,10 +5,10 @@ import string
import ampoule_ssg as ampoule
from ampoule_ssg import markdown
my_site = ampoule.Site("my_site")
site = ampoule.Site("my_site")
@my_site.filter("markdown")
@site.filter("markdown")
def markdown_filter(text):
return markdown.markdown2html(text)
@@ -27,20 +27,31 @@ def article_url(url):
# Set context
my_site.context["timestamp"] = datetime.now()
my_site.context["ampoule"] = ampoule
site.context["timestamp"] = datetime.now()
site.context["ampoule"] = ampoule
# Add articles
articles = ampoule.Index("articles", url_transform=article_url)
my_site.add_from_index(articles, "/articles", "article.html")
articles = ampoule.Index("articles", url_transform=article_url, sort_by=lambda x: x.date)
site.add_from_index(articles, "/articles", "article.html")
# Create the first page
my_first_page = ampoule.Page(my_site, "home.html", articles=articles)
# Add projects
projects = ampoule.Index("projects", url_transform=article_url, sort_by=lambda x: x.file_name)
site.add_from_index(projects, "/projects", "project.html")
# Add the page to the site
my_site.add_page("/", my_first_page)
# Create the index pages
main_page = ampoule.Page(site, "home.html", articles=articles)
projects_page = ampoule.Page(site, "projects.html", projects=projects)
# Add static files
my_site.add_from_index(ampoule.Index("static", recursive=True), "/static", None, static=True)
# Add the pages to the site
site.add_page("/", main_page)
site.add_page("/projects/", projects_page)
my_site.build()
# Add static files
site.add_from_index(
ampoule.Index("static", recursive=True, exclude=r"\.md$"),
"/static",
None,
static=True
)
site.build()
projects/ampoule.md
@@ -0,0 +1,117 @@
---
title: Ampoule
---
Ampoule is a lightweight, simple yet flexible, static site generator written in Python.
It uses Jinja2 for templating.
Features
--------
* Extremely simple and small, only a few hundred lines of code.
* Only depends on Jinja2, Ruamel YAML, and colorama.
* Jinja2 templating will be familiar to Flask users. Now you can use the same templates for
both dynamic and static sites.
* More of a framework. Sites are generated by a short Python script that you write to customise
what pages it loads, which templates it uses, and what data it passes to them, or create
custom filters, tests and more.
* Supports YAML front matter for pages. It can be accessed using indexing syntax.
* Indexes can be sorted using a function, iterated and can index any directory, recursively
or not. They can also transform URLs to make them end in ".html".
* Object-oriented design. The same objects used in that script can also be passed to the
templates.
* Any markup language can be used, as long as it can be converted to HTML. You just need to
configure a filter for it.
* Ships with a light markdown implementation.
* Easy to use for both programmers and non-programmers. While you do need a script, you can
also use an off-the-shelf one.
* Themes can be exactly how you want.
* Keeping static files is easy, because indexes can be static.
* Static files are always binary and not templated. The same happens for files that can't be
decoded.
* URL-based definitions. Pages are added using the URL that will be used to access them.
* Reinforces the web as a publishing medium. Static sites are not for everyone, but if you
want to publish something, it's the best way.
* And GitHub will give you free hosting, because it's static and very cheap to serve.
Roundabout-host will soon offer this as well (`your.site.roundabout-host.com`).
* It's free software and available under the GPLv3.
* No JavaScript is required, but it can of course be used if you want.
* Decently fast: even if you've got a huge site, it should not take more than 30 seconds.
Local rebuilding will also be added. And it's still much faster than any dynamic site.
* Beautiful logging thanks to colorama.
* Great for educational use; you can learn Python, HTML, CSS, JavaScript, and Jinja2
all at once.
* You can make your site in an hour, and then it's time to focus on writing what you want
to publish.
Minimal example
---------------
```python
import string
from datetime import datetime
import string
import ampoule_ssg as ampoule
from ampoule_ssg import markdown
# Create a site object. This is where we are adding pages to. The argument is the directory
# where the site will be built.
site = ampoule.Site("my_site")
# Use this as "| markdown" in Jinja2 templates to convert any Markdown source to HTML.
@site.filter("markdown")
def markdown_filter(text):
return markdown.markdown2html(text)
# Make the URLs web-friendly and make it end in ".html" so it will be correctly formatted
# by dumb servers.
def article_url(url):
url = url.lower().rpartition(".")[0]
new_url = ""
for i in url:
if i in string.ascii_lowercase:
new_url += i
elif i in string.whitespace:
new_url += "-"
return new_url + ".html"
# Set context that will be passed to all templates. You can still override this.
site.context["timestamp"] = datetime.now()
site.context["ampoule"] = ampoule
# Add the index of articles. In the template, we're looping over it to list them all.
articles = ampoule.Index("articles", url_transform=article_url, sort_by=lambda x: x.date)
# This makes it take all indexed files and put them under the /articles URL, keeping the
# index's URL transformation and placing all of them in the article.html template. This
# will be passed as "document" to the template.
site.add_from_index(articles, "/articles", "article.html")
# Create the main page which has access to the index so it can list all articles.
main_page = ampoule.Page(site, "home.html", articles=articles)
# Add the page. Note how we're binding it to a path; it will automatically be set as
# index.html in that directory, and the URL is site-relative, not the OS root.
site.add_page("/", main_page)
# Add static files using a recursive static index. It will add all files in the static
# directory and all its subdirectories, without putting them into templates. You could
# still use them in templates, so you can make a photo gallery or something.
site.add_from_index(
# We're excluding Markdown files because we're using them as licence information
# for when the site is distributed together with the fonts. You can exclude any
# file you want using regex.
ampoule.Index("static", recursive=True, exclude=r"\.md$"),
"/static",
# There is no template, because the index is static.
static=True
)
# Makes Ampoule take all pages and put them in a directory.
site.build()
```
static/style.css
@@ -46,6 +46,7 @@ body {
flex-direction: column;
align-items: center;
background: #eceff1;
font-variation-settings: "opsz" 14;
}
a {
templates/project.html
@@ -0,0 +1,10 @@
{% extends "default.html" %}
{% block title %}
{{ document["title"] }}
{% endblock %}
{% block content %}
<h1>{{ document["title"] }}</h1>
<article class="content-area">
{{ document.content | markdown }}
</article>
{% endblock %}
templates/projects.html
@@ -0,0 +1,12 @@
{% extends "default.html" %}
{% block title %}Projects{% endblock %}
{% block content %}
<h1>Projects</h1>
{% for article in projects %}
<article class="content-area">
<h2><a href="/projects/{{ article.file_name }}" class="article-title">{{ article["title"] }}</a></h2>
<p>{{ article.content | markdown }}</p>
</article>
{% endfor %}
{% endblock %}