update project statuses, move TILs into regular posts with tag, revamp site colors to kimber base16 palette

This commit is contained in:
Nathan Anderson 2026-02-21 16:00:14 -07:00
parent 5d099548fc
commit aae7d716a2
26 changed files with 615 additions and 305 deletions

View File

@ -71,3 +71,18 @@ Nephi
lol
Cyano
Cyanogen
Colemak
CPU
distro
heatmaps
keymap
Niri
Noctalia
Opencode
philips
repo's
spudger
statusColor
treking
upgradable
videogame

View File

@ -53,24 +53,7 @@
"maximum": 1
},
// MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md013.md
"MD013": {
// Number of characters
"line_length": 300,
// Number of characters for headings
"heading_line_length": 120,
// Number of characters for code blocks
"code_block_line_length": 120,
// Include code blocks
"code_blocks": true,
// Include tables
"tables": true,
// Include headings
"headings": true,
// Strict length checking
"strict": false,
// Stern length checking
"stern": false
},
"MD013": false,
// MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md014.md
"MD014": true,
// MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md018.md

View File

@ -5,6 +5,7 @@ lastmod: {{ .Date }}
showTableOfContents: true
type: "projects"
title: "{{ replace .Name "-" " " | title }}"
active: true
status: ""
statusColor: "" # green #99c899 | blue - #537c9c | yellow - #d8b56d | red - #704f4f | grey - #c3c3b4 | pink - #c88c8c
tags: []
---

View File

@ -1,16 +0,0 @@
---
date: {{ .Date }}
description: ""
lastmod: {{ .Date }}
showTableOfContents: true
type: "tils"
title: "TIL: {{ replace .Name "-" " " | title }}"
image: ""
image_credit: ""
image_alt: ""
tags: []
---
# Context
# Reflection

100
assets/css/dark.css Normal file
View File

@ -0,0 +1,100 @@
/* Kimber (base16) dark theme override for Gokarna */
/* Overrides themes/gokarna/assets/css/dark.css via Hugo asset resolution */
:root {
--dark-primary-color: 34, 34, 34; /* base00 #222222 */
--dark-secondary-color: #313131; /* base01 */
--dark-tertiary-color: #555D55; /* base02 */
--dark-text-color: #DEDEE7; /* base05 */
}
html {
background-color: rgb(var(--dark-primary-color));
color: var(--dark-text-color);
fill: var(--dark-text-color);
}
.header {
background-color: rgba(var(--dark-primary-color), 0.95);
}
.nav-hamburger-list {
background: rgba(var(--dark-primary-color), 1);
}
.nav-links .nav-icons-divider {
color: var(--dark-secondary-color);
}
a:hover .feather-moon {
fill: #DEDEE7; /* base05 */
color: #DEDEE7;
}
.gk-social-icons-list .gk-social-icon,
.gk-social-icons-list .gk-social-icon a:visited {
fill: var(--dark-text-color);
}
.gk-social-icons-list .gk-social-icon a:hover {
fill: var(--accent-color);
}
.post-tags .post-tag:hover {
background-color: var(--dark-text-color);
color: rgb(var(--dark-primary-color));
}
.tags-list .post-tags .post-tag a .tag-posts-count {
background-color: var(--dark-secondary-color);
}
.tags-list .post-tags .post-tag:hover a .tag-posts-count {
background-color: var(--dark-text-color);
color: rgb(var(--dark-primary-color));
}
.footer {
border-top-color: var(--dark-header-bg-color);
}
blockquote {
color: #C3C3B4; /* base06 */
border-left-color: var(--dark-secondary-color);
}
hr {
color: var(--dark-secondary-color);
}
table thead {
background-color: var(--dark-secondary-color);
}
table td,
table th {
border-bottom-color: var(--dark-secondary-color);
}
code,
pre {
background-color: var(--dark-secondary-color) !important;
}
:not(pre) > code {
background-color: var(--dark-tertiary-color) !important;
color: var(--dark-text-color);
}
.header-shadow {
box-shadow: rgb(49, 49, 49) 0px -1px 0px 0px inset; /* base01 */
}
.arrow-logo {
background-color: var(--dark-secondary-color);
}
/* Post dates */
.post-date {
color: #C3C3B4; /* base06 */
}

View File

@ -4,11 +4,11 @@ title = "Foss Cat"
theme = "gokarna"
enableRobotsTXT= true
enableEmoji = true
# code highlighting style
pygmentsStyle = "catppuccin-frappe"
# code highlighting: use CSS classes so customHeadHTML can override colors
# pygmentsStyle = "catppuccin-frappe"
[params]
accentColor = "#715483"
accentColor = "#D8B56D"
socialIcons = [
{name = "gitea", url = "https://git.fosscat.com/n8r"},
{name = "twitch", url = "https://twitch.tv/fosscat"},
@ -21,16 +21,152 @@ customHeadHTML = '''
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<!-- <script>console.log("Custom script")</script> -->
<!-- <script src="/js/custom.js"></script> -->
''' # Import file from `static/`
<style>
/* --- Maple Mono font --- */
@font-face {
font-family: 'Maple Mono';
font-style: normal;
font-display: swap;
font-weight: 400;
src: url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-normal.woff2) format('woff2'),
url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-normal.woff) format('woff');
}
@font-face {
font-family: 'Maple Mono';
font-style: normal;
font-display: swap;
font-weight: 700;
src: url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-700-normal.woff2) format('woff2'),
url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-700-normal.woff) format('woff');
}
@font-face {
font-family: 'Maple Mono';
font-style: italic;
font-display: swap;
font-weight: 400;
src: url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-italic.woff2) format('woff2'),
url(https://cdn.jsdelivr.net/fontsource/fonts/maple-mono@latest/latin-400-italic.woff) format('woff');
}
html, body, code, pre, .post-content, .post-title {
font-family: 'Maple Mono', monospace !important;
}
/* --- Kimber (base16) theme: Light mode overrides --- */
:root {
--light-primary-color: 255, 255, 230; /* base07 #FFFFE6 warm cream */
--light-secondary-color: #C3C3B4; /* base06 borders/dividers */
--light-tertiary-color: #222222; /* base00 code block bg */
--light-text-color: #222222; /* base00 text */
}
/* Light mode: header shadow */
.header-shadow {
box-shadow: rgb(195, 195, 180) 0px -1px 0px 0px inset;
}
/* Light mode: blockquote text */
blockquote {
color: #5A5A5A; /* base04 */
}
/* Light mode: post dates */
.post-date {
color: #5A5A5A; /* base04 */
}
/* --- Kimber syntax highlighting (base16 palette) --- */
/* Background */
.bg { color: #DEDEE7; background-color: #222222; }
.chroma { color: #DEDEE7; background-color: #222222; }
/* Line infrastructure */
.chroma .lnlinks { outline: none; text-decoration: none; color: inherit; }
.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
.chroma .hl { background-color: #313131; } /* base01 highlight */
.chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; color: #5A5A5A; } /* base04 */
.chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; color: #5A5A5A; } /* base04 */
.chroma .line { display: flex; }
/* Keywords - base0E #86CACD (light teal) */
.chroma .k { color: #86CACD; }
.chroma .kc { color: #86CACD; }
.chroma .kd { color: #86CACD; }
.chroma .kn { color: #86CACD; }
.chroma .kp { color: #86CACD; }
.chroma .kr { color: #86CACD; }
.chroma .kt { color: #D8B56D; } /* base0A types */
/* Names */
.chroma .na { color: #537C9C; } /* base0D attributes */
.chroma .nc { color: #D8B56D; } /* base0A classes */
.chroma .no { color: #476C88; } /* base09 constants */
.chroma .nd { color: #D8B56D; } /* base0A decorators */
.chroma .ni { color: #DEDEE7; } /* base05 entities */
.chroma .ne { color: #C88C8C; } /* base08 exceptions */
.chroma .nl { color: #DEDEE7; } /* base05 labels */
.chroma .nn { color: #D8B56D; } /* base0A namespaces */
.chroma .nt { color: #C88C8C; } /* base08 tags */
.chroma .nb { color: #DEDEE7; } /* base05 builtins */
.chroma .bp { color: #DEDEE7; } /* base05 builtin pseudo */
.chroma .nv { color: #C88C8C; } /* base08 variables */
.chroma .vc { color: #C88C8C; } /* base08 */
.chroma .vg { color: #C88C8C; } /* base08 */
.chroma .vi { color: #C88C8C; } /* base08 */
.chroma .vm { color: #C88C8C; } /* base08 */
.chroma .nf { color: #537C9C; } /* base0D functions */
.chroma .fm { color: #537C9C; } /* base0D */
/* Strings - base0B #99C899 (sage green) */
.chroma .s { color: #99C899; }
.chroma .sa { color: #99C899; }
.chroma .sb { color: #99C899; }
.chroma .sc { color: #99C899; }
.chroma .dl { color: #99C899; }
.chroma .sd { color: #99C899; font-style: italic; }
.chroma .s2 { color: #99C899; }
.chroma .se { color: #78B4B4; } /* base0C escape chars */
.chroma .sh { color: #99C899; }
.chroma .si { color: #78B4B4; } /* base0C interpolation */
.chroma .sx { color: #99C899; }
.chroma .sr { color: #78B4B4; } /* base0C regex */
.chroma .s1 { color: #99C899; }
.chroma .ss { color: #99C899; }
/* Numbers - base09 #476C88 (steel blue) */
.chroma .m { color: #476C88; }
.chroma .mb { color: #476C88; }
.chroma .mf { color: #476C88; }
.chroma .mh { color: #476C88; }
.chroma .mi { color: #476C88; }
.chroma .il { color: #476C88; }
.chroma .mo { color: #476C88; }
/* Operators - base05 #DEDEE7 */
.chroma .o { color: #DEDEE7; }
.chroma .ow { color: #86CACD; } /* base0E operator words */
/* Comments - base03 #644646 (dusty mauve) */
.chroma .c { color: #644646; font-style: italic; }
.chroma .ch { color: #644646; font-style: italic; }
.chroma .cm { color: #644646; font-style: italic; }
.chroma .c1 { color: #644646; font-style: italic; }
.chroma .cs { color: #644646; font-style: italic; }
.chroma .cp { color: #D8B56D; } /* base0A preprocessor */
.chroma .cpf { color: #99C899; } /* base0B preproc file */
/* Generic */
.chroma .gd { color: #C88C8C; } /* base08 deleted */
.chroma .ge { font-style: italic; }
.chroma .gr { color: #C88C8C; } /* base08 error */
.chroma .gh { color: #537C9C; font-weight: bold; } /* base0D heading */
.chroma .gi { color: #99C899; } /* base0B inserted */
.chroma .go { color: #644646; } /* base03 output */
.chroma .gp { color: #644646; } /* base03 prompt */
.chroma .gs { font-weight: bold; }
.chroma .gu { color: #78B4B4; font-weight: bold; } /* base0C subheading */
.chroma .gt { color: #C88C8C; } /* base08 traceback */
.chroma .gl { text-decoration: underline; }
.chroma .w { color: #555D55; } /* base02 whitespace */
</style>
'''
# customFooterHTML = '<p>foot123</p>'
togglePreviousAndNextButtons = "true"
avatarUrl = "/images/fosscat_icon.png"
avatarSize = "size-s"
numberPostsOnHomePage = 5
showPostsOnHomePage = "recent"
numberTilsOnHomePage = 3
[params.meta]
favicon = true
@ -42,6 +178,8 @@ svg = false
startLevel = 1
endLevel = 3
ordered = false
[markup.highlight]
noClasses = false
[menu]
[[menu.main]]
@ -53,11 +191,6 @@ name = "Posts"
url = "/posts/"
weight = 2
[[menu.main]]
identifier = "tils"
name = "TIL's"
url = "/tils/"
weight = 3
[[menu.main]]
name = "Projects"
url = "/projects/"
weight = 4

View File

@ -2,7 +2,7 @@
date: 2026-02-02
description: "Flash Fiction: Cyano Pill"
# image: ""
lastmod: 2026-02-03T09:21:07-07:00
lastmod: 2026-02-21T15:59:38-07:00
showTableOfContents: false
tags: ["flash fiction", "post apocalypse"]
title: "Cyano Pill"

View File

@ -3,11 +3,11 @@ date: 2025-03-03T09:19:07-07:00
description: "I learned the importance of taking time away from the computer in software development"
lastmod: 2025-03-03T09:19:07-07:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: Hammock Driven Development"
image: "images/hammock.jpg"
alt: "hammock with a cat"
tags: ["clojure", "practices", "rich hickey"]
tags: ["clojure", "practices", "rich hickey", "til"]
---
# Context

View File

@ -3,17 +3,17 @@ date: 2025-10-05T20:08:25-06:00
description: ""
lastmod: 2025-10-05T20:08:25-06:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: Hunter Gatherers Were Not Dumb"
image: ""
image_credit: ""
image_alt: ""
tags: ["history", ""]
tags: ["history", "til"]
---
# Context
[As I mentioned](/tils/we-created-dogs-and-dogs-created-us), I'm really enjoying the
[As I mentioned](/posts/we-created-dogs-and-dogs-created-us), I'm really enjoying the
[Sapiens](https://en.wikipedia.org/wiki/Sapiens:_A_Brief_History_of_Humankind) book. I was shocked to discover that
hunter gatherer societies were comprised of extremely intelligent humans!

View File

@ -2,9 +2,9 @@
date: 2025-03-16T22:17:30-06:00
description: "Wowzas, if things pan out, I'm going to be a Dad!"
showTableOfContents: true
type: "tils"
type: "post"
lastmod: 2025-03-16T22:17:30-06:00
tags: [ "life", "parenting" ]
tags: [ "life", "parenting", "til" ]
title: "I Learned My Wife Is Pregnant"
image: ""
---

View File

@ -3,9 +3,9 @@ date: 2025-03-10T22:35:46-06:00
description: "Did I discover the middle ground of calisthenics and weight lifting?"
lastmod: 2025-03-10T22:35:46-06:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: I Like Hybrid Weighted Body Weight Exercises"
tags: [ "fitness", "life" ]
tags: [ "fitness", "life", "til" ]
---
# Context

View File

@ -0,0 +1,86 @@
---
date: 2026-02-11
description: "My thoughts on the movie Everything Everywhere All At Once"
image: "https://image.tmdb.org/t/p/original/atQ5kACEJrCLqmRrUa4NwR6gbKe.jpg"
lastmod: 2026-02-21T15:59:38-07:00
showTableOfContents: false
tags: ["movie review", "nihilism", "life"]
title: "Movie Review Everything Everywhere All at Once"
type: "post"
---
# Media Wednesdays
My partner and I decided recently to institute a tradition of enjoying some piece of media every wednesday evening.
This is our second week, and I chose the movie
[Everything Everywhere All At Once](https://en.wikipedia.org/wiki/Everything_Everywhere_All_at_Once). I really enjoyed
it. Below are some of my thoughts. Its a first pass, and I'm not going to try to capture everything, but I do not want
to let the perfect be the enemy of the good here.
The film made me laugh and cry. It made me want to profess my love for my partner with reckless abandon. It made me
feel and think, and at the end of the day, what else could we ask from a piece of media.
*SPOILERS AHEAD*
## Younger Generation Embodying Chaos
The mother, Evelyn, struggles. I was going to add "with ...", but really, with everything. Her alternate-universe
husband says, she failed at everything she had tried. But, specifically with her daughter Joy, she fails to acknowledge
and truly accept her.
The villain of the movie is an alternate dimension version of her daughter, shattered across the multiverse,
being everywhere at once. This sounds wacky. It was. But it absolutely worked.
I thought it was apt to compare an unwillingness to understand and meet people where they are by blaming or diverting "the enemy's" intention --
Nobody understood the motivation of Jobu Tupaki, the name they gave this broken version of Joy. And the resolution
of the film involves finally meeting her, hugging her, and hearing her. When we don't understand someone, their actions
seem ridiculous, dangerous, or confusing. We fear that which we don't understand. The movie showed the common gap between generations, in meeting each other, seeing each other, really well.
## A Reframing of Nihilism
I started reading _The Myth of Sysafus_, a book about suicide and Nihilism (don't worry, I am doing very well right now,
I'm just trying to learn philosophy). The book spends thousands of words answering the question of absurdity, why we
exist in a universe with no aparent reason (which religion / sprituality attempt to resolve, but the author sees as
cop outs). _Everything Everywhere All At Once_ concludes with a beautifully simple reframing of the
phrase "Nothing really matters". The daughter has given in, allowing whatever base or benal desire to drive. Crumbling, chaos,
catastrophe, none of it mattered, and stepping in to care for anyone or anything was pointless. At the very end of the movie, the dialog
between the mother making up and the daughter opening up:
(loosely)
*Tons of stuff has just happened and the end of the world was held at bay*
Joy: "Do you still want to throw your party"
Evelyn: "We can do whatever we want. Nothing really matters."
Without changing the words, Evelyn turned the phrase on its head. If nothing really matters, sure it can lead to lack
of meaning. But also, it gives us all the room to find the meaning we want in life. Without the context of the movie, it
doesn't sound very impactful, and may still sound negative, but I promise, that line was a welcome blow of relief. An emptying of the tension that the first act did such a good job establishing.
## Positivity for Survival
The husband Waymond maintains a happy and optimistic attitude throughout, opting for peaceful understanding to break
tension at the end of the film. In his various dimensional versions, I recieved a message that the way he survived
through life was his positivity.
I've always thought myself a happy person. Lately, I've identified that I am, in fact, much more optimistic than the
average person. I related to Waymond. And I also see that those that don't move through life this way aren't flawed,
they just have learned to survive differently than I. It made me stop and reflect, how often we think the way we think
or do things is surely the most optimized or correct. In reality, its just _a_ way.
## Understanding > Resisting
In line with that thought, at the end, Evelyn has to fight off people to get to her daughter before losing her to the
everything bagel hole. Rather than resisting with force (which she had done previously), she sought to understand. And each
of these people in the way seemed to have some deeper unmet need that she helped fill, a listening ear, a strappy sexual
encounter lol, a perfume that reminded an older man of his deceased wife.
And really, I think that on a deep level, we all are broken in our own ways. If someone sought to really understand us
we wouldn't find any reason to fight each other.
## All At Once
What a wonderful film. I learned from the wikipedia page it is the first movie to win more awards than Return of the King! And it deserves it. It feels unfortunate that the political climate may prevent some people from engaging with it and enjoying it. Its lude and breaks lots of "norms". The plot sounds ridiculous. But the message was a real human one. I loved it.

View File

@ -3,12 +3,12 @@ date: 2025-09-30T23:28:53-06:00
description: "There's a reason things become mainstream"
lastmod: 2025-09-30T23:28:53-06:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: People Are Actually Right"
image: ""
image_credit: ""
image_alt: ""
tags: ["philosophy", "life"]
tags: ["philosophy", "life", "til"]
---
# Context

View File

@ -3,19 +3,19 @@ date: 2025-10-03T16:19:07-06:00
description: "We were made for dogs, and they us."
lastmod: 2025-10-03T16:19:07-06:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: We Created Dogs and Dogs Created Us"
image: "images/otto-1.webp"
image_caption: "Otto, Stalwart"
image_alt: "Image of my sweet pup Otto, Irish Setter 7 months"
tags: ["life", "dogs", "history"]
tags: ["life", "dogs", "history", "til"]
---
# Context
I started listening to [_Sapiens: A Brief History of Human
kind_](https://en.wikipedia.org/wiki/Sapiens:_A_Brief_History_of_Humankind) and was struck at the significance of dogs
in human history. I [realized](/tils/people-are-actually-right) that dogs really are man's best
in human history. I [realized](/posts/people-are-actually-right) that dogs really are man's best
friend, and only animal that has evolved alongside us since the hunter gatherer period. Its incredible!
# Reflection

View File

@ -3,12 +3,12 @@ date: 2025-10-01T22:32:18-06:00
description: ""
lastmod: 2025-10-01T22:32:18-06:00
showTableOfContents: true
type: "tils"
type: "post"
title: "TIL: Why We Cant Forget to Flush"
image: ""
image_credit: ""
image_alt: ""
tags: ["zig", "programming"]
tags: ["zig", "programming", "til"]
---
# Context

View File

@ -0,0 +1,114 @@
---
date: 2026-02-08T21:49:30-07:00
description: "A journey of tweaks to make my super fun Framework 12 Laptop amazing."
lastmod: 2026-02-21T15:59:38-07:00
showTableOfContents: true
type: "projects"
title: "Framework12 Tweaks"
status: "In Progress"
statusColor: "#537c9c"
tags: ["framework", "laptop", "linux", "colemak-dh", "keyboard layout", "nixos", "usability"]
---
![Image of various framework laptop components in an aesthetic, orderly array](/images/framework-banner.webp)
Credit: Image pulled from Framework's 12" laptop product page
# Framework 12 Laptop
I knew I would buy my next laptop from [Framework](https://frame.work/) the first time I saw what they represented -- a radical shift in the consumer market. (I think it was this [Linus Tech Tips video](https://www.youtube.com/watch?v=SYc922ntnKM) a few years ago...)
It was just a matter of when.
And then, the [Framework 12](https://frame.work/laptop12) launched.
I think I have a thing for small computing devices. They are just so cute and quirky and fun. For some reason their small form factor makes me feel like I'll code everywhere. On the train, on the plane, at the park, on a hike. I never actually do, but its the dreaming that makes it exciting probably lol.
This framework 12 laptop has it all: a smaller (than average) form factor, repairable & upgradable components, durable outer shell, __and__ bold aesthetic (non metal color) colors?! I also kind of saw the, at the time, previous gen (under powered) i3 CPU as a feature. This would be a laptop for *intentional* computing. I couldn't pass it up. I ordered the Sage Framework 12 with the i3 1315U and it has been a blast! After a few months with it, my wife was so allured by its cuteness she demanded the bubblegum colorway :grin:
## Keyboard Remapping
This was a bit of journey, but we got there!
Some people may know that there are some odd balls out there that don't use the default
### Colemak-DH - My Rant on Keyboard Layouts
I'm a converted acolyte of the anti-QWERTY alliance.
I don't hate QWERTY. But it is illogical to persist using the keyboard that was not designed to be used as it is today. In my opinion, it is an inherited sin, and I will not perpetuate its havoc.
QWERTY was designed for type writers. Keys connected directly to the actuating hammers that would press the inked letters onto paper (or through ink ribbon, etc). Letters that were commonly pressed together were moved apart to prevent jamming. Literally, an un-ergonomic layout.
I type for my living. The promise made by Colemak (and friends) is reduced strain.
Look up keyboard layout heatmaps if you are interested. The most commonly pressed keys are put closer to your fingers on home row (who would have thought?). Its like carrying the trash can along as you pick up trash along a path vs going back and treking back and forth between the can and the piece each time (assume trash piece == carrying capacity lol).
You may think the example is very exagerated. Yes, its physically a lot more movement. But, I probably type thousands of characters a day. Maybe tens of thousands on the heavier days. So an extra inch stretch * 1000, every day. These add up. And you can feel it.
I will be honest though, if finger or wrist (especially wrist) pain is a serious problem for you, a layout change provides some benefit, but requires a lot of effort.
I had burning wrists after a work day for a while. The thing that fixed it was a split keyboard. Holding both hands close together in front of you to type is akin to being handcuffed. No really! Put your hands out onto your computer keyboard (assuming you have a standard rectangle brick of a board). Your wrists come together, then your hands fan out cock-eyed. The best solution, in my opinion, is to cut your keyboard in half and center them to each hand! Or get the Wave keyboard. They both essentially accomplish the same thing - to keep
The only real downside to switching is that most of the world hasn't caught on to the genius of alternative layouts.
- You will need to spend ~2-10 minutes every time you start a new videogame or some piece of software to rebind keys. Sometimes this is kind of painful or impossible.
- Your keyboard / computer becomes less usable to others.
- People will expect you to type so much faster now. You may also hope that. Unfortunately, that I think is a separate skill you would have to develop. Proficiency and speed kind of feel like aerobics and resistance training. Yes, one improves the other, but not really directly. You sort of have to lift weights to gain muscle, and to expect to type super fast just because you moved common keys closer to your fingers is missing the point.
- Most people will think you made a poor life choice and not be very curious why you took time to tank your WPM speed just to confuse your keyboard letters. At least, that's been my experience lol.
As an aside, my qwerty literacy has not changed. I think I'm slightly slower, but for typing on qwerty keyboards like once a month, if that, you'd think it would atrophy a lot more, but it feels more like riding a bike. The muscle memory kicks in really fast. Its almost spooky lol. But my brain can certainly hold two layouts (or more?) at once.
### Moving the Keys Physically
Now, the easy part.
The Framework 12 shipped with a neat little philips / hex screw and spudger, it even matched the laptop colorway! :heart_eyes: I used the spudger to slide under the north-east corner of the key, and push the tip down into the base whilst lifting the spudger up slowly. This would pop the plastic key off the underlying butterfly switch, and allowed me to rearrange to my heart's content!
### Reprogramming the Keys
Now, the busy part.
I will confess, I happily used AI (Claude Opus 4.5 with Opencode) to figure out how to reprogram my framework's laptop keyboard in the firmware. I don't really like using a keymap setting in the desktop software as it changes external keyboards you plug in as well.
Turns out, the embedded keyboard controller is programmable, thanks to a [handy ec tool](https://github.com/DHowett/framework-ec)! So, I just had to throw AI at it and give me a big list of key code commands that look like
```sh
ectool raw 0x3E0C d1,d1,b7,b8,w2b # this converts the 'e' key to 'f'
```
---
After an hour, AI could not get it right. Instead, I had AI write a simple script to slowly turn the whole keyboard into semicolons _;_ and I would not the value in the keyboard matrix for each physical key. Apparently no one has documented the framework 12's matrix, and it did not match the 13.
After doing that and giving AI the correct values, it did it correctly! :roll_eyes:
It really is painful when AI makes things take way longer... I don't need more reasons to dislike AI, its already a tenuous relationship!
### Janky Homing Bumps with a Hot Iron
Finally, the risky part.
## NixOS - The Greatest OS
### NNN - The Greatest Desktop Experience
NixOS + Niri + Noctalia
These three pieces of software have changed the game for my linux experience.
Linux, I think, has a usability problem. It can often feel unapproachable and opaque.
Not that Windows is super user friendly...
However, that is to me what makes Linux so wonderful. Because you can't as easily assume functionality and get away with it, you actually learn to use your computer.
Who do you know has ever read their car owners manual substantially? Or at all? But they actually contain really helpful information! Like how to reset the oil change light if you were to change your own oil.
Anyway, linux is great, and you must learn it. Learning it is half of what makes it great: once you learn how it work, you can tweak it to your heart's content!
And NixOS makes (some) things so much easier!
I don't think I've written much on nix, but I've been daily driving it for a while. [My NixOS system repo's](https://git.fosscat.com/n8r/nixos) [first commit](https://git.fosscat.com/n8r/nixos/commit/917ae5f05ec9d62cd40cfdfeca212bd46a06bb43) was 3 years ago! It ended my distro hopping, either from sunk-cost-fallacy or it is just better lol.

View File

@ -2,9 +2,10 @@
title: "SDL Game in Zig"
image: ""
type: "projects"
active: false
status: "Paused"
statusColor: "#ff9800"
date: "2025-01-10"
lastmod: 2025-10-03T16:19:07-06:00
lastmod: 2026-02-21T15:59:38-07:00
---
# Zig Game Dev with SDL2

View File

@ -1,10 +0,0 @@
---
title: "Today I Learned"
type: "til"
---
Any day I learn something cool (and think to write it down), I will write up a brief Today I Learned (TIL) post.
If they help you, great! But I think having a collection for myself will be pretty neat!
# My TILs

View File

@ -56,16 +56,4 @@
{{ end }}
{{/* TILs Section */}}
{{ $tils := where .Site.Pages "Params.type" "tils" }}
{{ $tilsToShow := $tils.ByDate.Reverse | first (or .Site.Params.NumberTilsOnHomePage 3) }}
{{ if gt (len $tilsToShow) 0 }}
<div class="home-posts list-posts">
<h2>Recent TILs</h2>
{{ range $tilsToShow }}
{{- partial "list-posts.html" . -}}
{{ end }}
</div>
{{ end }}
{{ end }}

124
layouts/partials/post.html Normal file
View File

@ -0,0 +1,124 @@
<style>
.post-image {
display: flex;
justify-content: center;
margin: 1.5rem 0;
}
.post-image img {
height: auto;
border-radius: 8px;
border: 1.5px solid var(--light-secondary-color);
box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 8px;
object-fit: cover;
}
.image-caption p {
display: flex;
justify-content: center;
}
/* Mobile - small images fit better */
@media (max-width: 480px) {
.post-image img {
width: 100%;
max-width: 300px;
}
}
/* Tablet - medium size */
@media (min-width: 481px) and (max-width: 768px) {
.post-image img {
width: 80%;
max-width: 400px;
}
}
/* Desktop - larger but not overwhelming */
@media (min-width: 769px) {
.post-image img {
width: 60%;
max-width: 500px;
}
}
</style>
<div class="post container">
<div class="post-header-section">
<h1>{{ .Title | markdownify }}</h1>
{{/* Determine whether to display the date & description based on tags */}}
{{ $displayDate := true }}
{{ $displayDescription := true }}
{{ $postTags := or .Params.Tags slice }}
{{ $hiddenTags := or .Site.Params.Hidden.Tags slice }}
{{ $tagsHidePostDate := or .Site.Params.Hidden.TagsPostDate slice }}
{{ $tagsHidePostDescription := or .Site.Params.Hidden.TagsPostDescription slice }}
{{ if gt ($tagsHidePostDate | intersect $postTags | len) 0 }}
{{ $displayDate = false }}
{{ end }}
{{ if gt ($tagsHidePostDescription | intersect $postTags | len) 0 }}
{{ $displayDescription = false }}
{{ end }}
{{ if $displayDescription }}
<small role="doc-subtitle">{{ .Description }}</small>
{{ end }}
{{ if $displayDate }}
<p class="post-date">{{ dateFormat (or .Site.Params.dateFormat "January 2, 2006") .Date}}
{{ if lt .Date .Lastmod }} | Updated {{ dateFormat (or .Site.Params.dateFormat "January 2, 2006") .Lastmod }}{{ end }}
</p>
{{ end }}
<ul class="post-tags">
{{ range $tag := $postTags }}
{{ if not (in $hiddenTags $tag) }}
<li class="post-tag"><a href="{{ "tags/" | absLangURL }}{{ . | urlize }}">{{ . }}</a></li>
{{ end }}
{{ end }}
</ul>
</div>
{{/* Display image if present in front matter */}}
{{ if .Params.image }}
<div class="post-image">
<img src="{{ .Params.image | absURL }}" alt="{{ .Params.image_alt | default .Title }}">
</div>
{{ with .Params.image_caption }}
<div class="image-caption">
<p>{{ . }}</p>
</div>
{{ end }}
{{ end }}
<div class="post-content">
{{ .Content }}
{{ if .Site.Config.Services.Disqus.Shortname }}
<div class="post-comments">
{{ template "_internal/disqus.html" . }}
</div>
{{ end }}
</div>
<div class="prev-next">
{{ if eq .Site.Params.TogglePreviousAndNextButtons "true" }}
{{ if or .PrevInSection .NextInSection }}
{{ partial "prev-next.html" . }}
{{ end }}
{{ end }}
</div>
<!-- Back to top button -->
{{ if .Site.Params.ShowBackToTopButton }}
{{ partial "back-to-top.html" . }}
{{ end }}
{{ if .Site.Params.CustomCommentHTML }}
<div id="comments">
{{ .Site.Params.CustomCommentHTML | safeHTML }}
</div>
{{ end }}
</div>

View File

@ -10,17 +10,6 @@
vertical-align: middle;
}
.status-active {
background-color: rgba(76, 175, 80, 0.15);
color: #4caf50;
border: 1px solid #4caf50;
}
.status-inactive {
background-color: rgba(158, 158, 158, 0.15);
color: #9e9e9e;
border: 1px solid #9e9e9e;
}
.project-date {
font-size: 0.85rem;
@ -40,10 +29,10 @@
<header class="entry-header">
<h3 class="entry-title">
<a href="{{ .Permalink }}" rel="bookmark">{{ .Title }}</a>
{{ if .Params.active }}
<span class="status-badge status-active">Active</span>
{{ else }}
<span class="status-badge status-inactive">Inactive</span>
{{ $page := . }}
{{ with .Params.status }}
{{ $color := $page.Params.statusColor | default "#9e9e9e" }}
<span class="status-badge" style="color: {{ $color }}; border: 1px solid {{ $color }}; background-color: {{ $color }}20;">{{ . }}</span>
{{ end }}
<span class="project-date">Updated {{ .Lastmod.Format "Jan 2, 2006" }}</span>
</h3>

View File

@ -11,17 +11,6 @@
vertical-align: middle;
}
.status-active {
background-color: rgba(76, 175, 80, 0.15);
color: #4caf50;
border: 1px solid #4caf50;
}
.status-inactive {
background-color: rgba(158, 158, 158, 0.15);
color: #9e9e9e;
border: 1px solid #9e9e9e;
}
.post-image {
display: flex;
@ -68,10 +57,9 @@
<div class="post-header-section">
<h1>
{{ .Title | markdownify }}
{{ if .Params.active }}
<span class="status-badge status-active">Active</span>
{{ else }}
<span class="status-badge status-inactive">Inactive</span>
{{ with .Params.status }}
{{ $color := $.Params.statusColor | default "#9e9e9e" }}
<span class="status-badge" style="color: {{ $color }}; border: 1px solid {{ $color }}; background-color: {{ $color }}20;">{{ . }}</span>
{{ end }}
</h1>

View File

@ -1,26 +0,0 @@
{{ define "main" }}
<div class="container">
<h1>Today I Learned</h1>
<p>Debug Info:</p>
<ul>
<li>Total Pages: {{ len .Pages }}</li>
<li>Is Section: {{ .IsSection }}</li>
<li>Section Name: {{ .Section }}</li>
</ul>
{{ range .Pages }}
<article>
<h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2>
<p>Date: {{ .Date.Format "January 2, 2006" }}</p>
{{ with .Description }}
<p>{{ . }}</p>
{{ end }}
</article>
{{ end }}
{{ if eq (len .Pages) 0 }}
<p>No TIL posts found.</p>
{{ end }}
</div>
{{ end }}

View File

@ -1,31 +0,0 @@
{{ define "main" }}
<div class="container">
<h1 class="page-title">{{ .Title }}</h1>
{{ .Content }}
<div class="posts-list">
{{ range .Pages }}
<article class="post-entry">
<header class="entry-header">
<h2 class="entry-title">
<a href="{{ .Permalink }}" rel="bookmark">
{{ .Date.Format "Jan 2, 2006" }} - {{ .Title }}
</a>
</h2>
</header>
{{ with .Description }}
<div class="entry-summary">
{{ . }}
</div>
{{ end }}
</article>
{{ end }}
{{ if eq (len .Pages) 0 }}
<p>No TIL posts yet.</p>
{{ end }}
</div>
</div>
{{ end }}

View File

@ -1,129 +0,0 @@
{{ define "main" }}
<style>
.post-image {
display: flex;
justify-content: center;
margin: 1.5rem 0;
}
.post-image img {
height: auto;
border-radius: 8px;
border: 1.5px solid var(--light-secondary-color);
box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 8px;
object-fit: cover;
}
.image-caption p {
display: flex;
justify-content: center;
}
/* Mobile - small images fit better */
@media (max-width: 480px) {
.post-image img {
width: 100%;
max-width: 300px;
}
}
/* Tablet - medium size */
@media (min-width: 481px) and (max-width: 768px) {
.post-image img {
width: 80%;
max-width: 400px;
}
}
/* Desktop - larger but not overwhelming */
@media (min-width: 769px) {
.post-image img {
width: 60%;
max-width: 500px;
}
}
</style>
<div class="post container">
<div class="post-header-section">
<h1>{{ .Title | markdownify }}</h1>
{{/* Determine whether to display the date & description based on tags */}}
{{ $displayDate := true }}
{{ $displayDescription := true }}
{{ $postTags := or .Params.Tags slice }}
{{ $hiddenTags := or .Site.Params.Hidden.Tags slice }}
{{ $tagsHidePostDate := or .Site.Params.Hidden.TagsPostDate slice }}
{{ $tagsHidePostDescription := or .Site.Params.Hidden.TagsPostDescription slice }}
{{ if gt ($tagsHidePostDate | intersect $postTags | len) 0 }}
{{ $displayDate = false }}
{{ end }}
{{ if gt ($tagsHidePostDescription | intersect $postTags | len) 0 }}
{{ $displayDescription = false }}
{{ end }}
{{ if $displayDescription }}
<small role="doc-subtitle">{{ .Description }}</small>
{{ end }}
{{ if $displayDate }}
<p class="post-date">{{ dateFormat (or .Site.Params.dateFormat "January 2, 2006") .Date}}
{{ if lt .Date .Lastmod }} | Updated {{ dateFormat (or .Site.Params.dateFormat "January 2, 2006") .Lastmod }}{{
end }}
</p>
{{ end }}
<ul class="post-tags">
{{ range $tag := $postTags }}
{{ if not (in $hiddenTags $tag) }}
<li class="post-tag"><a href="{{ " tags/" | absLangURL }}{{ . | urlize }}">{{ . }}</a></li>
{{ end }}
{{ end }}
</ul>
</div>
{{/* Display image if present in front matter */}}
{{ if .Params.image }}
<div class="post-image">
<img src="{{ .Params.image | absURL }}" alt="{{ .Params.image_alt | default .Title }}">
</div>
<div class="image-caption">
<p>{{ .Params.image_caption}}</p>
</div>
{{ end }}
<div class="post-content">
{{ .Content }}
{{ if .Site.Config.Services.Disqus.Shortname }}
<div class="post-comments">
{{ template "_internal/disqus.html" . }}
</div>
{{ end }}
</div>
<div class="prev-next">
{{ if eq .Site.Params.TogglePreviousAndNextButtons "true" }}
{{ if or .PrevInSection .NextInSection }}
{{ partial "prev-next.html" . }}
{{ end }}
{{ end }}
</div>
<!-- Back to top button -->
{{ if .Site.Params.ShowBackToTopButton }}
{{ partial "back-to-top.html" . }}
{{ end }}
{{ if .Site.Params.CustomCommentHTML }}
<div id="comments">
{{ .Site.Params.CustomCommentHTML | safeHTML }}
</div>
{{ end }}
</div>
{{- partial "toc.html" . -}}
{{ end }}

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB