this repo has no description

Compare changes

Choose any two refs to compare.

+2 -2
.github/workflows/rebuild.yml
···
name: Trigger Netlify rebuild
on:
schedule:
-
# Run every 4h
-
- cron: '0 */4 * * *'
+
# Run every 8h
+
- cron: '0 */8 * * *'
jobs:
trigger:
-3
.gitmodules
···
-
[submodule "themes/zerm"]
-
path = themes/zerm
-
url = https://github.com/ejmg/zerm.git
+11 -5
config.toml
···
theme = "zerm"
-
generate_feed = true
+
generate_feeds = true
minify_html = true
taxonomies = [
-
{name = "tags"},
+
{name = "tags", feed = true},
]
[markdown]
···
[extra]
author = "Hauleth"
-
for_hire = false
+
for_hire = true
theme_color = "blue"
+
+
source = "https://tangled.sh/hauleth.dev/blog"
logo_text = "~hauleth"
logo_home_link = "/"
···
# Menu items to display. You define a url and the name of the menu item.
main_menu = [
{url="https://plan.cat/~hauleth", name=".plan", rel="me"},
-
{url="https://twitter.com/hauleth", name="twitter", rel="me"},
-
{url="https://fosstodon.org/@hauleth", name="mastodon", rel="me"},
+
{url="https://fosstodon.org/@hauleth", name="toots", rel="me"},
+
{url="https://sr.ht/~hauleth", name="sourcehut", rel="me"},
{url="https://github.com/hauleth", name="github", rel="me"},
{url="https://gitlab.com/hauleth", name="gitlab", rel="me"},
]
···
[extra.twitter]
site = "@hauleth"
creator = "@hauleth"
+
+
[[extra.webrings]]
+
name = "Beambloggers"
+
url = "https://beambloggers.com/"
+1 -4
content/post/beam-process-memory-usage.md
···
[taxonomies]
tags = [
"beam",
-
"elixir",
-
"erlang",
-
"benchmarks",
-
"programming"
+
"performance"
]
+++
+6 -11
content/post/common-test-for-elixir.md
···
[taxonomies]
tags = [
-
"erlang",
"beam",
-
"elixir",
-
"testing",
-
"programming",
-
"common_test",
-
"commoner"
+
"testing"
]
[[extra.thanks]]
···
Just check [this out](/common-test-example/simple/index.html). This is example report
generated by the Common Test. As you can see it contains a lot of information in
-
quite readable format. Not only it contains informations about current run, but
+
quite readable format. Not only it contains information about current run, but
all previous runs as well, which is really handy when tracking regressions.
-
But can we store even more informations there? Yes, as CT includes simple
-
logging facility it is completely possible to log your own informations during
-
tests, for example, lets modify our test to log some informations:
+
But can we store even more information there? Yes, as CT includes simple
+
logging facility it is completely possible to log your own information during
+
tests, for example, lets modify our test to log some information:
```erlang
test_function_name(_Config) ->
···
2 = 1 + 1.
```
-
Now when we run tests again, then we will see more informations (even coloured)
+
Now when we run tests again, then we will see more information (even coloured)
in [our test log](/common-test-example/log/ct_run.ct@NiunioBook.2019-07-16_11.03.21/common-test-example.log.logs/run.2019-07-16_11.03.22/example_suite.test_function_name.html):
![Common Test log "Example message" on green background](/img/common-test/log.png)
+1 -5
content/post/eli5-ownership.md
···
[taxonomies]
tags = [
-
"rust",
-
"programming",
-
"ownership",
-
"eli5",
-
"borrowing"
+
"rust"
]
+++
+1 -4
content/post/elixir-application.md
···
[taxonomies]
tags = [
-
"elixir",
-
"erlang",
-
"beam",
-
"programming"
+
"beam"
]
+++
+71
content/post/jeopardy-world.md
···
+
+++
+
title = "Jeopardy! world"
+
date = 2025-07-28
+
+
[taxonomies]
+
tags = ["ai", "culture"]
+
+++
+
+
Some time ago, there was an anime available on Netflix — *Godzilla Singular
+
Point*. It wasn't a spectacular success, but it featured a plot device that I
+
think reflects something increasingly common today: you need to know the answer
+
to your question before you can ask it.
+
+
This is something I see all the time in the current wave of AI hype. You need to
+
know what the answer *should* be before you can write a useful prompt.
+
+
<!-- more -->
+
+
The issue I have with many AI use cases is this: unless you have specialized
+
knowledge about the topic you're asking about, you can't reliably tell the
+
difference between a solid AI answer and complete nonsense.
+
+
I've had a few discussions about this on various Discord servers. The example I
+
often use is this simple question posed to an AI:
+
+
> Does 6 character long identification number, that contains digits and upper
+
> case letters (with exception to 0, O, 1, I, and L) is enough to randomly
+
> assign unique identification numbers for 10 million records?
+
+
You can see for your self answer from ChatGPT [there][chatgpt].
+
+
At first glance, the answer looks valid and sensible. The math checks out. It
+
calculates the number of available combinations correctly. Everything seems
+
*fine*.
+
+
**BUT…**
+
+
There is huge issue there, and probably most of the people who have been working
+
with basic statistic or cryptography will notice it. ChatGPT (and any other AI
+
that I have tested out) fail to notice very important word there
+
+
> \[…] randomly \[…]
+
+
This single word invalidates the entire reasoning, despite the correct
+
calculations. Because of the [birthday problem][], the answer isn't feasible.
+
While it's technically possible to assign a unique ID to every record, doing so
+
randomly introduces a high probability of collisions.
+
+
- At around 35,000 generated IDs, there's already a 50% chance of a collision
+
- At around 90,000, the chance of at least one duplicate reaches 99%
+
+
So even though the math is correct, the logic fails under the randomness constraint.
+
+
## *Jeopardy!* world
+
+
This is my main issue with AI tools: if you already have knowledge about the
+
subject, you don’t really need to ask the AI. But if you don’t have that
+
knowledge, you have no reliable way of knowing whether the answer makes sense or
+
not. It’s like playing *Jeopardy!* — you need to know the answer before you can
+
phrase the right question.
+
+
In my view, AI is most useful in areas where the results can be quickly reviewed
+
and discarded if needed. That’s why the whole “vibe coding” (aka slop
+
generation) approach falls short. If you don’t have a good sense of what the
+
output should look like, you probably don’t have the expertise to verify it.
+
+
[And gods forbid you from allowing AI to do anything on production][replit-fuckup].
+
+
[chatgpt]: https://chatgpt.com/share/68879fe7-d4e0-8007-9a30-3a9e2ace791d
+
[birthday problem]: https://en.wikipedia.org/wiki/Birthday_problem
+
[replit-fuckup]: https://www.businessinsider.com/replit-ceo-apologizes-ai-coding-tool-delete-company-database-2025-7?op=1
+2 -3
content/post/log-all-the-things.md
···
[taxonomies]
tags = [
-
"elixir",
-
"programming",
+
"beam",
"observability"
]
···
)
```
-
As we can see there, the report contains informations like:
+
As we can see there, the report contains information like:
- `:label` - that describes type of the event
- `:report` - content of the "main" event
-1
content/post/stop-spreading-crap-at-my-home.md
···
[taxonomies]
tags = [
-
"programming",
"culture"
]
+++
-2
content/post/treachery-of-representation.md
···
[taxonomies]
tags = [
-
"programming",
-
"linguistic",
"culture"
]
+++
+2 -5
content/post/vim-for-elixir.md
···
[taxonomies]
tags = [
-
"elixir",
-
"erlang",
-
"vim",
-
"neovim",
-
"programming"
+
"beam",
+
"vim"
]
+++
+5 -7
content/post/who-watches-watchmen-i.md
···
[taxonomies]
tags = [
-
"elixir",
-
"programming",
-
"systemd",
-
"deployment"
+
"beam",
+
"systemd"
]
+++
···
systemd supports the second approach via [`sd_notify`][sd_notify]. The approach
there is simple - we have `NOTIFY_SOCKET` environment variable that contain path
-
to the Unix datagram socket, that we can use to send informations about state of
+
to the Unix datagram socket, that we can use to send information about state of
our application. This socket accept set of different messages, but right now,
for our purposes, we will focus only on few of them:
- `READY=1` - marks our service as ready, aka it is ready to do its work (for
example accept incoming HTTP connections in our example). It need to be sent
-
withing given timespan after start of the VM, otherwise the process will be
+
within given timespan after start of the VM, otherwise the process will be
killed and possibly restarted
- `STATUS=name` - sets status of our application that can be checked via
`systemctl status hello.service`, this allows us to have better insight into
···
Next thing is that we can do, is to [disable crash dumps generated by BEAM][crash].
While not strictly needed in this case, it is worth remembering, that it isn't
-
hard to achieve, it is just using `Environment=ERL_CRASH_DUMP_SECONDS=0`.
+
hard to achieve, just use `Environment=ERL_CRASH_DUMP_SECONDS=0`.
Our new, more secure, `hello.service` will look like:
+7 -9
content/post/who-watches-watchmen-ii.md
···
+++
title = "Who Watches Watchmen? - Part 2"
-
date = 2023-07-04
+
date = 2023-11-14
description = """
Continuation of travel into making systemd to work for us, not against us. This
···
[taxonomies]
tags = [
-
"elixir",
-
"programming",
-
"systemd",
-
"deployment"
+
"beam",
+
"systemd"
]
[[extra.thanks]]
name = "Nicodemus"
-
why = "Helping me with my poor English"
+
why = "helping me with my poor English"
+++
This is continuation of [Part I][part-i] where I described the basics of the
···
Requires=sockets.target
[Socket]
-
# We declare the name of the file descriptor here to simplify extraction in
-
# the application afterwards. By default it will be the socket name (so
-
# `hello-http` in our case), but `http` is much cleaner.
+
# We declare the name of the file descriptor here to simplify extraction
+
# in the application afterwards. By default it will be the socket name
+
# (so `hello-http` in our case), but `http` is much cleaner.
FileDescriptorName=http
ListenStream=80
Service=hello.service
+291
content/post/writing-tests.md
···
+
+++
+
date = 2023-11-20
+
title = "How do I write Elixir tests?"
+
+
draft = true
+
+
[taxonomies]
+
tags = [
+
"beam",
+
"testing"
+
]
+
+++
+
+
This post was created for myself to codify some basic guides that I use while
+
writing tests. If you, my dear reader, read this then there is one important
+
thing for you to remember:
+
+
These are **guides** not *rules*. Each code base is different deviations and are
+
expected and *will* happen. Just use the thing between your ears.
+
+
## `@subject` module attribute for module under test
+
+
While writing test in Elixir it is not always obvious what we are testing. Just
+
imagine test like:
+
+
```elixir
+
test "foo should frobnicate when bar" do
+
bar = pick_bar()
+
+
assert :ok == MyBehaviour.foo(MyImplementation, bar)
+
end
+
```
+
+
It is not obvious at the first sight what we are testing. And it is pretty
+
simplified example, in real world it can became even harder to notice what is
+
module under test (MUT).
+
+
To resolve that I came up with a simple solution. I create module attribute
+
named `@subject` that points to the MUT:
+
+
```elixir
+
@subject MyImplementation
+
+
test "foo should frobnicate when bar" do
+
bar = pick_bar()
+
+
assert :ok == MyBehaviour.foo(@subject, bar)
+
end
+
```
+
+
Now it is more obvious what is MUT and what is just wrapper code around it.
+
+
In the past I have been using `alias` with `:as` option, like:
+
+
```elixir
+
alias MyImplementation, as: Subject
+
```
+
+
However I find module attribute to be more visually outstanding and make it
+
easier for me to notice `@subject` than `Subject`. But your mileage may vary.
+
+
## `describe` with function name
+
+
That one is pretty basic, and I have seen that it is pretty standard for people:
+
when you are writing tests for module functions, then group them in `describe`
+
blocks that will contain name (and arity) of the function in the name. Example:
+
+
```elixir
+
# Module under test
+
defmodule Foo do
+
def a(x, y, z) do
+
# some code
+
end
+
end
+
+
# Tests
+
defmodule FooTest do
+
use ExUnit.Case, async: true
+
+
@subject Foo
+
+
describe "a/3" do
+
# Some tests here
+
end
+
end
+
```
+
+
This allows me to see what functionality I am testing.
+
+
Of course that doesn't apply to the Phoenix controllers, as there we do not test
+
functions, but tuples in form `{method, path}` which I then write as `METHOD
+
path`, for example `POST /users`.
+
+
## Avoid module mocking
+
+
In Elixir we have bunch of the mocking libraries out there, but most of them
+
have quite substantial issue for me - these prevent me from using `async: true`
+
for my tests. This often causes substantial performance hit, as it prevents
+
different modules to run in parallel (not single tests, *modules*, but that is
+
probably material for another post).
+
+
Instead of mocks I prefer to utilise dependency injection. Some people may argue
+
that "Elixir is FP, not OOP, there is no need for DI" and they cannot be further
+
from truth. DI isn't related to OOP, it just have different form, called
+
function arguments. For example, if we want to have function that do something
+
with time, in particular - current time. Then instead of writing:
+
+
```elixir
+
def my_function(a, b) do
+
do_foo(a, b, DateTime.utc_now())
+
end
+
```
+
+
Which would require me to use mocks for `DateTime` or other workarounds to make
+
tests time-independent. I would do:
+
+
```elixir
+
def my_function(a, b, now \\ DateTime.utc_now()) do
+
do_foo(a, b, now)
+
end
+
```
+
+
Which still provide me the ergonomics of `my_function/2` as above, but is way
+
easier to test, as I can pass the date to the function itself. This allows me to
+
run this test in parallel as it will not cause other tests to do weird stuff
+
because of altered `DateTime` behaviour.
+
+
## Avoid `ex_machina` factories
+
+
I have poor experience with tools like `ex_machina` or similar. These often
+
bring whole [Banana Gorilla Jungle problem][bgj] back, just changed a little, as
+
now instead of just passing data around, we create all needless structures for
+
sole purpose of test, even when they aren't needed for anything.
+
+
[bgj]: https://softwareengineering.stackexchange.com/q/368797
+
+
Start with example from [ExMachina README](https://github.com/beam-community/ex_machina#overview):
+
+
```elixir
+
defmodule MyApp.Factory do
+
# with Ecto
+
use ExMachina.Ecto, repo: MyApp.Repo
+
+
# without Ecto
+
use ExMachina
+
+
def user_factory do
+
%MyApp.User{
+
name: "Jane Smith",
+
email: sequence(:email, &"email-#{&1}@example.com"),
+
role: sequence(:role, ["admin", "user", "other"]),
+
}
+
end
+
+
def article_factory do
+
title = sequence(:title, &"Use ExMachina! (Part #{&1})")
+
# derived attribute
+
slug = MyApp.Article.title_to_slug(title)
+
%MyApp.Article{
+
title: title,
+
slug: slug,
+
# associations are inserted when you call `insert`
+
author: build(:user),
+
}
+
end
+
+
# derived factory
+
def featured_article_factory do
+
struct!(
+
article_factory(),
+
%{
+
featured: true,
+
}
+
)
+
end
+
+
def comment_factory do
+
%MyApp.Comment{
+
text: "It's great!",
+
article: build(:article),
+
author: build(:user) # That line is added by me
+
}
+
end
+
end
+
```
+
+
For start we can see a single problem there - we do not validate our factories
+
against our schema changesets. Without additional tests like:
+
+
```elixir
+
@subject MyApp.Article
+
+
test "factory conforms to changeset" do
+
changeset = @subject.changeset(%@subject{}, params_for(:article))
+
+
assert changeset.valid?
+
end
+
```
+
+
We cannot be sure that our tests test what we want them to test. And if we pass
+
custom attribute values in some tests it gets even worse, because we cannot be
+
sure if these are conforming either.
+
+
That mean that our tests may be moot, because we aren't testing against real
+
situations, but against some predefined state.
+
+
Another problem is that if we need to alter the behaviour of the factory it can
+
became quite convoluted. Imagine situation when we want to test if comments by
+
author of the post have some special behaviour (for example it has some
+
additional CSS class to be able to mark them in CSS). That require from us to do
+
some dancing around passing custom attributes:
+
+
```elixir
+
test "comments by author are special" do
+
post = insert(:post)
+
comment = insert(:comment, post: post, author: post.author)
+
+
# rest of the test
+
end
+
```
+
+
And this is simplified example. In the past I needed to deal with situations
+
where I was creating a lot of data to pass through custom attributes to make
+
test sensible.
+
+
Instead I prefer to do stuff directly in code. Instead of relying on some
+
"magical" functions provided by some "magical" macros from external library I
+
can use what I already have - functions in my application.
+
+
Instead of:
+
+
```elixir
+
test "comments by author are special" do
+
post = insert(:post)
+
comment = insert(:comment, post: post, author: post.author)
+
+
# rest of the test
+
end
+
```
+
+
Write:
+
+
```elixir
+
test "comments by author are special" do
+
author = MyApp.Users.create(%{
+
name: "John Doe",
+
email: "john@example.com"
+
})
+
post = MyApp.Blog.create_article(%{
+
author: author,
+
content: "Foo bar",
+
title: "Foo bar"
+
})
+
comment = MyApp.Blog.create_comment_for(article, %{
+
author: author,
+
content: "Foo bar"
+
})
+
+
# rest of the test
+
end
+
```
+
+
It may be a little bit more verbose, but it makes tests way more readable in my
+
opinion. You have all details just in place and you know what to expect. And if
+
you need some piece of data in all (or almost all) tests within
+
module/`describe` block, then you can always can use `setup/1` blocks. Or you
+
can create function per module that will generate data for you. As long as your
+
test module is self-contained and do not receive "magical" data out of thin air,
+
it is ok for me. But `ex_machina` is, in my opinion, terrible idea brought from
+
Rails world, that make little to no sense in Elixir.
+
+
If you really need such factories, then just write your own functions that will
+
use your contexts instead of relying on another library. For example:
+
+
```elixir
+
import ExUnit.Assertions
+
+
def create_user(name, email \\ nil, attrs \\ %{}) do
+
email = email || "#{String.replace(name, " ", ".")}@example.com"
+
attrs = Map.merge(attrs, %{name: name, email: email})
+
+
assert {:ok, user} = MyApp.Users.create(attrs)
+
+
user
+
end
+
+
# And so on…
+
```
+
+
This way you do not need to check if all tests use correct validations any
+
longer, as your system will do that for you.
+2 -3
content/post/writing-vim-plugin.md
···
date = 2019-11-04T18:21:18+01:00
description = """
Article about writing Vim plugins, but not about writing Vim plugins. It is
-
how to concieve plugin, how to go from an idea to the full fledged plugin."""
+
how to conceive plugin, how to go from an idea to the full fledged plugin."""
[taxonomies]
tags = [
-
"vim",
-
"viml"
+
"vim"
]
+++
+6 -7
flake.lock
···
"systems": "systems"
},
"locked": {
-
"lastModified": 1687709756,
-
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
+
"lastModified": 1731533236,
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
-
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
···
},
"nixpkgs": {
"locked": {
-
"lastModified": 1687103638,
-
"narHash": "sha256-dwy/TK6Db5W7ivcgmcxUykhFwodIg0jrRzOFt7H5NUc=",
-
"path": "/nix/store/p57nnwjhfvmsn75y9l6hn00pl2xv7ivm-source",
-
"rev": "91430887645a0953568da2f3e9a3a3bb0a0378ac",
+
"lastModified": 0,
+
"narHash": "sha256-cnL5WWn/xkZoyH/03NNUS7QgW5vI7D1i74g48qplCvg=",
+
"path": "/nix/store/h15y13p2w17dhpiyh8pk42v1k4c38a0h-source",
"type": "path"
},
"original": {
+11 -4
flake.nix
···
nativeBuildInputs = [
pkgs.zola
-
pkgs.gitMinimal
];
buildPhase = ''
-
git submodule update --init --recursive --depth=1
-
zola build -o $out
+
zola --version
+
zola build --output-dir $out
'';
dontInstall = true;
};
in
{
+
apps.publish = let
+
program = pkgs.writeShellScript "publish" ''
+
cp -r ${self.packages.${system}.blog} public
+
'';
+
in {
+
type = "app";
+
program = "${program}";
+
};
packages = {
inherit blog;
};
···
inputsFrom = [ blog ];
packages = [
-
pkgs.netlify-cli
+
# pkgs.netlify-cli
pkgs.vale
pkgs.mdl
];
+15 -4
netlify.toml
···
command = "zola build"
publish = "public/"
+
[build.environment]
+
ZOLA_VERSION = "0.20.0"
+
[context.deploy-preview]
command = "zola build --drafts --base-url $DEPLOY_PRIME_URL"
···
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
-
Content-Security-Policy = "default-src 'self'; script-src 'self' https://plausible.io; connect-src https://plausible.io; img-src https:"
+
X-Clacks-Overhead = "GNU Terry Pratchett"
+
Content-Security-Policy = "default-src 'self'; script-src 'self'; connect-src 'self'; img-src https:"
Permissions-Policy = "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=()"
Referrer-Policy = "no-referrer-when-downgrade"
···
to = "https://fosstodon.org/.well-known/webfinger?resource=acct:hauleth@fosstodon.org"
status = 200
+
[[redirects]]
+
from = "/js/script.js"
+
to = "https://plausible.io/js/script.js"
+
status = 200
+
+
[[redirects]]
+
from = "/api/event"
+
to = "https://plausible.io/api/event"
+
status = 200
+
[[plugins]]
package = "netlify-plugin-webmentions"
[plugins.inputs]
feedPath = "atom.xml"
-
-
[[plugins]]
-
package = "netlify-plugin-submit-sitemap"
-12
sass/_header.scss
···
@import "variables";
-
@mixin menu {
-
position: absolute;
-
background: var(--background);
-
box-shadow: var(--shadow);
-
color: white;
-
border: 2px solid;
-
margin: 0;
-
padding: 10px;
-
list-style: none;
-
z-index: 99;
-
}
-
.header {
@media print {
display: none;
+15 -9
sass/_main.scss
···
@import "variables";
+
:root {
+
font-size: calc(1rem + 0.05vw);
+
line-height: 1.54;
+
color: var(--color);
+
+
@media print {
+
color: #000;
+
line-height: 1.2;
+
font-size: 10pt;
+
}
+
}
+
html {
box-sizing: border-box;
}
···
margin: 0;
padding: 0;
font-family: ui-monospace, monospace;
-
font-size: 12pt;
-
line-height: 1.54;
background-color: var(--background);
-
color: var(--color);
// text-shadow: 0 0 3px currentcolor;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-webkit-overflow-scrolling: touch;
-webkit-text-size-adjust: 100%;
-
-
@media print {
-
color: #000;
-
line-height: 1.2;
-
font-size: 10pt;
-
}
}
h1, h2, h3, h4, h5, h6 {
···
padding: 1px 6px;
margin: 0 2px;
font-size: .95rem;
+
hyphens: none;
}
pre {
···
display: block;
position: absolute;
left: -25px;
+
top: .1em;
color: var(--accent);
}
}
···
// Todo change it to ::marker when Safari will support it
&::before {
+
margin-left: -2rem;
content: counters(li, ".") ". ";
}
}
+5 -2
sass/_post.scss
···
.post {
width: 100%;
-
text-align: left;
+
text-align: justify;
+
text-wrap: pretty;
+
hyphens: auto;
+
hyphenate-limit-chars: 10;
margin: 20px auto;
padding: 20px 0 0 0;
+
@media (max-width: $tablet-max-width) {
margin: 0 auto;
···
&-content {
margin-top: 30px;
-
position: relative;
}
&-cover {
+40
sass/_rings.scss
···
+
.rings {
+
margin-top: 1rem;
+
text-align: center;
+
+
details > summary {
+
list-style: none;
+
cursor: pointer;
+
+
&::before, &::after {
+
margin: 0 .5rem;
+
}
+
+
&::before { content: '▶'; }
+
&::after { content: '◀'; }
+
+
&::-webkit-details-marker {
+
display: none;
+
}
+
}
+
+
details[open] {
+
summary {
+
&::before, &::after {
+
content: '▼';
+
}
+
+
margin-bottom: 1rem;
+
}
+
}
+
+
ul {
+
list-style: none;
+
margin: 0;
+
}
+
+
li {
+
margin: 0;
+
padding: 0;
+
}
+
}
+1
sass/style.scss
···
@import 'post';
@import 'pagination';
@import 'footer';
+
@import 'rings';
:root {
--phoneWidth: (max-width: #{$phone-max-width + 1px});
+9
static/_headers
···
+
*
+
Permission-Policy: interest-cohort=()
+
X-Frame-Options: DENY
+
X-XSS-Protection: 1; mode=block
+
X-Clacks-Overhead: GNU Terry Pratchet
+
X-Clacks-Overhead: GNU Joe Armstrong
+
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self'; frame-src 'none'; frame-ancestors 'none'; form-action 'none'; base-uri 'self';
+
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=()
+
Referrer-Policy: strict-origin-when-cross-origin
+3
static/_redirects
···
+
/js/script.js https://plausible.io/js/script.js 200
+
/api/event https://plausible.io/api/event 200
+
/.well-known/webfinger https://fosstodon.org/.well-known/webfinger?resource=acct:hauleth@fosstodon.org 200
+9
statichost.yml
···
+
image: ghcr.io/getzola/zola:v0.21.0
+
command: build
+
public: public
+
image_entrypoint: true
+
+
# TODO: Usa nix there
+
# image: ghcr.io/lix-project/lix:latest
+
# command: nix --extra-experimental-features nix-command --extra-experimental-features flakes run .#publish
+
# public: public
+45 -7
templates/index.html
···
{% extends "zerm/templates/index.html" %}
{% block fonts %}
-
{% endblock %}
+
{% endblock fonts %}
{% block rss %}
{%- if config.generate_feed -%}
-
<link rel="alternate" type="application/atom+xml" title="{{ config.title }} Feed" href="{{ get_url(path=config.feed_filename) | safe}}">
+
<link rel="alternate" type="application/atom+xml" title="{{ config.title }} Feed" href="{{ get_url(path="atom.xml", trailing_slash=false) }}">
{%- endif -%}
-
{% endblock %}
+
{% endblock rss %}
{% block og_preview %}
+
{%- if section -%}
+
<link rel="canonical" href="{{ section.permalink }}" />
+
{%- elif page -%}
+
<link rel="canonical" href="{{ page.permalink }}" />
+
{%- else -%}
+
<link rel="canonical" href="{{ current_url }}" />
+
{%- endif -%}
{{ social::og_preview() }}
{%- if config.extra.twitter.site -%}
···
{%- if config.extra.webmention -%}
<link rel="webmention" href="{{ config.extra.webmention }}" >
{%- endif -%}
-
{% endblock %}
+
{% endblock og_preview %}
{% block copyright %}
<div class="copyright">
<div class="copyright--user">{{ config.extra.copyright | safe }}</div>
<div class="copyright--tracking">
-
Public tracking available at <a href="https://plausible.io/hauleth.dev">Plausible.io</a>
+
public tracking available at <a href="https://plausible.io/hauleth.dev">Plausible.io</a>
+
</div>
+
<div class="copyright--source">
+
<a href="{{ config.extra.source }}">source code</a>
</div>
</div>
{% endblock copyright %}
{% block script %}
-
<script async defer data-domain="hauleth.dev" src="https://plausible.io/js/plausible.js"></script>
+
<script defer data-domain="hauleth.dev" src="/js/script.js"></script>
{% endblock script %}
{% block css %}
···
</ul>
</nav>
</header>
-
{% endblock %}
+
{% endblock header %}
+
+
{% block general_meta %}
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
+
<meta name="viewport" content="width=device-width, initial-scale=1">
+
{%- if page.title -%}
+
<meta name="description" content="{{ config.description }} {{ page.title }} {{ page.description }}"/>
+
{%- else -%}
+
<meta name="description" content="{{ config.description }}"/>
+
{%- endif -%}
+
+
{%- if page.taxonomies.tags or page.taxonomies.categories -%}
+
<meta name="keywords" content="
+
{%- if page.taxonomies.categories -%}
+
{%- for cat in page.taxonomies.categories -%}
+
{{ cat }}, {% endfor -%}
+
{%- endif -%}
+
+
{%- if page.taxonomies.tags -%}
+
{%- for tag in page.taxonomies.tags -%}
+
{%- if loop.last -%}
+
{{ tag }}
+
{%- else -%}
+
{{ tag }}, {% endif -%}
+
{%- endfor -%}
+
{%- endif -%}
+
" />
+
{%- endif -%}
+
{% endblock general_meta %}
+16
templates/macros/extended_footer.html
···
+
{% macro extended_footer() %}
+
<div class="rings">
+
<details>
+
<summary>Webrings</summary>
+
<ul>
+
{%- for ring in config.extra.webrings -%}
+
<li>
+
<a href="{{ ring.url }}/prev?referrer={{ get_url(path = "/") | safe }}">&laquo;</a>
+
<a href="{{ ring.url }}">{{ ring.name }}</a>
+
<a href="{{ ring.url }}/next?referrer={{ get_url(path = "/") | safe }}">&raquo;</a>
+
</li>
+
{%- endfor -%}
+
</ul>
+
</details>
+
</div>
+
{% endmacro extended_footer %}
+1 -1
templates/robots.txt
···
User-agent: *
-
Disallow: /cv/
+
Disallow: /cv/ /404/ /common-test-example/
Allow: /
Sitemap: {{ get_url(path="sitemap.xml") }}
+65 -44
templates/shortcodes/cv.md
···
## Personal information
Email:
-
<lukasz@niemier.pl>
+
<~@hauleth.dev>
Website:
<https://hauleth.dev>
···
## Experience
-
- Prograils - Junior Developer - 2013
-
- Nukomeet - Full-stack Developer - 2015-2016
+
- Hauleth.dev - Consultant - 2021+
+
+ DockYard/Karambit.ai - 2025
+
* Architectural analysis of Karambit product
+
* Prepared security analysis with detailed report with fixes
+
+ Eiger - 2022-2023
+
* Forte.io
+
- Implemented Interledger protocol for cross-chain transactions
+
* Aleo Blockchain
+
- Implemented GraphQL API for the on-chain data
+
- Created syntax colouring library for Aleo assembly-like language
+
for smart contracts
+
+ Erlang Solutions/Kloeckner GmbH - 2021 - Consultant for Elixir, Ruby,
+
and SQL (PostgreSQL)
+
* Optimised DB query performance by providing PostgreSQL structure
+
analysis and improving indices usage
+
+ Remote Inc. - Senior Backend Engineer - 2020-2021
+
* Architectural analysis of existing codebase
+
+ Kobil GmbH - Erlang/Elixir Developer - 2019-2020
+
* Maintained MongoDB driver for Elixir
+
* Implemented transactions for MongoDB driver in Elixir
+
- Supabase - 2023-2025
+
+ Logflare - logs aggregation service:
+
* Implemented on-the-fly decompression of incoming data that improved
+
ingestion possibilities and reduced transfer usage (created library
+
[`plug_caisson`][] for that purpose)
+
* Implemented DataDog-compatible ingestion endpoint for seamless
+
transition from DataDog provider to Logflare
+
* Improved BigQuery pipeline workflow to reduce congestion on database
+
connections
+
* Added support for AWS Cloud Events metadata extraction
+
* Improved CI usage by splitting different actions to separate steps ran
+
in parallel
+
* Replaced dynamic generation of connection modules for PostgreSQL
+
storage system with Ecto's dynamic repositories to avoid atom exhaustion
+
+ Supavisor - a cloud-native, multi-tenant Postgres connection pooler
+
* Deployment management
+
* Optimised metrics gathering system that resulted in an order of
+
magnitude performance boost
+
* Updated used OTP and Elixir versions from OTP 24 to OTP 27 and Elixir
+
from 1.14 to 1.18
+
* Reduced usage of mocking in tests to improve tests performance and
+
volatility, resulting in reduced CI usage and improved developer
+
experience
+
* Implemented e2e tests against existing Node.js PostgreSQL clients to
+
improve production issues
+
* Implemented multi-region deployment system to provide blue/green
+
deployments
+
* Improved system observability features by making it more resilient and
+
performant
+
* Replaced usage of `ct_slave` with newer `peer` module in OTP
- AppUnite - Full-stack Developer/DevOps - 2016-2019:
+ JaFolders/AlleFolders
* 2x performance improvement by optimising PostgreSQL usage
* Reduced geo-queries using PostGIS thanks to better indices and
-
materialised views usage
-
* Implementation of the UI and brochure viewer in Vue and SVG
+
materialised views usage
+
* Implemented UI and brochure viewer in Vue and SVG
+ OneMedical/Helium Health
* Architectural redesign of application from Rails/MongoDB to
-
Phoenix/PostgreSQL
-
* Preparing hybrid deployment with on-premise/in-cloud system
-
* Migration of the existing deployments from MongoDB to PostgreSQL
-
- Kobil GmbH - Erlang/Elixir Developer - 2019-2020
-
+ Maintaining MongoDB driver for Elixir
-
+ Implemented transactions for MongoDB driver in Elixir
-
- Remote Inc. - Senior Backend Engineer - 2020-2021
-
- Hauleth.dev - Consultant - 2021+
-
+ Erlang Solutions/Kloeckner GmbH - 2021 - Consultant for Elixir, Ruby, and SQL (PostgreSQL)
-
* Substantial query performance optimisations by optimising PostgreSQL indices
-
- Eiger - Senior Backend Engineer - 2022-2023
-
+ Forte.io
-
- Implementation of the Interledger protocol for cross-chain transactions
-
+ Aleo Blokchain
-
- Implementation of the GraphQL API for the on-chain data
+
Phoenix/PostgreSQL
+
* Prepared hybrid deployment with on-premise/in-cloud system
+
* Migrated of the existing deployments from MongoDB to PostgreSQL
+
- Nukomeet - Full-stack Developer - 2015-2016
+
- Prograils - Junior Developer - 2013
### Organisations
···
* Organizer - 2015
+ UEFA Championship 2012 - Poland-Ukraine
* ICT Accreditation support
+
- Times Person of the Year - 2006
### Languages
···
- GitHub: <https://github.com/hauleth>
- GitLab: <https://gitlab.com/hauleth>
-
- StackOverflow: <https://stackoverflow.com/users/1017941/hauleth>
+
- SourceHut: <https://sr.ht/~hauleth>
+
- StackOverflow: <https://stackoverflow.com/u/1017941>
### Notable contributions
···
+ `is_struct/1`
+ Fixing module inspection on case-insensitive file systems
+ Support for parsing extra arguments via `mix eval` and `eval` command in
-
release
+
release
- Erlang OTP:
+ Support for custom devices in `logger_std_h`
+ Fixing `socket` module to support broader set of protocols (for example
···
projects
- <https://github.com/hauleth/mix_unused> - Mix compiler for detecting unused
code
-
- Elixir's Logger implementation in 1.10+
- <https://github.com/open-telemetry/opentelemetry-erlang> - maintainer of
the Erlang's OpenTelemetry implementation
- Vim plugins:
···
+ <https://gitlab.com/hauleth/qfx.vim> - display signs next to QF matches
### Languages and Frameworks
-
-
Expert:
- Elixir
+ Phoenix
···
+ OpenTelemetry collaborator
+ EEF Member
+ OTP contributor
-
-
Advanced:
-
+
- Nix/NixOS
- Rust
-
- C
- PostgreSQL
- sh/Bash
- Ruby
+ Ruby on Rails
-
Fluent:
-
-
- C++
-
- JavaScript
-
### Technologies
-
Expert:
-
- Git
- Vim
-
-
Advanced:
-
- HashiStack
+ Terraform
+ Consul
···
- TDD/BDD methodologies
- Property testing
-
Fluent:
-
-
- SaltStack
-
- Puppet
-
## Other
- Viking reenactor
- Keyboard fan
- Sci-fi/Fantasy fan and Poznań's Sci-fi/Fantasy club member
+
+
[`plug_caisson`]: https://github.com/supabase/plug_caisson
+1
themes/zerm/.gitignore
···
+
public
+22
themes/zerm/LICENSE.md
···
+
The MIT License (MIT)
+
+
Copyright (c) 2019 elias julian marko garcia
+
Copyright (c) 2019 Paweł Romanowski
+
Copyright (c) 2019 panr
+
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
+
this software and associated documentation files (the "Software"), to deal in
+
the Software without restriction, including without limitation the rights to
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+
the Software, and to permit persons to whom the Software is furnished to do so,
+
subject to the following conditions:
+
+
The above copyright notice and this permission notice shall be included in all
+
copies or substantial portions of the Software.
+
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+61
themes/zerm/README.md
···
+
# zerm
+
+
a minimalist and dark theme for [Zola](https://getzola.org).
+
+
![Screenshot](../master/zerm-preview.png?raw=true)
+
+
[**Live Preview!**](https://zerm.ejmg.now.sh/)
+
+
Largely a port of Radek Kozieł's [Terminal
+
Theme](https://github.com/panr/hugo-theme-terminal) for Hugo. 4/5ths of my way
+
through porting this theme, I discovered Paweł Romanowski own independent fork
+
for Zola, [Terminimal](https://github.com/pawroman/zola-theme-terminimal),
+
which helped me get the PostCSS to Sass styling conversion done more
+
quickly. My sincerest thanks to both of you!
+
+
## differences
+
+
This theme is largely true to the original by Radek, but there are some mild
+
differences. They are almost all stylistic in nature and are intended to
+
emphasize minimalism even more. Some of them are as follows:
+
- tags are now included in a post's meta data.
+
- no post image previews.
+
- categories are included in the taxonomy.
+
- bullet points have slightly more margin and different symbols for nesting.
+
- no social media or comment support.
+
+
Some of these might be added later and [PR's are always
+
welcomed](https://github.com/ejmg/zerm/pulls).
+
+
## configuration
+
+
Please follow the Zola documentation for [how to use a
+
theme](https://www.getzola.org/documentation/themes/installing-and-using-themes/#installing-a-theme).
+
+
In `config.toml`, you will find all values for customization that are supported
+
thus far have documentation explaining how they are used. If there is any confusion or something is not working as intended, [please open an issue](https://github.com/ejmg/zerm/issues)!
+
+
## math
+
You can use KaTeX for mathematical typesetting.
+
Assets are only available if you opt-in on a per-page level through
+
a single line (`math=true`) on the extra section of the page frontmatter.
+
+
``` md
+
# index.md
+
+++
+
title="this page title"
+
...
+
+
[extra]
+
math=true
+
+++
+
+
Content
+
```
+
+
Pages wich doesn't opt-in are not affected in any way, so you doesn't have
+
to worry about any performance hit.
+
+
## license
+
+
MIT. See `LICENSE.md` for more details.
+131
themes/zerm/config.toml
···
+
# The URL the site will be built for
+
base_url = "/"
+
+
# Used in RSS by default
+
title = "My blog!"
+
description = "placeholder description text for your blog!"
+
+
# The default language, used in RSS
+
# TODO: I would love to support more languages and make this easy to handle
+
# with other facets of the theme.
+
default_language = "en"
+
+
# Whether to generate a RSS feed automatically
+
generate_feed = true
+
# 'atom.xml' (default if unspecified) and 'rss.xml' are officially supported
+
# values for feed_filename in this theme. All other filenames will assume a
+
# link type of 'application/rss+xml'.
+
# feed_filename = "atom.xml"
+
+
# Theme name to use.
+
# NOTE: should not need to mess with this if you are using zerm directly, i.e. cloning the
+
# repository at root and not using as directed by the Zola docs via themes/ directory.
+
# theme = ""
+
+
# Whether to automatically compile all Sass files in the sass directory
+
compile_sass = true
+
+
# Whether to do syntax highlighting
+
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
+
highlight_code = true
+
+
# Syntax highlighting theme. See:
+
# https://www.getzola.org/documentation/getting-started/configuration/#syntax-highlighting
+
# for more information and themes built into Zola.
+
highlight_theme = "axar" # Other dark themes that work: "1337", "agola-dark",
+
# "visual-studio-dark"
+
+
# Whether to build a search index to be used later on by a JavaScript library
+
build_search_index = false
+
+
# Built in taxonomies of zerm.
+
taxonomies = [
+
{name = "tags"},
+
{name = "categories"},
+
]
+
+
[extra]
+
# Put all your custom variables here
+
#
+
# Many configurations are taken directly from Terminal's config.toml
+
# ---------------------------------------------------------
+
+
# Author name to be added to posts, if enabled.
+
author = "you!"
+
+
# Show author's name in blog post meta data.
+
show_author = false
+
+
# Show categories a blog post is marked with in its meta data.
+
show_categories = true
+
+
# Show tags a blog post is marked with in its meta data.
+
show_tags = true
+
+
# Theme color. You can have any color you want, so long as it's...
+
# ["orange", "blue", "red", "green", "pink"]
+
theme_color = "orange"
+
+
# Custom css to style over the defaults. This is useful when you only have a
+
# few small tweaks to make rather than a major rehaul to the theme.
+
# It would be best to make this a proper .sass or .scss file in sass/ rather
+
# than placing in static/
+
# custom_css = "custom.css"
+
+
# How many menu items to show on desktop. if you set this to 0, only submenu
+
# button will be visible.
+
show_menu_items = 2
+
+
# set theme to full screen width.
+
full_width = false
+
+
# center theme with default width.
+
center = false
+
+
# set a custom favicon. Must be placed in root of static/ directory...
+
# favicon = ""
+
+
+
# Set a custom preview image for your website when posted as a link.
+
# Must be placed in root of static/ directory...
+
# og_preview_img = ""
+
+
# Copyright notice if desired. Defaults to
+
# copyright = "copyright notice here"
+
+
# What is displayed in the top left corner of the website. Default is zerm.
+
logo_text = "zerm"
+
+
# Link in logo. Default returns you to $BASE_URL.
+
logo_home_link = "/"
+
+
# Menu items to display. You define a url and the name of the menu item.
+
# NOTE: `$BASE_URL/` must be included in the url name.
+
main_menu = [
+
{url="/about/", name="about"},
+
{url="/contact/", name="contact"},
+
{url="https://google.com", name="Google", external=true},
+
]
+
+
# Displayed as title of drop-down menu when size of main_menu > show_menu_items.
+
menu_more = "show more"
+
+
# Displayed after teaser text for a blog post.
+
read_more = "read more"
+
+
# not currently used from previous theme, but leaving here for now in case I
+
# feel like adding it.
+
read_other_posts = "read other posts"
+
+
+
# Enable math typesetting with KaTeX
+
# Show math in pages with `math=true` in the TOML frontmatter
+
enable_katex = true
+
+
# Options for disqus
+
disqus = { enabled=false, short_name="" }
+
+
# generate Table of Contents for all articles
+
# Table of Contents can be generated for individual articles
+
# by adding `ToC = true` in [extra] section in frontmatter
+
# ToC = true
+6
themes/zerm/content/_index.md
···
+
+++
+
sort_by = "date"
+
transparent = true
+
paginate_by = 3
+
insert_anchor_links = "right"
+
+++
+10
themes/zerm/content/about/_index.md
···
+
+++
+
title = "about"
+
path = "about"
+
+
[extra]
+
date = 2019-03-21
+
+++
+
+
Yet another theme for yet another static site generator; that said, I hope you
+
like it.
+14
themes/zerm/content/contact/_index.md
···
+
+++
+
title="contact"
+
description="a basic demo of zola. Does it work?"
+
+
[extra]
+
date=2019-03-26
+
+++
+
+
# some links
+
+
- [zola, the static site generator written in rust](https://getzola.org)
+
- [source code for zerm](https://github.com/ejmg/zerm)
+
- [Terminal, the theme zerm was derived from](https://github.com/panr/hugo-theme-terminal)
+
- [Terminimal, another theme for zola based on Terminal](https://github.com/pawroman/zola-theme-terminimal)
+65
themes/zerm/content/demo/index.md
···
+
+++
+
title="demo"
+
description="a basic demo of zola."
+
date=2019-08-06
+
+
[taxonomies]
+
tags = ["demo", "zola", "highlighting"]
+
categories = ["programming", "wu tang",]
+
+
[extra]
+
+++
+
+
Here's a general demo of Zola and how things look with zerm.
+
+
# Header I
+
+
Inline code: `println!("Wu Tang!");`
+
+
Zola has built in syntax highlighting. If there's not a theme you like, you can
+
easily add more.
+
+
zerm uses Fira Code fonts, which means we get ligatures in addition to
+
Zola's powerful syntax highlighting ✨.
+
+
```rs
+
fn foo(arg: String) -> Result<u32, Io::Error> {
+
println!("Nice!"); // TODO: the thingy
+
if 1 != 0 {
+
println!("How many ligatures can I contrive??");
+
println!("Turns out a lot! ==> -/-> <!-- <$> >>=");
+
}
+
Ok(42)
+
}
+
```
+
+
## Header II
+
+
Want block quotes? We got block quotes.
+
+
Remember the wise words of Ras Kass:
+
+
> In Hotel Rwanda, reminder to honor these street scholars who ask why
+
U.S. Defense is twenty percent of the tax dollar. Bush gave 6.46 billion to
+
Halliburton for troops support efforts in Iraq; meanwhile, the hood is hurting,
+
please believe that.
+
>
+
> -- "Verses", _Wu-Tang Meets The Indie Culture_
+
+
### Header III
+
+
| members | age | notable album | to be messed with? |
+
|------------------|-----|----------------------------------------------|-------------------------------------------------------------------------|
+
| GZA | 52 | Liquid Swords | no |
+
| ODB | 35 | Return to the 36 Chambers: The Dirty Version | absolutely not |
+
| Raekwon Da Chef | 49 | Only Built 4 Cuban Linx... | `"no"` that's spanish for "no" |
+
| Ghostface Killah | 49 | Fishscale | i swear you keep asking that question and the answer ain't gonna change |
+
| Inspectah Deck | 49 | CZARFACE | `protect ya neck, boy` |
+
+
+
#### Header IV
+
+
Here's a video of my rabbit, Smalls, loaf'n to lofi beats:
+
+
{{ youtube(id="UUpuz8IObcs") }}
+
+13
themes/zerm/content/fiz/index.md
···
+
+++
+
title="fiz"
+
description="a basic demo of zola. Does it work?"
+
date=2019-03-25
+
author="elias"
+
+
[taxonomies]
+
tags = ["rust", "test"]
+
# categories = ["misc."]
+
+++
+
+
+
Foo Bar Buzz Fizz Qux Fum
+21
themes/zerm/content/technology_is_hell/index.md
···
+
+++
+
title="technology is hell!"
+
description="Yet another blog post ranting about XYZ technology for ABC reasons"
+
date=2019-03-26
+
+
[taxonomies]
+
tags = ["rust", "test"]
+
categories = ["misc."]
+
+++
+
+
+
Nullam eu ante vel est convallis dignissim. Fusce suscipit, wisi nec facilisis
+
facilisis, est dui fermentum leo, quis tempor ligula erat quis odio. Nunc
+
porta vulputate tellus. Nunc rutrum turpis sed pede. Sed bibendum. Aliquam
+
posuere. Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada
+
massa, quis varius mi purus non odio. Pellentesque condimentum, magna ut
+
suscipit hendrerit, ipsum augue ornare nulla, non luctus diam neque sit amet
+
urna. Curabitur vulputate vestibulum lorem. Fusce sagittis, libero non
+
molestie mollis, magna orci ultrices dolor, at vulputate neque nulla lacinia
+
eros. Sed id ligula quis est convallis tempor. Curabitur lacinia pulvinar
+
nibh. Nam a sapien.
+57
themes/zerm/content/using_katex/index.md
···
+
+++
+
title="Using KaTeX for mathematical typesetting"
+
date=2021-06-16
+
+
[taxonomies]
+
categories=["test"]
+
tags=["math", "zola"]
+
+
[extra]
+
math=true
+
+++
+
+
The usual way to include LaTeX is to use `$$`, as shown in the examples below.
+
+
These examples are taken from <http://khan.github.io/KaTeX/>
+
+
### Example 1
+
If the text between `$$` contains newlines it will rendered in display mode:
+
```
+
$$
+
f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
+
$$
+
```
+
+
$$
+
f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
+
$$
+
+
### Example 2
+
+
```
+
$$
+
\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }
+
$$
+
+
1(ϕ5−ϕ)e25π=1+e−2π1+e−4π1+e−6π1+e−8π1+⋯ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }
+
```
+
+
$$
+
\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }
+
$$
+
+
1(ϕ5−ϕ)e25π=1+e−2π1+e−4π1+e−6π1+e−8π1+⋯ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }
+
+
### Example 3
+
+
```
+
$$
+
1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}, \quad\quad \text{for }\lvert q\rvert<1.
+
$$
+
```
+
+
$$
+
1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}, \quad\quad \text{for }\lvert q\rvert<1.
+
$$
+
+
+42
themes/zerm/content/waz/index.md
···
+
+++
+
title="waz"
+
description="a basic demo of zola. Does it work? This old man, he played one. He played knick knack on my drum."
+
date=2019-03-27
+
+
[taxonomies]
+
tags = ["rust", "test", "zola"]
+
categories = ["programming", "misc.", "programming languages"]
+
+
[extra]
+
+++
+
+
# Hello Hello
+
+
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec hendrerit
+
tempor tellus. Donec pretium posuere tellus. Proin quam nisl, tincidunt et,
+
mattis eget, convallis nec, purus. Cum sociis natoque penatibus et magnis dis
+
parturient montes, nascetur ridiculus mus. Nulla posuere. Donec vitae dolor.
+
Nullam tristique diam non turpis. Cras placerat accumsan nulla. Nullam
+
rutrum. Nam vestibulum accumsan nisl.
+
+
+
```python
+
def foo(bar, **kwargs):
+
print("yo, this is nice!")
+
```
+
+
## a list
+
+
* Donec hendrerit tempor tellus.
+
* Nam a sapien.
+
* Phasellus at dui in ligula mollis ultricies.
+
* Mauris mollis tincidunt felis.
+
* Nullam rutrum.
+
+
### Yet another list
+
1. Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada massa,
+
quis varius mi purus non odio.
+
2. Donec hendrerit tempor tellus.
+
3. Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada massa,
+
quis varius mi purus non odio.
+
+69
themes/zerm/content/zerm/index.md
···
+
+++
+
title="what is zerm?"
+
description="a summary of what zerm is and why it is different."
+
date=2019-08-07
+
updated=2021-02-03
+
+
[taxonomies]
+
tags = ["rust", "test", "zola"]
+
categories = ["programming", "misc.",]
+
+
[extra]
+
ToC = true
+
+++
+
+
# hello
+
+
This is zerm, a minimalist theme for Zola based[^1] off of [panr's](https://twitter.com/panr)
+
theme for Hugo.
+
+
While it's largely faithful to the original, there are some changes:
+
- no prism.js integration, instead we use Zola's syntax highlighting to reduce overhead.
+
- removal of PostCSS and leveraging Zola's use of Sass for simple styling.
+
- much thanks to [Paweł
+
Romanowski's](https://github.com/pawroman/zola-theme-terminimal/)
+
independent fork of Terminal. Their Sass stylings saved me the overhead of
+
figuring it out myself.
+
- no preview images. I want a theme that is focused on content.
+
- support for anchor links.
+
- Other small, opinionated changes that I think lend to the minimalism and
+
aesthetic of zerm.
+
+
+
Things this theme does not have but either Terminal or Terminimal might:
+
- better short-codes for things like embedding videos or images, though I will
+
work on this over time.
+
- better support for things like comments and social media. As of now, I have
+
no plans to add this but [PR's are always
+
**welcomed**](https://github.com/ejmg/zerm/pulls).
+
+
## A quick demo
+
+
`println!("inline code");`
+
+
```rs
+
fn main(n: String) {
+
println!("hello, zola!");
+
}
+
```
+
+
### Header III
+
+
> a somewhat kinda maybe large quote that maybe spans
+
> more than one line but I mean really who even cares
+
> okay maybe I do but point being is yes nice.
+
+
#### Header IV
+
+
| hello | tables | nice |
+
|:-----:|:---------:|------|
+
| wow | much love | yes |
+
+
+
Like zerm? Then [install
+
Zola](https://www.getzola.org/documentation/getting-started/installation/) and
+
[get started](https://www.getzola.org/documentation/themes/installing-and-using-themes/#installing-a-theme)!
+
+
---
+
+
[^1]: fork? port? a little bit of the former, more of the latter?
+7
themes/zerm/package.json
···
+
{
+
"scripts":
+
{
+
"install": "curl -L -O https://github.com/getzola/zola/releases/download/v0.13.0/zola-v0.13.0-x86_64-unknown-linux-gnu.tar.gz && tar -xzf zola-v0.13.0-x86_64-unknown-linux-gnu.tar.gz",
+
"build": "./zola build"
+
}
+
}
+29
themes/zerm/sass/_buttons.scss
···
+
button,
+
.button,
+
a.button {
+
position: relative;
+
display: flex;
+
align-items: center;
+
justify-content: center;
+
padding: 8px 18px;
+
margin-bottom: 5px;
+
text-decoration: none;
+
text-align: center;
+
border-radius: 8px;
+
border: 1px solid transparent;
+
appearance: none;
+
cursor: pointer;
+
outline: none;
+
}
+
+
a.read-more,
+
a.read-more:hover,
+
a.read-more:active {
+
display: inline-flex;
+
background: none;
+
box-shadow: none;
+
padding: 0;
+
margin: 20px 0;
+
max-width: 100%;
+
}
+
+15
themes/zerm/sass/_font.scss
···
+
@font-face {
+
font-family: 'Fira Code';
+
font-style: normal;
+
font-weight: 400;
+
font-display: swap;
+
src: url("assets/fonts/FiraCode-Regular.woff2") format("woff2"), url("assets/fonts/FiraCode-Regular.woff") format("woff");
+
}
+
+
@font-face {
+
font-family: 'Fira Code';
+
font-style: normal;
+
font-weight: 800;
+
font-display: swap;
+
src: url("assets/fonts/FiraCode-Bold.woff2") format("woff2"), url("assets/fonts/FiraCode-Bold.woff") format("woff");
+
}
+67
themes/zerm/sass/_footer.scss
···
+
@import "variables";
+
+
.footer {
+
padding: 40px 0;
+
flex-grow: 0;
+
opacity: .5;
+
+
&__inner {
+
display: flex;
+
align-items: center;
+
justify-content: space-between;
+
margin: 0;
+
width: 760px;
+
max-width: 100%;
+
+
@media (max-width: $tablet-max-width) {
+
flex-direction: column;
+
}
+
}
+
+
a {
+
color: inherit;
+
}
+
+
.copyright {
+
display: flex;
+
flex-direction: row;
+
align-items: center;
+
font-size: 1rem;
+
// so `--light-color-secondary` color exists no where else in the stylings
+
// color: var(--light-color-secondary);
+
// As a substitute, I'm going to use the alpha-70 version of accent.
+
color: var(--accent-alpha-70);
+
+
&--user {
+
margin: auto;
+
text-align: center;
+
}
+
+
& > *:first-child:not(:only-child) {
+
margin-right: 10px;
+
+
@media (max-width: $tablet-max-width) {
+
border: none;
+
padding: 0;
+
margin: 0;
+
}
+
}
+
+
@media (max-width: $tablet-max-width) {
+
flex-direction: column;
+
margin-top: 10px;
+
}
+
}
+
+
// .copyright-theme-sep {
+
// @media (max-width: $tablet-max-width) {
+
// display: none;
+
// }
+
// }
+
+
// .copyright-theme {
+
// @media (max-width: $tablet-max-width) {
+
// font-size: 0.75rem;
+
// }
+
// }
+
}
+139
themes/zerm/sass/_header.scss
···
+
@import "variables";
+
+
@mixin menu {
+
background: var(--background);
+
box-shadow: var(--shadow);
+
color: var(--color);
+
border: 2px solid;
+
margin: 0;
+
padding: 10px;
+
list-style: none;
+
z-index: 99;
+
}
+
+
.header {
+
display: flex;
+
flex-direction: column;
+
position: relative;
+
+
&__inner {
+
display: flex;
+
align-items: center;
+
justify-content: space-between;
+
}
+
+
&__logo {
+
display: flex;
+
flex: 1;
+
+
&:after {
+
content: '';
+
background: repeating-linear-gradient(90deg, var(--accent), var(--accent) 2px, transparent 0, transparent 10px);
+
display: block;
+
width: 100%;
+
right: 10px;
+
}
+
+
a {
+
flex: 0 0 auto;
+
max-width: 100%;
+
text-decoration: none;
+
}
+
}
+
+
.menu {
+
margin: 20px 0;
+
--shadow-color: var(--accent-alpha-70);
+
--shadow: 0 10px var(--shadow-color), -10px 10px var(--shadow-color), 10px 10px var(--shadow-color);
+
+
@media (max-width: $phone-max-width) {
+
@include menu;
+
position: absolute;
+
top: 50px;
+
right: 0;
+
}
+
+
&__inner {
+
// @include menu;
+
display: flex;
+
flex-wrap: wrap;
+
list-style: none;
+
margin: 0;
+
padding: 0;
+
+
&--desktop {
+
@media (max-width: $phone-max-width) {
+
display: none;
+
}
+
}
+
+
&--mobile {
+
display: none;
+
+
@media (max-width: $phone-max-width) {
+
display: block;
+
}
+
}
+
+
li {
+
// &.active {
+
// color: var(--accent-alpha-70);
+
// }
+
&:not(:last-of-type) {
+
margin-right: 20px;
+
margin-bottom: 10px;
+
flex: 0 0 auto;
+
}
+
}
+
+
@media (max-width: $phone-max-width) {
+
flex-direction: column;
+
align-items: flex-start;
+
padding: 0;
+
+
li {
+
margin: 0;
+
padding: 5px;
+
}
+
}
+
}
+
+
&__sub-inner {
+
position: relative;
+
list-style: none;
+
padding: 0;
+
margin: 0;
+
+
&:not(:only-child) {
+
margin-left: 20px;
+
}
+
+
&-more {
+
@include menu;
+
top: 35px;
+
left: 0;
+
+
&-trigger {
+
color: var(--accent);
+
user-select: none;
+
cursor: pointer;
+
}
+
+
li {
+
margin: 0;
+
padding: 5px;
+
white-space: nowrap;
+
}
+
}
+
}
+
+
&-trigger {
+
color: var(--accent);
+
border: 2px solid;
+
margin-left: 10px;
+
height: 100%;
+
padding: 3px 8px;
+
position: relative;
+
}
+
}
+
}
+8
themes/zerm/sass/_logo.scss
···
+
.logo {
+
display: flex;
+
align-items: center;
+
text-decoration: none;
+
background: var(--accent);
+
color: black;
+
padding: 5px 10px;
+
}
+335
themes/zerm/sass/_main.scss
···
+
@import "variables";
+
+
html {
+
box-sizing: border-box;
+
}
+
+
*,
+
*:before,
+
*:after {
+
box-sizing: inherit;
+
}
+
+
body {
+
margin: 0;
+
padding: 0;
+
font-family: 'Fira Code', Menlo, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;
+
font-size: 1rem;
+
line-height: 1.54;
+
background-color: var(--background);
+
color: var(--color);
+
text-rendering: optimizeLegibility;
+
-webkit-font-smoothing: antialiased;
+
-webkit-overflow-scrolling: touch;
+
-webkit-text-size-adjust: 100%;
+
font-feature-settings: "liga";
+
+
@media (max-width: $phone-max-width) {
+
font-size: 1rem;
+
}
+
}
+
+
h1, h2, h3, h4, h5, h6 {
+
line-height: 1.3;
+
+
&:not(first-child) {
+
margin-top: 40px;
+
}
+
+
.zola-anchor {
+
font-size: 1.5rem;
+
visibility: hidden;
+
margin-left: 0.5rem;
+
vertical-align: 1%;
+
text-decoration: none;
+
border-bottom-color: transparent;
+
cursor: pointer;
+
+
@media(max-width: $phone-max-width){
+
visibility: visible;
+
}
+
}
+
+
&:hover {
+
.zola-anchor {
+
visibility: visible;
+
}
+
}
+
}
+
+
// Actually keeping Pawroman's stylings here for font-size over h1-h6.
+
// I prefer differentiated header height.
+
+
// OLD
+
// ---------------------
+
// h1, h2, h3 {
+
// font-size: 1.4rem;
+
// }
+
+
// h4, h5, h6 {
+
// font-size: 1.2rem;
+
// }
+
+
// Pawroman's
+
// ---------------------
+
h1 {
+
font-size: 1.4rem;
+
}
+
+
h2 {
+
font-size: 1.3rem;
+
}
+
+
h3 {
+
font-size: 1.2rem;
+
}
+
+
h4, h5, h6 {
+
font-size: 1.15rem;
+
}
+
+
+
a {
+
color: inherit;
+
}
+
+
img {
+
display: block;
+
max-width: 100%;
+
+
&.left {
+
margin-right: auto;
+
}
+
+
&.center {
+
margin-left: auto;
+
margin-right: auto;
+
}
+
+
&.right {
+
margin-left: auto;
+
}
+
}
+
+
p {
+
margin-bottom: 20px;
+
}
+
+
figure {
+
display: table;
+
max-width: 100%;
+
margin: 25px 0;
+
+
&.left {
+
// img {
+
margin-right: auto;
+
// }
+
}
+
+
&.center {
+
// img {
+
margin-left: auto;
+
margin-right: auto;
+
// }
+
}
+
+
&.right {
+
// img {
+
margin-left: auto;
+
// }
+
}
+
+
figcaption {
+
font-size: 14px;
+
padding: 5px 10px;
+
margin-top: 5px;
+
background: var(--accent);
+
color: var(--background);
+
+
&.left {
+
text-align: left;
+
}
+
+
&.center {
+
text-align: center;
+
}
+
+
&.right {
+
text-align: right;
+
}
+
}
+
}
+
+
code {
+
font-family: 'Fira Code', Menlo, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;
+
font-feature-settings: normal;
+
background: var(--accent-alpha-20);
+
color: var(--accent);
+
padding: 1px 6px;
+
margin: 0 2px;
+
font-size: .95rem;
+
}
+
+
pre {
+
font-family: 'Fira Code', Menlo, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;
+
font-feature-settings: "liga";
+
padding: 20px 10px;
+
font-size: .95rem;
+
overflow: auto;
+
border-top: 1px solid rgba(255, 255, 255, .1);
+
border-bottom: 1px solid rgba(255, 255, 255, .1);
+
+
+ pre {
+
border-top: 0;
+
margin-top: -40px;
+
}
+
+
@media (max-width: $phone-max-width) {
+
white-space: pre-wrap;
+
word-wrap: break-word;
+
}
+
+
code {
+
background: none !important;
+
margin: 0;
+
padding: 0;
+
font-size: inherit;
+
border: none;
+
+
table {
+
table-layout: auto;
+
border-collapse: collapse;
+
box-sizing: border-box;
+
width: 100%;
+
margin: 00px 0;
+
}
+
+
table, th, td {
+
border: none;
+
padding: 0px;
+
}
+
+
table tr td:first-child {
+
padding-right: 10px;
+
}
+
+
}
+
}
+
+
blockquote {
+
border-top: 1px solid var(--accent);
+
border-bottom: 1px solid var(--accent);
+
margin: 40px 0;
+
padding: 25px;
+
+
@media (max-width: $phone-max-width) {
+
padding-right: 0;
+
}
+
+
&:before {
+
content: '”';
+
font-family: Georgia, serif;
+
font-size: 3.875rem;
+
position: absolute;
+
left: -40px;
+
top: -20px;
+
}
+
+
p:first-of-type {
+
margin-top: 0;
+
}
+
+
p:last-of-type {
+
margin-bottom: 0;
+
}
+
+
p {
+
position: relative;
+
}
+
+
p:before {
+
content: '>';
+
display: block;
+
position: absolute;
+
left: -25px;
+
color: var(--accent);
+
}
+
}
+
+
table {
+
table-layout: fixed;
+
border-collapse: collapse;
+
width: 100%;
+
margin: 40px 0;
+
}
+
+
table, th, td {
+
border: 1px dashed var(--accent);
+
padding: 10px;
+
}
+
+
th {
+
color: var(--accent);
+
}
+
+
ul, ol {
+
margin-left: 30px;
+
padding: 0;
+
+
li {
+
position: relative;
+
margin-top: 5px;
+
margin-bottom: 5px;
+
}
+
+
@media (max-width: $phone-max-width) {
+
margin-left: 20px;
+
}
+
+
ul, ol {
+
margin-top: 10px;
+
margin-bottom: 10px;
+
}
+
}
+
+
ol ol {
+
list-style-type: lower-alpha;
+
}
+
+
.container {
+
display: flex;
+
flex-direction: column;
+
padding: 40px;
+
max-width: 864px;
+
min-height: 100vh;
+
border-right: 1px solid rgba(255, 255, 255, 0.1);
+
+
&.full,
+
&.center {
+
border: none;
+
margin: 0 auto;
+
}
+
+
&.full {
+
max-width: 100%;
+
}
+
+
@media (max-width: $phone-max-width) {
+
padding: 20px;
+
}
+
}
+
+
.content {
+
display: flex;
+
}
+
+
hr {
+
width: 100%;
+
border: none;
+
background: var(--border-color);
+
height: 1px;
+
}
+
+
.hidden {
+
display: none;
+
}
+63
themes/zerm/sass/_pagination.scss
···
+
@import 'variables';
+
+
.pagination {
+
margin-top: 50px;
+
+
&__buttons {
+
display: flex;
+
align-items: center;
+
justify-content: center;
+
+
// @media (max-width: $phone-max-width) {
+
// flex-direction: column;
+
// }
+
+
a {
+
text-decoration: none;
+
}
+
}
+
}
+
+
.button {
+
position: relative;
+
display: inline-flex;
+
align-items: center;
+
justify-content: center;
+
font-size: 1rem;
+
border-radius: 8px;
+
max-width: 40%;
+
padding: 0;
+
cursor: pointer;
+
appearance: none;
+
+
+ .button {
+
margin-left: 10px;
+
}
+
+
// @media (max-width: $phone-max-width) {
+
// max-width: 80%;
+
// }
+
+
a {
+
display: flex;
+
padding: 8px 16px;
+
text-decoration: none;
+
text-overflow: ellipsis;
+
white-space: nowrap;
+
overflow: hidden;
+
}
+
+
&__text {
+
text-overflow: ellipsis;
+
white-space: nowrap;
+
overflow: hidden;
+
}
+
+
&.next .button__icon {
+
margin-left: 8px;
+
}
+
+
&.previous .button__icon {
+
margin-right: 8px;
+
}
+
}
+114
themes/zerm/sass/_post.scss
···
+
@import "variables";
+
+
// .posts {
+
// margin: 0 auto;
+
// }
+
+
.post {
+
width: 100%;
+
text-align: left;
+
margin: 20px auto;
+
padding: 20px 0;
+
+
@media (max-width: $tablet-max-width) {
+
max-width: 660px;
+
}
+
+
&:not(:last-of-type) {
+
border-bottom: 1px solid var(--border-color);
+
}
+
+
// %meta {
+
// font-size: 1rem;
+
// margin-bottom: 10px;
+
// color: var(--accent-alpha-70);
+
// }
+
+
// &-meta {
+
// @extend %meta;
+
// }
+
// &-meta-inline {
+
// @extend %meta;
+
+
// display: inline;
+
// }
+
+
&-meta {
+
font-size: 1rem;
+
margin-bottom: 10px;
+
color: var(--accent-alpha-70);
+
}
+
+
&-title {
+
--border: 3px dotted var(--accent);
+
position: relative;
+
color: var(--accent);
+
margin: 0 0 15px;
+
padding-bottom: 15px;
+
border-bottom: var(--border);
+
+
&:after {
+
content: '';
+
position: absolute;
+
bottom: 2px;
+
display: block;
+
width: 100%;
+
border-bottom: var(--border);
+
}
+
+
a {
+
text-decoration: none;
+
}
+
}
+
+
&-content {
+
margin-top: 30px;
+
}
+
+
ul {
+
list-style: none;
+
+
li:before {
+
content: '⦿';
+
position: absolute;
+
left: -20px;
+
color: var(--accent);
+
}
+
ul {
+
+
li:before {
+
content: '■';
+
position: absolute;
+
left: -20px;
+
color: var(--accent);
+
}
+
+
ul {
+
+
li:before {
+
content: '►';
+
position: absolute;
+
left: -20px;
+
color: var(--accent);
+
}
+
}
+
}
+
}
+
}
+
+
// TODO: try adapting this using a properly nested selector in the block above
+
// for ul items.
+
.tag-list {
+
@media(max-width: $phone-max-width) {
+
margin-left: 5%;
+
}
+
}
+
+
.footnote-definition {
+
color: var(--accent);
+
+
p {
+
display: inline;
+
color: var(--footnote-color);
+
}
+
}
+3
themes/zerm/sass/_semantic.scss
···
+
section, article, aside, footer, header, nav {
+
display: block;
+
}
+6
themes/zerm/sass/_toc.scss
···
+
div.toc {
+
--shadow-color: var(--accent-alpha-70);
+
--shadow: 10px 10px var(--shadow-color);
+
@include menu;
+
margin: 20px 0;
+
}
+10
themes/zerm/sass/_variables.scss
···
+
:root {
+
// *NOTE*:
+
// ------------------------------------------------
+
//Keep the same as the values in variables.scss!!!!!
+
--phoneWidth: (max-width: 684px);
+
--tabletWidth: (max-width: 900px);
+
}
+
+
$phone-max-width: 683px;
+
$tablet-max-width: 899px;
+9
themes/zerm/sass/color/blue.scss
···
+
:root {
+
--accent: rgb(35,176,255);
+
--accent-alpha-20: rgba(35,176,255,.2);
+
--accent-alpha-70: rgba(35,176,255,.7);
+
--background: #1D1E28;
+
--color: whitesmoke;
+
--border-color: rgba(255, 255, 255, .1);
+
--footnote-color: rgba(255, 255, 255, .5);
+
}
+12
themes/zerm/sass/color/green.scss
···
+
:root {
+
// --accent: rgb(120,226,160);
+
// --accent-alpha-20: rgba(120,226,160,.2);
+
// --accent-alpha-70: rgba(120,226,160,.7);
+
--accent: rgb(72, 251, 53);
+
--accent-alpha-20: rgba(72, 251, 53,.2);
+
--accent-alpha-70: rgba(72, 251, 53,.7);
+
--background: #1F222A;
+
--color: whitesmoke;
+
--border-color: rgba(255, 255, 255, .1);
+
--footnote-color: rgba(255, 255, 255, .5);
+
}
+9
themes/zerm/sass/color/orange.scss
···
+
:root {
+
--accent: rgb(255,168,106);
+
--accent-alpha-20: rgba(255, 168, 106, .2);
+
--accent-alpha-70: rgba(255, 168, 106,.7);
+
--background: #211f1a;
+
--color: whitesmoke;
+
--border-color: rgba(255, 255, 255, .1);
+
--footnote-color: rgba(255, 255, 255, .5);
+
}
+9
themes/zerm/sass/color/pink.scss
···
+
:root {
+
--accent: rgb(238,114,241);
+
--accent-alpha-20: rgba(238,114,241,.2);
+
--accent-alpha-70: rgba(238,114,241,.7);
+
--background: #21202C;
+
--color: whitesmoke;
+
--border-color: rgba(255, 255, 255, .1);
+
--footnote-color: rgba(255, 255, 255, .5);
+
}
+9
themes/zerm/sass/color/red.scss
···
+
:root {
+
--accent: rgb(255,98,102);
+
--accent-alpha-20: rgba(255,98,102,.2);
+
--accent-alpha-70: rgba(255,98,102,.7);
+
--background: #221F29;
+
--color: whitesmoke;
+
--border-color: rgba(255, 255, 255, .1);
+
--footnote-color: rgba(255, 255, 255, .5);
+
}
+10
themes/zerm/sass/style.scss
···
+
@import 'buttons';
+
@import 'font';
+
@import 'header';
+
@import 'logo';
+
@import 'main';
+
@import 'post';
+
@import 'pagination';
+
@import 'footer';
+
@import 'semantic';
+
@import 'toc';
themes/zerm/screenshot.png

This is a binary file and will not be displayed.

+1
themes/zerm/static/assets/js/main.js
···
+
!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){n(1),e.exports=n(2)},function(e,t){},function(e,t){var n=document.querySelector(".container"),o=document.querySelector(".menu"),r=document.querySelector(".menu-trigger"),i=(document.querySelector(".menu__inner--desktop"),document.querySelector(".menu__sub-inner-more-trigger")),u=document.querySelector(".menu__sub-inner-more"),c=getComputedStyle(document.body).getPropertyValue("--phoneWidth"),d=function(){return window.matchMedia(c).matches},s=function(){r&&r.classList.toggle("hidden",!d()),o&&o.classList.toggle("hidden",d()),u&&u.classList.toggle("hidden",!d())};o&&o.addEventListener("click",function(e){return e.stopPropagation()}),u&&u.addEventListener("click",function(e){return e.stopPropagation()}),s(),document.body.addEventListener("click",function(){d()||!u||u.classList.contains("hidden")?d()&&!o.classList.contains("hidden")&&o.classList.add("hidden"):u.classList.add("hidden")}),window.addEventListener("resize",s),r&&r.addEventListener("click",function(e){e.stopPropagation(),o&&o.classList.toggle("hidden")}),i&&i.addEventListener("click",function(e){e.stopPropagation(),u&&u.classList.toggle("hidden"),u.getBoundingClientRect().right>n.getBoundingClientRect().right&&(u.style.left="auto",u.style.right=0)})}]);
+1
themes/zerm/templates/.gitignore
···
+
!tags
+12
themes/zerm/templates/404.html
···
+
{% extends "index.html" -%}
+
+
{%- block title %}
+
<title>Page not found</title>
+
{%- endblock title -%}
+
+
{%- block main -%}
+
+
Sorry, this page doesn't exist.
+
+
Go back&nbsp<a href="{%- if config.extra.logo_home_link -%}{{ config.extra.logo_home_link }}{%- else -%}{{ config.base_url }}{%- endif -%}">home?</a>
+
{%- endblock main -%}
+1
themes/zerm/templates/anchor-link.html
···
+
<a class="zola-anchor" href="#{{ id }}" aria-label="Anchor link for: {{ id }}">§</a>
+18
themes/zerm/templates/categories/list.html
···
+
{% extends "index.html" %}
+
{%- block main -%}
+
<div class="post">
+
+
<h1 class="post-title">{categories}</h1>
+
<ul>
+
{%- for cat in terms -%}
+
<li class="tag-list">
+
<a href="{{ get_taxonomy_url(kind="categories", name=cat.name) }}">{{ "{" }}{{ cat.name }}{{ "}" }}</a>
+
({{ cat.pages | length }} post{{ cat.pages | length | pluralize }})
+
</li>
+
{# End of pagination for-loop #}
+
{%- endfor -%}
+
{#- I don't put pagination here like Terminal does. I don't like how
+
the buttons move with the size of post entries in the content div. -#}
+
</ul>
+
</div>
+
{%- endblock main -%}
+30
themes/zerm/templates/categories/single.html
···
+
{%- extends "index.html"-%}
+
{%- block main-%}
+
<div class="post">
+
<h1 class="post-title">
+
categories ∋ {{ "{" }}{{ term.name }}{{ "}" }}
+
({{ term.pages | length }} post{{ term.pages | length | pluralize }})
+
</h1>
+
+
<ul>
+
{%- for post in term.pages -%}
+
<li class="tag-list">
+
{{ post.date | date(format="%Y.%m.%d") }}
+
:: <a href="{{ post.permalink }}">{{ post.title }}</a>
+
:: {{ "{" }}
+
{%- for cat in post.taxonomies["categories"] -%}
+
{%- set _cat = get_taxonomy_url(kind="categories", name=cat) -%}
+
{%- if loop.last -%}
+
<a href="{{ _cat }}">{{ cat }}</a>
+
{%- else -%}
+
<a href="{{ _cat }}">{{ cat }}</a>,&nbsp;
+
{%- endif -%}
+
{% endfor %}{{ "}" }}
+
</li>
+
{# End of pagination for-loop #}
+
{%- endfor -%}
+
{#- I don't put pagination here like Terminal does. I don't like how
+
the buttons move with the size of post entries in the content div. -#}
+
</ul>
+
</div>
+
{%- endblock main-%}
+97
themes/zerm/templates/index.html
···
+
{% import "macros/head.html" as head -%}
+
{% import "macros/logo.html" as logo -%}
+
{% import "macros/header.html" as header -%}
+
{% import "macros/extended_header.html" as extended_header -%}
+
{% import "macros/lists.html" as lists -%}
+
{% import "macros/posts.html" as posts -%}
+
{% import "macros/social.html" as social -%}
+
{% import "macros/utils.html" as utils -%}
+
{% import "macros/menu.html" as menu -%}
+
{% import "macros/pagination.html" as pagination -%}
+
{% import "macros/footer.html" as footer -%}
+
{% import "macros/extended_footer.html" as extended_footer -%}
+
{% import "macros/comments.html" as comments -%}
+
+
<!DOCTYPE html>
+
<html lang="{{ lang }}">
+
<head>
+
{%- block title -%}
+
<title>{{ config.title }}</title>
+
{%- endblock title -%}
+
+
{%- block general_meta -%}
+
{{ head::general_meta() }}
+
{%- endblock general_meta -%}
+
+
{%- block og_preview -%}
+
{{ social::og_preview() }}
+
{%- endblock og_preview -%}
+
+
{%- block fonts -%}
+
{{ head::fonts() }}
+
{%- endblock fonts -%}
+
+
{%- block css -%}
+
{{ head::styling() }}
+
{%- endblock css -%}
+
+
{%- block favicon -%}
+
{{ head::favicon() }}
+
{%- endblock favicon -%}
+
+
{%- block rss -%}
+
{{ head::rss() }}
+
{%- endblock rss -%}
+
+
{%- block math -%}
+
{%- endblock math -%}
+
</head>
+
<body>
+
{#
+
"container full" when width == True, regardless of center
+
"container center" when only center == True
+
"container" when both false.
+
#}
+
{%- if config.extra.full_width -%}
+
{%- set container = "container full" -%}
+
{%- elif config.extra.center -%}
+
{%- set container = "container center" -%}
+
{%- else -%}
+
{%- set container = "container" -%}
+
{%- endif -%}
+
+
<div class="{{ container }}">
+
{%- block header -%}
+
{{ header::header() }}
+
{%- endblock header -%}
+
<div class="content">
+
{%- block main -%}
+
{{ lists::list_pages() }}
+
{%- endblock main -%}
+
</div>
+
{#-
+
I keep pagination out of list, unlike Terminal, because I don't
+
like how the pagination buttons move with the width of the content
+
div.
+
-#}
+
{%- block pagination -%}
+
{{ pagination::paginate() }}
+
{%- endblock pagination -%}
+
+
{%- block footer -%}
+
<footer class="footer">
+
<div class="footer__inner">
+
{%- block copyright -%}
+
{{ footer::copyright() }}
+
{%- endblock copyright -%}
+
+
{%- block script -%}
+
{{ footer::script() }}
+
{%- endblock script -%}
+
</div>
+
{{ extended_footer::extended_footer() }}
+
</footer>
+
{%- endblock footer -%}
+
</div>
+
</body>
+
</html>
+30
themes/zerm/templates/macros/comments.html
···
+
{% macro comments() %}
+
{% if config.extra.disqus.enabled is defined and config.extra.disqus.enabled == true %}
+
{{ comments::disqus() }}
+
{% endif %}
+
{% endmacro comments %}
+
+
+
{% macro disqus() %}
+
+
<div id="disqus_thread"></div>
+
<script>
+
/**
+
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
+
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
+
+
var disqus_config = function () {
+
this.page.url = '{{ page.permalink | safe }}'; // Replace PAGE_URL with your page's canonical URL variable
+
this.page.identifier = '{{ page.permalink | safe }}'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
+
};
+
+
(function() { // DON'T EDIT BELOW THIS LINE
+
var d = document, s = d.createElement('script');
+
s.src = 'https://{{config.extra.disqus.short_name}}.disqus.com/embed.js';
+
s.setAttribute('data-timestamp', +new Date());
+
(d.head || d.body).appendChild(s);
+
})();
+
</script>
+
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+
+
{% endmacro disqus %}
+2
themes/zerm/templates/macros/extended_footer.html
···
+
{% macro extended_footer() %}
+
{% endmacro extended_footer %}
+2
themes/zerm/templates/macros/extended_header.html
···
+
{% macro extended_header() %}
+
{% endmacro extended_header %}
+14
themes/zerm/templates/macros/footer.html
···
+
{% macro copyright() %}
+
{%- if config.extra.copyright -%}
+
<div class="copyright copyright--user">{{ config.extra.copyright | safe }}</div>
+
{%- else -%}
+
<div class="copyright">
+
<span>© {{ now() | date(format="%Y") }} <a href="https://github.com/ejmg/zerm">zerm</a> :: Powered by <a href="https://www.getzola.org/">Zola</a></span>
+
<span>:: Theme made by <a href="https://github.com/ejmg">ejmg</a></span>
+
</div>
+
{%- endif -%}
+
{% endmacro copyright %}
+
+
{% macro script() %}
+
<script type="text/javascript" src="{{ get_url(path="assets/js/main.js") }}"></script>
+
{% endmacro script %}
+84
themes/zerm/templates/macros/head.html
···
+
{#- TODO:
+
- [x] add rss
+
- [x] favicons
+
- [x] media preview
+
- [ ] twitter
+
- [ ] maybe google adsense
+
-#}
+
+
{% macro fonts() %}
+
<link rel="preload" href="{{ get_url(path="/assets/fonts/FiraCode-Regular.woff2") }}" as="font" type="font/woff2" crossorigin="anonymous">
+
<link rel="preload" href="{{ get_url(path="/assets/fonts/FiraCode-Bold.woff2") }}" as="font" type="font/woff2" crossorigin="anonymous">
+
{% endmacro fonts %}
+
+
{% macro styling() %}
+
<link rel="stylesheet" href="{{ get_url(path="style.css", cachebust=true) }}">
+
{% if config.extra.theme_color != "orange" -%}
+
{% set color = "color/" ~ config.extra.theme_color ~ ".css" -%}
+
<link rel="stylesheet" href="{{ get_url(path=color, cachebust=true) }}">
+
{%- else -%}
+
<link rel="stylesheet" href=" {{ get_url(path="color/orange.css", cachebust=true) }}">
+
{% endif %}
+
{%- if config.extra.custom_css is defined -%}
+
<link rel="stylesheet" href="{{ get_url(path="custom.css", cachebust=true) }}">
+
{% endif %}
+
{% endmacro styling %}
+
+
{% macro favicon() %}
+
{%- if config.extra.favicon is defined -%}
+
<link rel="shortcut icon" href="{{ get_url(path=config.extra.favicon) }}" type="image/x-icon" />
+
{%- endif -%}
+
{% endmacro favicon %}
+
+
{% macro rss() %}
+
{%- if config.generate_feed -%}
+
<link rel="alternate" type={% if config.feed_filename == "atom.xml" %}"application/atom+xml"{% else %}"application/rss+xml"{% endif %} title="{{ config.title }} RSS" href="{{ get_url(path=config.feed_filename) }}">
+
{%- endif -%}
+
{% endmacro rss %}
+
+
{% macro general_meta() %}
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
+
{%- if page.title -%}
+
<meta name="description" content="{{ config.description }} {{ page.title }} {{ page.description }}"/>
+
{%- else -%}
+
<meta name="description" content="{{ config.description }}"/>
+
{%- endif -%}
+
+
{%- if page.taxonomies.tags or page.taxonomies.categories -%}
+
<meta name="keywords" content="
+
{%- if page.taxonomies.categories -%}
+
{%- for cat in page.taxonomies.categories -%}
+
{{ cat }}, {% endfor -%}
+
{%- endif -%}
+
+
{%- if page.taxonomies.tags -%}
+
{%- for tag in page.taxonomies.tags -%}
+
{%- if loop.last -%}
+
{{ tag }}
+
{%- else -%}
+
{{ tag }}, {% endif -%}
+
{%- endfor -%}
+
{%- endif -%}
+
" />
+
{%- endif -%}
+
{% endmacro general_meta %}
+
+
{% macro katex() %}
+
{% if config.extra.enable_katex %}
+
<link rel="stylesheet" href="{{ get_url(path="assets/katex/katex.min.css") }}">
+
+
<script defer type="text/javascript" src="{{ get_url(path="assets/katex/katex.min.js") | safe }}"></script>
+
<script defer type="text/javascript" src="{{ get_url(path="assets/katex/mathtex-script-type.min.js") | safe }}"></script>
+
+
<script defer src="{{ get_url(path="assets/katex/auto-render.min.js") | safe }}"
+
onload="renderMathInElement(document.body, {
+
delimiters: [
+
{left: '$$', right: '$$', display: true},
+
{left: '$', right: '$', display: false},
+
{left: '\\(', right: '\\)', display: false},
+
{left: '\\[', right: '\\]', display: true}
+
]
+
});"></script>
+
{% endif %}
+
{% endmacro katek %}
+16
themes/zerm/templates/macros/header.html
···
+
{% macro header() %}
+
<header class="header">
+
<div class="header__inner">
+
<div class="header__logo">
+
{{ logo::logo() }}
+
</div>
+
<div class="menu-trigger">menu</div>
+
</div>
+
{# Check if there are menu items to render, yes if > 0 #}
+
{%- set num = config.extra.main_menu | length -%}
+
{% if num > 0 -%}
+
{{ menu::menu() }}
+
{% endif -%}
+
</header>
+
{% endmacro header %}
+
+39
themes/zerm/templates/macros/lists.html
···
+
{% macro list_pages() %}
+
<section class="posts">
+
+
{%- for page in paginator.pages -%}
+
+
<div class="post on-list">
+
<header>
+
<h1 class="post-title">
+
<a href="{{ page.permalink }}">{{ page.title }}</a>
+
</h1>
+
</header>
+
+
+
{{ posts::meta(page=page, author=config.extra.show_author) }}
+
+
{#- NOTE -#}
+
{#- -------------------------------- -#}
+
{#- Skipping the Cover page implementation. Not included/covered for now. -#}
+
+
<div class="post-content">
+
{% if page.description -%}
+
{{ page.description }}
+
{#- end if-check for description -#}
+
{% elif page.summary -%}
+
{{ page.summary | safe }}
+
{% endif -%}
+
</div>
+
{% if page.description or page.summary -%}
+
<div>
+
<a class="read-more button" href="{{ page.permalink }}">{{ config.extra.read_more }} →</a>
+
</div>
+
{% endif -%}
+
</div>
+
{# End of pagination for-loop #}
+
{%- endfor -%}
+
{#- I don't put pagination here like Terminal does. I don't like how
+
the buttons move with the size of post entries in the content div. -#}
+
</section>
+
{% endmacro list_pages %}
+7
themes/zerm/templates/macros/logo.html
···
+
{%- macro logo() -%}
+
<a href="{%- if config.extra.logo_home_link -%}{{ config.extra.logo_home_link }}{%- else -%}{{ config.base_url }}{%- endif -%}">
+
<div class="logo">
+
{{config.extra.logo_text | default(value="Terminal") }}
+
</div>
+
</a>
+
{%- endmacro logo -%}
+58
themes/zerm/templates/macros/menu.html
···
+
{% macro menu() %}
+
<nav class="menu">
+
<ul class="menu__inner menu__inner--desktop">
+
{#
+
I am almost certain this check isn't necessary, that it will always
+
return true, but the only alternative I can think of is the case where
+
this config value simply doesn't exist (null)? IDK, just copying from
+
original implementation for now.
+
#}
+
{% if config.extra.show_menu_items is defined -%}
+
{% for menu_item in config.extra.main_menu | slice(end=config.extra.show_menu_items) -%}
+
{#
+
Original theme has sub-children checks on main-menu, not worrying about that here.
+
#}
+
<li>
+
<a href="{{ menu::get_link(item=menu_item) }}">{{ menu_item.name }}</a>
+
</li>
+
{% endfor -%}
+
+
{%- set main_len = config.extra.main_menu | length -%}
+
{%- set show_len = config.extra.show_menu_items -%}
+
+
{%- if main_len > show_len -%}
+
<ul class="menu__sub-inner">
+
<li class="menu__sub-inner-more-trigger">{{ config.extra.menu_more }} ▾</li>
+
<ul class="menu__sub-inner-more hidden">
+
{{ menu::items(menu=config.extra.main_menu | slice(start=config.extra.show_menu_items)) }}
+
</ul>
+
</ul>
+
{%- endif -%}
+
{# Continues the original if-check at top of file for show_menu_items #}
+
{% else -%}
+
{{ menu::items(menu=config.extra.main_menu) }}
+
{%- endif -%}
+
</ul>
+
+
<ul class="menu__inner menu__inner--mobile">
+
{{ menu::items(menu=config.extra.main_menu) }}
+
</ul>
+
</nav>
+
{% endmacro menu %}
+
+
{% macro items(menu) %}
+
{%- for menu_item in menu -%}
+
{# skipping sub-child check #}
+
<li>
+
<a href="{{ menu::get_link(item=menu_item) }}">{{ menu_item.name }}</a>
+
</li>
+
{%- endfor-%}
+
{% endmacro items %}
+
+
{% macro get_link(item) %}
+
{% if item.external is defined and item.external == true %}
+
{{ item.url }}
+
{% else %}
+
{{ get_url(path=item.url) }}
+
{% endif %}
+
{% endmacro get_link %}
+24
themes/zerm/templates/macros/pagination.html
···
+
{% macro paginate() %}
+
<div class="pagination">
+
<div class="pagination__buttons">
+
{% if paginator.previous -%}
+
<span class="button previous">
+
<a href="{{ paginator.previous }}">
+
<span class="button__icon">←</span>
+
<span class="button__text">newer</span>
+
</a>
+
</span>
+
{# end of if-check for page.previous #}
+
{% endif -%}
+
{% if paginator.next -%}
+
{# end of if-check for page.next #}
+
<span class="button next">
+
<a href="{{ paginator.next }}">
+
<span class="button__text">older</span>
+
<span class="button__icon">→</span>
+
</a>
+
</span>
+
{% endif-%}
+
</div>
+
</div>
+
{% endmacro paginate %}
+100
themes/zerm/templates/macros/posts.html
···
+
{% macro section_meta(section, author) %}
+
<div class="post-meta">
+
<span class="post-date">
+
+
{%- if section.extra["date"] -%}
+
{{ section.extra["date"] | date(format="%Y.%m.%d") }}
+
{# end of section.date if-check #}
+
{%- endif -%}
+
</span>
+
+
<span class="post-author">
+
{%- if author -%}
+
{{ utils::author(page=section) }}
+
{%- endif -%}
+
</span>
+
</div>
+
{% endmacro section_meta %}
+
+
{% macro langs(page) %}
+
{% if page.translations | length > 1 %}
+
<div class="post-langs" style="opacity: .5;">
+
<span>Translations: </span>{# TODO translate the span content too #}
+
{% for translated in page.translations %}
+
<a href="{{ translated.permalink }}">{{ translated.lang }}</a>
+
{% endfor %}
+
</div>
+
{% endif %}
+
{% endmacro langs %}
+
+
{% macro meta(page, author) %}
+
<div class="post-meta">
+
<span class="post-date">
+
{%- if page.date -%}
+
{{ page.date | date(format="%Y.%m.%d") }}
+
{# end of page.date if-check #}
+
{%- endif -%}
+
+
{%- if page.updated -%}
+
[Updated: {{ page.updated | date(format="%Y.%m.%d") }}]
+
{# end of page.updated if-check #}
+
{%- endif -%}
+
</span>
+
+
<span class="post-author">
+
{%- if author -%}
+
{{ utils::author(page=page) }}
+
{%- endif -%}
+
</span>
+
+
{{ posts::taxonomies(taxonomy=page.taxonomies,
+
disp_cat=config.extra.show_categories,
+
disp_tag=config.extra.show_tags) }}
+
</div>
+
{% endmacro meta %}
+
+
{% macro taxonomies(taxonomy, disp_cat, disp_tag) %}
+
+
{% if disp_cat and disp_tag -%}
+
{% if taxonomy.categories -%}
+
{{ posts::categories(categories=taxonomy.categories) }}
+
{# end if-check for categories #}
+
{%- endif -%}
+
+
{% if taxonomy.tags -%}
+
{{ posts::tags(tags=taxonomy.tags) }}
+
{# end if-check for tags #}
+
{% endif -%}
+
{% elif disp_cat -%}
+
{% if taxonomy.categories-%}
+
{{ posts::categories(categories=taxonomy.categories) }}
+
{# end if-check for categories #}
+
{% endif -%}
+
{% elif disp_tag -%}
+
{% if taxonomy.tags -%}
+
{{ posts::tags(tags=taxonomy.tags) }}
+
{# end if-check for tags #}
+
{% endif -%}
+
{# end if-check for BOTH disp bools #}
+
{% endif -%}
+
{% endmacro taxonomies %}
+
+
{% macro categories(categories) %}
+
:: {
+
{%- for cat in categories -%}
+
{%- if loop.last -%}
+
<a href="{{ get_taxonomy_url(kind="categories", name=cat ) }}">{{ cat }}</a>
+
{%- else -%}
+
<a href="{{ get_taxonomy_url(kind="categories", name=cat ) }}">{{ cat }}</a>,
+
{# end of if-check for whether last item or not #}
+
{%- endif -%}
+
{%- endfor -%}} {# <--- NOTE: OPEN CURLY BRACE #}
+
{% endmacro categories %}
+
+
{% macro tags(tags) %}
+
::
+
{% for tag in tags -%}
+
#<a href="{{get_taxonomy_url(kind="tags", name=tag )}}">{{ tag }}</a>
+
{# end of tags for-loop #}
+
{% endfor -%}
+
{% endmacro tags %}
+46
themes/zerm/templates/macros/social.html
···
+
{% macro og_preview() %}
+
<meta property="og:title" content="{{ social::og_title() }}" />
+
<meta property="og:type" content="website"/>
+
{%- if current_url -%}
+
<meta property="og:url" content="{{ current_url }}"/>
+
{%- endif -%}
+
<meta property="og:description" content="{{ social::og_description() }}"/>
+
{%- if config.extra.og_preview_img -%}
+
<meta property="og:image" content="{{ get_url(path=config.extra.og_preview_img) }}"/>
+
{%- endif -%}
+
{% endmacro og_preview %}
+
+
{% macro og_description() %}
+
{%- if section -%}
+
{%- if section.description -%}
+
{{ section.description }}
+
{%- else -%}
+
{{ config.description }}
+
{%- endif -%}
+
{%- elif page -%}
+
{%- if page.summary | string -%}
+
{{ page.summary | striptags | truncate(length=200) }}
+
{%- elif page.description -%}
+
{{ page.description }}
+
{%- else -%}
+
{{ config.description }}
+
{%- endif -%}
+
{%- endif -%}
+
{% endmacro og_description %}
+
+
{% macro og_title() -%}
+
{{ config.title }} -&nbsp;
+
{%- if section -%}
+
{%- if section.title -%}
+
{{ section.title | striptags }}
+
{%- else -%}
+
{{ config.description }}
+
{%- endif -%}
+
{%- elif page -%}
+
{%- if page.title -%}
+
{{ page.title | striptags }}
+
{%- else -%}
+
{{ config.description }}
+
{%- endif -%}
+
{%- endif -%}
+
{% endmacro og_title %}
+61
themes/zerm/templates/macros/toc.html
···
+
{% macro toc (t) %}
+
{% if t %}
+
<div class="toc" id="nav-container">
+
<p class="toc-head">Table of Contents</p>
+
<div id="nav-content" >
+
<ul>
+
{% for h1 in page.toc %}
+
<li>
+
<a href="{{ h1.permalink | safe }}">{{ h1.title }}</a>
+
{% if h1.children %}
+
<ul>
+
{% for h2 in h1.children %}
+
<li>
+
<a href="{{ h2.permalink | safe }}">{{ h2.title }}</a>
+
</li>
+
{% if h2.children %}
+
<ul>
+
{% for h3 in h2.children %}
+
<li>
+
<a href="{{ h3.permalink | safe }}">{{ h3.title }}</a>
+
{% if h3.children %}
+
<ul>
+
{% for h4 in h3.children %}
+
<li>
+
<a href="{{ h4.permalink | safe }}">{{ h4.title }}</a>
+
</li>
+
{% if h4.children %}
+
<ul>
+
{% for h5 in h4.children %}
+
<li>
+
<a href="{{ h5.permalink | safe }}">{{ h5.title }}</a>
+
{% if h5.children %}
+
<ul>
+
{% for h6 in h5.children %}
+
<li>
+
<a href="{{ h5.permalink | safe }}">{{ h6.title }}</a>
+
</li>
+
{% endfor %}
+
</ul>
+
{% endif %}
+
</li>
+
{% endfor %}
+
</ul>
+
{% endif %}
+
{% endfor %}
+
</ul>
+
{% endif %}
+
</li>
+
{% endfor %}
+
</ul>
+
{% endif %}
+
{% endfor %}
+
</ul>
+
{% endif %}
+
</li>
+
{% endfor %}
+
</ul>
+
</div>
+
</div>
+
{% endif %}
+
{% endmacro %}
+8
themes/zerm/templates/macros/utils.html
···
+
{% macro author(page) %}
+
::
+
{% if page.extra.author -%}
+
{{ page.extra.author }}
+
{%- else -%}
+
{{ config.extra.author }}
+
{%- endif -%}
+
{%- endmacro author-%}
+41
themes/zerm/templates/page.html
···
+
{% import "macros/head.html" as head -%}
+
{% import "macros/toc.html" as toc -%}
+
{% extends "index.html" -%}
+
+
{%- block math -%}
+
{% if page.extra.math %}
+
{{ head::katex() }}
+
{% endif %}
+
{%- endblock math -%}
+
+
{%- block title %}
+
<title>{{ page.title }} - {{ config.extra.author }}</title>
+
{# TODO: make some kind of social media linking, i guess? #}
+
{#%- include "snippets/social.html" %#}
+
{%- endblock title -%}
+
+
{%- block main -%}
+
<article class="post">
+
<header>
+
<h1 class="post-title">
+
<a href="{{ page.permalink }}">{{ page.title }}</a>
+
</h1>
+
{{ posts::meta(page=page, author=config.extra.show_author) }}
+
{{ posts::langs(page=page) }}
+
+
{%- block ToC -%}
+
{%- if page.extra.ToC or config.extra.ToC -%}
+
{{ toc::toc(t=page.toc) }}
+
{%- endif -%}
+
{%- endblock ToC -%}
+
</header>
+
+
{#- Skipping logic for cover as was in original Terminal theme -#}
+
+
{{ page.content | safe }}
+
+
{{ comments::comments() }}
+
{# TODO: Decide if any sort of commenting functionality is desired? #}
+
{#%- include "snippets/comments.html" -%#}
+
</article>
+
{%- endblock main -%}
+24
themes/zerm/templates/section.html
···
+
{% extends "index.html" -%}
+
+
{%- block title %}
+
<title>{{ section.title }} - {{ config.extra.author }}</title>
+
{# TODO: make some kind of social media linking, i guess? #}
+
{#%- include "snippets/social.html" %#}
+
{%- endblock title -%}
+
+
{%- block main -%}
+
<article class="post">
+
<header>
+
<h1 class="post-title">
+
<a href="{{ section.permalink }}">{{ section.title }}</a>
+
</h1>
+
{{ posts::section_meta(section=section, author=config.extra.show_author) }}
+
</header>
+
+
{#- Skipping logic for cover as was in original Terminal theme -#}
+
+
{{ section.content | safe }}
+
{# TODO: Decide if any sort of commenting functionality is desired? #}
+
{#%- include "snippets/comments.html" -%#}
+
</article>
+
{%- endblock main -%}
+12
themes/zerm/templates/static.html
···
+
{% extends "index.html" %}
+
+
{% block header %}
+
<title>{{ page.title }} | elias </title>
+
{% endblock header %}
+
+
{% block content %}
+
<article>
+
<h1>{{ page.title }}</h1>
+
{{ page.content | safe }}
+
</article>
+
{% endblock content %}
+18
themes/zerm/templates/tags/list.html
···
+
{% extends "index.html" %}
+
{%- block main -%}
+
<div class="post">
+
+
<h1 class="post-title">#tags</h1>
+
<ul>
+
{%- for term in terms -%}
+
<li class="tag-list">
+
<a href="{{ get_taxonomy_url(kind="tags", name=term.name) }}">#{{ term.name }}</a>
+
({{ term.pages | length }} post{{ term.pages | length | pluralize}})
+
</li>
+
{# End of pagination for-loop #}
+
{%- endfor -%}
+
{#- I don't put pagination here like Terminal does. I don't like how
+
the buttons move with the size of post entries in the content div. -#}
+
</ul>
+
</div>
+
{%- endblock main -%}
+23
themes/zerm/templates/tags/single.html
···
+
{%- extends "index.html"-%}
+
{%- block main-%}
+
<div class="post">
+
<h1 class="post-title">
+
tags ∋ #{{ term.name }}
+
({{ term.pages | length }} post{{ term.pages | length | pluralize}})
+
</h1>
+
+
<ul>
+
{%- for post in term.pages -%}
+
<li class="tag-list">
+
{{ post.date | date(format="%Y.%m.%d") }}
+
:: <a href="{{ post.permalink }}">{{ post.title }}</a>
+
:: {% for tag in post.taxonomies["tags"] -%} <a href="{%- set _tag = get_taxonomy_url(kind="tags", name=tag) -%}{{ _tag }}">#{{ tag }}</a> {% endfor %}
+
</li>
+
{# End of pagination for-loop #}
+
{%- endfor -%}
+
{#- I don't put pagination here like Terminal does. I don't like how
+
the buttons move with the size of post entries in the content div. -#}
+
</ul>
+
+
</div>
+
{%- endblock main-%}
+26
themes/zerm/theme.toml
···
+
name = "zerm"
+
description = "A minimalistic and dark theme based on Radek Kozieł's theme for Hugo"
+
license = "MIT"
+
homepage = "https://github.com/ejmg/zerm"
+
# The minimum version of Zola required
+
min_version = "0.8.0"
+
# An optional live demo URL
+
demo = "https://zerm.ejmg.now.sh/"
+
+
# Any variable there can be overriden in the end user `config.toml`
+
# You don't need to prefix variables by the theme name but as this will
+
# be merged with user data, some kind of prefix or nesting is preferable
+
# Use snake_casing to be consistent with the rest of Zola
+
[extra]
+
+
# The theme author info: you!
+
[author]
+
name = "elias julian marko garcia"
+
homepage = "https://github.com/ejmg"
+
+
# If this is porting a theme from another static site engine, provide
+
# the info of the original author here
+
[original]
+
author = "Radek Kozieł"
+
homepage = "https://radoslawkoziel.pl/"
+
repo = "https://github.com/panr/hugo-theme-terminal"
themes/zerm/zerm-preview.png

This is a binary file and will not be displayed.