Skip to content

Commit d41ec4e

Browse files
committed
Add new website blog post
1 parent 9fafca3 commit d41ec4e

File tree

9 files changed

+204
-2
lines changed

9 files changed

+204
-2
lines changed

content/blog/20230321174710.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ crossposts:
88
- url: https://cohost.org/exodrifter/post/1211994-pipes
99
time: 2023-03-21T17:47:10.788Z
1010
tags:
11+
- bad-shape
1112
- cohost
1213
- haskell
1314
---
Lines changed: 3 additions & 0 deletions
Loading

content/blog/20250715200219.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
---
2+
title: new website powered by shake and pandoc
3+
published: 2025-07-16T00:12:03Z
4+
created: 2025-07-15T20:02:19Z
5+
aliases:
6+
- new website powered by shake and pandoc
7+
crossposts:
8+
- url: https://x.com/exodrifter/status/1945280659746119967
9+
time: 2025-07-16T00:33:45Z
10+
- url: https://bsky.app/profile/exodrifter.bsky.social/post/3lu26aa3phs2v
11+
time: 2025-07-15T17:33-0700
12+
- url: https://vt.social/@exodrifter/114860101285501596
13+
time: 2025-07-15T17:34-0700
14+
- url: https://www.patreon.com/posts/134222456
15+
time: 2025-07-15T17:40-0700
16+
- url: https://ko-fi.com/post/new-website-powered-by-shake-and-pandoc-N4N71I46YA
17+
time: 2025-07-15T17:46-0700
18+
tags:
19+
- bad-shape
20+
- haskell
21+
- pandoc
22+
- shake
23+
- website
24+
---
25+
26+
# new website powered by shake and pandoc
27+
28+
![a screenshot of the new website layout](20250715200219-screenshot.png)
29+
30+
Starting with [my last stream](https://vods.exodrifter.space/2025/07/08/1930), over the last week I've been hyper-focused on improving my website. I've lost quite a bit of sleep and missed a few meals, but I'm quite happy to say that my website is now written in Haskell, powered by the Shake and Pandoc libraries, and is verifiably Very Cool.
31+
32+
The website used to be [powered by Quartz and Nix](20240916090424.md), a setup that I found myself frequently frustrated with. The Nix part of the project was fine -- though I still need to learn Nix more fully. The Quartz part of the website, however, was a major source of the frustration. It has the Bad Shape.
33+
34+
# the Bad Shape
35+
36+
In general, with programming, there's a particular kind of "shape" that I really dislike. This shape is hard to understand, hard to maintain over long periods of time, and hard to customize. I like to call this "shape" the Bad Shape, and [I've briefly written about it before](20230321174710.md). This is in contrast to the "pipe" shape, which is easy to understand, easy to maintain, and easy to customize. I think pipes are the best kind of shape for a program to have.
37+
38+
Visually, the difference between the two looks like this:
39+
40+
![at the top, there are three rectangles in a horizontal line with a plus sign inbetween them, with the middle rectangle being swapped out with a different rectangle with the caption "nice!". at the bottom, there is a rectangle with three sockets for smaller rectangles and last two of the smaller rectangles are being swapped out for a larger rectangle the same size as the smaller two which cannot fit in either of the sockets combined with the caption "fml".](20230321174710-fml.png)
41+
42+
I've found myself showing this particular image to others a lot when I explain my design sense when it comes to programs and APIs. When you're programming, you can imagine that each part of the program is a piece that can snap neatly into other pieces, like Lego bricks or sockets.
43+
44+
I find the process of creatively snapping pieces of functionality together to be really enjoyable. With pipes, as long as the input and output types match, you can connect them or change them in or out for other pipes without any issue. You can do whatever you want at any step along the way.
45+
46+
The problem comes when you need to snap pieces together that need predefined shapes. The Bad Shape is when you have some kind of "wrapper" around the pieces that you want to use. No longer can you snap together whatever piece you want that happens to have a matching type; now, because the Bad Shape defines both the input and output constraints, you have to make the exact piece that the Bad Shape wants.
47+
48+
## concrete example
49+
50+
What this means, concretely, is that the Bad Shape is harder to use. Consider, for example, this typescript code from my old website:
51+
52+
```ts
53+
let repo: Repository | undefined = undefined
54+
return async (_tree, file) => {
55+
let date: MaybeDate = undefined
56+
57+
const fp = file.data.filePath!
58+
const fullFp = path.isAbsolute(fp) ? fp : path.posix.join(file.cwd, fp)
59+
for (const source of opts.priority) {
60+
date ||= file.data.frontmatter[source] as MaybeDate
61+
}
62+
63+
file.data.dates = {
64+
created: coerceDate(fp, date),
65+
modified: coerceDate(fp, date),
66+
published: coerceDate(fp, date),
67+
}
68+
}
69+
```
70+
71+
In brief, what this function does is extract date information from a Markdown's frontmatter. However, it's shaped in a very specific way:
72+
73+
- It only takes a `file` as input, which is a parsed Markdown file.
74+
- It changes the state of the `file` to have the same date for the `created`, `modified`, and `published` date.
75+
- It doesn't return anything.
76+
77+
It's written this way because that's how "transformers" are defined in Quartz. I have no freedom to change the input or output types, and furthermore **I have to understand the machinations of Quartz** in order to implement this function. This partially explains why I return the same value for `created`, `modified`, and `published` -- it's because of what Quartz does with those dates later, after my function has run.
78+
79+
Suddenly, I'm required to understand the changing mutable state of the entire Quartz program. This happens whenever Bad Shapes are involved. Instead of being able to focus on the individual transformation I want to make, I now have to understand much more about what the program does before it gets to my function and what it does after my function runs.
80+
81+
This increases the mental workload, increasing the difficulty of writing and maintaining the program. It also becomes less fun!
82+
83+
## counterargument
84+
85+
Of course, I would be remiss not to mention that Bad Shapes are sometimes necessary and "good, actually". If you're already familiar with Quartz, you might notice that the code I posted earlier looks a lot like a `QuartzTransformerPlugin`, and that's because it is. Bad Shapes like to turn into the plugin pattern, and sometimes plugins are a good solution to a problem despite the fact that they impose limitations on what you can do.
86+
87+
However, "avoid bad shapes" is one of the design principles that I try to follow, along with "avoid shared mutable state". You can't avoid either completely, but I think they're good rules of thumb.
88+
89+
This is why I like functional programming so much; since most libraries and functions are just "pipes", it lets you compose functionality easily, increasing how enjoyable it is to write programs. Another example on the "good" end of this spectrum are unix-style commands. `cat` prints a file, `tail` lets you get the last few lines of the input, and `cowsay` lets you print an ascii cow saying whatever the input was:
90+
91+
```
92+
$ cat content/support.md | tail -n 1 | cowsay
93+
_________________________________________
94+
/ I also love it when you share the stuff \
95+
| I've made that you like with your |
96+
| friends and post nice comments on the |
97+
\ things I make. <3 /
98+
-----------------------------------------
99+
\ ^__^
100+
\ (oo)\_______
101+
(__)\ )\/\
102+
||----w |
103+
|| ||
104+
```
105+
106+
This works because the types match! And you can change up the pipeline as much as you want.
107+
108+
## it's Bad Shapes all the way down
109+
110+
However, static site generators (or SSGs for short) are particularly rife with this problem. If you try to find a static site generator to use, they all _prescribe_ some kind of workflow, because they're trying to get you to provide the pieces that the Bad Shape wants. I feel like SSGs like to embrace the Bad Shape... and it makes me feel disappointed in software. You end up having to spend a lot of time reading the SSG documentation to know how things work so you can make it do what you want, and that sucks! It's like you have a box that does... something, and [you just have to pull random levers until it starts working](20240109152210.md). Writing or using software could be a lot more fun if I didn't have to do that all the time.
111+
112+
Quartz, in particular, takes this to an extent that I found a little hard to believe when I first started using it. It does the typical things that most other SSGs do, like requiring that you put all of your content in a specific `/content` folder. But, it takes the Bad Shape pattern even further. To use Quartz you have to literally clone the repository and make your own changes ontop. I'm not making this up, that is how they tell users to [get started](https://quartz.jzhao.xyz/#-get-started):
113+
114+
> Then, in your terminal of choice, enter the following commands line by line:
115+
>
116+
> ```sh
117+
> git clone https://github.com/jackyzha0/quartz.git
118+
> cd quartz
119+
> npm i
120+
> npx quartz create
121+
> ```
122+
123+
The content folder, custom plugins, custom components, _even the configuration file_ have to be modified directly inside of the repository. Hopefully this makes it abundantly clear why I didn't like working with Quartz.
124+
125+
# what's changed
126+
127+
The website looks very similar to what the site looked like a week ago -- the styling is mostly the same, but as you may have noticed by now, the biggest visual change is that elements on the page are actually aligned with the dot grid.
128+
129+
On the old website, the dot grid was static and didn't move when you scrolled the page. Elements on the page also didn't have a grid size. Now, almost[^1] every single element on the page is sized in an exact manner such that everything stays aligned to the grid, through to the bottom of the page. This helps invoke the sense that you're reading a dot grid bullet journal, which was the original desire for the background. If you're interested in seeing how this is done, the [style.css](../style.css) is thoroughly documented.
130+
131+
Data is also generally more consistent now. Almost[^1] every page has well-formed and well-defined timestamps, every tag uses `kebab-case`, and every index page contains a listing of files -- including the home page.
132+
133+
There are several things that are missing, notably:
134+
- No search function.
135+
- No description in RSS feeds.
136+
- Backlinks are missing.
137+
- Code syntax highlighting.
138+
- No dark/light theme toggle.
139+
140+
I plan on fixing these issues slowly over time. However, the software I wrote to build this website is much easier for me to maintain and extend, opening the doors for further modifications and features which were previously impossible or difficult to do:
141+
142+
- Unify the VOD website with this website, so notes can link to VODs directly and vice-versa.
143+
- Compress/resize images on build to reduce the size of pages.
144+
- Check to make sure that there are no broken internal links.
145+
- Add RSS feeds for every tag, so you can get notifications about a specific thing if you want.
146+
- Export the notes in different formats, like the original Markdown source and JSON.
147+
148+
But, I think a week of near-constant working is enough for now. I need to get back to taking care of myself, telling people that my game [_no signal_ is coming out](20250625201455.md), and looking for jobs.
149+
150+
I hope you enjoyed this write-up and like the new website! I'm looking forward to making the site better in the future ❤️
151+
152+
[^1]: I still have some bugs I need to fix. I'll get to them! pinky promise?

content/entries/20250715232211.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
created: 2025-07-15T23:22:11Z
3+
---
4+
5+
While writing a blog post about the new website backend, I wrote some markdown that used the same footnote twice. I noticed that this resulted in a duplicated footnote, which is not what I want. Looking up the problem, I find [jgm/pandoc#1603](https://github.com/jgm/pandoc/issues/1603).
6+
7+
Apparently, this is a fundamental design issue with Pandoc's AST and has been open since 2014, so I cannot expect it to be fixed anytime soon. However, it appears that the main hangup about fixing the issue is that changing the AST will affect a lot of users, so they don't want to change the AST very often.
8+
9+
A footnote is just a `Note [Block]`, so there's no way to work backwards to find out which footnotes were actually the same. However, I think this is only a problem if I have footnotes that have the same content but different footnote IDs. I don't, so I think I can work around the issue by de-deplicating the notes by their contents.
10+

content/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: exodrifter
33
created: 2024-09-19T23:54:07Z
4-
modified: 2025-07-12T10:53:10Z
4+
modified: 2025-07-15T16:12:47-0700
55
---
66

77
<div class="home-banner">
@@ -19,12 +19,12 @@ modified: 2025-07-12T10:53:10Z
1919

2020
# blog
2121

22+
- **2025-07-15** - [new website powered by shake and pandoc](blog/20250715200219.md)
2223
- **2025-07-08** - [offkai recap and being myself](blog/20250707063429.md)
2324
- **2025-06-25** - [no signal release date](blog/20250625201455.md)
2425
- **2025-05-16** - [godotcon boston 2025](blog/20250516012109.md)
2526
- **2025-03-17** - [no signal dev hiatus](blog/20250317203824.md)
2627
- **2025-01-01** - [2024 review](blog/20250101011413.md)
27-
- **2024-11-24** - [toronto game expo 2024](blog/20241124185224.md)
2828

2929
[<i class="ri-rss-fill"></i> RSS](blog/index.xml)
3030
[See all posts...](blog/index.md)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: Pandoc creates duplicate footnotes
3+
created: 2025-07-15T23:46:14Z
4+
aliases:
5+
- Pandoc creates duplicate footnotes
6+
tags:
7+
- pandoc
8+
---
9+
10+
# Pandoc creates duplicate footnotes
11+
12+
Pandoc's AST doesn't maintain footnote references at all, instead opting to inline the footnote's contents into the AST whenever a footnote appears. The issue [jgm/pandoc#1603](https://github.com/jgm/pandoc/issues/1603), which is about this problem, has been open since 2014. A fix would have to involve changing the Pandoc AST.

content/tags/bad-shape.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
created: 2025-07-15T21:23:48Z
3+
aliases:
4+
- bad shape
5+
tags:
6+
- bad-shape
7+
---

content/tags/pandoc.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
created: 2025-07-15T20:05:14Z
3+
aliases:
4+
- Pandoc
5+
tags:
6+
- pandoc
7+
- haskell
8+
---
9+

content/tags/shake.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
created: 2025-07-15T20:04:41Z
3+
aliases:
4+
- Shake
5+
tags:
6+
- haskell
7+
- shake
8+
---

0 commit comments

Comments
 (0)