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 root = true
[*] [*]
indent_style = tab indent_style = space
indent_size = 2 indent_size = 2
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true

3
.gitignore vendored
View file

@ -13,3 +13,6 @@ result
/styles/config/vocabularies/* /styles/config/vocabularies/*
!/styles/config/vocabularies/Base !/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 ## Getting Started
* [Want a more generic/detailed getting started guide?](https://www.11ty.dev/docs/getting-started/) - [Want a more generic/detailed getting started guide?](https://www.11ty.dev/docs/getting-started/)
1. Make a directory and navigate to it: 1. Make a directory and navigate to it:
@ -46,38 +46,38 @@ Or you can run [debug mode](https://www.11ty.dev/docs/debugging/) to see all the
## Features ## Features
- Using [Eleventy v2.0](https://www.11ty.dev/blog/eleventy-v2/) with zero-JavaScript output. - 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). - 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/) - 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. - 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/) - Configure templates via the [Eleventy Data Cascade](https://www.11ty.dev/docs/data-cascade/)
- **Performance focused**: four-hundos Lighthouse score out of the box! - **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). - [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_ - _0 Cumulative Layout Shift_
- _0ms Total Blocking Time_ - _0ms Total Blocking Time_
- Local development live reload provided by [Eleventy Dev Server](https://www.11ty.dev/docs/dev-server/). - 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/) - Content-driven [navigation menu](https://www.11ty.dev/docs/plugins/navigation/)
- [Image optimization](https://www.11ty.dev/docs/plugins/image/) via the `{% image %}` shortcode. - [Image optimization](https://www.11ty.dev/docs/plugins/image/) via the `{% image %}` shortcode.
- Zero-JavaScript output. - Zero-JavaScript output.
- Support for modern image formats automatically (e.g. AVIF and WebP) - 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. - 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` - Automated `<picture>` syntax markup with `srcset` and optional `sizes`
- Includes `width`/`height` attributes to avoid [content layout shift](https://web.dev/cls/). - Includes `width`/`height` attributes to avoid [content layout shift](https://web.dev/cls/).
- Includes `loading="lazy"` for native lazy loading without JavaScript. - Includes `loading="lazy"` for native lazy loading without JavaScript.
- Includes [`decoding="async"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding) - Includes [`decoding="async"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding)
- Images can be co-located with blog post files. - 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) - 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). - 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). - Built-in [syntax highlighter](https://www.11ty.dev/docs/plugins/syntaxhighlight/) (zero-JavaScript output).
- Blog Posts - 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). - 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 - Automated next/previous links
- Accessible deep links to headings - Accessible deep links to headings
- Generated Pages - Generated Pages
- Home, Archive, and About pages. - Home, Archive, and About pages.
- [Feeds for Atom and JSON](https://www.11ty.dev/docs/plugins/rss/) - [Feeds for Atom and JSON](https://www.11ty.dev/docs/plugins/rss/)
- `sitemap.xml` - `sitemap.xml`
- Zero-maintenance tag pages ([View on the Demo](https://eleventy-base-blog.netlify.app/tags/)) - Zero-maintenance tag pages ([View on the Demo](https://eleventy-base-blog.netlify.app/tags/))
- Content not found (404) page - Content not found (404) page
## Demos ## 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`. - 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. - 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: - Provides two content feeds:
- `content/feed/feed.njk` - `content/feed/feed.njk`
- `content/feed/json.njk` - `content/feed/json.njk`
- This project uses three [Eleventy Layouts](https://www.11ty.dev/docs/layouts/): - This project uses three [Eleventy Layouts](https://www.11ty.dev/docs/layouts/):
- `_includes/layouts/base.njk`: the top level HTML structure - `_includes/layouts/base.njk`: the top level HTML structure
- `_includes/layouts/home.njk`: the home page template (wrapped into `base.njk`) - `_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/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. - `_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 #### Content Security Policy

View file

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

View file

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

View file

@ -7,19 +7,23 @@
<title>{{ title or metadata.title }}</title> <title>{{ title or metadata.title }}</title>
<meta name="description" content="{{ description or metadata.description }}"> <meta name="description" content="{{ description or metadata.description }}">
<!-- <link rel="stylesheet" href="{% getBundleFileUrl "css" %}">
<link rel="stylesheet" href="dist/reset.css"> <script src="{% getBundleFileUrl "js" %}"></script>
<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>
{%- css %}{% include "node_modules/reveal.js/dist/reset.css" %} {% endcss %} {%- 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/reveal.css" %} {% endcss %}
{%- css %}{% include "node_modules/reveal.js/dist/theme/black.css" %} {% endcss %} {%- css %}{% include "node_modules/reveal.js/dist/theme/black.css" %} {% endcss %}
{%- css %}{% include "node_modules/reveal.js/plugin/highlight/monokai.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> </head>
<body> <body>
<div class="reveal"> <div class="reveal">
@ -27,22 +31,5 @@
{{ content | safe }} {{ content | safe }}
</div> </div>
</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> </body>
</html> </html>

View file

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

View file

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

View file

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

View file

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

View file

@ -2,8 +2,8 @@
title: Abusing regex in GitHub code search title: Abusing regex in GitHub code search
date: 2025-04-24 date: 2025-04-24
tags: tags:
- short - short
- cybersec - cybersec
--- ---
I recently discovered that the new(ish) GitHub Code Search feature 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. I'm sure someone is using it, since it's powerful.
Case in point: Case in point:
``` ```
/"[a-z]{4}(?: [a-z]{4}){3}"/ language:Python SMTP /"[a-z]{4}(?: [a-z]{4}){3}"/ language:Python SMTP
``` ```

View file

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

View file

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

View file

@ -113,7 +113,7 @@ This highlights the two styles of troubleshooting:
2. Hypothesis -> Validate -> Solution 2. Hypothesis -> Validate -> Solution
It feels pretty good to zero-shot a repair or fix, but it also can leave you 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 hot, but later it became necessary unless I just started replacing chips
indiscriminately. Once I started probing, I wanted to find something that indiscriminately. Once I started probing, I wanted to find something that
seemed "off" before going further. I would have noticed the character RAM 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. fine at 9600 baud using software flow control.
To the folks who rave about the terminals of yore, I get it now. To the folks who rave about the terminals of yore, I get it now.

View file

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

View file

@ -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. - `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. - 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 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. a Mac binary on Windows like it's nothing.
- Web target, both WASI and freestanding. - Web target, both WASI and freestanding.
- SIMD Vectors? We take those I guess. - SIMD Vectors? We take those I guess.
- It's got a build/test system built in. More on that later. - It's got a build/test system built in. More on that later.

View file

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

View file

@ -1,14 +1,12 @@
<section> <section>
<h2>Object (dis)orientation</h2> <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> <section>
<section> <section>Class/Inheritance has challenges</section>
Class/Inheritance has challenges <section>
</section> Combining Data + Code
<section> <pre><code data-trim data-noescape data-line-numbers="5|7-9">
Combining Data + Code
<pre><code data-trim data-noescape data-line-numbers="5|7-9">
class MyBase { class MyBase {
protected: protected:
int useless; int useless;
@ -20,35 +18,46 @@
}; };
}; };
</code></pre> </code></pre>
<p class="fragment">You have to know if this is an "interface", abstract class, or regular class.</p> <p class="fragment">
<small class="fragment">(it's an abstract class, since <code>makeSound</code> is pure virtual)</small> You have to know if this is an "interface", abstract class, or regular
</section> class.
<section> </p>
<p> A class can be one of many things </p> <small class="fragment"
<ul> >(it's an abstract class, since <code>makeSound</code> is pure
<li>Interface - abstract class with no members, and purely virtual </li> virtual)</small
<li>Abstract Class - class with <em>some</em> pure virtual methods </li> >
<li>"Regular" Class - class that is standalone and is fully implemented </li> </section>
</ul> <section>
<small class="fragment">These concepts all exist under the <code>class</code> keyword </small> <p>A class can be one of many things</p>
</section> <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>
<section> <section>
In Rust, you trade <em>Inheritance</em> for <em>Traits</em> and <em>Types</em>. In Rust, you trade <em>Inheritance</em> for <em>Traits</em> and
<em>Types</em>.
</section> </section>
<section> <section>
<section> <section>
<p>Traits are <em>Interfaces</em></p> <p>Traits are <em>Interfaces</em></p>
<pre><code data-trim data-noescape> <pre><code data-trim data-noescape>
pub trait Mammal { pub trait Mammal {
fn get_temp(&self) -> i32; fn get_temp(&self) -> i32;
} }
</code></pre> </code></pre>
</section> </section>
<section> <section>
<p>... that are explicitly implemented</p> <p>... that are explicitly implemented</p>
<pre><code data-trim data-noescape> <pre><code data-trim data-noescape>
pub struct Cat { pub struct Cat {
age: i32, age: i32,
outdoor: bool, outdoor: bool,
@ -63,11 +72,10 @@
} }
} }
</code></pre> </code></pre>
</section>
</section> <section>
<section> <p>And can be used statically <em>or</em> dynamically!</p>
<p> And can be used statically <em>or</em> dynamically!</p> <pre><code data-trim>
<pre><code data-trim>
fn static_mammal&lt;T: Mammal&gt;(m: T) { fn static_mammal&lt;T: Mammal&gt;(m: T) {
// the type of T must be known at compile time. // the type of T must be known at compile time.
} }
@ -75,6 +83,5 @@
// This uses a vtable to dispatch at runtime. // This uses a vtable to dispatch at runtime.
} }
</code></pre> </code></pre>
</section> </section>
</section> </section>

View file

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

View file

@ -1,47 +1,53 @@
export function eleventyComputedPermalink() { export function eleventyComputedPermalink() {
// When using `addGlobalData` and you *want* to return a function, you must nest functions like this. // When using `addGlobalData` and you *want* to return a function, you must nest functions like this.
// `addGlobalData` acts like a global data file and runs the top level function it receives. // `addGlobalData` acts like a global data file and runs the top level function it receives.
return (data) => { return (data) => {
// Always skip during non-watch/serve builds // Always skip during non-watch/serve builds
if(data.draft && !process.env.BUILD_DRAFTS) { if (data.draft && !process.env.BUILD_DRAFTS) {
return false; return false;
} }
return data.permalink; return data.permalink;
} };
}; }
export function eleventyComputedExcludeFromCollections() { export function eleventyComputedExcludeFromCollections() {
// When using `addGlobalData` and you *want* to return a function, you must nest functions like this. // When using `addGlobalData` and you *want* to return a function, you must nest functions like this.
// `addGlobalData` acts like a global data file and runs the top level function it receives. // `addGlobalData` acts like a global data file and runs the top level function it receives.
return (data) => { return (data) => {
// Always exclude from non-watch/serve builds // Always exclude from non-watch/serve builds
if(data.draft && !process.env.BUILD_DRAFTS) { if (data.draft && !process.env.BUILD_DRAFTS) {
return true; return true;
} }
return data.eleventyExcludeFromCollections; return data.eleventyExcludeFromCollections;
} };
}; }
export default function (eleventyConfig) { export default function (eleventyConfig) {
eleventyConfig.addGlobalData("eleventyComputed.permalink", eleventyComputedPermalink); eleventyConfig.addGlobalData(
eleventyConfig.addGlobalData("eleventyComputed.eleventyExcludeFromCollections", eleventyComputedExcludeFromCollections); "eleventyComputed.permalink",
eleventyComputedPermalink,
);
eleventyConfig.addGlobalData(
"eleventyComputed.eleventyExcludeFromCollections",
eleventyComputedExcludeFromCollections,
);
let logged = false; let logged = false;
eleventyConfig.on("eleventy.before", ({runMode}) => { eleventyConfig.on("eleventy.before", ({ runMode }) => {
let text = "Excluding"; let text = "Excluding";
// Only show drafts in serve/watch modes // Only show drafts in serve/watch modes
if(runMode === "serve" || runMode === "watch") { if (runMode === "serve" || runMode === "watch") {
process.env.BUILD_DRAFTS = true; process.env.BUILD_DRAFTS = true;
text = "Including"; text = "Including";
} }
// Only log once. // Only log once.
if(!logged) { if (!logged) {
console.log( `[11ty/eleventy-base-blog] ${text} drafts.` ); 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 { EleventyHtmlBasePlugin } from "@11ty/eleventy";
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img"; import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
import pluginDrafts from "./eleventy.config.drafts.js"; import pluginDrafts from "./eleventy.config.drafts.js";
import fs from "fs/promises";
/** @param {import('@11ty/eleventy').UserConfig} eleventyConfig */ /** @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 // Copy the contents of the `public` folder to the output folder
// For example, `./public/css/` ends up in `_site/css/` // For example, `./public/css/` ends up in `_site/css/`
eleventyConfig.addPassthroughCopy({ eleventyConfig.addPassthroughCopy({
"./public/": "/", "./public/": "/",
"./node_modules/prismjs/themes/prism-okaidia.css": "/css/prism-okaidia.css", "./node_modules/katex/dist/fonts/": "/bundle/fonts/", // needs to be here for css reasons.
"./node_modules/katex/dist/fonts/": "/bundle/fonts/", });
"./node_modules/reveal.js/dist/": "/revealjs/",
"./node_modules/reveal.js/plugin/": "/revealjs-plugins/",
});
// Watch content images for the image pipeline. // Watch content images for the image pipeline.
eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpeg}"); eleventyConfig.addWatchTarget("content/**/*.{svg,webp,png,jpeg}");
// App plugins // App plugins
eleventyConfig.addPlugin(pluginDrafts); eleventyConfig.addPlugin(pluginDrafts);
// eleventyConfig.addPlugin(pluginFonts); // eleventyConfig.addPlugin(pluginFonts);
// Official plugins // Official plugins
let mkFeed = function(type, path) { let mkFeed = function (type, path) {
eleventyConfig.addPlugin(feedPlugin, { eleventyConfig.addPlugin(feedPlugin, {
type: type, type: type,
outputPath: path, outputPath: path,
collection: { collection: {
name: "posts", name: "posts",
limit: 0 limit: 0,
}, },
metadata: { metadata: {
title: "Musings", title: "Musings",
base: "https://saji.dev", base: "https://saji.dev",
language: "en", language: "en",
},
});
};
mkFeed("rss", "/rss.feed.xml");
mkFeed("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",
);
});
}; eleventyConfig.addFilter("htmlDateString", (dateObj) => {
mkFeed("rss", "/rss.feed.xml"); // dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
mkFeed("atom", "/atom.feed.xml"); return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat("yyyy-LL-dd");
mkFeed("json", "/feed.json"); });
eleventyConfig.addPlugin(pluginSyntaxHighlight, { // Get the first `n` elements of a collection.
preAttributes: { tabindex: 0 } eleventyConfig.addFilter("head", (array, n) => {
}); if (!Array.isArray(array) || array.length === 0) {
eleventyConfig.addPlugin(pluginNavigation); return [];
eleventyConfig.addPlugin(EleventyHtmlBasePlugin); }
eleventyConfig.addPlugin(pluginBundle); if (n < 0) {
eleventyConfig.addPlugin(eleventyImageTransformPlugin, { return array.slice(n);
extensions: "html", }
formats: ["webp", "jpeg", null],
widths: [400, 800, 1280, null],
defaultAttributes: {
loading: "lazy",
decoding: "async",
sizes: "auto",
}
})
return array.slice(0, n);
});
// Return the smallest number argument
eleventyConfig.addFilter("min", (...numbers) => {
return Math.min.apply(null, numbers);
});
// Filters // Return all the tags used in a collection
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => { eleventyConfig.addFilter("getAllTags", (collection) => {
// Formatting tokens for Luxon: https://moment.github.io/luxon/#/formatting?id=table-of-tokens let tagSet = new Set();
return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(format || "dd LLLL yyyy"); for (let item of collection) {
}); (item.data.tags || []).forEach((tag) => tagSet.add(tag));
}
return Array.from(tagSet);
});
eleventyConfig.addFilter('htmlDateString', (dateObj) => { eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
// dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string return (tags || []).filter(
return DateTime.fromJSDate(dateObj, { zone: 'utc' }).toFormat('yyyy-LL-dd'); (tag) => ["all", "nav", "post", "posts"].indexOf(tag) === -1,
}); );
});
// Get the first `n` elements of a collection. let mdIt;
eleventyConfig.addFilter("head", (array, n) => { // Customize Markdown library settings:
if (!Array.isArray(array) || array.length === 0) { eleventyConfig.amendLibrary("md", (mdLib) => {
return []; // hack to let us use the markdown renderer for later.
} mdIt = mdLib;
if (n < 0) { mdLib.use(markdownItAnchor, {
return array.slice(n); 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.addShortcode("includeRaw", async (file) =>
eleventyConfig.addFilter("min", (...numbers) => { fs.readFile(file, "utf8"),
return Math.min.apply(null, numbers); );
});
// Return all the tags used in a collection //eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
eleventyConfig.addFilter("getAllTags", collection => { // return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
let tagSet = new Set(); //})
for (let item of collection) { //
(item.data.tags || []).forEach(tag => tagSet.add(tag)); eleventyConfig.addPairedShortcode("callout", function (content) {
} // The 'content' variable holds the text/HTML placed between
return Array.from(tagSet); // {% callout %} and {% endcallout %}
}); // We wrap it with our div structure.
return `<div class="callout">${content}</div>`;
});
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) { eleventyConfig.addBundle("css");
return (tags || []).filter(tag => ["all", "nav", "post", "posts"].indexOf(tag) === -1); eleventyConfig.addBundle("js");
}); eleventyConfig.addBundle("html");
let mdIt; // Features to make your build faster (when you need them)
// 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);
});
eleventyConfig.addShortcode("currentBuildDate", () => { // If your passthrough copy gets heavy and cumbersome, add this line
return (new Date()).toISOString(); // 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") => { // eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
// 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.addBundle("css"); return {
eleventyConfig.addBundle("js"); // Control which files Eleventy will process
eleventyConfig.addBundle("html"); // 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 // Pre-process *.html files with: (default: `liquid`)
// to emulate the file copy on the dev server. Learn more: htmlTemplateEngine: "njk",
// https://www.11ty.dev/docs/copy/#emulate-passthrough-copy-during-serve
// 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 // Optional items:
// e.g.: *.md, *.njk, *.html, *.liquid // -----------------------------------------------------------------
templateFormats: [
"md",
"njk",
"html",
"liquid",
],
// Pre-process *.md files with: (default: `liquid`) // If your site deploys to a subdirectory, change `pathPrefix`.
markdownTemplateEngine: "njk", // Read more: https://www.11ty.dev/docs/config/#deploy-to-a-subdirectory-with-a-path-prefix
// Pre-process *.html files with: (default: `liquid`) // When paired with the HTML <base> plugin https://www.11ty.dev/docs/plugins/html-base/
htmlTemplateEngine: "njk", // 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.
// These are all optional: pathPrefix: "/",
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: "/",
};
};

View file

@ -68,10 +68,28 @@
"type": "github" "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": { "root": {
"inputs": { "inputs": {
"deploy-rs": "deploy-rs", "deploy-rs": "deploy-rs",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2",
"systems": "systems_2",
"treefmt-nix": "treefmt-nix"
} }
}, },
"systems": { "systems": {
@ -89,6 +107,38 @@
"type": "github" "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": { "utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"

117
flake.nix
View file

@ -1,58 +1,75 @@
{ {
description = "Bloggy time!"; description = "Bloggy time!";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 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 outputs =
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; {
forAllSystems = function: self,
nixpkgs.lib.genAttrs systems (system: function ( nixpkgs,
import nixpkgs { deploy-rs,
inherit system; systems,
config.allowUnfree = true; 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);
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
'';
};
};
devShells = forAllSystems (pkgs: { in
default = pkgs.mkShell { {
packages = with pkgs; [ apps = forAllSystems (pkgs: {
nodejs "deploy" = {
vips type = "app";
pkg-config 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++) { for (let i = 0; i < tokens.length; i++) {
// Check for paragraph containing only an image // Check for paragraph containing only an image
if ( if (
tokens[i].type === 'paragraph_open' && tokens[i].type === "paragraph_open" &&
i + 2 < tokens.length && i + 2 < tokens.length &&
tokens[i + 1].type === 'inline' && tokens[i + 1].type === "inline" &&
tokens[i + 1].children && tokens[i + 1].children &&
tokens[i + 1].children.length === 1 && tokens[i + 1].children.length === 1 &&
tokens[i + 1].children[0].type === 'image' && tokens[i + 1].children[0].type === "image" &&
tokens[i + 2].type === 'paragraph_close' tokens[i + 2].type === "paragraph_close"
) { ) {
// Check if the next token is a paragraph starting with emphasis // Check if the next token is a paragraph starting with emphasis
if ( if (
i + 5 < tokens.length && i + 5 < tokens.length &&
tokens[i + 3].type === 'paragraph_open' && tokens[i + 3].type === "paragraph_open" &&
tokens[i + 4].type === 'inline' && tokens[i + 4].type === "inline" &&
tokens[i + 4].children && tokens[i + 4].children &&
tokens[i + 4].children.length > 0 && tokens[i + 4].children.length > 0 &&
tokens[i + 4].children[0].type === 'em_open' && tokens[i + 4].children[0].type === "em_open" &&
tokens[i + 5].type === 'paragraph_close' tokens[i + 5].type === "paragraph_close"
) { ) {
figcaptionStartIndex = i + 3; // Start index of the caption paragraph figcaptionStartIndex = i + 3; // Start index of the caption paragraph
// --- Replace tokens --- // --- Replace tokens ---
// 1. Change paragraph_open to figure_open // 1. Change paragraph_open to figure_open
const figureOpen = new state.Token('figure_open', 'figure', 1); const figureOpen = new state.Token("figure_open", "figure", 1);
tokens[i] = figureOpen; // Replace paragraph_open tokens[i] = figureOpen; // Replace paragraph_open
// 2. Keep the inline token with the image as is (tokens[i+1]) // 2. Keep the inline token with the image as is (tokens[i+1])
// 3. Change paragraph_close to figcaption_open // 3. Change paragraph_close to figcaption_open
const figcaptionOpen = new state.Token('figcaption_open', 'figcaption', 1); const figcaptionOpen = new state.Token(
"figcaption_open",
"figcaption",
1,
);
tokens[i + 2] = figcaptionOpen; // Replace paragraph_close tokens[i + 2] = figcaptionOpen; // Replace paragraph_close
// 4. Remove the caption's paragraph_open // 4. Remove the caption's paragraph_open
@ -45,23 +49,29 @@ export default function figcaptionPlugin(md) {
// 5. Modify the caption's inline content: remove outer <em> tags // 5. Modify the caption's inline content: remove outer <em> tags
const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice
if ( if (
captionInlineToken.children[0].type === 'em_open' && captionInlineToken.children[0].type === "em_open" &&
captionInlineToken.children[captionInlineToken.children.length - 1].type === 'em_close' captionInlineToken.children[captionInlineToken.children.length - 1]
.type === "em_close"
) { ) {
captionInlineToken.children.shift(); // Remove em_open captionInlineToken.children.shift(); // Remove em_open
captionInlineToken.children.pop(); // Remove em_close captionInlineToken.children.pop(); // Remove em_close
} }
// Adjust level for caption content // Adjust level for caption content
captionInlineToken.level += 1; captionInlineToken.level += 1;
captionInlineToken.children.forEach(child => { child.level += 1; }); captionInlineToken.children.forEach((child) => {
child.level += 1;
});
// 6. Change the caption's paragraph_close to figcaption_close // 6. Change the caption's paragraph_close to figcaption_close
const figcaptionClose = new state.Token('figcaption_close', 'figcaption', -1); const figcaptionClose = new state.Token(
"figcaption_close",
"figcaption",
-1,
);
tokens[figcaptionStartIndex + 1] = figcaptionClose; // Replace paragraph_close (now at i+4) tokens[figcaptionStartIndex + 1] = figcaptionClose; // Replace paragraph_close (now at i+4)
// 7. Add figure_close after figcaption_close // 7. Add figure_close after figcaption_close
const figureClose = new state.Token('figure_close', 'figure', -1); const figureClose = new state.Token("figure_close", "figure", -1);
tokens.splice(figcaptionStartIndex + 2, 0, figureClose); // Insert figure_close (at i+5) tokens.splice(figcaptionStartIndex + 2, 0, figureClose); // Insert figure_close (at i+5)
// Adjust token index to skip the newly inserted/modified tokens // Adjust token index to skip the newly inserted/modified tokens
@ -71,13 +81,27 @@ export default function figcaptionPlugin(md) {
} }
} }
md.core.ruler.after('inline', 'figcaption', figcaptionRule); md.core.ruler.after("inline", "figcaption", figcaptionRule);
// Add renderer rules if they don't exist // Add renderer rules if they don't exist
md.renderer.rules.figure_open = md.renderer.rules.figure_open || function () { return '<figure>\n'; }; md.renderer.rules.figure_open =
md.renderer.rules.figure_close = md.renderer.rules.figure_close || function () { return '</figure>\n'; }; md.renderer.rules.figure_open ||
md.renderer.rules.figcaption_open = md.renderer.rules.figcaption_open || function () { return '<figcaption>'; }; function () {
md.renderer.rules.figcaption_close = md.renderer.rules.figcaption_close || function () { return '</figcaption>\n'; }; return "<figure>\n";
};
md.renderer.rules.figure_close =
md.renderer.rules.figure_close ||
function () {
return "</figure>\n";
};
md.renderer.rules.figcaption_open =
md.renderer.rules.figcaption_open ||
function () {
return "<figcaption>";
};
md.renderer.rules.figcaption_close =
md.renderer.rules.figcaption_close ||
function () {
return "</figcaption>\n";
};
} }

View file

@ -3,7 +3,6 @@
const HSEP = "---"; const HSEP = "---";
const VSEP = "==="; const VSEP = "===";
/** /**
* MarkdownIt plugin to generate revealjs friendly markup. * MarkdownIt plugin to generate revealjs friendly markup.
* *
@ -14,142 +13,140 @@ const VSEP = "===";
* @param {*} options * @param {*} options
*/ */
export default function revealjs_plugin(md, options) { export default function revealjs_plugin(md, options) {
function isVSep(token) { function isVSep(token) {
return token.type === "inline" && token.content === VSEP; 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) { function previousSlideOpen(tokens, before) {
return token.type === "hr" && token.markup === HSEP; for (let i = before - 1; i >= 0; i--) {
if (tokens[i].type === "slide_open") {
return i;
}
} }
return -1;
}
function isSep(token) { var openSlides = 0;
return isHSep(token) || isVSep(token);
}
function renderOpening(tokens, idx, options, env, slf) { function presentationOpen(state) {
var token = tokens[idx]; var token = new state.Token("pres_open", "section", 1);
return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`; token.block = true;
}; token.attrs = [["class", "reveal"]];
return token;
}
function renderClosing(tokens, idx, options, env, slf) { function presentationClose(state) {
var token = tokens[idx]; return new state.Token("pres_close", "section", -1);
return `</${tokens[idx].tag}>`; }
};
md.renderer.rules.pres_open = renderOpening; function slidesOpen(state) {
md.renderer.rules.pres_close = renderClosing; var token = new state.Token("slides_open", "div", 1);
md.renderer.rules.slide_open = renderOpening; token.block = true;
md.renderer.rules.slide_close = renderClosing; token.attrs = [["class", "slides"]];
return token;
}
function nextDivider(tokens, start) { function slidesClose(state) {
for (let i = start; i < tokens.length; i++) { return new state.Token("slides_close", "div", -1);
if (isSep(tokens[i])) { }
return i;
}
}
return -1;
}
function previousSlideOpen(tokens, before) { function slideOpen(state) {
for (let i = before - 1; i >= 0; i--) { openSlides++;
if (tokens[i].type === "slide_open") { return new state.Token("slide_open", "section", 1);
return i; }
}
}
return -1;
}
var openSlides = 0; function slideClose(state) {
openSlides--;
function presentationOpen(state) { return new state.Token("slide_close", "section", -1);
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);
}
}
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) { while (openSlides > 0) {
state.tokens.push(slideClose(state)); tags.push(slideClose(state));
} }
state.tokens.unshift(slidesOpen(state)); tags.push(slideOpen(state));
state.tokens.unshift(presentationOpen(state)); // because "---" is hijacked from plain markdown, it translates
state.tokens.push(slidesClose(state)); // to one token which we remove
state.tokens.push(presentationClose(state)); 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", "name": "eleventy-base-blog",
"version": "8.0.0", "version": "8.0.0",
"description": "A starter repository for a blog web site using the Eleventy site generator.", "description": "A starter repository for a blog web site using the Eleventy site generator.",
"scripts": { "scripts": {
"build": "npx @11ty/eleventy", "build": "npx @11ty/eleventy",
"build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/", "build-ghpages": "npx @11ty/eleventy --pathprefix=/eleventy-base-blog/",
"start": "npx @11ty/eleventy --serve --quiet", "start": "npx @11ty/eleventy --serve --quiet",
"debug": "DEBUG=Eleventy* npx @11ty/eleventy", "debug": "DEBUG=Eleventy* npx @11ty/eleventy",
"debugstart": "DEBUG=Eleventy* npx @11ty/eleventy --serve --quiet", "debugstart": "DEBUG=Eleventy* npx @11ty/eleventy --serve --quiet",
"benchmark": "DEBUG=Eleventy:Benchmark* npx @11ty/eleventy" "benchmark": "DEBUG=Eleventy:Benchmark* npx @11ty/eleventy"
}, },
"type": "module", "type": "module",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/11ty/eleventy-base-blog.git" "url": "git://github.com/11ty/eleventy-base-blog.git"
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
}, },
"dependencies": { "dependencies": {
"@11ty/eleventy": "^3.0.0", "@11ty/eleventy": "^3.0.0",
"@11ty/eleventy-img": "^4.0.2", "@11ty/eleventy-img": "^4.0.2",
"@vscode/markdown-it-katex": "^1.1.1", "@vscode/markdown-it-katex": "^1.1.1",
"katex": "^0.16.22", "katex": "^0.16.22",
"markdown-it-abbr": "^2.0.0", "markdown-it-abbr": "^2.0.0",
"markdown-it-container": "^4.0.0", "markdown-it-container": "^4.0.0",
"reveal.js": "^5.2.1", "reveal.js": "^5.2.1",
"ttf2woff2": "^6.0.1" "ttf2woff2": "^6.0.1"
}, },
"devDependencies": { "devDependencies": {
"@11ty/eleventy-navigation": "^0.3.5", "@11ty/eleventy-navigation": "^0.3.5",
"@11ty/eleventy-plugin-bundle": "^1.0.4", "@11ty/eleventy-plugin-bundle": "^1.0.4",
"@11ty/eleventy-plugin-rss": "^2.0.2", "@11ty/eleventy-plugin-rss": "^2.0.2",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"luxon": "^3.3.0", "luxon": "^3.3.0",
"markdown-it-anchor": "^8.6.7" "markdown-it-anchor": "^8.6.7"
}, },
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
} }

View file

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

View file

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

View file

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

View file

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