Commit
+101 -0 +/-7 browse
1 | diff --git a/gen/blog/building-this-website/index.html b/gen/blog/building-this-website/index.html |
2 | new file mode 100644 |
3 | index 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 |
106 | new file mode 100644 |
107 | index 0000000..e74e5b7 |
108 | Binary files /dev/null and b/gen/contact.png differ |
109 | diff --git a/gen/favicon.ico b/gen/favicon.ico |
110 | new file mode 100644 |
111 | index 0000000..59a4e28 |
112 | Binary files /dev/null and b/gen/favicon.ico differ |
113 | diff --git a/gen/index.html b/gen/index.html |
114 | new file mode 100644 |
115 | index 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 |
122 | new file mode 100644 |
123 | index 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 |
131 | new file mode 100644 |
132 | index 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 |
135 | new file mode 100644 |
136 | index 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} |