1
0
Fork 0

Compare commits

...

5 commits

Author SHA1 Message Date
Saji 1cd021d0a7 lint
All checks were successful
Build Blog / Build (push) Successful in 5m32s
2025-05-02 22:38:37 -05:00
Saji 2fe8b81523 add treefmt 2025-05-02 22:37:18 -05:00
Saji 24d195d07c fix indentation in markdown yaml frontmatter 2025-05-02 22:26:41 -05:00
Saji 7d3b28919d cleanup and restructure bundle resources 2025-05-02 22:26:41 -05:00
Saji 3d95f440cc update gitignore for jj 2025-05-02 21:40:54 -05:00
32 changed files with 969 additions and 903 deletions

View file

@ -1,7 +1,7 @@
root = true
[*]
indent_style = tab
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true

3
.gitignore vendored
View file

@ -13,3 +13,6 @@ result
/styles/config/vocabularies/*
!/styles/config/vocabularies/Base
.jj/

2
.ignore Normal file
View file

@ -0,0 +1,2 @@
.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
* [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:

View file

@ -1,92 +1,92 @@
export default {
dark: {
"source": "rootloops.sh",
"hex": {
"background": "#0a0808",
"foreground": "#e7e1e2",
"cursor": "#c2b5b8",
"black": "#272022",
"red": "#cf5080",
"green": "#7b8d39",
"yellow": "#b57235",
"blue": "#3c8cbf",
"magenta": "#9468d8",
"cyan": "#3b9586",
"white": "#c2b5b8",
"brightBlack": "#5a4e51",
"brightRed": "#e26b95",
"brightGreen": "#8fa445",
"brightYellow": "#cf8544",
"brightBlue": "#4fa1d7",
"brightMagenta": "#a782e5",
"brightCyan": "#47ac9b",
"brightWhite": "#f3f0f1"
source: "rootloops.sh",
hex: {
background: "#0a0808",
foreground: "#e7e1e2",
cursor: "#c2b5b8",
black: "#272022",
red: "#cf5080",
green: "#7b8d39",
yellow: "#b57235",
blue: "#3c8cbf",
magenta: "#9468d8",
cyan: "#3b9586",
white: "#c2b5b8",
brightBlack: "#5a4e51",
brightRed: "#e26b95",
brightGreen: "#8fa445",
brightYellow: "#cf8544",
brightBlue: "#4fa1d7",
brightMagenta: "#a782e5",
brightCyan: "#47ac9b",
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: {
"source": "rootloops.sh",
"hex": {
"background": "#f3f0f1",
"foreground": "#40373a",
"cursor": "#74666a",
"black": "#e7e1e2",
"red": "#cf5080",
"green": "#7b8d39",
"yellow": "#b57235",
"blue": "#3c8cbf",
"magenta": "#9468d8",
"cyan": "#3b9586",
"white": "#74666a",
"brightBlack": "#b5a7ab",
"brightRed": "#e26b95",
"brightGreen": "#8fa445",
"brightYellow": "#cf8544",
"brightBlue": "#4fa1d7",
"brightMagenta": "#a782e5",
"brightCyan": "#47ac9b",
"brightWhite": "#272022"
source: "rootloops.sh",
hex: {
background: "#f3f0f1",
foreground: "#40373a",
cursor: "#74666a",
black: "#e7e1e2",
red: "#cf5080",
green: "#7b8d39",
yellow: "#b57235",
blue: "#3c8cbf",
magenta: "#9468d8",
cyan: "#3b9586",
white: "#74666a",
brightBlack: "#b5a7ab",
brightRed: "#e26b95",
brightGreen: "#8fa445",
brightYellow: "#cf8544",
brightBlue: "#4fa1d7",
brightMagenta: "#a782e5",
brightCyan: "#47ac9b",
brightWhite: "#272022",
},
"hsl": {
"background": "hsl(345.03, 10.68%, 94.62%)",
"foreground": "hsl(344.11, 7.75%, 23.41%)",
"cursor": "hsl(344.2, 6.49%, 42.82%)",
"black": "hsl(344.94, 10.53%, 89.29%)",
"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.2, 6.49%, 42.82%)",
"brightBlack": "hsl(344.53, 8.76%, 68.33%)",
"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(344.1, 8.9%, 13.97%)"
}
hsl: {
background: "hsl(345.03, 10.68%, 94.62%)",
foreground: "hsl(344.11, 7.75%, 23.41%)",
cursor: "hsl(344.2, 6.49%, 42.82%)",
black: "hsl(344.94, 10.53%, 89.29%)",
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.2, 6.49%, 42.82%)",
brightBlack: "hsl(344.53, 8.76%, 68.33%)",
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(344.1, 8.9%, 13.97%)",
},
}
},
};

View file

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

View file

@ -7,19 +7,23 @@
<title>{{ title or metadata.title }}</title>
<meta name="description" content="{{ description or metadata.description }}">
<!--
<link rel="stylesheet" href="dist/reset.css">
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/black.css">
<link rel="stylesheet" href="plugin/highlight/monokai.css">
-->
<style>{% getBundle "css" %}</style>
<link rel="stylesheet" href="{% getBundleFileUrl "css" %}">
<script src="{% getBundleFileUrl "js" %}"></script>
{%- css %}{% include "node_modules/reveal.js/dist/reset.css" %} {% endcss %}
{%- css %}{% include "node_modules/reveal.js/dist/reveal.css" %} {% endcss %}
{%- css %}{% include "node_modules/reveal.js/dist/theme/black.css" %} {% endcss %}
{%- css %}{% include "node_modules/reveal.js/plugin/highlight/monokai.css" %} {% endcss %}
{%- js %}
{% includeRaw "node_modules/reveal.js/dist/reveal.js" %}
{% includeRaw "node_modules/reveal.js/plugin/notes/notes.js" %}
{% includeRaw "node_modules/reveal.js/plugin/highlight/highlight.js" %}
Reveal.initialize({
hash: true,
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealHighlight, RevealNotes ]
});
{%- endjs %}
</head>
<body>
<div class="reveal">
@ -27,22 +31,5 @@
{{ content | safe }}
</div>
</div>
<script>
{% include "node_modules/reveal.js/dist/reveal.js" %}
{% include "node_modules/reveal.js/plugin/notes/notes.js" %}
</script>
<script src="/revealjs-plugins/highlight/highlight.js"></script>
<script>
// More info about initialization & config:
// - https://revealjs.com/initialization/
// - https://revealjs.com/config/
Reveal.initialize({
hash: true,
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealHighlight, RevealNotes ]
});
</script>
</body>
</html>

View file

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

View file

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

View file

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

View file

@ -1,6 +1,4 @@
export default {
tags: [
"posts"
],
"layout": "layouts/post.njk",
tags: ["posts"],
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.
Case in point:
```
/"[a-z]{4}(?: [a-z]{4}){3}"/ language:Python SMTP
```

View file

@ -13,7 +13,7 @@ Part one focuses on the image format.
# 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.
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
@ -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
scratch.
# Getting the picture
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,
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
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
like servers or embedded-ish machines.
We can use NixOS and nixpkgs to derive a fully defined operating system and services.
Then, we can:
@ -118,12 +117,10 @@ $ ls /nix/store | grep myblog
mqhssdlmg9f03avpajwcqaah2apknl02-myblog
```
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
add a user here that we can use to deploy.
```nix
{
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
in remotely.
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
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:
```nix
custom = {
__functor = customSelf: base: activate:
@ -246,7 +241,6 @@ custom = {
};
```
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,
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
exists to make rollbacks easier.
# End
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.
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
---
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
with certain wires being connected at different coordinates:
@ -41,11 +39,9 @@ DESCRIPTION
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?
Then we could take advantage of the simulation and synthesis capabilities, which would make these chips a bit more useful.
# 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.
@ -54,20 +50,15 @@ 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.
## Yosys Technology Mapping
This process is cursed and low level, but it works a little like this:
- Yosys takes a generic synthesis pass at a netlist, which simplifies the design without looking at any physical implementation
- The simplified design is then mapped into Verilog-based "blocks", which could be things like AND gates, or complex blocks like a LUT.
This process turns generic blocks like an adder into a set of gates.
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`
into a single `mul_add` block.
into a single `mul_add` block.
## A history lesson
@ -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
removes the need for a level shifter.
In practice, this isn't all great. Programming GALs is an exercise in frustration.
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
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
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
the chip, and even integrate our designs with FPGAs later down the line.
# The idea
GAL assembly appears occasionally when working with older systems, especially in a retro emulation context.
# Is this useful?
Not particularly.

View file

@ -15,8 +15,8 @@ C interop, interesting features, and fast. Some of the headlines:
- `comptime`: Lispers are in shambles. Run Zig code to generate Zig code.
- Cross compilation. For real. Not Rust cross where you need to find a sysroot.
Not Go cross locking you out of CGO. You can literally change a variable and make
a Mac binary on Windows like it's nothing.
Not Go cross locking you out of CGO. You can literally change a variable and make
a Mac binary on Windows like it's nothing.
- Web target, both WASI and freestanding.
- SIMD Vectors? We take those I guess.
- It's got a build/test system built in. More on that later.

View file

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

View file

@ -1,11 +1,9 @@
<section>
<h2>Object (dis)orientation</h2>
<h6> How to survive a "post-OO" world</h6>
<h6>How to survive a "post-OO" world</h6>
</section>
<section>
<section>
Class/Inheritance has challenges
</section>
<section>Class/Inheritance has challenges</section>
<section>
Combining Data + Code
<pre><code data-trim data-noescape data-line-numbers="5|7-9">
@ -20,21 +18,32 @@
};
};
</code></pre>
<p class="fragment">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>
<p class="fragment">
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>
<p> A class can be one of many things </p>
<p>A class can be one of many things</p>
<ul>
<li>Interface - abstract class with no members, and purely virtual </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>Interface - abstract class with no members, and purely virtual</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>
</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>
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>
@ -63,10 +72,9 @@
}
}
</code></pre>
</section>
<section>
<p> And can be used statically <em>or</em> dynamically!</p>
<p>And can be used statically <em>or</em> dynamically!</p>
<pre><code data-trim>
fn static_mammal&lt;T: Mammal&gt;(m: T) {
// the type of T must be known at compile time.
@ -77,4 +85,3 @@
</code></pre>
</section>
</section>

View file

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

View file

@ -3,43 +3,49 @@ export function eleventyComputedPermalink() {
// `addGlobalData` acts like a global data file and runs the top level function it receives.
return (data) => {
// Always skip during non-watch/serve builds
if(data.draft && !process.env.BUILD_DRAFTS) {
if (data.draft && !process.env.BUILD_DRAFTS) {
return false;
}
return data.permalink;
}
};
};
}
export function eleventyComputedExcludeFromCollections() {
// When using `addGlobalData` and you *want* to return a function, you must nest functions like this.
// `addGlobalData` acts like a global data file and runs the top level function it receives.
return (data) => {
// Always exclude from non-watch/serve builds
if(data.draft && !process.env.BUILD_DRAFTS) {
if (data.draft && !process.env.BUILD_DRAFTS) {
return true;
}
return data.eleventyExcludeFromCollections;
}
};
};
}
export default function (eleventyConfig) {
eleventyConfig.addGlobalData("eleventyComputed.permalink", eleventyComputedPermalink);
eleventyConfig.addGlobalData("eleventyComputed.eleventyExcludeFromCollections", eleventyComputedExcludeFromCollections);
eleventyConfig.addGlobalData(
"eleventyComputed.permalink",
eleventyComputedPermalink,
);
eleventyConfig.addGlobalData(
"eleventyComputed.eleventyExcludeFromCollections",
eleventyComputedExcludeFromCollections,
);
let logged = false;
eleventyConfig.on("eleventy.before", ({runMode}) => {
eleventyConfig.on("eleventy.before", ({ runMode }) => {
let text = "Excluding";
// Only show drafts in serve/watch modes
if(runMode === "serve" || runMode === "watch") {
if (runMode === "serve" || runMode === "watch") {
process.env.BUILD_DRAFTS = true;
text = "Including";
}
// Only log once.
if(!logged) {
console.log( `[11ty/eleventy-base-blog] ${text} drafts.` );
if (!logged) {
console.log(`[11ty/eleventy-base-blog] ${text} drafts.`);
}
logged = true;

View file

@ -11,20 +11,17 @@ import pluginNavigation from "@11ty/eleventy-navigation";
import { EleventyHtmlBasePlugin } from "@11ty/eleventy";
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
import pluginDrafts from "./eleventy.config.drafts.js";
import fs from "fs/promises";
/** @param {import('@11ty/eleventy').UserConfig} eleventyConfig */
export default async function(eleventyConfig) {
export default async function (eleventyConfig) {
// Copy the contents of the `public` folder to the output folder
// For example, `./public/css/` ends up in `_site/css/`
eleventyConfig.addPassthroughCopy({
"./public/": "/",
"./node_modules/prismjs/themes/prism-okaidia.css": "/css/prism-okaidia.css",
"./node_modules/katex/dist/fonts/": "/bundle/fonts/",
"./node_modules/reveal.js/dist/": "/revealjs/",
"./node_modules/reveal.js/plugin/": "/revealjs-plugins/",
"./node_modules/katex/dist/fonts/": "/bundle/fonts/", // needs to be here for css reasons.
});
// Watch content images for the image pipeline.
@ -34,30 +31,27 @@ export default async function(eleventyConfig) {
eleventyConfig.addPlugin(pluginDrafts);
// eleventyConfig.addPlugin(pluginFonts);
// Official plugins
let mkFeed = function(type, path) {
let mkFeed = function (type, path) {
eleventyConfig.addPlugin(feedPlugin, {
type: type,
outputPath: path,
collection: {
name: "posts",
limit: 0
limit: 0,
},
metadata: {
title: "Musings",
base: "https://saji.dev",
language: "en",
},
});
};
mkFeed("rss", "/rss.feed.xml");
mkFeed("atom", "/atom.feed.xml");
mkFeed("json", "/feed.json");
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
preAttributes: { tabindex: 0 }
preAttributes: { tabindex: 0 },
});
eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
@ -70,20 +64,20 @@ export default async function(eleventyConfig) {
loading: "lazy",
decoding: "async",
sizes: "auto",
}
})
},
});
// Filters
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => {
// 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
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.
@ -104,21 +98,23 @@ export default async function(eleventyConfig) {
});
// Return all the tags used in a collection
eleventyConfig.addFilter("getAllTags", collection => {
eleventyConfig.addFilter("getAllTags", (collection) => {
let tagSet = new Set();
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);
});
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;
// Customize Markdown library settings:
eleventyConfig.amendLibrary("md", mdLib => {
eleventyConfig.amendLibrary("md", (mdLib) => {
// hack to let us use the markdown renderer for later.
mdIt = mdLib;
mdLib.use(markdownItAnchor, {
@ -129,21 +125,25 @@ export default async function(eleventyConfig) {
ariaHidden: false,
}),
level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slugify")
slugify: eleventyConfig.getFilter("slugify"),
});
mdLib.use(markdownItAbbr);
mdLib.use(markdownItKatex.default);
});
eleventyConfig.addShortcode("currentBuildDate", () => {
return (new Date()).toISOString();
})
return new Date().toISOString();
});
eleventyConfig.addShortcode("includeRaw", async (file) =>
fs.readFile(file, "utf8"),
);
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
//})
//
eleventyConfig.addPairedShortcode("callout", function(content) {
eleventyConfig.addPairedShortcode("callout", function (content) {
// The 'content' variable holds the text/HTML placed between
// {% callout %} and {% endcallout %}
// We wrap it with our div structure.
@ -165,12 +165,7 @@ export default async function(eleventyConfig) {
return {
// Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: [
"md",
"njk",
"html",
"liquid",
],
templateFormats: ["md", "njk", "html", "liquid"],
// Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: "njk",
@ -183,7 +178,7 @@ export default async function(eleventyConfig) {
input: "content", // default: "."
includes: "../_includes", // default: "_includes"
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.
pathPrefix: "/",
};
};
}

View file

@ -68,10 +68,28 @@
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1745377448,
"narHash": "sha256-jhZDfXVKdD7TSEGgzFJQvEEZ2K65UMiqW5YJ2aIqxMA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "507b63021ada5fee621b6ca371c4fca9ca46f52c",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"deploy-rs": "deploy-rs",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs_2",
"systems": "systems_2",
"treefmt-nix": "treefmt-nix"
}
},
"systems": {
@ -89,6 +107,38 @@
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"id": "systems",
"type": "indirect"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1746216483,
"narHash": "sha256-4h3s1L/kKqt3gMDcVfN8/4v2jqHrgLIe4qok4ApH5x4=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "29ec5026372e0dec56f890e50dbe4f45930320fd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"

View file

@ -3,34 +3,45 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
deploy-rs.url = "github:serokell/deploy-rs";
treefmt-nix.url = "github:numtide/treefmt-nix";
};
outputs = { self, nixpkgs, deploy-rs }: let
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
forAllSystems = function:
nixpkgs.lib.genAttrs systems (system: function (
import nixpkgs {
inherit system;
config.allowUnfree = true;
}
));
outputs =
{
self,
nixpkgs,
deploy-rs,
systems,
treefmt-nix,
}:
let
# systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
forAllSystems =
fn: nixpkgs.lib.genAttrs (import systems) (system: fn nixpkgs.legacyPackages.${system});
in {
treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
in
{
apps = forAllSystems (pkgs: {
"deploy" = {
type = "app";
program = let
ci = (pkgs.writeShellApplication {
program =
let
ci = (
pkgs.writeShellApplication {
name = "ci.sh";
text = ''
nix build
'';
});
in "${ci}/ci.sh";
}
);
in
"${ci}/ci.sh";
};
});
packages = forAllSystems (pkgs: {
default = pkgs.callPackage ./package.nix {};
default = pkgs.callPackage ./package.nix { };
});
deploy.nodes.myblog = {
hostname = "saji.dev";
@ -44,6 +55,12 @@
};
};
formatter = forAllSystems (pkgs: treefmtEval.${pkgs.system}.config.build.wrapper);
checks = forAllSystems (pkgs: {
formatting = treefmtEval.${pkgs.system}.config.build.check self;
});
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
packages = with pkgs; [

View file

@ -7,36 +7,40 @@ export default function figcaptionPlugin(md) {
for (let i = 0; i < tokens.length; i++) {
// Check for paragraph containing only an image
if (
tokens[i].type === 'paragraph_open' &&
tokens[i].type === "paragraph_open" &&
i + 2 < tokens.length &&
tokens[i + 1].type === 'inline' &&
tokens[i + 1].type === "inline" &&
tokens[i + 1].children &&
tokens[i + 1].children.length === 1 &&
tokens[i + 1].children[0].type === 'image' &&
tokens[i + 2].type === 'paragraph_close'
tokens[i + 1].children[0].type === "image" &&
tokens[i + 2].type === "paragraph_close"
) {
// Check if the next token is a paragraph starting with emphasis
if (
i + 5 < tokens.length &&
tokens[i + 3].type === 'paragraph_open' &&
tokens[i + 4].type === 'inline' &&
tokens[i + 3].type === "paragraph_open" &&
tokens[i + 4].type === "inline" &&
tokens[i + 4].children &&
tokens[i + 4].children.length > 0 &&
tokens[i + 4].children[0].type === 'em_open' &&
tokens[i + 5].type === 'paragraph_close'
tokens[i + 4].children[0].type === "em_open" &&
tokens[i + 5].type === "paragraph_close"
) {
figcaptionStartIndex = i + 3; // Start index of the caption paragraph
// --- Replace tokens ---
// 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
// 2. Keep the inline token with the image as is (tokens[i+1])
// 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
// 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
const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice
if (
captionInlineToken.children[0].type === 'em_open' &&
captionInlineToken.children[captionInlineToken.children.length - 1].type === 'em_close'
captionInlineToken.children[0].type === "em_open" &&
captionInlineToken.children[captionInlineToken.children.length - 1]
.type === "em_close"
) {
captionInlineToken.children.shift(); // Remove em_open
captionInlineToken.children.pop(); // Remove em_close
}
// Adjust level for caption content
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
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)
// 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)
// 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
md.renderer.rules.figure_open = md.renderer.rules.figure_open || function () { 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'; };
md.renderer.rules.figure_open =
md.renderer.rules.figure_open ||
function () {
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 VSEP = "===";
/**
* 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) {
var token = tokens[idx];
return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`;
};
}
function renderClosing(tokens, idx, options, env, slf) {
var token = tokens[idx];
return `</${tokens[idx].tag}>`;
};
}
md.renderer.rules.pres_open = renderOpening;
md.renderer.rules.pres_close = renderClosing;
@ -64,9 +63,7 @@ export default function revealjs_plugin(md, options) {
function presentationOpen(state) {
var token = new state.Token("pres_open", "section", 1);
token.block = true;
token.attrs = [
["class", "reveal"]
];
token.attrs = [["class", "reveal"]];
return token;
}
@ -77,9 +74,7 @@ export default function revealjs_plugin(md, options) {
function slidesOpen(state) {
var token = new state.Token("slides_open", "div", 1);
token.block = true;
token.attrs = [
["class", "slides"]
];
token.attrs = [["class", "slides"]];
return token;
}
@ -97,7 +92,7 @@ export default function revealjs_plugin(md, options) {
return new state.Token("slide_close", "section", -1);
}
md.core.ruler.push("revealjs", function(state) {
md.core.ruler.push("revealjs", function (state) {
let divIdx = 0;
while (true) {
divIdx = nextDivider(state.tokens, divIdx);
@ -123,9 +118,11 @@ export default function revealjs_plugin(md, options) {
// in it's own section
if (openSlides === 1) {
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
slideOpen(state)
slideOpen(state),
);
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(presentationClose(state));
});
};
}

View file

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

View file

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

View file

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

View file

@ -31,7 +31,7 @@ pre[class*="language-diff-"] {
.token.prefix.inserted,
.token.prefix.deleted {
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 */

6
treefmt.nix Normal file
View file

@ -0,0 +1,6 @@
{ pkgs, ... }:
{
projectRootFile = "flake.nix";
programs.nixfmt.enable = true;
programs.prettier.enable = true;
}