mirror of
https://github.com/RGBCube/Site
synced 2025-08-02 14:07:47 +00:00
Compare commits
9 commits
156ccda761
...
0acf135cc5
Author | SHA1 | Date | |
---|---|---|---|
0acf135cc5 | |||
8bbaad2e1c | |||
67d5fa028e | |||
5180fc70bd | |||
1a4c99f355 | |||
73115d08d2 | |||
a2f3fc2e5c | |||
350ecd5e97 | |||
5a30d40a3e |
5 changed files with 241 additions and 24 deletions
21
_config.ts
21
_config.ts
|
@ -23,12 +23,24 @@ site.process([".html"], (pages) => {
|
||||||
document.querySelectorAll("table").forEach((table) => {
|
document.querySelectorAll("table").forEach((table) => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
|
|
||||||
div.classList.add("room", "rotated");
|
|
||||||
table.classList.add("rotated");
|
table.classList.add("rotated");
|
||||||
|
div.classList.add("overflow", "rotated");
|
||||||
|
|
||||||
table.parentNode!.insertBefore(div, table);
|
table.parentNode!.insertBefore(div, table);
|
||||||
div.appendChild(table);
|
div.appendChild(table);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".hljs").forEach((code) => {
|
||||||
|
const pre = code.parentElement!;
|
||||||
|
const div = document.createElement("div");
|
||||||
|
|
||||||
|
code.classList.add("rotated");
|
||||||
|
pre.classList.add("rotated");
|
||||||
|
div.classList.add("overflow", "rotated");
|
||||||
|
|
||||||
|
pre.parentNode?.insertBefore(div, pre);
|
||||||
|
div.appendChild(pre);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,21 +48,20 @@ site.use(feed({
|
||||||
output: ["/blog.rss"],
|
output: ["/blog.rss"],
|
||||||
|
|
||||||
query: "type=article",
|
query: "type=article",
|
||||||
sort: "date=desc",
|
sort: "date=asc",
|
||||||
|
limit: Infinity,
|
||||||
|
|
||||||
info: {
|
info: {
|
||||||
title: "RGBCube's Blog",
|
title: "RGBCube's Blog",
|
||||||
description:
|
description:
|
||||||
"The blog where RGBCube dumps his schizophrenic ramblings about software and all the likes.",
|
"The blog where RGBCube dumps his schizophrenic ramblings about software and all the likes.",
|
||||||
lang: "en",
|
|
||||||
generator: false,
|
generator: false,
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
title: "=title",
|
title: "=title",
|
||||||
description: "=description",
|
description: "=description",
|
||||||
published: "=date",
|
published: "=date",
|
||||||
content: "$ content",
|
content: "$.content",
|
||||||
lang: "en",
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
layout: text.vto
|
layout: text.vto
|
||||||
---
|
---
|
||||||
|
|
||||||
{{ content }}
|
<div class="content">{{ content }}</div>
|
||||||
|
|
||||||
{{ if tags.length !== 0 }}
|
{{ if tags.length !== 0 }}
|
||||||
<p>Tags: {{ tags.join(", ") }}</p>
|
<p>Tags: {{ tags.join(", ") }}</p>
|
||||||
|
|
|
@ -3,12 +3,21 @@ layout: base.vto
|
||||||
---
|
---
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Centering */
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
font-size: large;
|
||||||
|
|
||||||
|
display: initial;
|
||||||
|
width: min(100vw - 2rem, 50rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make wrapping pretty */
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
|
@ -20,21 +29,17 @@ layout: base.vto
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered {
|
/* Rotate nav, tables and codeblocks so the scrollbar is at the top */
|
||||||
font-size: large;
|
|
||||||
|
|
||||||
display: initial;
|
|
||||||
width: min(100vw - 2rem, 50rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotated {
|
.rotated {
|
||||||
transform: rotateX(180deg);
|
transform: rotateX(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
/* Make tables and other elements scroll horizontally */
|
||||||
overflow-wrap: break-word;
|
.overflow {
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style nav */
|
||||||
nav {
|
nav {
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
|
@ -63,6 +68,11 @@ layout: base.vto
|
||||||
margin-right: 0.6rem;
|
margin-right: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style content */
|
||||||
|
.content {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
}
|
}
|
||||||
|
@ -71,10 +81,27 @@ layout: base.vto
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room {
|
blockquote {
|
||||||
overflow-x: auto;
|
border-left: 0.15rem solid var(--foreground);
|
||||||
|
padding-top: 0.01rem;
|
||||||
|
padding-bottom: 0.01rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
blockquote {
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
blockquote {
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style tables */
|
||||||
table {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -87,8 +114,8 @@ layout: base.vto
|
||||||
} */
|
} */
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
border: 0.15em solid var(--foreground);
|
border: 0.15rem solid var(--foreground);
|
||||||
padding: 0.3em;
|
padding: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
|
@ -96,9 +123,42 @@ layout: base.vto
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
/* Codeblocks */
|
||||||
border-top: 0.15rem solid var(--foreground);
|
.rotated:has(pre) {
|
||||||
|
padding: 0.6rem;
|
||||||
|
border: 0.15rem solid var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre:has(code) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.hljs-attr { color: lightblue; }
|
||||||
|
.hljs-built_in { color: firebrick; }
|
||||||
|
.hljs-keyword { color: firebrick; }
|
||||||
|
.hljs-number { color: mediumslateblue; }
|
||||||
|
.hljs-string { color: limegreen; }
|
||||||
|
.hljs-title { color: lightcoral; }
|
||||||
|
.hljs-type { color: aquamarine; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.hljs-attr { color: darkblue; }
|
||||||
|
.hljs-built_in { color: darkred; }
|
||||||
|
.hljs-keyword { color: darkred; }
|
||||||
|
.hljs-number { color: darkslateblue; }
|
||||||
|
.hljs-string { color: darkgreen; }
|
||||||
|
.hljs-title { color: darkgoldenrod; }
|
||||||
|
.hljs-type { color: darkcyan; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The end */
|
||||||
|
hr {
|
||||||
|
border: 0.15rem solid var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
@ -119,7 +179,8 @@ layout: base.vto
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="content">{{ content }}</div>
|
{{ content }}
|
||||||
|
|
||||||
<footer>Copyright © RGBCube</footer>
|
<hr>
|
||||||
|
<footer>Copyright {{ new Date().getFullYear() }} © RGBCube</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,7 +10,7 @@ Are you old? Then you might want to check out my super cool
|
||||||
<a href="/blog.rss">RSS Feed</a> too!
|
<a href="/blog.rss">RSS Feed</a> too!
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{{ for article of search.pages("type=article", "date")}}
|
{{ for article of search.pages("type=article", "order=asc title=date")}}
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ article.url }}">{{ article.date.toISOString().slice(0, 10) }}</a>:
|
<a href="{{ article.url }}">{{ article.date.toISOString().slice(0, 10) }}</a>:
|
||||||
|
|
145
site/blog/htmnix.md
Normal file
145
site/blog/htmnix.md
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
---
|
||||||
|
title: HTMNIX
|
||||||
|
description: How the absolutely cursed HTMNIX project works.
|
||||||
|
|
||||||
|
date: 2024-03-04
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- html
|
||||||
|
- nix
|
||||||
|
---
|
||||||
|
|
||||||
|
So, you may have seen the [HTMNIX](https://github.com/RGBCube/HTMNIX) project I've
|
||||||
|
been working on the last few weeks. If not, no worries. Here is a Nix snippet
|
||||||
|
that uses it:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>"Hello, Internet!"<.title>
|
||||||
|
<.head>
|
||||||
|
<body>
|
||||||
|
<p>"Yep, this is 100% Nix!"<.p>
|
||||||
|
|
||||||
|
<img.>{src="/foo.png"; alt="Attributes also work!";}
|
||||||
|
<.body>
|
||||||
|
<.html>
|
||||||
|
```
|
||||||
|
|
||||||
|
> (hightlight.js shits the bed while highlighting this abomination - just ignore it)
|
||||||
|
|
||||||
|
You are probably thinking furiously right now, maybe you've noticed something:
|
||||||
|
|
||||||
|
> Aha! In Nix, `<foo>` is used to find stuff from the Nix path like so:
|
||||||
|
>
|
||||||
|
> ```nix
|
||||||
|
> import <nixpkgs> {}
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> That means you have to add hundreds of elements to your Nix Path to make this work?
|
||||||
|
|
||||||
|
You are somewhat correct. But not quite.
|
||||||
|
|
||||||
|
Nix `<foo>` expressions actually boil down to a call of the builtin `__findFile`, like so:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
❯ nix-instantiate --parse --expr "<foo>"
|
||||||
|
|
||||||
|
(__findFile __nixPath "foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
> In case you didn't know, [`nix-instantiate`](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html)
|
||||||
|
> is a nice tool to see what your Nix code is desugared and un-precedence'd into.
|
||||||
|
|
||||||
|
Aha! So this means we can override the builtin `__findFile` and put whatever we would like in
|
||||||
|
its place. So this will work:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
__findFile = nixPath: name: {
|
||||||
|
content = "<${name}>";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
<foo>
|
||||||
|
```
|
||||||
|
|
||||||
|
Evaluating this (by running `nix eval -f test.nix`), we get `{ content = "<foo>"; }`
|
||||||
|
|
||||||
|
So, then. How do we make it work for multiple tags, all coming after one another
|
||||||
|
(and attribute sets, strings, etc.)?
|
||||||
|
|
||||||
|
Another hack! We need to set the [magic `__functor` attribute](https://noogle.dev/md/tutorials/functors)
|
||||||
|
of the attrset we return, so we can call our set and have it store the tags inside it (while also
|
||||||
|
preserving its callability!).
|
||||||
|
|
||||||
|
We can do that like so:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
__findFile = nixPath: name: {
|
||||||
|
content = "<${name}>";
|
||||||
|
__functor = self: next: self // {
|
||||||
|
content = self.content + next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
<foo>
|
||||||
|
"bar"
|
||||||
|
"baz"
|
||||||
|
```
|
||||||
|
|
||||||
|
Great news! When we evaluate this, we get `{ __functor = <LAMBDA>; content = "<foo>barbaz"; }`.
|
||||||
|
|
||||||
|
We can also add a case to check if the next element is a tag, and use its content if it is:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
__findFile = nixPath: name: {
|
||||||
|
content = "<${name}>";
|
||||||
|
__functor = self: next: self // {
|
||||||
|
content = self.content + (if next ? content then next.content else next);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
<foo>
|
||||||
|
"bar"
|
||||||
|
"baz"
|
||||||
|
<endfoo>
|
||||||
|
```
|
||||||
|
|
||||||
|
Enter another hack! We can utilize the `outPath` property that exists on derivations
|
||||||
|
and gets returned whenever you call `toString` with an attrset that has the property to make our code a little simpler:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
__findFile = nixPath: name: {
|
||||||
|
outPath = "<${name}>";
|
||||||
|
__functor = self: next: self // {
|
||||||
|
outPath = self.outPath + toString next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
<foo>
|
||||||
|
"bar"
|
||||||
|
"baz"
|
||||||
|
123
|
||||||
|
<endfoo>
|
||||||
|
```
|
||||||
|
|
||||||
|
We also got support for other types for free, as well!
|
||||||
|
|
||||||
|
These are all the hidden builtins that [HTMNIX](https://github.com/RGBCube/HTMNIX) depends on
|
||||||
|
and extends upon, making HTML in Nix an actually usable reality. It also
|
||||||
|
has extra logic like turning attribute sets into HTML tags, which is fairly trivial
|
||||||
|
compared to actaully discovering these hidden builtins in the first place.
|
||||||
|
|
||||||
|
You can read more about it in the project's README and
|
||||||
|
see [an example site using it](https://github.com/RGBCube/NixSite).
|
||||||
|
|
||||||
|
I might even try to port this site to HTMNIX to ensure it is usable with more complex setups :-)
|
||||||
|
|
||||||
|
Soon, maybe...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thanks for reading my first ever proper blog post! :-)
|
Loading…
Add table
Add a link
Reference in a new issue