1
0
Fork 0

lint
All checks were successful
Build Blog / Build (push) Successful in 5m32s

This commit is contained in:
Saji 2025-05-02 22:37:23 -05:00
parent 2fe8b81523
commit 1cd021d0a7
27 changed files with 886 additions and 871 deletions

View file

@ -1 +1,2 @@
.jj/ .jj/
node_modules/

View file

@ -4,7 +4,7 @@ A starter repository showing how to build a blog with the [Eleventy](https://www
## Getting Started ## Getting Started
* [Want a more generic/detailed getting started guide?](https://www.11ty.dev/docs/getting-started/) - [Want a more generic/detailed getting started guide?](https://www.11ty.dev/docs/getting-started/)
1. Make a directory and navigate to it: 1. Make a directory and navigate to it:

View file

@ -1,92 +1,92 @@
export default { export default {
dark: { dark: {
"source": "rootloops.sh", source: "rootloops.sh",
"hex": { hex: {
"background": "#0a0808", background: "#0a0808",
"foreground": "#e7e1e2", foreground: "#e7e1e2",
"cursor": "#c2b5b8", cursor: "#c2b5b8",
"black": "#272022", black: "#272022",
"red": "#cf5080", red: "#cf5080",
"green": "#7b8d39", green: "#7b8d39",
"yellow": "#b57235", yellow: "#b57235",
"blue": "#3c8cbf", blue: "#3c8cbf",
"magenta": "#9468d8", magenta: "#9468d8",
"cyan": "#3b9586", cyan: "#3b9586",
"white": "#c2b5b8", white: "#c2b5b8",
"brightBlack": "#5a4e51", brightBlack: "#5a4e51",
"brightRed": "#e26b95", brightRed: "#e26b95",
"brightGreen": "#8fa445", brightGreen: "#8fa445",
"brightYellow": "#cf8544", brightYellow: "#cf8544",
"brightBlue": "#4fa1d7", brightBlue: "#4fa1d7",
"brightMagenta": "#a782e5", brightMagenta: "#a782e5",
"brightCyan": "#47ac9b", brightCyan: "#47ac9b",
"brightWhite": "#f3f0f1" brightWhite: "#f3f0f1",
},
hsl: {
background: "hsl(345.13, 15.37%, 3.54%)",
foreground: "hsl(344.94, 10.53%, 89.29%)",
cursor: "hsl(344.63, 9.37%, 73.52%)",
black: "hsl(344.1, 8.9%, 13.97%)",
red: "hsl(337.55, 56.97%, 56.34%)",
green: "hsl(72.88, 42.49%, 38.93%)",
yellow: "hsl(28.49, 54.76%, 45.89%)",
blue: "hsl(203.33, 52.42%, 49.09%)",
magenta: "hsl(264.05, 58.81%, 62.59%)",
cyan: "hsl(169.71, 43.35%, 40.84%)",
white: "hsl(344.63, 9.37%, 73.52%)",
brightBlack: "hsl(344.15, 7.1%, 32.99%)",
brightRed: "hsl(339.06, 67.64%, 65.41%)",
brightGreen: "hsl(72.99, 40.55%, 45.65%)",
brightYellow: "hsl(28.18, 58.94%, 53.93%)",
brightBlue: "hsl(203.73, 63.27%, 57.72%)",
brightMagenta: "hsl(262.71, 65.56%, 70.29%)",
brightCyan: "hsl(169.62, 41.51%, 47.79%)",
brightWhite: "hsl(345.03, 10.68%, 94.62%)",
}, },
"hsl": {
"background": "hsl(345.13, 15.37%, 3.54%)",
"foreground": "hsl(344.94, 10.53%, 89.29%)",
"cursor": "hsl(344.63, 9.37%, 73.52%)",
"black": "hsl(344.1, 8.9%, 13.97%)",
"red": "hsl(337.55, 56.97%, 56.34%)",
"green": "hsl(72.88, 42.49%, 38.93%)",
"yellow": "hsl(28.49, 54.76%, 45.89%)",
"blue": "hsl(203.33, 52.42%, 49.09%)",
"magenta": "hsl(264.05, 58.81%, 62.59%)",
"cyan": "hsl(169.71, 43.35%, 40.84%)",
"white": "hsl(344.63, 9.37%, 73.52%)",
"brightBlack": "hsl(344.15, 7.1%, 32.99%)",
"brightRed": "hsl(339.06, 67.64%, 65.41%)",
"brightGreen": "hsl(72.99, 40.55%, 45.65%)",
"brightYellow": "hsl(28.18, 58.94%, 53.93%)",
"brightBlue": "hsl(203.73, 63.27%, 57.72%)",
"brightMagenta": "hsl(262.71, 65.56%, 70.29%)",
"brightCyan": "hsl(169.62, 41.51%, 47.79%)",
"brightWhite": "hsl(345.03, 10.68%, 94.62%)"
}
}, },
light: { light: {
"source": "rootloops.sh", source: "rootloops.sh",
"hex": { hex: {
"background": "#f3f0f1", background: "#f3f0f1",
"foreground": "#40373a", foreground: "#40373a",
"cursor": "#74666a", cursor: "#74666a",
"black": "#e7e1e2", black: "#e7e1e2",
"red": "#cf5080", red: "#cf5080",
"green": "#7b8d39", green: "#7b8d39",
"yellow": "#b57235", yellow: "#b57235",
"blue": "#3c8cbf", blue: "#3c8cbf",
"magenta": "#9468d8", magenta: "#9468d8",
"cyan": "#3b9586", cyan: "#3b9586",
"white": "#74666a", white: "#74666a",
"brightBlack": "#b5a7ab", brightBlack: "#b5a7ab",
"brightRed": "#e26b95", brightRed: "#e26b95",
"brightGreen": "#8fa445", brightGreen: "#8fa445",
"brightYellow": "#cf8544", brightYellow: "#cf8544",
"brightBlue": "#4fa1d7", brightBlue: "#4fa1d7",
"brightMagenta": "#a782e5", brightMagenta: "#a782e5",
"brightCyan": "#47ac9b", brightCyan: "#47ac9b",
"brightWhite": "#272022" brightWhite: "#272022",
}, },
"hsl": { hsl: {
"background": "hsl(345.03, 10.68%, 94.62%)", background: "hsl(345.03, 10.68%, 94.62%)",
"foreground": "hsl(344.11, 7.75%, 23.41%)", foreground: "hsl(344.11, 7.75%, 23.41%)",
"cursor": "hsl(344.2, 6.49%, 42.82%)", cursor: "hsl(344.2, 6.49%, 42.82%)",
"black": "hsl(344.94, 10.53%, 89.29%)", black: "hsl(344.94, 10.53%, 89.29%)",
"red": "hsl(337.55, 56.97%, 56.34%)", red: "hsl(337.55, 56.97%, 56.34%)",
"green": "hsl(72.88, 42.49%, 38.93%)", green: "hsl(72.88, 42.49%, 38.93%)",
"yellow": "hsl(28.49, 54.76%, 45.89%)", yellow: "hsl(28.49, 54.76%, 45.89%)",
"blue": "hsl(203.33, 52.42%, 49.09%)", blue: "hsl(203.33, 52.42%, 49.09%)",
"magenta": "hsl(264.05, 58.81%, 62.59%)", magenta: "hsl(264.05, 58.81%, 62.59%)",
"cyan": "hsl(169.71, 43.35%, 40.84%)", cyan: "hsl(169.71, 43.35%, 40.84%)",
"white": "hsl(344.2, 6.49%, 42.82%)", white: "hsl(344.2, 6.49%, 42.82%)",
"brightBlack": "hsl(344.53, 8.76%, 68.33%)", brightBlack: "hsl(344.53, 8.76%, 68.33%)",
"brightRed": "hsl(339.06, 67.64%, 65.41%)", brightRed: "hsl(339.06, 67.64%, 65.41%)",
"brightGreen": "hsl(72.99, 40.55%, 45.65%)", brightGreen: "hsl(72.99, 40.55%, 45.65%)",
"brightYellow": "hsl(28.18, 58.94%, 53.93%)", brightYellow: "hsl(28.18, 58.94%, 53.93%)",
"brightBlue": "hsl(203.73, 63.27%, 57.72%)", brightBlue: "hsl(203.73, 63.27%, 57.72%)",
"brightMagenta": "hsl(262.71, 65.56%, 70.29%)", brightMagenta: "hsl(262.71, 65.56%, 70.29%)",
"brightCyan": "hsl(169.62, 41.51%, 47.79%)", brightCyan: "hsl(169.62, 41.51%, 47.79%)",
"brightWhite": "hsl(344.1, 8.9%, 13.97%)" brightWhite: "hsl(344.1, 8.9%, 13.97%)",
}
}, },
} },
};

View file

@ -6,6 +6,6 @@ export default {
author: { author: {
name: "Saji", name: "Saji",
email: "saji@saji.dev", email: "saji@saji.dev",
url: "https://saji.dev/about-me/" url: "https://saji.dev/about-me/",
} },
} };

View file

@ -3,6 +3,7 @@ layout: layouts/home.njk
permalink: 404.html permalink: 404.html
eleventyExcludeFromCollections: true eleventyExcludeFromCollections: true
--- ---
# Content not found. # Content not found.
Go <a href="/">home</a>. Go <a href="/">home</a>.

View file

@ -4,6 +4,7 @@ eleventyNavigation:
key: About Me key: About Me
order: 3 order: 3
--- ---
# About Me # About Me
I am a person that writes stuff. I am a person that writes stuff.

View file

@ -6,6 +6,4 @@ tags:
- project - project
--- ---
This is a test This is a test

View file

@ -1,6 +1,4 @@
export default { export default {
tags: [ tags: ["posts"],
"posts" layout: "layouts/post.njk",
],
"layout": "layouts/post.njk",
}; };

View file

@ -12,6 +12,7 @@ has been documented to death by skids, I haven't seen anyone reference this yet.
I'm sure someone is using it, since it's powerful. I'm sure someone is using it, since it's powerful.
Case in point: Case in point:
``` ```
/"[a-z]{4}(?: [a-z]{4}){3}"/ language:Python SMTP /"[a-z]{4}(?: [a-z]{4}){3}"/ language:Python SMTP
``` ```

View file

@ -13,7 +13,7 @@ Part one focuses on the image format.
# Introduction # Introduction
*[ITAR]: International Traffic in Arms Regulations \*[ITAR]: International Traffic in Arms Regulations
In the past few years, Chinese manufacturers have brought cheap, performant microbolometer arrays to the consumer market. In the past few years, Chinese manufacturers have brought cheap, performant microbolometer arrays to the consumer market.
These arrays are higher resolution and faster framerates than what can reasonably be acquired in the West - mostly due These arrays are higher resolution and faster framerates than what can reasonably be acquired in the West - mostly due
to low competition and ITAR restrictions. Most popular are the low-cost modules by Infiray, which provides whitelabel solutions to low competition and ITAR restrictions. Most popular are the low-cost modules by Infiray, which provides whitelabel solutions
@ -35,10 +35,8 @@ odd phone app. However this choice seems less popular on forums and there is no
reverse engineering project that exists already, so we'll have to start from reverse engineering project that exists already, so we'll have to start from
scratch. scratch.
# Getting the picture # Getting the picture
To start with something simple, lets figure out how their image format works. They call it "radiometric JPEG". This To start with something simple, lets figure out how their image format works. They call it "radiometric JPEG". This
isn't to be confused with FLIR's RJPEG format, which is already well understood.. When using regular image viewers, isn't to be confused with FLIR's RJPEG format, which is already well understood.. When using regular image viewers,
we get a screenshot of the display. Where things get interesting is when we open the image with the Hikmicro Analyzer software, we can see the full we get a screenshot of the display. Where things get interesting is when we open the image with the Hikmicro Analyzer software, we can see the full
@ -69,9 +67,3 @@ Well that's something. It's not perfect, but it means that our thermal data is r
# I'm hexing here # I'm hexing here
Imhex is pretty great. You can perform a lot of analysis without needing other tools. Imhex is pretty great. You can perform a lot of analysis without needing other tools.

View file

@ -11,7 +11,6 @@ complex Ansible playbooks. While I don't think NixOS is ready for
personal computers, it absolutely makes sense on less dynamic devices personal computers, it absolutely makes sense on less dynamic devices
like servers or embedded-ish machines. like servers or embedded-ish machines.
We can use NixOS and nixpkgs to derive a fully defined operating system and services. We can use NixOS and nixpkgs to derive a fully defined operating system and services.
Then, we can: Then, we can:
@ -118,12 +117,10 @@ $ ls /nix/store | grep myblog
mqhssdlmg9f03avpajwcqaah2apknl02-myblog mqhssdlmg9f03avpajwcqaah2apknl02-myblog
``` ```
Before we go any further, let's set up the nginx server, Before we go any further, let's set up the nginx server,
as well as a well-known path for our website. I'll also as well as a well-known path for our website. I'll also
add a user here that we can use to deploy. add a user here that we can use to deploy.
```nix ```nix
{ {
config, config,
@ -194,17 +191,15 @@ in
} }
``` ```
Now we have a scoped user, with an ssh key authorized. It needs a shell so we can actually log Now we have a scoped user, with an ssh key authorized. It needs a shell so we can actually log
in remotely. in remotely.
The last step is creating that symlink. This is where the concept of "activation" comes into play. The last step is creating that symlink. This is where the concept of "activation" comes into play.
For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make
the system change the profile. We can effectively do whatever we want here. the system change the profile. We can effectively do whatever we want here.
Reading the [custom activator](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/flake.nix#L58C13-L96C17) source: Reading the [custom activator](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/flake.nix#L58C13-L96C17) source:
```nix ```nix
custom = { custom = {
__functor = customSelf: base: activate: __functor = customSelf: base: activate:
@ -246,7 +241,6 @@ custom = {
}; };
``` ```
This is a bit difficult to parse because there's the whole `__functor` bit. This is a bit difficult to parse because there's the whole `__functor` bit.
Essentially, we use `buildEnv` to add some new scripts to the `base` package, Essentially, we use `buildEnv` to add some new scripts to the `base` package,
which are then used to call `activate`, which can be a shell script or a binary. which are then used to call `activate`, which can be a shell script or a binary.
@ -314,7 +308,6 @@ The chain looks like this:
A profile is just a symlink to a derivation in the nix store. One layer of indirection A profile is just a symlink to a derivation in the nix store. One layer of indirection
exists to make rollbacks easier. exists to make rollbacks easier.
# End # End
I hope this was useful for you. I think non-root deployment is I hope this was useful for you. I think non-root deployment is

View file

@ -140,4 +140,3 @@ to hack around that with a shim but that's a problem for later. It works
fine at 9600 baud using software flow control. fine at 9600 baud using software flow control.
To the folks who rave about the terminals of yore, I get it now. To the folks who rave about the terminals of yore, I get it now.

View file

@ -4,8 +4,6 @@ description: Bringing modern synthesis to 30-year old technology with Yosys and
date: 2024-06-14 date: 2024-06-14
--- ---
GAL/PALs are ancient chips designed to implement custom logic as a precursor to CPLDs and FPGAs. GAL/PALs are ancient chips designed to implement custom logic as a precursor to CPLDs and FPGAs.
They can implement any combinational logic using a Sum-of-Products architecture and a grid of wires They can implement any combinational logic using a Sum-of-Products architecture and a grid of wires
with certain wires being connected at different coordinates: with certain wires being connected at different coordinates:
@ -41,11 +39,9 @@ DESCRIPTION
Simple test of combinatorial logic. Simple test of combinatorial logic.
``` ```
I don't want to deal with this. It's annoying to think about. What if we could create our own Verilog flow for GAL chips? I don't want to deal with this. It's annoying to think about. What if we could create our own Verilog flow for GAL chips?
Then we could take advantage of the simulation and synthesis capabilities, which would make these chips a bit more useful. Then we could take advantage of the simulation and synthesis capabilities, which would make these chips a bit more useful.
# The idea # The idea
We use Yosys to synthesize Verilog, and do technology-mapping onto a set of primitive blocks that are then mapped by a custom tool. We use Yosys to synthesize Verilog, and do technology-mapping onto a set of primitive blocks that are then mapped by a custom tool.
@ -54,8 +50,6 @@ onto actual positions based on a pin constraints file.
There's two components - Yosys technology mapping, and writing a "place-and-route" tool. We'll start with Yosys. There's two components - Yosys technology mapping, and writing a "place-and-route" tool. We'll start with Yosys.
## Yosys Technology Mapping ## Yosys Technology Mapping
This process is cursed and low level, but it works a little like this: This process is cursed and low level, but it works a little like this:
@ -66,9 +60,6 @@ This process turns generic blocks like an adder into a set of gates.
- certain sequences of gates are then grouped and unified in the "extract" pass. Extraction would convert an operation like `mul -> add` - certain sequences of gates are then grouped and unified in the "extract" pass. Extraction would convert an operation like `mul -> add`
into a single `mul_add` block. into a single `mul_add` block.
## A history lesson ## A history lesson
During the semiconductor revolution, a dilemma appeared: Designing new integrated circuits During the semiconductor revolution, a dilemma appeared: Designing new integrated circuits
@ -127,7 +118,6 @@ logic chip, changing the function depending on what you need. Additionally,
GALs operate at 5 volts is useful when interfacing with older systems and GALs operate at 5 volts is useful when interfacing with older systems and
removes the need for a level shifter. removes the need for a level shifter.
In practice, this isn't all great. Programming GALs is an exercise in frustration. In practice, this isn't all great. Programming GALs is an exercise in frustration.
Take a look at a basic combinatorial assembly file: Take a look at a basic combinatorial assembly file:
@ -159,18 +149,16 @@ test this in a larger system (we'll get back to this). Compared to the Verilog
flow, with simulation, testbenches, and synthesis, the raw assembly is stuck in flow, with simulation, testbenches, and synthesis, the raw assembly is stuck in
the 80s and requires manual logic simplification. the 80s and requires manual logic simplification.
Verilog compilers for GALs *did exist*, but they ran on old-as-dirt systems, Verilog compilers for GALs _did exist_, but they ran on old-as-dirt systems,
didn't have any significant optimization capabilities, and were almost always didn't have any significant optimization capabilities, and were almost always
proprietary. What if we could make our own open-source Verilog flow for GAL proprietary. What if we could make our own open-source Verilog flow for GAL
chips? Then we could write test benches in Verilog, map complex designs onto chips? Then we could write test benches in Verilog, map complex designs onto
the chip, and even integrate our designs with FPGAs later down the line. the chip, and even integrate our designs with FPGAs later down the line.
# The idea # The idea
GAL assembly appears occasionally when working with older systems, especially in a retro emulation context. GAL assembly appears occasionally when working with older systems, especially in a retro emulation context.
# Is this useful? # Is this useful?
Not particularly. Not particularly.

View file

@ -1,5 +1,4 @@
export default { export default {
tags: [ tags: [],
], layout: "layouts/deck.njk",
"layout": "layouts/deck.njk",
}; };

View file

@ -3,9 +3,7 @@
<h6>How to survive a "post-OO" world</h6> <h6>How to survive a "post-OO" world</h6>
</section> </section>
<section> <section>
<section> <section>Class/Inheritance has challenges</section>
Class/Inheritance has challenges
</section>
<section> <section>
Combining Data + Code Combining Data + Code
<pre><code data-trim data-noescape data-line-numbers="5|7-9"> <pre><code data-trim data-noescape data-line-numbers="5|7-9">
@ -20,21 +18,32 @@
}; };
}; };
</code></pre> </code></pre>
<p class="fragment">You have to know if this is an "interface", abstract class, or regular class.</p> <p class="fragment">
<small class="fragment">(it's an abstract class, since <code>makeSound</code> is pure virtual)</small> You have to know if this is an "interface", abstract class, or regular
class.
</p>
<small class="fragment"
>(it's an abstract class, since <code>makeSound</code> is pure
virtual)</small
>
</section> </section>
<section> <section>
<p>A class can be one of many things</p> <p>A class can be one of many things</p>
<ul> <ul>
<li>Interface - abstract class with no members, and purely virtual</li> <li>Interface - abstract class with no members, and purely virtual</li>
<li>Abstract Class - class with <em>some</em> pure virtual methods</li> <li>Abstract Class - class with <em>some</em> pure virtual methods</li>
<li>"Regular" Class - class that is standalone and is fully implemented </li> <li>
"Regular" Class - class that is standalone and is fully implemented
</li>
</ul> </ul>
<small class="fragment">These concepts all exist under the <code>class</code> keyword </small> <small class="fragment"
>These concepts all exist under the <code>class</code> keyword
</small>
</section> </section>
</section> </section>
<section> <section>
In Rust, you trade <em>Inheritance</em> for <em>Traits</em> and <em>Types</em>. In Rust, you trade <em>Inheritance</em> for <em>Traits</em> and
<em>Types</em>.
</section> </section>
<section> <section>
@ -63,7 +72,6 @@
} }
} }
</code></pre> </code></pre>
</section> </section>
<section> <section>
<p>And can be used statically <em>or</em> dynamically!</p> <p>And can be used statically <em>or</em> dynamically!</p>
@ -77,4 +85,3 @@
</code></pre> </code></pre>
</section> </section>
</section> </section>

View file

@ -6,6 +6,7 @@ eleventyNavigation:
key: Now key: Now
order: 4 order: 4
--- ---
## Save Slot 0 ## Save Slot 0
Rate-Monotonic scheduling my life. Rate-Monotonic scheduling my life.

View file

@ -8,8 +8,8 @@ export function eleventyComputedPermalink() {
} }
return data.permalink; return data.permalink;
}
}; };
}
export function eleventyComputedExcludeFromCollections() { export function eleventyComputedExcludeFromCollections() {
// When using `addGlobalData` and you *want* to return a function, you must nest functions like this. // When using `addGlobalData` and you *want* to return a function, you must nest functions like this.
@ -21,12 +21,18 @@ export function eleventyComputedExcludeFromCollections() {
} }
return data.eleventyExcludeFromCollections; return data.eleventyExcludeFromCollections;
}
}; };
}
export default function (eleventyConfig) { export default function (eleventyConfig) {
eleventyConfig.addGlobalData("eleventyComputed.permalink", eleventyComputedPermalink); eleventyConfig.addGlobalData(
eleventyConfig.addGlobalData("eleventyComputed.eleventyExcludeFromCollections", eleventyComputedExcludeFromCollections); "eleventyComputed.permalink",
eleventyComputedPermalink,
);
eleventyConfig.addGlobalData(
"eleventyComputed.eleventyExcludeFromCollections",
eleventyComputedExcludeFromCollections,
);
let logged = false; let logged = false;
eleventyConfig.on("eleventy.before", ({ runMode }) => { eleventyConfig.on("eleventy.before", ({ runMode }) => {

View file

@ -11,10 +11,9 @@ import pluginNavigation from "@11ty/eleventy-navigation";
import { EleventyHtmlBasePlugin } from "@11ty/eleventy"; import { EleventyHtmlBasePlugin } from "@11ty/eleventy";
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img"; import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
import pluginDrafts from "./eleventy.config.drafts.js"; import pluginDrafts from "./eleventy.config.drafts.js";
import fs from 'fs/promises' import fs from "fs/promises";
/** @param {import('@11ty/eleventy').UserConfig} eleventyConfig */ /** @param {import('@11ty/eleventy').UserConfig} eleventyConfig */
export default async function (eleventyConfig) { export default async function (eleventyConfig) {
@ -38,24 +37,21 @@ export default async function(eleventyConfig) {
outputPath: path, outputPath: path,
collection: { collection: {
name: "posts", name: "posts",
limit: 0 limit: 0,
}, },
metadata: { metadata: {
title: "Musings", title: "Musings",
base: "https://saji.dev", base: "https://saji.dev",
language: "en", language: "en",
}, },
}); });
}; };
mkFeed("rss", "/rss.feed.xml"); mkFeed("rss", "/rss.feed.xml");
mkFeed("atom", "/atom.feed.xml"); mkFeed("atom", "/atom.feed.xml");
mkFeed("json", "/feed.json"); mkFeed("json", "/feed.json");
eleventyConfig.addPlugin(pluginSyntaxHighlight, { eleventyConfig.addPlugin(pluginSyntaxHighlight, {
preAttributes: { tabindex: 0 } preAttributes: { tabindex: 0 },
}); });
eleventyConfig.addPlugin(pluginNavigation); eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(EleventyHtmlBasePlugin); eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
@ -68,20 +64,20 @@ export default async function(eleventyConfig) {
loading: "lazy", loading: "lazy",
decoding: "async", decoding: "async",
sizes: "auto", sizes: "auto",
} },
}) });
// Filters // Filters
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => { eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => {
// Formatting tokens for Luxon: https://moment.github.io/luxon/#/formatting?id=table-of-tokens // Formatting tokens for Luxon: https://moment.github.io/luxon/#/formatting?id=table-of-tokens
return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(format || "dd LLLL yyyy"); return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(
format || "dd LLLL yyyy",
);
}); });
eleventyConfig.addFilter('htmlDateString', (dateObj) => { eleventyConfig.addFilter("htmlDateString", (dateObj) => {
// dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string // dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
return DateTime.fromJSDate(dateObj, { zone: 'utc' }).toFormat('yyyy-LL-dd'); return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat("yyyy-LL-dd");
}); });
// Get the first `n` elements of a collection. // Get the first `n` elements of a collection.
@ -102,21 +98,23 @@ export default async function(eleventyConfig) {
}); });
// Return all the tags used in a collection // Return all the tags used in a collection
eleventyConfig.addFilter("getAllTags", collection => { eleventyConfig.addFilter("getAllTags", (collection) => {
let tagSet = new Set(); let tagSet = new Set();
for (let item of collection) { for (let item of collection) {
(item.data.tags || []).forEach(tag => tagSet.add(tag)); (item.data.tags || []).forEach((tag) => tagSet.add(tag));
} }
return Array.from(tagSet); return Array.from(tagSet);
}); });
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) { eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
return (tags || []).filter(tag => ["all", "nav", "post", "posts"].indexOf(tag) === -1); return (tags || []).filter(
(tag) => ["all", "nav", "post", "posts"].indexOf(tag) === -1,
);
}); });
let mdIt; let mdIt;
// Customize Markdown library settings: // Customize Markdown library settings:
eleventyConfig.amendLibrary("md", mdLib => { eleventyConfig.amendLibrary("md", (mdLib) => {
// hack to let us use the markdown renderer for later. // hack to let us use the markdown renderer for later.
mdIt = mdLib; mdIt = mdLib;
mdLib.use(markdownItAnchor, { mdLib.use(markdownItAnchor, {
@ -127,17 +125,19 @@ export default async function(eleventyConfig) {
ariaHidden: false, ariaHidden: false,
}), }),
level: [1, 2, 3, 4], level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slugify") slugify: eleventyConfig.getFilter("slugify"),
}); });
mdLib.use(markdownItAbbr); mdLib.use(markdownItAbbr);
mdLib.use(markdownItKatex.default); mdLib.use(markdownItKatex.default);
}); });
eleventyConfig.addShortcode("currentBuildDate", () => { eleventyConfig.addShortcode("currentBuildDate", () => {
return (new Date()).toISOString(); return new Date().toISOString();
}) });
eleventyConfig.addShortcode("includeRaw", async (file) => fs.readFile(file, "utf8")); eleventyConfig.addShortcode("includeRaw", async (file) =>
fs.readFile(file, "utf8"),
);
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => { //eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`; // return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
@ -165,12 +165,7 @@ export default async function(eleventyConfig) {
return { return {
// Control which files Eleventy will process // Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid // e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: [ templateFormats: ["md", "njk", "html", "liquid"],
"md",
"njk",
"html",
"liquid",
],
// Pre-process *.md files with: (default: `liquid`) // Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: "njk", markdownTemplateEngine: "njk",
@ -183,7 +178,7 @@ export default async function(eleventyConfig) {
input: "content", // default: "." input: "content", // default: "."
includes: "../_includes", // default: "_includes" includes: "../_includes", // default: "_includes"
data: "../_data", // default: "_data" data: "../_data", // default: "_data"
output: "_site" output: "_site",
}, },
// ----------------------------------------------------------------- // -----------------------------------------------------------------
@ -198,4 +193,4 @@ export default async function(eleventyConfig) {
// folder name and does **not** affect where things go in the output folder. // folder name and does **not** affect where things go in the output folder.
pathPrefix: "/", pathPrefix: "/",
}; };
}; }

View file

@ -6,25 +6,38 @@
treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.url = "github:numtide/treefmt-nix";
}; };
outputs = { self, nixpkgs, deploy-rs, systems, treefmt-nix }: let outputs =
{
self,
nixpkgs,
deploy-rs,
systems,
treefmt-nix,
}:
let
# systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; # systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
forAllSystems = fn: forAllSystems =
nixpkgs.lib.genAttrs (import systems) (system: fn nixpkgs.legacyPackages.${system}); fn: nixpkgs.lib.genAttrs (import systems) (system: fn nixpkgs.legacyPackages.${system});
treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix); treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
in { in
{
apps = forAllSystems (pkgs: { apps = forAllSystems (pkgs: {
"deploy" = { "deploy" = {
type = "app"; type = "app";
program = let program =
ci = (pkgs.writeShellApplication { let
ci = (
pkgs.writeShellApplication {
name = "ci.sh"; name = "ci.sh";
text = '' text = ''
nix build nix build
''; '';
}); }
in "${ci}/ci.sh"; );
in
"${ci}/ci.sh";
}; };
}); });
packages = forAllSystems (pkgs: { packages = forAllSystems (pkgs: {

View file

@ -7,36 +7,40 @@ export default function figcaptionPlugin(md) {
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
// Check for paragraph containing only an image // Check for paragraph containing only an image
if ( if (
tokens[i].type === 'paragraph_open' && tokens[i].type === "paragraph_open" &&
i + 2 < tokens.length && i + 2 < tokens.length &&
tokens[i + 1].type === 'inline' && tokens[i + 1].type === "inline" &&
tokens[i + 1].children && tokens[i + 1].children &&
tokens[i + 1].children.length === 1 && tokens[i + 1].children.length === 1 &&
tokens[i + 1].children[0].type === 'image' && tokens[i + 1].children[0].type === "image" &&
tokens[i + 2].type === 'paragraph_close' tokens[i + 2].type === "paragraph_close"
) { ) {
// Check if the next token is a paragraph starting with emphasis // Check if the next token is a paragraph starting with emphasis
if ( if (
i + 5 < tokens.length && i + 5 < tokens.length &&
tokens[i + 3].type === 'paragraph_open' && tokens[i + 3].type === "paragraph_open" &&
tokens[i + 4].type === 'inline' && tokens[i + 4].type === "inline" &&
tokens[i + 4].children && tokens[i + 4].children &&
tokens[i + 4].children.length > 0 && tokens[i + 4].children.length > 0 &&
tokens[i + 4].children[0].type === 'em_open' && tokens[i + 4].children[0].type === "em_open" &&
tokens[i + 5].type === 'paragraph_close' tokens[i + 5].type === "paragraph_close"
) { ) {
figcaptionStartIndex = i + 3; // Start index of the caption paragraph figcaptionStartIndex = i + 3; // Start index of the caption paragraph
// --- Replace tokens --- // --- Replace tokens ---
// 1. Change paragraph_open to figure_open // 1. Change paragraph_open to figure_open
const figureOpen = new state.Token('figure_open', 'figure', 1); const figureOpen = new state.Token("figure_open", "figure", 1);
tokens[i] = figureOpen; // Replace paragraph_open tokens[i] = figureOpen; // Replace paragraph_open
// 2. Keep the inline token with the image as is (tokens[i+1]) // 2. Keep the inline token with the image as is (tokens[i+1])
// 3. Change paragraph_close to figcaption_open // 3. Change paragraph_close to figcaption_open
const figcaptionOpen = new state.Token('figcaption_open', 'figcaption', 1); const figcaptionOpen = new state.Token(
"figcaption_open",
"figcaption",
1,
);
tokens[i + 2] = figcaptionOpen; // Replace paragraph_close tokens[i + 2] = figcaptionOpen; // Replace paragraph_close
// 4. Remove the caption's paragraph_open // 4. Remove the caption's paragraph_open
@ -45,23 +49,29 @@ export default function figcaptionPlugin(md) {
// 5. Modify the caption's inline content: remove outer <em> tags // 5. Modify the caption's inline content: remove outer <em> tags
const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice
if ( if (
captionInlineToken.children[0].type === 'em_open' && captionInlineToken.children[0].type === "em_open" &&
captionInlineToken.children[captionInlineToken.children.length - 1].type === 'em_close' captionInlineToken.children[captionInlineToken.children.length - 1]
.type === "em_close"
) { ) {
captionInlineToken.children.shift(); // Remove em_open captionInlineToken.children.shift(); // Remove em_open
captionInlineToken.children.pop(); // Remove em_close captionInlineToken.children.pop(); // Remove em_close
} }
// Adjust level for caption content // Adjust level for caption content
captionInlineToken.level += 1; captionInlineToken.level += 1;
captionInlineToken.children.forEach(child => { child.level += 1; }); captionInlineToken.children.forEach((child) => {
child.level += 1;
});
// 6. Change the caption's paragraph_close to figcaption_close // 6. Change the caption's paragraph_close to figcaption_close
const figcaptionClose = new state.Token('figcaption_close', 'figcaption', -1); const figcaptionClose = new state.Token(
"figcaption_close",
"figcaption",
-1,
);
tokens[figcaptionStartIndex + 1] = figcaptionClose; // Replace paragraph_close (now at i+4) tokens[figcaptionStartIndex + 1] = figcaptionClose; // Replace paragraph_close (now at i+4)
// 7. Add figure_close after figcaption_close // 7. Add figure_close after figcaption_close
const figureClose = new state.Token('figure_close', 'figure', -1); const figureClose = new state.Token("figure_close", "figure", -1);
tokens.splice(figcaptionStartIndex + 2, 0, figureClose); // Insert figure_close (at i+5) tokens.splice(figcaptionStartIndex + 2, 0, figureClose); // Insert figure_close (at i+5)
// Adjust token index to skip the newly inserted/modified tokens // Adjust token index to skip the newly inserted/modified tokens
@ -71,13 +81,27 @@ export default function figcaptionPlugin(md) {
} }
} }
md.core.ruler.after('inline', 'figcaption', figcaptionRule); md.core.ruler.after("inline", "figcaption", figcaptionRule);
// Add renderer rules if they don't exist // Add renderer rules if they don't exist
md.renderer.rules.figure_open = md.renderer.rules.figure_open || function () { return '<figure>\n'; }; md.renderer.rules.figure_open =
md.renderer.rules.figure_close = md.renderer.rules.figure_close || function () { return '</figure>\n'; }; md.renderer.rules.figure_open ||
md.renderer.rules.figcaption_open = md.renderer.rules.figcaption_open || function () { return '<figcaption>'; }; function () {
md.renderer.rules.figcaption_close = md.renderer.rules.figcaption_close || function () { return '</figcaption>\n'; }; return "<figure>\n";
};
md.renderer.rules.figure_close =
md.renderer.rules.figure_close ||
function () {
return "</figure>\n";
};
md.renderer.rules.figcaption_open =
md.renderer.rules.figcaption_open ||
function () {
return "<figcaption>";
};
md.renderer.rules.figcaption_close =
md.renderer.rules.figcaption_close ||
function () {
return "</figcaption>\n";
};
} }

View file

@ -3,7 +3,6 @@
const HSEP = "---"; const HSEP = "---";
const VSEP = "==="; const VSEP = "===";
/** /**
* MarkdownIt plugin to generate revealjs friendly markup. * MarkdownIt plugin to generate revealjs friendly markup.
* *
@ -29,12 +28,12 @@ export default function revealjs_plugin(md, options) {
function renderOpening(tokens, idx, options, env, slf) { function renderOpening(tokens, idx, options, env, slf) {
var token = tokens[idx]; var token = tokens[idx];
return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`; return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`;
}; }
function renderClosing(tokens, idx, options, env, slf) { function renderClosing(tokens, idx, options, env, slf) {
var token = tokens[idx]; var token = tokens[idx];
return `</${tokens[idx].tag}>`; return `</${tokens[idx].tag}>`;
}; }
md.renderer.rules.pres_open = renderOpening; md.renderer.rules.pres_open = renderOpening;
md.renderer.rules.pres_close = renderClosing; md.renderer.rules.pres_close = renderClosing;
@ -64,9 +63,7 @@ export default function revealjs_plugin(md, options) {
function presentationOpen(state) { function presentationOpen(state) {
var token = new state.Token("pres_open", "section", 1); var token = new state.Token("pres_open", "section", 1);
token.block = true; token.block = true;
token.attrs = [ token.attrs = [["class", "reveal"]];
["class", "reveal"]
];
return token; return token;
} }
@ -77,9 +74,7 @@ export default function revealjs_plugin(md, options) {
function slidesOpen(state) { function slidesOpen(state) {
var token = new state.Token("slides_open", "div", 1); var token = new state.Token("slides_open", "div", 1);
token.block = true; token.block = true;
token.attrs = [ token.attrs = [["class", "slides"]];
["class", "slides"]
];
return token; return token;
} }
@ -123,9 +118,11 @@ export default function revealjs_plugin(md, options) {
// in it's own section // in it's own section
if (openSlides === 1) { if (openSlides === 1) {
let slideOpenIdx = previousSlideOpen(state.tokens, divIdx); let slideOpenIdx = previousSlideOpen(state.tokens, divIdx);
state.tokens.splice(slideOpenIdx, 1, state.tokens.splice(
slideOpenIdx,
1,
state.tokens[slideOpenIdx], // reuse it to avoid increasing openSlides state.tokens[slideOpenIdx], // reuse it to avoid increasing openSlides
slideOpen(state) slideOpen(state),
); );
divIdx++; // we added a token before the divider, we need to update divIdx divIdx++; // we added a token before the divider, we need to update divIdx
} }
@ -152,4 +149,4 @@ export default function revealjs_plugin(md, options) {
state.tokens.push(slidesClose(state)); state.tokens.push(slidesClose(state));
state.tokens.push(presentationClose(state)); state.tokens.push(presentationClose(state));
}); });
}; }

View file

@ -5,7 +5,8 @@
importNpmLock, importNpmLock,
vips, vips,
lib, lib,
}: buildNpmPackage { }:
buildNpmPackage {
name = "myblog"; name = "myblog";
version = "unstable"; version = "unstable";
buildInputs = [ buildInputs = [

View file

@ -1,46 +1,51 @@
@font-face { @font-face {
font-family: 'WO3'; font-family: "WO3";
src: url('/fonts/WO3.woff2') format('woff2'), src:
url('/fonts/WO3.ttf') format('ttf'); url("/fonts/WO3.woff2") format("woff2"),
url("/fonts/WO3.ttf") format("ttf");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "wip3out"; font-family: "wip3out";
src: url('/fonts/wip3out.woff2') format('woff2'), src:
url('/fonts/wip3out.ttf') format('ttf'); url("/fonts/wip3out.woff2") format("woff2"),
url("/fonts/wip3out.ttf") format("ttf");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-stretch: semi-expanded; font-stretch: semi-expanded;
} }
@font-face { @font-face {
font-family: 'SometypeMono'; font-family: "SometypeMono";
src: url('/fonts/SometypeMono-Regular.woff2') format('woff2'), src:
url('/fonts/SometypeMono-Regular.ttf') format('ttf') ; url("/fonts/SometypeMono-Regular.woff2") format("woff2"),
url("/fonts/SometypeMono-Regular.ttf") format("ttf");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'SometypeMono'; font-family: "SometypeMono";
src: url('/fonts/SometypeMono-Bold.woff2') format('woff2'), src:
url('/fonts/SometypeMono-Bold.ttf') format('ttf') ; url("/fonts/SometypeMono-Bold.woff2") format("woff2"),
url("/fonts/SometypeMono-Bold.ttf") format("ttf");
font-weight: bold; font-weight: bold;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'SometypeMono'; font-family: "SometypeMono";
src: url('/fonts/SometypeMono-Medium.woff2') format('woff2'), src:
url('/fonts/SometypeMono-Medium.ttf') format('ttf') ; url("/fonts/SometypeMono-Medium.woff2") format("woff2"),
url("/fonts/SometypeMono-Medium.ttf") format("ttf");
font-weight: 500; font-weight: 500;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "Orbitron"; font-family: "Orbitron";
src: url('/fonts/Orbitron-VariableFont_wght.woff2') format('woff2'); src: url("/fonts/Orbitron-VariableFont_wght.woff2") format("woff2");
font-weight: 600; font-weight: 600;
font-style: normal; font-style: normal;
} }

View file

@ -7,8 +7,6 @@
/* Theme colors */ /* Theme colors */
:root { :root {
--text-color-link: var(--color-cyan); --text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright); --text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan); --text-color-link-visited: var(--color-cyan);
@ -18,7 +16,6 @@
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
/* --text-color is assigned to --color-gray-_ above */ /* --text-color is assigned to --color-gray-_ above */
/* --text-color-link: #1493fb; */ /* --text-color-link: #1493fb; */
/* --text-color-link-active: #6969f7; */ /* --text-color-link-active: #6969f7; */
@ -26,11 +23,9 @@
--text-color-link: var(--color-cyan); --text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright); --text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan); --text-color-link-visited: var(--color-cyan);
} }
} }
/* list style custom */ /* list style custom */
ul { ul {
@ -130,7 +125,7 @@ code {
font-family: var(--font-family-monospace); font-family: var(--font-family-monospace);
} }
pre:not([class*="language-"]) { pre:not([class*="language-"]) {
margin: .5em 0; margin: 0.5em 0;
line-height: 1.375; /* 22px /16 */ line-height: 1.375; /* 22px /16 */
-moz-tab-size: var(--syntax-tab-size); -moz-tab-size: var(--syntax-tab-size);
-o-tab-size: var(--syntax-tab-size); -o-tab-size: var(--syntax-tab-size);
@ -154,7 +149,7 @@ code {
/* Header */ /* Header */
header { header {
display: flex; display: flex;
gap: 1em .5em; gap: 1em 0.5em;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
padding: 1em; padding: 1em;
@ -219,8 +214,8 @@ header {
font-size: 1.1875em; /* 19px /16 */ font-size: 1.1875em; /* 19px /16 */
font-weight: 700; font-weight: 700;
flex-basis: calc(100% - 1.5rem); flex-basis: calc(100% - 1.5rem);
padding-left: .25em; padding-left: 0.25em;
padding-right: .5em; padding-right: 0.5em;
text-underline-position: from-font; text-underline-position: from-font;
text-underline-offset: 0; text-underline-offset: 0;
text-decoration-thickness: 1px; text-decoration-thickness: 1px;
@ -245,7 +240,7 @@ header {
.post-metadata { .post-metadata {
display: inline-flex; display: inline-flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: .5em; gap: 0.5em;
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -259,7 +254,7 @@ header {
text-decoration: none; text-decoration: none;
font-style: normal; font-style: normal;
font-size: 1em; font-size: 1em;
margin-left: .1em; margin-left: 0.1em;
} }
a[href].header-anchor, a[href].header-anchor,
a[href].header-anchor:visited { a[href].header-anchor:visited {
@ -277,12 +272,13 @@ a[href].header-anchor:focus,
h2 + .header-anchor { h2 + .header-anchor {
font-size: 1.5em; font-size: 1.5em;
} }
h1, h2, h3 { h1,
h2,
h3 {
font-family: WO3; font-family: WO3;
letter-spacing: .1rem; letter-spacing: 0.1rem;
} }
/* blockquote styling */ /* blockquote styling */
blockquote { blockquote {
@ -299,7 +295,7 @@ blockquote p {
/* Top-left corner bracket */ /* Top-left corner bracket */
blockquote::before { blockquote::before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0.5em; /* Adjust position */ top: 0.5em; /* Adjust position */
left: 0.5em; /* Adjust position */ left: 0.5em; /* Adjust position */
@ -312,7 +308,7 @@ blockquote::before {
/* Bottom-right corner bracket */ /* Bottom-right corner bracket */
blockquote::after { blockquote::after {
content: ''; content: "";
position: absolute; position: absolute;
bottom: 0.5em; /* Adjust position */ bottom: 0.5em; /* Adjust position */
right: 0.5em; /* Adjust position */ right: 0.5em; /* Adjust position */
@ -322,4 +318,3 @@ blockquote::after {
border-right: 3px solid var(--color-white); /* White border, adjust thickness */ border-right: 3px solid var(--color-white); /* White border, adjust thickness */
box-sizing: border-box; box-sizing: border-box;
} }

View file

@ -31,7 +31,7 @@ pre[class*="language-diff-"] {
.token.prefix.inserted, .token.prefix.inserted,
.token.prefix.deleted { .token.prefix.deleted {
width: var(--eleventy-code-padding); width: var(--eleventy-code-padding);
background-color: rgba(0,0,0,.2); background-color: rgba(0, 0, 0, 0.2);
} }
/* Optional: full-width background color */ /* Optional: full-width background color */