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:
@ -46,38 +46,38 @@ Or you can run [debug mode](https://www.11ty.dev/docs/debugging/) to see all the
## Features
- Using [Eleventy v2.0](https://www.11ty.dev/blog/eleventy-v2/) with zero-JavaScript output.
- Content is exclusively pre-rendered (this is a static site).
- Can easily [deploy to a subfolder without changing any content](https://www.11ty.dev/docs/plugins/html-base/)
- All URLs are decoupled from the contents location on the file system.
- Configure templates via the [Eleventy Data Cascade](https://www.11ty.dev/docs/data-cascade/)
- Content is exclusively pre-rendered (this is a static site).
- Can easily [deploy to a subfolder without changing any content](https://www.11ty.dev/docs/plugins/html-base/)
- All URLs are decoupled from the contents location on the file system.
- Configure templates via the [Eleventy Data Cascade](https://www.11ty.dev/docs/data-cascade/)
- **Performance focused**: four-hundos Lighthouse score out of the box!
- [View the Lighthouse report for the latest build](https://eleventy-base-blog.netlify.app/reports/lighthouse/) courtesy of the [Netlify Lighthouse plugin](https://github.com/netlify/netlify-plugin-lighthouse).
- _0 Cumulative Layout Shift_
- _0ms Total Blocking Time_
- [View the Lighthouse report for the latest build](https://eleventy-base-blog.netlify.app/reports/lighthouse/) courtesy of the [Netlify Lighthouse plugin](https://github.com/netlify/netlify-plugin-lighthouse).
- _0 Cumulative Layout Shift_
- _0ms Total Blocking Time_
- Local development live reload provided by [Eleventy Dev Server](https://www.11ty.dev/docs/dev-server/).
- Content-driven [navigation menu](https://www.11ty.dev/docs/plugins/navigation/)
- [Image optimization](https://www.11ty.dev/docs/plugins/image/) via the `{% image %}` shortcode.
- Zero-JavaScript output.
- Support for modern image formats automatically (e.g. AVIF and WebP)
- Prefers `<img>` markup if possible (single image format) but switches automatically to `<picture>` for multiple image formats.
- Automated `<picture>` syntax markup with `srcset` and optional `sizes`
- Includes `width`/`height` attributes to avoid [content layout shift](https://web.dev/cls/).
- Includes `loading="lazy"` for native lazy loading without JavaScript.
- Includes [`decoding="async"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding)
- Images can be co-located with blog post files.
- View the [Image plugin source code](https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.images.js)
- Zero-JavaScript output.
- Support for modern image formats automatically (e.g. AVIF and WebP)
- Prefers `<img>` markup if possible (single image format) but switches automatically to `<picture>` for multiple image formats.
- Automated `<picture>` syntax markup with `srcset` and optional `sizes`
- Includes `width`/`height` attributes to avoid [content layout shift](https://web.dev/cls/).
- Includes `loading="lazy"` for native lazy loading without JavaScript.
- Includes [`decoding="async"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding)
- Images can be co-located with blog post files.
- View the [Image plugin source code](https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.images.js)
- Per page CSS bundles [via `eleventy-plugin-bundle`](https://github.com/11ty/eleventy-plugin-bundle).
- Built-in [syntax highlighter](https://www.11ty.dev/docs/plugins/syntaxhighlight/) (zero-JavaScript output).
- Blog Posts
- Draft posts: use `draft: true` to mark a blog post as a draft. Drafts are **only** included during `--serve`/`--watch` and are excluded from full builds. View the [Drafts plugin source code](https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.drafts.js).
- Automated next/previous links
- Accessible deep links to headings
- Draft posts: use `draft: true` to mark a blog post as a draft. Drafts are **only** included during `--serve`/`--watch` and are excluded from full builds. View the [Drafts plugin source code](https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.drafts.js).
- Automated next/previous links
- Accessible deep links to headings
- Generated Pages
- Home, Archive, and About pages.
- [Feeds for Atom and JSON](https://www.11ty.dev/docs/plugins/rss/)
- `sitemap.xml`
- Zero-maintenance tag pages ([View on the Demo](https://eleventy-base-blog.netlify.app/tags/))
- Content not found (404) page
- Home, Archive, and About pages.
- [Feeds for Atom and JSON](https://www.11ty.dev/docs/plugins/rss/)
- `sitemap.xml`
- Zero-maintenance tag pages ([View on the Demo](https://eleventy-base-blog.netlify.app/tags/))
- Content not found (404) page
## Demos
@ -105,12 +105,12 @@ Deploy this Eleventy site in just a few clicks on these services:
- Content can be in _any template format_ (blog posts neednt exclusively be markdown, for example). Configure your projects supported templates in `eleventy.config.js` -> `templateFormats`.
- The `public` folder in your input directory will be copied to the output folder (via `addPassthroughCopy` in the `eleventy.config.js` file). This means `./public/css/*` will live at `./_site/css/*` after your build completes.
- Provides two content feeds:
- `content/feed/feed.njk`
- `content/feed/json.njk`
- `content/feed/feed.njk`
- `content/feed/json.njk`
- This project uses three [Eleventy Layouts](https://www.11ty.dev/docs/layouts/):
- `_includes/layouts/base.njk`: the top level HTML structure
- `_includes/layouts/home.njk`: the home page template (wrapped into `base.njk`)
- `_includes/layouts/post.njk`: the blog post template (wrapped into `base.njk`)
- `_includes/layouts/base.njk`: the top level HTML structure
- `_includes/layouts/home.njk`: the home page template (wrapped into `base.njk`)
- `_includes/layouts/post.njk`: the blog post template (wrapped into `base.njk`)
- `_includes/postslist.njk` is a Nunjucks include and is a reusable component used to display a list of all the posts. `content/index.njk` has an example of how to use it.
#### Content Security Policy

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"
},
"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"
},
"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%)"
}
},
}
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",
},
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",
},
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

@ -1,11 +1,11 @@
export default {
title: "Musings",
url: "https://saji.dev/",
language: "en",
description: "Random notes and ideas.",
author: {
name: "Saji",
email: "saji@saji.dev",
url: "https://saji.dev/about-me/"
}
}
title: "Musings",
url: "https://saji.dev/",
language: "en",
description: "Random notes and ideas.",
author: {
name: "Saji",
email: "saji@saji.dev",
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

@ -2,8 +2,8 @@
title: Abusing regex in GitHub code search
date: 2025-04-24
tags:
- short
- cybersec
- short
- cybersec
---
I recently discovered that the new(ish) GitHub Code Search feature
@ -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

@ -2,8 +2,8 @@
title: Reversing an image format
date: 2025-01-15
tags:
- reverse-engineering
- hacking-hikmicro
- reverse-engineering
- hacking-hikmicro
drafts: true
---
@ -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

@ -113,7 +113,7 @@ This highlights the two styles of troubleshooting:
2. Hypothesis -> Validate -> Solution
It feels pretty good to zero-shot a repair or fix, but it also can leave you
with blind spots. In my case, I didn't want to try to probe the board while
with blind spots. In my case, I didn't want to try to probe the board while
hot, but later it became necessary unless I just started replacing chips
indiscriminately. Once I started probing, I wanted to find something that
seemed "off" before going further. I would have noticed the character RAM
@ -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,14 +1,12 @@
<section>
<h2>Object (dis)orientation</h2>
<h6> How to survive a "post-OO" world</h6>
<h2>Object (dis)orientation</h2>
<h6>How to survive a "post-OO" world</h6>
</section>
<section>
<section>
Class/Inheritance has challenges
</section>
<section>
Combining Data + Code
<pre><code data-trim data-noescape data-line-numbers="5|7-9">
<section>Class/Inheritance has challenges</section>
<section>
Combining Data + Code
<pre><code data-trim data-noescape data-line-numbers="5|7-9">
class MyBase {
protected:
int useless;
@ -20,35 +18,46 @@
};
};
</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>
</section>
<section>
<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>
</ul>
<small class="fragment">These concepts all exist under the <code>class</code> keyword </small>
</section>
<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>
<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>
</ul>
<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>
<section>
<p>Traits are <em>Interfaces</em></p>
<pre><code data-trim data-noescape>
<section>
<p>Traits are <em>Interfaces</em></p>
<pre><code data-trim data-noescape>
pub trait Mammal {
fn get_temp(&self) -> i32;
}
</code></pre>
</section>
<section>
<p>... that are explicitly implemented</p>
<pre><code data-trim data-noescape>
</section>
<section>
<p>... that are explicitly implemented</p>
<pre><code data-trim data-noescape>
pub struct Cat {
age: i32,
outdoor: bool,
@ -63,11 +72,10 @@
}
}
</code></pre>
</section>
<section>
<p> And can be used statically <em>or</em> dynamically!</p>
<pre><code data-trim>
</section>
<section>
<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.
}
@ -75,6 +83,5 @@
// This uses a vtable to dispatch at runtime.
}
</code></pre>
</section>
</section>
</section>

View file

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

View file

@ -1,47 +1,53 @@
export function eleventyComputedPermalink() {
// 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 skip during non-watch/serve builds
if(data.draft && !process.env.BUILD_DRAFTS) {
return false;
}
// 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 skip during non-watch/serve builds
if (data.draft && !process.env.BUILD_DRAFTS) {
return false;
}
return data.permalink;
}
};
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) {
return true;
}
// 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) {
return true;
}
return data.eleventyExcludeFromCollections;
}
};
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}) => {
let text = "Excluding";
// Only show drafts in serve/watch modes
if(runMode === "serve" || runMode === "watch") {
process.env.BUILD_DRAFTS = true;
text = "Including";
}
let logged = false;
eleventyConfig.on("eleventy.before", ({ runMode }) => {
let text = "Excluding";
// Only show drafts in serve/watch modes
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.` );
}
// Only log once.
if (!logged) {
console.log(`[11ty/eleventy-base-blog] ${text} drafts.`);
}
logged = true;
});
logged = true;
});
}

View file

@ -11,191 +11,186 @@ 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) {
// 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/",
});
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/katex/dist/fonts/": "/bundle/fonts/", // needs to be here for css reasons.
});
// Watch content images for the image pipeline.
eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpeg}");
// Watch content images for the image pipeline.
eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpeg}");
// App plugins
eleventyConfig.addPlugin(pluginDrafts);
// eleventyConfig.addPlugin(pluginFonts);
// Official plugins
let mkFeed = function(type, path) {
eleventyConfig.addPlugin(feedPlugin, {
type: type,
outputPath: path,
collection: {
name: "posts",
limit: 0
},
metadata: {
title: "Musings",
base: "https://saji.dev",
language: "en",
// App plugins
eleventyConfig.addPlugin(pluginDrafts);
// eleventyConfig.addPlugin(pluginFonts);
// Official plugins
let mkFeed = function (type, path) {
eleventyConfig.addPlugin(feedPlugin, {
type: type,
outputPath: path,
collection: {
name: "posts",
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 },
});
eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
eleventyConfig.addPlugin(pluginBundle);
eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
extensions: "html",
formats: ["webp", "jpeg", null],
widths: [400, 800, 1280, null],
defaultAttributes: {
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",
);
});
};
mkFeed("rss", "/rss.feed.xml");
mkFeed("atom", "/atom.feed.xml");
mkFeed("json", "/feed.json");
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");
});
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
preAttributes: { tabindex: 0 }
});
eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
eleventyConfig.addPlugin(pluginBundle);
eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
extensions: "html",
formats: ["webp", "jpeg", null],
widths: [400, 800, 1280, null],
defaultAttributes: {
loading: "lazy",
decoding: "async",
sizes: "auto",
}
})
// Get the first `n` elements of a collection.
eleventyConfig.addFilter("head", (array, n) => {
if (!Array.isArray(array) || array.length === 0) {
return [];
}
if (n < 0) {
return array.slice(n);
}
return array.slice(0, n);
});
// Return the smallest number argument
eleventyConfig.addFilter("min", (...numbers) => {
return Math.min.apply(null, numbers);
});
// 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 all the tags used in a collection
eleventyConfig.addFilter("getAllTags", (collection) => {
let tagSet = new Set();
for (let item of collection) {
(item.data.tags || []).forEach((tag) => tagSet.add(tag));
}
return Array.from(tagSet);
});
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');
});
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
return (tags || []).filter(
(tag) => ["all", "nav", "post", "posts"].indexOf(tag) === -1,
);
});
// Get the first `n` elements of a collection.
eleventyConfig.addFilter("head", (array, n) => {
if (!Array.isArray(array) || array.length === 0) {
return [];
}
if (n < 0) {
return array.slice(n);
}
let mdIt;
// Customize Markdown library settings:
eleventyConfig.amendLibrary("md", (mdLib) => {
// hack to let us use the markdown renderer for later.
mdIt = mdLib;
mdLib.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: "after",
class: "header-anchor",
symbol: "@",
ariaHidden: false,
}),
level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slugify"),
});
mdLib.use(markdownItAbbr);
mdLib.use(markdownItKatex.default);
});
return array.slice(0, n);
});
eleventyConfig.addShortcode("currentBuildDate", () => {
return new Date().toISOString();
});
// Return the smallest number argument
eleventyConfig.addFilter("min", (...numbers) => {
return Math.min.apply(null, numbers);
});
eleventyConfig.addShortcode("includeRaw", async (file) =>
fs.readFile(file, "utf8"),
);
// Return all the tags used in a collection
eleventyConfig.addFilter("getAllTags", collection => {
let tagSet = new Set();
for (let item of collection) {
(item.data.tags || []).forEach(tag => tagSet.add(tag));
}
return Array.from(tagSet);
});
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
//})
//
eleventyConfig.addPairedShortcode("callout", function (content) {
// The 'content' variable holds the text/HTML placed between
// {% callout %} and {% endcallout %}
// We wrap it with our div structure.
return `<div class="callout">${content}</div>`;
});
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
return (tags || []).filter(tag => ["all", "nav", "post", "posts"].indexOf(tag) === -1);
});
eleventyConfig.addBundle("css");
eleventyConfig.addBundle("js");
eleventyConfig.addBundle("html");
let mdIt;
// Customize Markdown library settings:
eleventyConfig.amendLibrary("md", mdLib => {
// hack to let us use the markdown renderer for later.
mdIt = mdLib;
mdLib.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: "after",
class: "header-anchor",
symbol: "@",
ariaHidden: false,
}),
level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slugify")
});
mdLib.use(markdownItAbbr);
mdLib.use(markdownItKatex.default);
});
// Features to make your build faster (when you need them)
eleventyConfig.addShortcode("currentBuildDate", () => {
return (new Date()).toISOString();
})
// If your passthrough copy gets heavy and cumbersome, add this line
// to emulate the file copy on the dev server. Learn more:
// https://www.11ty.dev/docs/copy/#emulate-passthrough-copy-during-serve
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
//})
//
eleventyConfig.addPairedShortcode("callout", function(content) {
// The 'content' variable holds the text/HTML placed between
// {% callout %} and {% endcallout %}
// We wrap it with our div structure.
return `<div class="callout">${content}</div>`;
});
// eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
eleventyConfig.addBundle("css");
eleventyConfig.addBundle("js");
eleventyConfig.addBundle("html");
return {
// Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: ["md", "njk", "html", "liquid"],
// Features to make your build faster (when you need them)
// Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: "njk",
// If your passthrough copy gets heavy and cumbersome, add this line
// to emulate the file copy on the dev server. Learn more:
// https://www.11ty.dev/docs/copy/#emulate-passthrough-copy-during-serve
// Pre-process *.html files with: (default: `liquid`)
htmlTemplateEngine: "njk",
// eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
// These are all optional:
dir: {
input: "content", // default: "."
includes: "../_includes", // default: "_includes"
data: "../_data", // default: "_data"
output: "_site",
},
return {
// Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: [
"md",
"njk",
"html",
"liquid",
],
// -----------------------------------------------------------------
// Optional items:
// -----------------------------------------------------------------
// Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: "njk",
// If your site deploys to a subdirectory, change `pathPrefix`.
// Read more: https://www.11ty.dev/docs/config/#deploy-to-a-subdirectory-with-a-path-prefix
// Pre-process *.html files with: (default: `liquid`)
htmlTemplateEngine: "njk",
// These are all optional:
dir: {
input: "content", // default: "."
includes: "../_includes", // default: "_includes"
data: "../_data", // default: "_data"
output: "_site"
},
// -----------------------------------------------------------------
// Optional items:
// -----------------------------------------------------------------
// If your site deploys to a subdirectory, change `pathPrefix`.
// Read more: https://www.11ty.dev/docs/config/#deploy-to-a-subdirectory-with-a-path-prefix
// When paired with the HTML <base> plugin https://www.11ty.dev/docs/plugins/html-base/
// it will transform any absolute URLs in your HTML to include this
// folder name and does **not** affect where things go in the output folder.
pathPrefix: "/",
};
};
// When paired with the HTML <base> plugin https://www.11ty.dev/docs/plugins/html-base/
// it will transform any absolute URLs in your HTML to include this
// 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"

117
flake.nix
View file

@ -1,58 +1,75 @@
{
description = "Bloggy time!";
description = "Bloggy time!";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
deploy-rs.url = "github:serokell/deploy-rs";
};
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 {
apps = forAllSystems (pkgs: {
"deploy" = {
type = "app";
program = let
ci = (pkgs.writeShellApplication {
name = "ci.sh";
text = ''
nix build
'';
});
in "${ci}/ci.sh";
};
});
packages = forAllSystems (pkgs: {
default = pkgs.callPackage ./package.nix {};
});
deploy.nodes.myblog = {
hostname = "saji.dev";
profiles.mysite = {
sshUser = "static-site";
user = "static-site";
path = deploy-rs.lib.x86_64-linux.activate.custom self.packages.x86_64-linux.default ''
rm -rf /var/lib/static-site/public
ln -sn $PROFILE/public /var/lib/static-site/public
'';
};
};
treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
packages = with pkgs; [
nodejs
vips
pkg-config
];
};
});
};
in
{
apps = forAllSystems (pkgs: {
"deploy" = {
type = "app";
program =
let
ci = (
pkgs.writeShellApplication {
name = "ci.sh";
text = ''
nix build
'';
}
);
in
"${ci}/ci.sh";
};
});
packages = forAllSystems (pkgs: {
default = pkgs.callPackage ./package.nix { };
});
deploy.nodes.myblog = {
hostname = "saji.dev";
profiles.mysite = {
sshUser = "static-site";
user = "static-site";
path = deploy-rs.lib.x86_64-linux.activate.custom self.packages.x86_64-linux.default ''
rm -rf /var/lib/static-site/public
ln -sn $PROFILE/public /var/lib/static-site/public
'';
};
};
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; [
nodejs
vips
pkg-config
];
};
});
};
}

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.
*
@ -14,142 +13,140 @@ const VSEP = "===";
* @param {*} options
*/
export default function revealjs_plugin(md, options) {
function isVSep(token) {
return token.type === "inline" && token.content === VSEP;
function isVSep(token) {
return token.type === "inline" && token.content === VSEP;
}
function isHSep(token) {
return token.type === "hr" && token.markup === HSEP;
}
function isSep(token) {
return isHSep(token) || isVSep(token);
}
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;
md.renderer.rules.slide_open = renderOpening;
md.renderer.rules.slide_close = renderClosing;
function nextDivider(tokens, start) {
for (let i = start; i < tokens.length; i++) {
if (isSep(tokens[i])) {
return i;
}
}
return -1;
}
function isHSep(token) {
return token.type === "hr" && token.markup === HSEP;
function previousSlideOpen(tokens, before) {
for (let i = before - 1; i >= 0; i--) {
if (tokens[i].type === "slide_open") {
return i;
}
}
return -1;
}
function isSep(token) {
return isHSep(token) || isVSep(token);
}
var openSlides = 0;
function renderOpening(tokens, idx, options, env, slf) {
var token = tokens[idx];
return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`;
};
function presentationOpen(state) {
var token = new state.Token("pres_open", "section", 1);
token.block = true;
token.attrs = [["class", "reveal"]];
return token;
}
function renderClosing(tokens, idx, options, env, slf) {
var token = tokens[idx];
return `</${tokens[idx].tag}>`;
};
function presentationClose(state) {
return new state.Token("pres_close", "section", -1);
}
md.renderer.rules.pres_open = renderOpening;
md.renderer.rules.pres_close = renderClosing;
md.renderer.rules.slide_open = renderOpening;
md.renderer.rules.slide_close = renderClosing;
function slidesOpen(state) {
var token = new state.Token("slides_open", "div", 1);
token.block = true;
token.attrs = [["class", "slides"]];
return token;
}
function nextDivider(tokens, start) {
for (let i = start; i < tokens.length; i++) {
if (isSep(tokens[i])) {
return i;
}
}
return -1;
}
function slidesClose(state) {
return new state.Token("slides_close", "div", -1);
}
function previousSlideOpen(tokens, before) {
for (let i = before - 1; i >= 0; i--) {
if (tokens[i].type === "slide_open") {
return i;
}
}
return -1;
}
function slideOpen(state) {
openSlides++;
return new state.Token("slide_open", "section", 1);
}
var openSlides = 0;
function presentationOpen(state) {
var token = new state.Token("pres_open", "section", 1);
token.block = true;
token.attrs = [
["class", "reveal"]
];
return token;
}
function presentationClose(state) {
return new state.Token("pres_close", "section", -1);
}
function slidesOpen(state) {
var token = new state.Token("slides_open", "div", 1);
token.block = true;
token.attrs = [
["class", "slides"]
];
return token;
}
function slidesClose(state) {
return new state.Token("slides_close", "div", -1);
}
function slideOpen(state) {
openSlides++;
return new state.Token("slide_open", "section", 1);
}
function slideClose(state) {
openSlides--;
return new state.Token("slide_close", "section", -1);
}
md.core.ruler.push("revealjs", function(state) {
let divIdx = 0;
while (true) {
divIdx = nextDivider(state.tokens, divIdx);
if (divIdx === -1) {
break;
}
let divider = state.tokens[divIdx];
if (isSep(divider) && openSlides === 0) {
state.tokens.unshift(slideOpen(state));
divIdx++; // we added a token at the beginning, we need to update divIdx
}
let tags = [];
if (isHSep(divider)) {
while (openSlides > 0) {
tags.push(slideClose(state));
}
tags.push(slideOpen(state));
// because "---" is hijacked from plain markdown, it translates
// to one token which we remove
state.tokens.splice(divIdx, 1, ...tags);
} else if (isVSep(divider)) {
// if it is a vertical separator, we need to wrap the current slide
// in it's own section
if (openSlides === 1) {
let slideOpenIdx = previousSlideOpen(state.tokens, divIdx);
state.tokens.splice(slideOpenIdx, 1,
state.tokens[slideOpenIdx], // reuse it to avoid increasing openSlides
slideOpen(state)
);
divIdx++; // we added a token before the divider, we need to update divIdx
}
// if the current slide is already a vertical child, we need to close
// it; this will be indicated by a nesting level of two:
// <section horizontal-parent>
// <section current-vertical-child>
// <!-- this is where we are right now -->
if (openSlides === 2) {
tags.push(slideClose(state));
}
tags.push(slideOpen(state));
// because this is a custom token, it is first wrapped by the processor
// in a paragraph, so we need to replace para_open, sep, para_close
state.tokens.splice(divIdx - 1, 3, ...tags);
}
}
function slideClose(state) {
openSlides--;
return new state.Token("slide_close", "section", -1);
}
md.core.ruler.push("revealjs", function (state) {
let divIdx = 0;
while (true) {
divIdx = nextDivider(state.tokens, divIdx);
if (divIdx === -1) {
break;
}
let divider = state.tokens[divIdx];
if (isSep(divider) && openSlides === 0) {
state.tokens.unshift(slideOpen(state));
divIdx++; // we added a token at the beginning, we need to update divIdx
}
let tags = [];
if (isHSep(divider)) {
while (openSlides > 0) {
state.tokens.push(slideClose(state));
tags.push(slideClose(state));
}
state.tokens.unshift(slidesOpen(state));
state.tokens.unshift(presentationOpen(state));
state.tokens.push(slidesClose(state));
state.tokens.push(presentationClose(state));
});
};
tags.push(slideOpen(state));
// because "---" is hijacked from plain markdown, it translates
// to one token which we remove
state.tokens.splice(divIdx, 1, ...tags);
} else if (isVSep(divider)) {
// if it is a vertical separator, we need to wrap the current slide
// in it's own section
if (openSlides === 1) {
let slideOpenIdx = previousSlideOpen(state.tokens, divIdx);
state.tokens.splice(
slideOpenIdx,
1,
state.tokens[slideOpenIdx], // reuse it to avoid increasing openSlides
slideOpen(state),
);
divIdx++; // we added a token before the divider, we need to update divIdx
}
// if the current slide is already a vertical child, we need to close
// it; this will be indicated by a nesting level of two:
// <section horizontal-parent>
// <section current-vertical-child>
// <!-- this is where we are right now -->
if (openSlides === 2) {
tags.push(slideClose(state));
}
tags.push(slideOpen(state));
// because this is a custom token, it is first wrapped by the processor
// in a paragraph, so we need to replace para_open, sep, para_close
state.tokens.splice(divIdx - 1, 3, ...tags);
}
}
while (openSlides > 0) {
state.tokens.push(slideClose(state));
}
state.tokens.unshift(slidesOpen(state));
state.tokens.unshift(presentationOpen(state));
state.tokens.push(slidesClose(state));
state.tokens.push(presentationClose(state));
});
}

View file

@ -1,41 +1,41 @@
{
"name": "eleventy-base-blog",
"version": "8.0.0",
"description": "A starter repository for a blog web site using the Eleventy site generator.",
"scripts": {
"build": "npx @11ty/eleventy",
"build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/",
"start": "npx @11ty/eleventy --serve --quiet",
"debug": "DEBUG=Eleventy* npx @11ty/eleventy",
"debugstart": "DEBUG=Eleventy* npx @11ty/eleventy --serve --quiet",
"benchmark": "DEBUG=Eleventy:Benchmark* npx @11ty/eleventy"
},
"type": "module",
"repository": {
"type": "git",
"url": "git://github.com/11ty/eleventy-base-blog.git"
},
"license": "MIT",
"engines": {
"node": ">=14"
},
"dependencies": {
"@11ty/eleventy": "^3.0.0",
"@11ty/eleventy-img": "^4.0.2",
"@vscode/markdown-it-katex": "^1.1.1",
"katex": "^0.16.22",
"markdown-it-abbr": "^2.0.0",
"markdown-it-container": "^4.0.0",
"reveal.js": "^5.2.1",
"ttf2woff2": "^6.0.1"
},
"devDependencies": {
"@11ty/eleventy-navigation": "^0.3.5",
"@11ty/eleventy-plugin-bundle": "^1.0.4",
"@11ty/eleventy-plugin-rss": "^2.0.2",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"luxon": "^3.3.0",
"markdown-it-anchor": "^8.6.7"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
"name": "eleventy-base-blog",
"version": "8.0.0",
"description": "A starter repository for a blog web site using the Eleventy site generator.",
"scripts": {
"build": "npx @11ty/eleventy",
"build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/",
"start": "npx @11ty/eleventy --serve --quiet",
"debug": "DEBUG=Eleventy* npx @11ty/eleventy",
"debugstart": "DEBUG=Eleventy* npx @11ty/eleventy --serve --quiet",
"benchmark": "DEBUG=Eleventy:Benchmark* npx @11ty/eleventy"
},
"type": "module",
"repository": {
"type": "git",
"url": "git://github.com/11ty/eleventy-base-blog.git"
},
"license": "MIT",
"engines": {
"node": ">=14"
},
"dependencies": {
"@11ty/eleventy": "^3.0.0",
"@11ty/eleventy-img": "^4.0.2",
"@vscode/markdown-it-katex": "^1.1.1",
"katex": "^0.16.22",
"markdown-it-abbr": "^2.0.0",
"markdown-it-container": "^4.0.0",
"reveal.js": "^5.2.1",
"ttf2woff2": "^6.0.1"
},
"devDependencies": {
"@11ty/eleventy-navigation": "^0.3.5",
"@11ty/eleventy-plugin-bundle": "^1.0.4",
"@11ty/eleventy-plugin-rss": "^2.0.2",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"luxon": "^3.3.0",
"markdown-it-anchor": "^8.6.7"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View file

@ -1,32 +1,33 @@
{
nodejs,
pkg-config,
buildNpmPackage,
importNpmLock,
vips,
nodejs,
pkg-config,
buildNpmPackage,
importNpmLock,
vips,
lib,
}: buildNpmPackage {
name = "myblog";
version = "unstable";
buildInputs = [
];
nativeBuildInputs = [
pkg-config
nodejs
vips
];
npmDeps = importNpmLock {
npmRoot = ./.;
};
npmConfigHook = importNpmLock.npmConfigHook;
src = ./.;
installPhase = ''
mkdir -p $out/public
cp -ar _site/* $out/public/
'';
meta = {
description = "My blog, yay";
homepage = "https://saji.dev";
platforms = lib.platforms.all;
};
}:
buildNpmPackage {
name = "myblog";
version = "unstable";
buildInputs = [
];
nativeBuildInputs = [
pkg-config
nodejs
vips
];
npmDeps = importNpmLock {
npmRoot = ./.;
};
npmConfigHook = importNpmLock.npmConfigHook;
src = ./.;
installPhase = ''
mkdir -p $out/public
cp -ar _site/* $out/public/
'';
meta = {
description = "My blog, yay";
homepage = "https://saji.dev";
platforms = lib.platforms.all;
};
}

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-weight: normal;
font-style: normal;
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');
font-weight: normal;
font-style: normal;
font-stretch: semi-expanded;
font-family: "wip3out";
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-weight: normal;
font-style: normal;
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-weight: bold;
font-style: normal;
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-weight: 500;
font-style: normal;
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');
font-weight: 600;
font-style: normal;
}
font-family: "Orbitron";
src: url("/fonts/Orbitron-VariableFont_wght.woff2") format("woff2");
font-weight: 600;
font-style: normal;
}

View file

@ -1,288 +1,284 @@
/* Defaults */
:root {
/* --font-family: -apple-system, system-ui, sans-serif; */
--font-family: SometypeMono;
--font-family-monospace: SometypeMono;
/* --font-family: -apple-system, system-ui, sans-serif; */
--font-family: SometypeMono;
--font-family-monospace: SometypeMono;
}
/* Theme colors */
:root {
--text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan);
--text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan);
--syntax-tab-size: 2;
--syntax-tab-size: 2;
}
@media (prefers-color-scheme: dark) {
:root {
/* --text-color is assigned to --color-gray-_ above */
/* --text-color-link: #1493fb; */
/* --text-color-link-active: #6969f7; */
/* --text-color-link-visited: #a6a6f8; */
--text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan);
}
:root {
/* --text-color is assigned to --color-gray-_ above */
/* --text-color-link: #1493fb; */
/* --text-color-link-active: #6969f7; */
/* --text-color-link-visited: #a6a6f8; */
--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 {
/* list-style: symbols(cyclic "\25E5" "\25E2" "\25E3" "\25E4"); */
list-style: symbols(cyclic "\25E9");
/* list-style: symbols(cyclic "\25E5" "\25E2" "\25E3" "\25E4"); */
list-style: symbols(cyclic "\25E9");
}
/* Global stylesheet */
* {
box-sizing: border-box;
box-sizing: border-box;
}
html,
body {
padding: 0;
margin: 0 auto;
font-family: var(--font-family);
color: var(--color-fg);
background-color: var(--color-bg);
padding: 0;
margin: 0 auto;
font-family: var(--font-family);
color: var(--color-fg);
background-color: var(--color-bg);
}
html {
overflow-y: scroll;
overflow-y: scroll;
}
body {
max-width: 40em;
max-width: 40em;
}
img {
max-width: 100%;
height: auto;
max-width: 100%;
height: auto;
}
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
p:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
p {
line-height: 1.5;
line-height: 1.5;
}
li {
line-height: 1.5;
line-height: 1.5;
}
a[href] {
color: var(--text-color-link);
color: var(--text-color-link);
}
a[href]:visited {
color: var(--text-color-link-visited);
color: var(--text-color-link-visited);
}
a[href]:hover,
a[href]:active {
color: var(--text-color-link-active);
color: var(--text-color-link-active);
}
main {
padding: 1rem;
padding: 1rem;
}
main :first-child {
margin-top: 0;
margin-top: 0;
}
header {
border-bottom: 1px dashed var(--color-white);
border-bottom: 1px dashed var(--color-white);
}
header:after {
content: "";
display: table;
clear: both;
content: "";
display: table;
clear: both;
}
.links-nextprev {
list-style: none;
border-top: 1px dashed var(--color-white);
padding: 1em 0;
list-style: none;
border-top: 1px dashed var(--color-white);
padding: 1em 0;
}
table {
margin: 1em 0;
margin: 1em 0;
}
table td,
table th {
padding-right: 1em;
padding-right: 1em;
}
pre,
code {
font-family: var(--font-family-monospace);
font-family: var(--font-family-monospace);
}
pre:not([class*="language-"]) {
margin: .5em 0;
line-height: 1.375; /* 22px /16 */
-moz-tab-size: var(--syntax-tab-size);
-o-tab-size: var(--syntax-tab-size);
tab-size: var(--syntax-tab-size);
-webkit-hyphens: none;
-ms-hyphens: none;
hyphens: none;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
margin: 0.5em 0;
line-height: 1.375; /* 22px /16 */
-moz-tab-size: var(--syntax-tab-size);
-o-tab-size: var(--syntax-tab-size);
tab-size: var(--syntax-tab-size);
-webkit-hyphens: none;
-ms-hyphens: none;
hyphens: none;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
}
code {
/* word-break: break-all; */
/* word-break: break-all; */
color: var(--color-black);
background-color: var(--color-white);
color: var(--color-black);
background-color: var(--color-white);
}
/* Header */
header {
display: flex;
gap: 1em .5em;
flex-wrap: wrap;
align-items: center;
padding: 1em;
display: flex;
gap: 1em 0.5em;
flex-wrap: wrap;
align-items: center;
padding: 1em;
}
.home-link {
font-size: 1em; /* 16px /16 */
font-weight: 700;
margin-right: 2em;
font-size: 1em; /* 16px /16 */
font-weight: 700;
margin-right: 2em;
}
.home-link:link:not(:hover) {
text-decoration: none;
text-decoration: none;
}
/* Nav */
.nav {
display: flex;
padding: 0;
margin: 0;
list-style: none;
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.nav-item {
display: inline-block;
margin-right: 1em;
display: inline-block;
margin-right: 1em;
}
.nav-item a[href]:not(:hover) {
text-decoration: none;
text-decoration: none;
}
.nav a[href][aria-current="page"] {
text-decoration: underline;
text-decoration: underline;
}
/* Posts list */
.postlist {
list-style: none;
padding: 0;
padding-left: 1.5rem;
list-style: none;
padding: 0;
padding-left: 1.5rem;
}
.postlist-item {
display: flex;
flex-wrap: wrap;
align-items: baseline;
counter-increment: start-from -1;
margin-bottom: 1em;
display: flex;
flex-wrap: wrap;
align-items: baseline;
counter-increment: start-from -1;
margin-bottom: 1em;
}
.postlist-item:before {
display: inline-block;
pointer-events: none;
content: "" counter(start-from, decimal-leading-zero) ". ";
line-height: 100%;
text-align: right;
margin-left: -1.5rem;
display: inline-block;
pointer-events: none;
content: "" counter(start-from, decimal-leading-zero) ". ";
line-height: 100%;
text-align: right;
margin-left: -1.5rem;
}
.postlist-date,
.postlist-item:before {
font-size: 0.8125em; /* 13px /16 */
color: var(--color-gray-90);
font-size: 0.8125em; /* 13px /16 */
color: var(--color-gray-90);
}
.postlist-date {
word-spacing: -0.5px;
word-spacing: -0.5px;
}
.postlist-link {
font-size: 1.1875em; /* 19px /16 */
font-weight: 700;
flex-basis: calc(100% - 1.5rem);
padding-left: .25em;
padding-right: .5em;
text-underline-position: from-font;
text-underline-offset: 0;
text-decoration-thickness: 1px;
font-size: 1.1875em; /* 19px /16 */
font-weight: 700;
flex-basis: calc(100% - 1.5rem);
padding-left: 0.25em;
padding-right: 0.5em;
text-underline-position: from-font;
text-underline-offset: 0;
text-decoration-thickness: 1px;
}
.postlist-item-active .postlist-link {
font-weight: bold;
font-weight: bold;
}
/* Tags */
.post-tag {
display: inline-flex;
align-items: center;
justify-content: center;
text-transform: capitalize;
font-style: italic;
display: inline-flex;
align-items: center;
justify-content: center;
text-transform: capitalize;
font-style: italic;
}
.postlist-item > .post-tag {
align-self: center;
align-self: center;
}
/* Tags list */
.post-metadata {
display: inline-flex;
flex-wrap: wrap;
gap: .5em;
list-style: none;
padding: 0;
margin: 0;
display: inline-flex;
flex-wrap: wrap;
gap: 0.5em;
list-style: none;
padding: 0;
margin: 0;
}
.post-metadata time {
margin-right: 1em;
margin-right: 1em;
}
/* Direct Links / Markdown Headers */
.header-anchor {
text-decoration: none;
font-style: normal;
font-size: 1em;
margin-left: .1em;
text-decoration: none;
font-style: normal;
font-size: 1em;
margin-left: 0.1em;
}
a[href].header-anchor,
a[href].header-anchor:visited {
color: transparent;
color: transparent;
}
a[href].header-anchor:focus,
a[href].header-anchor:hover {
text-decoration: underline;
text-decoration: underline;
}
a[href].header-anchor:focus,
:hover > a[href].header-anchor {
color: #aaa;
color: #aaa;
}
h2 + .header-anchor {
font-size: 1.5em;
font-size: 1.5em;
}
h1, h2, h3 {
font-family: WO3;
letter-spacing: .1rem;
h1,
h2,
h3 {
font-family: WO3;
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

@ -3,43 +3,43 @@
*/
pre[class*="language-diff-"] {
--eleventy-code-padding: 1.25em;
padding-left: var(--eleventy-code-padding);
padding-right: var(--eleventy-code-padding);
--eleventy-code-padding: 1.25em;
padding-left: var(--eleventy-code-padding);
padding-right: var(--eleventy-code-padding);
}
.token.deleted {
background-color: hsl(0, 51%, 37%);
color: inherit;
background-color: hsl(0, 51%, 37%);
color: inherit;
}
.token.inserted {
background-color: hsl(126, 31%, 39%);
color: inherit;
background-color: hsl(126, 31%, 39%);
color: inherit;
}
/* Make the + and - characters unselectable for copy/paste */
.token.prefix.unchanged,
.token.prefix.inserted,
.token.prefix.deleted {
-webkit-user-select: none;
user-select: none;
display: inline-flex;
align-items: center;
justify-content: center;
padding-top: 2px;
padding-bottom: 2px;
-webkit-user-select: none;
user-select: none;
display: inline-flex;
align-items: center;
justify-content: center;
padding-top: 2px;
padding-bottom: 2px;
}
.token.prefix.inserted,
.token.prefix.deleted {
width: var(--eleventy-code-padding);
background-color: rgba(0,0,0,.2);
width: var(--eleventy-code-padding);
background-color: rgba(0, 0, 0, 0.2);
}
/* Optional: full-width background color */
.token.inserted:not(.prefix),
.token.deleted:not(.prefix) {
display: block;
margin-left: calc(-1 * var(--eleventy-code-padding));
margin-right: calc(-1 * var(--eleventy-code-padding));
text-decoration: none; /* override del, ins, mark defaults */
color: inherit; /* override del, ins, mark defaults */
display: block;
margin-left: calc(-1 * var(--eleventy-code-padding));
margin-right: calc(-1 * var(--eleventy-code-padding));
text-decoration: none; /* override del, ins, mark defaults */
color: inherit; /* override del, ins, mark defaults */
}

6
treefmt.nix Normal file
View file

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