initial commit
This commit is contained in:
@@ -0,0 +1,348 @@
|
|||||||
|
:root {
|
||||||
|
--color-background: rgba( 18, 15, 18, 1);
|
||||||
|
--color-dark: rgba( 9, 7, 9, 1);
|
||||||
|
--color-text: rgba(255, 255, 255, 1);
|
||||||
|
--color-purple: rgba(171, 90, 237, 1);
|
||||||
|
--color-purple-translucent: rgba(171, 90, 237, 0.8);
|
||||||
|
--color-purple-transparent: rgba(171, 90, 237, 0.3);
|
||||||
|
--color-purple-hover: rgba(194, 135, 242, 1);
|
||||||
|
|
||||||
|
--content-width: 800px;
|
||||||
|
--extra-width: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "linja-pona-4";
|
||||||
|
src: url('https://ajlee2006.github.io/linjaponasandbox/linja-pona-4.2.otf') format('opentype');
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-background) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="2em" viewBox="0 0 3 1.7320508076"><path d="M0 0.8660254038h1L1.5 0h1L3 0.8660254038L2.5 1.7320508076h-1L1 0.8660254038" stroke="%232D3931" stroke-width="0.05" fill="none"/></svg>');
|
||||||
|
background-position: 1.25em 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
& *::selection {
|
||||||
|
background-color: var(--color-purple-transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--color-background);
|
||||||
|
border: 1px solid var(--color-purple-translucent);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 0;
|
||||||
|
max-width: var(--content-width);
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
padding: 2em;
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
& > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > * + * {
|
||||||
|
border-top: 1px solid var(--color-purple-translucent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h2 { font-size: 1.25rem; }
|
||||||
|
h3 { font-size: 1rem; }
|
||||||
|
|
||||||
|
sup {
|
||||||
|
vertical-align: baseline;
|
||||||
|
position: relative;
|
||||||
|
top: -0.4em;
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-purple);
|
||||||
|
transition-property: color;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
text-underline-offset: 2.5px;
|
||||||
|
text-underline-thickness: 1px;
|
||||||
|
|
||||||
|
&:hover, &:focus, &:active {
|
||||||
|
color: var(--color-purple-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span.t {
|
||||||
|
font-family: linja-pona-4;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: "Monaspace Neon", ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace !important;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-size-adjust: from-font;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-dark);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.05em 0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: transparent !important;
|
||||||
|
|
||||||
|
& > code {
|
||||||
|
display: block;
|
||||||
|
overflow-x: scroll;
|
||||||
|
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: var(--color-purple-translucent);
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 1em;
|
||||||
|
|
||||||
|
& li:before {
|
||||||
|
content: "–";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 6em 1fr;
|
||||||
|
gap: 0.25em 2em;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
& > dd {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img, video {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding-top: 0.5em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
& > h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
& > p:last-child {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
border-radius: 8px;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
height: 5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 0.7ch;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
|
||||||
|
& a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > ul {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
font-size: smaller;
|
||||||
|
gap: 0.25em;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.25em;
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
background-color: var(--color-dark);
|
||||||
|
border-radius: 2em;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
|
border-radius: 1.25em;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
color: var(--color-text);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--color-purple);
|
||||||
|
color: var(--color-dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding-top: 1.5em;
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
|
||||||
|
& * {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 881px) {
|
||||||
|
section {
|
||||||
|
& > div {
|
||||||
|
margin-left: calc(var(--extra-width) * -1);
|
||||||
|
width: calc(var(--content-width) + var(--extra-width));
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 6px;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
border: 1px solid var(--color-purple-translucent);
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
clip-path: polygon(
|
||||||
|
-1px -1px,
|
||||||
|
calc(var(--extra-width) / 2) -1px,
|
||||||
|
calc(var(--extra-width) / 2) 3px,
|
||||||
|
calc(100% - 2px - var(--extra-width) / 2) 3px,
|
||||||
|
calc(100% - 2px - var(--extra-width) / 2) -1px,
|
||||||
|
calc(100% + 1px) -1px,
|
||||||
|
calc(100% + 1px) calc(100% + 1px),
|
||||||
|
calc(100% - 2px - var(--extra-width) / 2) calc(100% + 1px),
|
||||||
|
calc(100% - 2px - var(--extra-width) / 2) calc(100% - 3px),
|
||||||
|
calc(var(--extra-width) / 2) calc(100% - 3px),
|
||||||
|
calc(var(--extra-width) / 2) calc(100% + 1px),
|
||||||
|
-1px calc(100% + 1px)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > pre > code, & > img, & > video {
|
||||||
|
padding: 1em 1.5em;
|
||||||
|
padding-bottom: calc(0.85em);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > img, & > video {
|
||||||
|
width: calc(100% - 2.85em);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 880px) {
|
||||||
|
pre > code, div > img, div > video {
|
||||||
|
margin: 0 -2em;
|
||||||
|
padding: 1.5em 2em;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code, div > img, div > video {
|
||||||
|
margin: 0 -1em;
|
||||||
|
padding: 1.5em 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr { display: none; }
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style: none;
|
||||||
|
counter-reset: counter;
|
||||||
|
padding-left: 2em;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
counter-increment: counter;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 0.25em;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "[" counter(counter) "]";
|
||||||
|
position: absolute;
|
||||||
|
left: -2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
title: "Lilac's Website"
|
||||||
|
description: "Computer Engineer & [Certified Punk Rocker](https://youtu.be/z9LiPuVRyU8&t=261)"
|
||||||
|
---
|
||||||
|
# About Me
|
||||||
|
|
||||||
|
Howdy! My name is Lilac, and this is my website. I am interested in low-level
|
||||||
|
computing, networking, and Linux. When not tinkering with 'puters, I birdwatch,
|
||||||
|
crochet, and garden.
|
||||||
|
|
||||||
|
# Technology
|
||||||
|
|
||||||
|
I write embedded firmware and POSIX/Linux applications using C and several
|
||||||
|
dialects of assembly. I do the server administration for all my own web services
|
||||||
|
using Linux and Nix.
|
||||||
|
|
||||||
|
Here is some of the software I've written:
|
||||||
|
|
||||||
|
[Tanager](https://code.grasswren.net/Lilac/Tanager)
|
||||||
|
: QMK keyboard firmware with flashy backlighting
|
||||||
|
|
||||||
|
[grasswren.net](https://code.grasswren.net/Lilac/grasswren.net)
|
||||||
|
: Content and Hugo theme for this website
|
||||||
|
|
||||||
|
# Music
|
||||||
|
|
||||||
|
My favourite genres of music are grunge, punk, hyperpop, breakcore, and digital
|
||||||
|
hardcore. I really enjoy the music scene where I live; I think everybody should
|
||||||
|
get out there and interact with their local artists.
|
||||||
|
|
||||||
|
I self-host my music library, so I don't have opportunities to discover new
|
||||||
|
music very often. If you are visiting this site for the first time, [shoot me an
|
||||||
|
email](mailto:lilac@grasswren.net) telling me what I should listen to next!
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: Lilac's Birds
|
||||||
|
description: They're cute little guys, aren't they?
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Lilac's Blog"
|
||||||
|
description: "I love pretending I know what's going on!"
|
||||||
|
---
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
title: "Building Custom Keyboard Backlighting"
|
||||||
|
description: "Upgrading my [Ergodox EZ](https://ergodox-ez.com/) using [QMK](https://github.com/qmk/qmk_firmware)"
|
||||||
|
date: 2026-07-09
|
||||||
|
---
|
||||||
|
# Intro
|
||||||
|
|
||||||
|
I own an Ergodox EZ, a very comfortable, split, columnar keyboard. I bought it
|
||||||
|
because I gave myself carpal tunnel typing on my last keyboard, and I wanted to
|
||||||
|
see if I could type any faster on a split board.[^1] Ergodox keyboards run
|
||||||
|
open-source QMK firmware, meaning they are extensively configurable for a
|
||||||
|
programmer like me! I love my keyboard, and I would recommend a split keyboard
|
||||||
|
to anybody looking for something more comfortable than a standard keyboard.
|
||||||
|
|
||||||
|
# Key Layout
|
||||||
|
|
||||||
|
My key layout isn't the main focus of this post, but I wanted to highlight it
|
||||||
|
because it uses some cool features QMK offers.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The first thing to notice is that I have plenty of thumb keys for common
|
||||||
|
keycodes. I never really liked that common keys like Enter, Backspace, and
|
||||||
|
Escape[^2] were off to the side, so I moved them to be more easily accessible.
|
||||||
|
|
||||||
|
On the bottom left side, there is a key labelled Layer. QMK has a concept called
|
||||||
|
[layers](https://docs.qmk.fm/feature_layers), not unlike how a phone keyboard
|
||||||
|
works: tap or hold a layer key, and access different keys. I use this for my
|
||||||
|
function keys because I use them so infrequently, and I don't want them wasting
|
||||||
|
space normally.
|
||||||
|
|
||||||
|
Finally, I have a key labelled Macro. I commonly type `->` when programming, so
|
||||||
|
I made this key type both characters in a single stroke. I also modified the
|
||||||
|
handling of the Backspace key so it deletes both characters if the previous
|
||||||
|
keycode was Macro.
|
||||||
|
|
||||||
|
# Backlighting
|
||||||
|
|
||||||
|
All the default backlighting options provided by QMK are too boring for me, so I
|
||||||
|
rolled my own. It works like this, with each keyboard half operating
|
||||||
|
independently:
|
||||||
|
|
||||||
|
- Every keystroke changes the backlight colour
|
||||||
|
- When all keys are released, the backlight LEDs fade out nicely
|
||||||
|
- Backlight LEDs closer to the most recently pressed key are brighter than
|
||||||
|
those further from it
|
||||||
|
|
||||||
|
Every time a keystroke is detected, the following function runs, tracking which
|
||||||
|
key was pressed and updating the colours:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void post_process_record_user(uint16_t keycode, keyrecord_t *record)
|
||||||
|
{
|
||||||
|
if (!backlight || !record->event.pressed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record->event.key.row < 7) {
|
||||||
|
hue_left = rand();
|
||||||
|
brightness_left = 255;
|
||||||
|
|
||||||
|
if (record->event.key.col != 5) {
|
||||||
|
position_left = 29 - 2 * record->event.key.row;
|
||||||
|
} else {
|
||||||
|
position_left = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
color(hue_left, brightness_left, position_left, 15);
|
||||||
|
} else {
|
||||||
|
hue_right = rand();
|
||||||
|
brightness_right = 255;
|
||||||
|
|
||||||
|
if (record->event.key.col != 5) {
|
||||||
|
position_right = 26 - 2 * record->event.key.row;
|
||||||
|
} else {
|
||||||
|
position_right = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
color(hue_right, brightness_right, position_right, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This function is rather long because I have to control the keyboard halves
|
||||||
|
independently, but the main idea is simple. Every keystroke:
|
||||||
|
|
||||||
|
- Randomize the colour hue
|
||||||
|
- Maximize the brightness
|
||||||
|
- Record the column the key is in
|
||||||
|
- Turn on the LEDs using those values
|
||||||
|
|
||||||
|
In another function, I keep track of how many keys are pressed at any given
|
||||||
|
time, so I can dim the LEDs once no more are pressed:
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (record->event.key.row < 7) {
|
||||||
|
count_left += record->event.pressed ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
count_right += record->event.pressed ? 1 : -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Brightness Calculation
|
||||||
|
|
||||||
|
Now that I track these values, I need to use them to calculate the brightness of
|
||||||
|
each LED:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void color(uint8_t hue, uint8_t brightness, uint8_t position,
|
||||||
|
uint8_t index)
|
||||||
|
{
|
||||||
|
for (int i = index; i < (index + 15); ++i) {
|
||||||
|
const uint32_t cube = (uint32_t)brightness * brightness * brightness;
|
||||||
|
const uint8_t distance = position > i ? position - i : i - position;
|
||||||
|
sethsv(hue, 255, cube / 65025 * 5 / (5 + distance), i);
|
||||||
|
}
|
||||||
|
rgblight_set();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This function takes in a hue, brightness, position, and starting index. There
|
||||||
|
are 30 LEDs on an Ergodox EZ, meaning that I can pass either `0` or `15` as the
|
||||||
|
starting index to have the function loop over the right or left LEDs
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
The `brightness` and `position` parameters contribute to the brightness of the
|
||||||
|
LED. First, the brightness is passed through a cubic ease-out function. This
|
||||||
|
produces a more pleasing dimming as opposed to linearly dimming the LED, as the
|
||||||
|
human eye senses brightness on a logarithmic scale. Second, the brightness is
|
||||||
|
scaled down by an amount proportional to its distance from the last key pressed.
|
||||||
|
The scaling function being used is `5 / (5 + n)` where `n` is the distance in
|
||||||
|
LED index from the brightest LED, which is not dimmed because its scaling
|
||||||
|
fraction evaluates to `1`.
|
||||||
|
|
||||||
|
The `sethsv` function can then be passed a hue (given as a parameter and
|
||||||
|
unchanging as the LED is dimmed), a saturation (always the maximum), the value
|
||||||
|
(or brightness, as just calculated), and the index of the LED. Finally, I flush
|
||||||
|
my LED settings out to the LED peripheral with the `rgblight_set` function.
|
||||||
|
|
||||||
|
# Result
|
||||||
|
|
||||||
|
With a little logic, the backlights on each keyboard half act just as I wanted,
|
||||||
|
lighting up reactively and fading when no more keys are pressed.
|
||||||
|
|
||||||
|
Take a look!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Footnotes
|
||||||
|
|
||||||
|
[^1]: Quite a bit!
|
||||||
|
|
||||||
|
[^2]: I use the [Helix](https://helix-editor.com/) editor, meaning I type
|
||||||
|
Escape almost as much as I type Space.
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
title: 'grasswren.net'
|
||||||
|
baseURL: 'https://grasswren.net'
|
||||||
|
|
||||||
|
uglyURLs: true
|
||||||
|
disableKinds:
|
||||||
|
- RSS
|
||||||
|
- sitemap
|
||||||
|
- taxonomy
|
||||||
|
- term
|
||||||
|
|
||||||
|
markup: { goldmark: { parser: { wrapStandAloneImageWithinParagraph: false } } }
|
||||||
|
minify: { minifyOutput: true }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h{{ add .Level 1 }} id="{{ .Anchor | safeURL }}">{{ .Text | safeHTML }}</h{{ add .Level 1 }}>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{{- if eq (path.Ext .Destination) ".mp4" -}}
|
||||||
|
<div><video src='{{ .Destination | safeURL }}' alt='{{ .Text }}' title='{{ .Text }}' controls preload="metadata" playsinline></video></div>
|
||||||
|
{{- else -}}
|
||||||
|
<div><img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" title="{{ .Text }}"></div>
|
||||||
|
{{- end -}}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<meta name='viewport' content='width=device-width'>
|
||||||
|
<meta name='theme-color' content='#ab5aed'>
|
||||||
|
<meta name='og:type' content='website'>
|
||||||
|
<meta name='og:title' content='{{ .Title }}'>
|
||||||
|
<meta name='og:description' content='{{ .Description | markdownify | plainify }}'>
|
||||||
|
<meta name='og:url' content='{{ .Permalink }}'>
|
||||||
|
<meta name='og:image' content='/espurr.png'>
|
||||||
|
<link rel='preconnect' href='https://rsms.me'>
|
||||||
|
<link rel='preconnect' href='https://ajlee2006.github.io'>
|
||||||
|
<link rel='stylesheet' href='https://rsms.me/inter/inter.css'>
|
||||||
|
{{ $css := resources.Get "style.css" | resources.Minify | resources.Fingerprint }}
|
||||||
|
<link rel='stylesheet' href='{{ $css.RelPermalink }}'>
|
||||||
|
<link rel='icon' href='/espurr.png'>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<div><a href='https://grasswren.net'>grasswren.net</a></div>
|
||||||
|
<ul>
|
||||||
|
<li><a {{ if .IsHome }}class='active'{{ end }} href='https://grasswren.net'>Home</a></li>
|
||||||
|
<li><a {{ if eq .Type "blog" }}class='active'{{ end }} href='https://blog.grasswren.net'>Blog</a></li>
|
||||||
|
<li><a {{ if eq .Type "birds" }}class='active'{{ end }} href='https://birds.grasswren.net'>Birds</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<h1>{{ .Title }} {{ if eq .Page.BundleType "branch" }}<span class='t'>[_lipu_pi_soweli_kili]</span>{{ end }}</h1>
|
||||||
|
<p>{{ .Description | markdownify }}</p>
|
||||||
|
</div>
|
||||||
|
<img src='/espurr.png' alt='Espurr'>
|
||||||
|
</header>
|
||||||
|
{{ block "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
<footer>
|
||||||
|
<div><p>by <a href='mailto:lilac@grasswren.net'>Lilac</a> <small>(she/her)</small></p></div>
|
||||||
|
<div><p>ΘΔ ⸱ 🏳️⚧️</p></div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ $sections := split .Content "<h2" }}
|
||||||
|
{{ range after 1 $sections }}
|
||||||
|
<section>
|
||||||
|
{{ print "<h2" . | safeHTML }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<section>
|
||||||
|
<dl>
|
||||||
|
{{ range .Pages }}
|
||||||
|
<time>{{ time.Format "2006-01-02" .Date }}</time>
|
||||||
|
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{- $content := .Content -}}
|
||||||
|
{{- $content = replaceRE `<div class="footnotes" role="doc-endnotes">` "" $content -}}
|
||||||
|
{{- $content = replaceRE `</div>\s*$` "" $content -}}
|
||||||
|
{{- $tmp := $content | safeHTML -}}
|
||||||
|
{{ $sections := split $tmp "<h2" }}
|
||||||
|
{{ range after 1 $sections }}
|
||||||
|
<section>
|
||||||
|
{{ print "<h2" . | safeHTML }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 365 KiB |
Reference in New Issue
Block a user