This commit is contained in:
parent
2fe8b81523
commit
1cd021d0a7
60
README.md
60
README.md
|
@ -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 content’s location on the file system.
|
- All URLs are decoupled from the content’s 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 needn’t exclusively be markdown, for example). Configure your project’s supported templates in `eleventy.config.js` -> `templateFormats`.
|
- Content can be in _any template format_ (blog posts needn’t exclusively be markdown, for example). Configure your project’s 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
|
||||||
|
|
182
_data/colors.js
182
_data/colors.js
|
@ -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%)",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
@ -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/",
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
|
@ -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>.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -6,6 +6,4 @@ tags:
|
||||||
- project
|
- project
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
This is a test
|
This is a test
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
export default {
|
export default {
|
||||||
tags: [
|
tags: ["posts"],
|
||||||
"posts"
|
layout: "layouts/post.njk",
|
||||||
],
|
|
||||||
"layout": "layouts/post.njk",
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export default {
|
export default {
|
||||||
tags: [
|
tags: [],
|
||||||
],
|
layout: "layouts/deck.njk",
|
||||||
"layout": "layouts/deck.njk",
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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<T: Mammal>(m: T) {
|
fn static_mammal<T: Mammal>(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>
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
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/katex/dist/fonts/": "/bundle/fonts/", // needs to be here for css reasons.
|
"./node_modules/katex/dist/fonts/": "/bundle/fonts/", // needs to be here for css reasons.
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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.addShortcode("includeRaw", async (file) => fs.readFile(file, "utf8"));
|
// eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
|
||||||
|
|
||||||
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
|
return {
|
||||||
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
|
// Control which files Eleventy will process
|
||||||
//})
|
// e.g.: *.md, *.njk, *.html, *.liquid
|
||||||
//
|
templateFormats: ["md", "njk", "html", "liquid"],
|
||||||
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");
|
// Pre-process *.md files with: (default: `liquid`)
|
||||||
eleventyConfig.addBundle("js");
|
markdownTemplateEngine: "njk",
|
||||||
eleventyConfig.addBundle("html");
|
|
||||||
|
|
||||||
// Features to make your build faster (when you need them)
|
// Pre-process *.html files with: (default: `liquid`)
|
||||||
|
htmlTemplateEngine: "njk",
|
||||||
|
|
||||||
// If your passthrough copy gets heavy and cumbersome, add this line
|
// These are all optional:
|
||||||
// to emulate the file copy on the dev server. Learn more:
|
dir: {
|
||||||
// https://www.11ty.dev/docs/copy/#emulate-passthrough-copy-during-serve
|
input: "content", // default: "."
|
||||||
|
includes: "../_includes", // default: "_includes"
|
||||||
|
data: "../_data", // default: "_data"
|
||||||
|
output: "_site",
|
||||||
|
},
|
||||||
|
|
||||||
// eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
|
// -----------------------------------------------------------------
|
||||||
|
// Optional items:
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
return {
|
// If your site deploys to a subdirectory, change `pathPrefix`.
|
||||||
// Control which files Eleventy will process
|
// Read more: https://www.11ty.dev/docs/config/#deploy-to-a-subdirectory-with-a-path-prefix
|
||||||
// e.g.: *.md, *.njk, *.html, *.liquid
|
|
||||||
templateFormats: [
|
|
||||||
"md",
|
|
||||||
"njk",
|
|
||||||
"html",
|
|
||||||
"liquid",
|
|
||||||
],
|
|
||||||
|
|
||||||
// Pre-process *.md files with: (default: `liquid`)
|
// When paired with the HTML <base> plugin https://www.11ty.dev/docs/plugins/html-base/
|
||||||
markdownTemplateEngine: "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.
|
||||||
// Pre-process *.html files with: (default: `liquid`)
|
pathPrefix: "/",
|
||||||
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: "/",
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
113
flake.nix
113
flake.nix
|
@ -1,62 +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";
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, deploy-rs, systems, treefmt-nix }: let
|
outputs =
|
||||||
# systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
|
{
|
||||||
forAllSystems = fn:
|
self,
|
||||||
nixpkgs.lib.genAttrs (import systems) (system: fn nixpkgs.legacyPackages.${system});
|
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});
|
||||||
|
|
||||||
treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
|
treefmtEval = forAllSystems (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
|
||||||
|
|
||||||
in {
|
in
|
||||||
apps = forAllSystems (pkgs: {
|
{
|
||||||
"deploy" = {
|
apps = forAllSystems (pkgs: {
|
||||||
type = "app";
|
"deploy" = {
|
||||||
program = let
|
type = "app";
|
||||||
ci = (pkgs.writeShellApplication {
|
program =
|
||||||
name = "ci.sh";
|
let
|
||||||
text = ''
|
ci = (
|
||||||
nix build
|
pkgs.writeShellApplication {
|
||||||
'';
|
name = "ci.sh";
|
||||||
});
|
text = ''
|
||||||
in "${ci}/ci.sh";
|
nix build
|
||||||
};
|
'';
|
||||||
});
|
}
|
||||||
packages = forAllSystems (pkgs: {
|
);
|
||||||
default = pkgs.callPackage ./package.nix {};
|
in
|
||||||
});
|
"${ci}/ci.sh";
|
||||||
deploy.nodes.myblog = {
|
};
|
||||||
hostname = "saji.dev";
|
});
|
||||||
profiles.mysite = {
|
packages = forAllSystems (pkgs: {
|
||||||
sshUser = "static-site";
|
default = pkgs.callPackage ./package.nix { };
|
||||||
user = "static-site";
|
});
|
||||||
path = deploy-rs.lib.x86_64-linux.activate.custom self.packages.x86_64-linux.default ''
|
deploy.nodes.myblog = {
|
||||||
rm -rf /var/lib/static-site/public
|
hostname = "saji.dev";
|
||||||
ln -sn $PROFILE/public /var/lib/static-site/public
|
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);
|
formatter = forAllSystems (pkgs: treefmtEval.${pkgs.system}.config.build.wrapper);
|
||||||
|
|
||||||
checks = forAllSystems (pkgs: {
|
checks = forAllSystems (pkgs: {
|
||||||
formatting = treefmtEval.${pkgs.system}.config.build.check self;
|
formatting = treefmtEval.${pkgs.system}.config.build.check self;
|
||||||
});
|
});
|
||||||
|
|
||||||
devShells = forAllSystems (pkgs: {
|
devShells = forAllSystems (pkgs: {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
nodejs
|
nodejs
|
||||||
vips
|
vips
|
||||||
pkg-config
|
pkg-config
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
78
package.json
78
package.json
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
59
package.nix
59
package.nix
|
@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue