Commit
Author: Kevin Schoon [me@kevinschoon.com]
Hash: 34ff2c0238d62d2096e9d2a728adcc5e074f0bda
Timestamp: Thu, 06 Feb 2025 21:31:58 +0000 (2 weeks ago)

+101 -0 +/-7 browse
automated www update
1diff --git a/gen/blog/building-this-website/index.html b/gen/blog/building-this-website/index.html
2new file mode 100644
3index 0000000..a8bffa3
4--- /dev/null
5+++ b/gen/blog/building-this-website/index.html
6 @@ -0,0 +1,97 @@
7+ <!doctypehtml><html lang=en><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta content=width=device-width,initial-scale=1 name=viewport><link href=/main.css rel=stylesheet><link href=https://kevinschoon.com/index.xml rel=alternate title=RSS type=application/rss+xml><body><section><div class=wrapper><header class=page-header><a href=/>Home</a></header><main class=page-body><h1>Building This Website</h1><p>There is no deficiency of static website generators these days and so naturally I had to write my own.<h2>Overview</h2><p>The main functionality of the tool will be encapsulated in a Python script called <code>render.py</code>. Even though <a href=https://www.devever.net/~hl/stringtemplates#narrow>string based templates are the wrong solution</a> we're going to use a combination of <a href=https://jinja.palletsprojects.com/>Jinja</a> and markdown to generate HTML output. Each page will be generated by a single call to the script which will allow the whole thing to be ran in parallel across multiple CPUs by wiring it together using <a href=https://ninja-build.org/>ninja</a>.<p>Here you can see an example invocation of the <code>render.py</code> script with some annotated flags.<pre><code>./render.py \
8+ # the HTML template file
9+ --template=index.jinja \
10+ # this contains a map of each webpage which is available in the renderer
11+ # as well as page level configuration options.
12+ --sitemap sitemap.yaml \
13+ # markdown file, this one contains the text I'm currently typing!
14+ --content=blog/building-this-website/README.md \
15+ # output of the rendered HTML page
16+ gen/blog/building-this-website/index.html
17+ </code></pre><h3>Hierarchical Sitemap</h3><p>I want to generate a "tree" sitemap similar to a file browser like NERDTree. We'll pass in a YAML document to the <code>render.py</code> script and then transform it into an XML tree.<pre><code>def _load_sitemap(path, current_path):
18+ by_name = dict()
19+ flattened = []
20+ with open(path, "r") as fp:
21+ links = yaml.safe_load(fp.read())
22+
23+ def _link(links, parent=None):
24+ for link in links:
25+ flattened.append(link)
26+ link["parent"] = parent
27+ if "others" in link:
28+ _link(link["others"], parent=link)
29+
30+ def _make_url(link):
31+ path = [link["name"]]
32+ parent = link["parent"]
33+ while parent is not None:
34+ path.append(parent["name"])
35+ parent = parent["parent"]
36+ path.reverse()
37+ return "/".join(path)
38+
39+ _link(links, parent=None)
40+
41+ for link in flattened:
42+ target = "/" + _make_url(link)
43+ if target == current_path:
44+ link["active"] = True
45+ else:
46+ link["active"] = False
47+ link["url"] = target
48+ by_name[target] = link
49+
50+ return dict(by_name=by_name, links=links)
51+
52+
53+ def _make_tree(links):
54+ def _populate(root, links):
55+ for link in links:
56+ elm = ET.Element("li")
57+ if "directory" in link and link["directory"]:
58+ elm.text = link["name"]
59+ else:
60+ lref = ET.Element("a", href=link["url"])
61+ lref.text = link["name"]
62+ if link["active"]:
63+ lref.text = lref.text + " <"
64+ elm.append(lref)
65+ if "others" in link:
66+ others = link["others"]
67+ ul = ET.Element("ul")
68+ elm.append(ul)
69+ _populate(root=ul, links=others)
70+ root.append(elm)
71+
72+ params = {"class": "tree"}
73+
74+ root = ET.Element("ul", **params)
75+ _populate(root=root, links=links)
76+ return ET.tostring(root).decode()
77+ </code></pre><h3>Dithering Engine</h3><p>Images can be reduced in size with <a href=https://en.wikipedia.org/wiki/Dither>dithering algorithms</a> and they also look very cool. The idea for this was inspired by <a href=https://solar.lowtechmagazine.com/>LOW←TECH MAGAZINE</a>.<h3>Spell & Grammar Checking</h3><p>I can implement some basic spell checking against the website with <a href=http://aspell.net/>aspell</a>.<pre><code class=language-sh>find content -name '*.md' -exec aspell --mode=markdown check {} \;
78+ </code></pre><h3>Filesystem "Watch" Support</h3><p>We'll use the native <a href=https://en.wikipedia.org/wiki/Inotify>inotify</a> via the <a href=https://man7.org/linux/man-pages/man1/inotifywatch.1.html>inotifywatch</a> tool which is available on all Linux distributions.<pre><code class=language-sh>#!/bin/sh
79+
80+ # the actual command we want to run each time any file changes in the project
81+ BUILD_CMD="$@"
82+
83+ # some pretty colorized output
84+ _do_build() {
85+ $BUILD_CMD && {
86+ echo -e "\033[32;1;4mSuccess\033[0m"
87+ } || {
88+ echo -e "\033[31;1;4mFailure\033[0m"
89+ }
90+ }
91+
92+ # build the project the first time you start up the watch
93+ _do_build
94+
95+ # watch all the files in the project except for the output directory
96+ find . -type d -not -path './gen*' -printf '%p ' | xargs inotifywait \
97+ -m -e close_write \
98+ --format %e/%f |
99+ while IFS=/ read -r events file; do
100+ echo "file $file modified, rebuilding"
101+ _do_build
102+ done
103+ </code></pre><p>Now we can test the script by launching it <code>scripts/watch.sh build.py</code>.<p>You can checkout the source for the whole site <a href=https://ayllu-forge.org/web/kevinschoon-dot-com>here</a>.</main><footer class=page-footer></footer></div></section>
104\ No newline at end of file
105 diff --git a/gen/contact.png b/gen/contact.png
106new file mode 100644
107index 0000000..e74e5b7
108 Binary files /dev/null and b/gen/contact.png differ
109 diff --git a/gen/favicon.ico b/gen/favicon.ico
110new file mode 100644
111index 0000000..59a4e28
112 Binary files /dev/null and b/gen/favicon.ico differ
113 diff --git a/gen/index.html b/gen/index.html
114new file mode 100644
115index 0000000..833bcae
116--- /dev/null
117+++ b/gen/index.html
118 @@ -0,0 +1 @@
119+ <!doctypehtml><html lang=en><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta content=width=device-width,initial-scale=1 name=viewport><link href=/main.css rel=stylesheet><link href=https://kevinschoon.com/index.xml rel=alternate title=RSS type=application/rss+xml><body><section><div class=wrapper><header class=page-header></header><main class=page-body><p><a href=/><img alt="dithered photo of me"class=me src=/ks_stylized.png></a><h1>Hi, thanks for stopping by!</h1><p>I'm a programmer interested in free software.<h3>Sitemap</h3><p><ul class=tree><li><a href=/blog>blog</a><ul><li><a href=/blog/building-this-website>building-this-website [2023-04-04]</a></ul></ul><h3>Email</h3><p>You can contact me at <a href=mailto:me@kevinschoon.com>me@kevinschoon.com</a>.<h3>Software</h3><p>Check out some of my past and present projects <a href=https://ayllu-forge.org/browse>here</a>.<h3>Contact (QR)</h3><p>Contact info (same as above, but a QR code):<p><img alt="qr code"class=contact src=/contact.png><h3>Subscribe</h3><p><a href=/index.xml>RSS</a></main><footer class=page-footer></footer></div></section>
120\ No newline at end of file
121 diff --git a/gen/index.xml b/gen/index.xml
122new file mode 100644
123index 0000000..cc3663d
124--- /dev/null
125+++ b/gen/index.xml
126 @@ -0,0 +1,2 @@
127+ <?xml version='1.0' encoding='utf-8'?>
128+ <rss version="2.0" encoding="UTF-8"><channel><title>Blog of Kevin Schoon</title><link href="https://kevinschoon.com" /><description>Blog of Kevin Schoon</description><item><title>building-this-website</title><link href="https://kevinschoon.com/blog/building-this-website" /><guid>https://kevinschoon.com/blog/building-this-website</guid><description>There is no deficiency of static website generators these days and so I had to write my own.</description></item></channel></rss>
129\ No newline at end of file
130 diff --git a/gen/ks_stylized.png b/gen/ks_stylized.png
131new file mode 100644
132index 0000000..0123974
133 Binary files /dev/null and b/gen/ks_stylized.png differ
134 diff --git a/gen/main.css b/gen/main.css
135new file mode 100644
136index 0000000..418a962
137--- /dev/null
138+++ b/gen/main.css
139 @@ -0,0 +1 @@
140+ :root{--accent: #b1675d;--light-gray: #eee;--dark-gray: #c2c2c2;--white: #f8f9fa;--black: #2d2d2d;--border-width: 5px}table{text-align:justify;width:100%;border-collapse:collapse;margin-bottom:2rem}td,th{padding:0.5em;border-bottom:1px solid #4a4a4a}html,body{box-sizing:border-box;font-size:16px;height:100%;margin:0;padding:0;max-width:800px}section{height:100%}.wrapper{min-height:100%;display:grid;grid-template-rows:auto 1fr auto}.page-body{padding:20px}*,*:before,*:after{box-sizing:inherit}ol,ul{list-style:none}p{padding:1px;margin:10px}span{background:white;border:1px solid black}pre{background:#e2e2e2;font-family:monospace}img.me{border-radius:2em;max-width:350px}@media (prefers-color-scheme: dark){:root{background-color:#2c2c2c;color:white}pre{color:white;background:#3f3f3f}a:link{color:white;text-decoration:underline}a:visited{color:white;text-decoration:none}.notice{border-color:white}}@media (prefers-color-scheme: light){:root{background-color:#f5f5f5;color:black}pre{color:black;background:#e2e2e2}a:link{color:black;text-decoration:underline}a:visited{color:black;text-decoration:none}.notice{border-color:black}}.tree,.tree ul{font-size:18px;font:normal normal 14px/20px Helvetica, Arial, sans-serif;list-style-type:none;margin-left:0 0 0 10px;padding:0;position:relative;overflow:hidden}.tree li{margin:0;padding:0 12px;position:relative}.tree li::before,.tree li::after{content:"";position:absolute;left:0}.tree li::before{border-top:2px solid #999;top:10px;width:10px;height:0}.tree li:after{border-left:2px solid #999;height:100%;width:0px;top:-10px}.tree>li::after{top:10px}.tree>li:last-child::after{display:none}