Compare commits
6 commits
020a745a68
...
1f413b099c
Author | SHA1 | Date | |
---|---|---|---|
1f413b099c | |||
8fac51f4d0 | |||
307f27f3fc | |||
5611df962e | |||
301210fde8 | |||
b0144d06b4 |
6 changed files with 423 additions and 202 deletions
|
@ -8,7 +8,7 @@ jobs:
|
|||
container:
|
||||
image: node:alpine
|
||||
env:
|
||||
ZONA: git+https://git.ficd.sh/ficd/zona.git@dacea2756af75d1151788cc0c1b2eefbead3c01f
|
||||
ZONA: git+https://git.ficd.sh/ficd/zona.git@10d1772a2d7a14c977e8359e3df25a2a40948daa
|
||||
site: ficd.sh
|
||||
site_draft: draft.ficd.sh
|
||||
steps:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This is the source code for my personal website, [ficd.sh], built with the
|
||||
[zona] static site generator. It's hosted on [srht.site] and automatically
|
||||
deployed via [builds.sr.ht] when changes are pushed to the `main` branch
|
||||
deployed via Forgejo actions when changes are pushed to the `main` branch
|
||||
of this repository. I use `zona serve` to preview locally before pushing
|
||||
changes.
|
||||
|
||||
|
@ -10,9 +10,9 @@ changes.
|
|||
2. The output is packaged into `.tar.gz` format.
|
||||
3. The tarball is published with `hut pages publish`.
|
||||
|
||||
You can see the entire pipeline in [`.build.yml`](./.build.yml).
|
||||
You can see the entire pipeline in
|
||||
[here](./.forgejo/workflows/deploy.yml).
|
||||
|
||||
[zona]: https://git.ficd.sh/ficd/zona
|
||||
[ficd.sh]: https://ficd.sh
|
||||
[srht.site]: https://srht.site
|
||||
[builds.sr.ht]: https://builds.sr.ht
|
||||
|
|
189
content/blog/email-formatting.md
Normal file
189
content/blog/email-formatting.md
Normal file
|
@ -0,0 +1,189 @@
|
|||
---
|
||||
title: Email Formatting Is Harder Than It Looks
|
||||
date: 2025-07-13
|
||||
draft: true
|
||||
---
|
||||
|
||||
*[UTF-8]: Unicode Transformation Format - 8 bit.
|
||||
|
||||
[Kakoune]: https://kakoune.org
|
||||
|
||||
As I've [mentioned before](./email-in-kakoune.md), I like using [Kakoune] for
|
||||
reading & writing emails. Of course, Kakoune is a text editor, not a _rich text_
|
||||
editor. It operates on UTF-8 _plaintext_ --- which means that the emails I write
|
||||
need to be in plaintext, too.
|
||||
|
||||
As I went down this path, I quickly discovered that I needed an **email
|
||||
formatter**. I eventually wrote [`mailfmt`](https://git.sr.ht/~ficd/mailfmt) to
|
||||
fill this niche. It provides consistent paragraph spacing, hard-wrapping and
|
||||
paragraph reflow, while preserving Markdown syntax, email headers, quotes,
|
||||
sign-offs, and signature blocks. Additionally, the wrapped output can be made
|
||||
safe for passing to a Markdown parser. This is useful if you want to build an
|
||||
HTML email from plain-text.
|
||||
|
||||
`mailfmt` open-source under the ISC license, and is available on
|
||||
[PyPI](https://pypi.org/project/mailfmt/) for installation with tools like
|
||||
`pipx` and `uv`. The source code is available on sourcehut at
|
||||
[git.ficd.sh/ficd/mailfmt](https://git.ficd.sh/ficd/mailfmt).
|
||||
|
||||
## Target Audience
|
||||
|
||||
I wrote this tool primarily for myself. It's served me very well over the past
|
||||
few months. `mailfmt` could be helpful for anyone that prefers writing email in
|
||||
plain-text using text editors like Kakoune, Helix, and Vim. It can format via
|
||||
`stdin`/`stdout` and read/write files, making `mailfmt` easy to configure as a
|
||||
formatter for the `mail` filetype in your editor.
|
||||
|
||||
I'm including a very lengthy explanation of exactly why I built this tool. You
|
||||
may think it's overkill for such a small program — but I like to be crystal
|
||||
clear about justifying my work. It reads like blog post rather than the
|
||||
emoji-filled `README`/marketing style we're accustomed to seeing on this
|
||||
platform. I've put a lot of thought into this, and I want to share my work. I
|
||||
hope you enjoy reading about my thought process.
|
||||
|
||||
## Why I Built It (Comparison)
|
||||
|
||||
Unsurprisingly, it all started with a specific problem I was having composing
|
||||
emails in plain-text format in my preferred text editor. As I searched for a
|
||||
solution, I couldn't find anything that met all my needs, so I wrote it myself.
|
||||
|
||||
Here's what I wanted:
|
||||
|
||||
- A way to consistently format my outgoing emails in my text editor.
|
||||
- Paragraph reflow and automatic line wrapping.
|
||||
- Not all plain-text clients are capable of line-wrap. In some contexts, such
|
||||
as mailing lists, the author is expected to wrap the text themselves.
|
||||
- Inline Markdown syntax `can _still_ look great, **even** in plain-text!` Thus,
|
||||
I wanted to use it:
|
||||
- Without it being broken by reflow & wrap.
|
||||
- While looking good and retaining the same semantics in _both_ rendered
|
||||
**and** plain-text form — ideal for `multipart` emails.
|
||||
- Ensure signature block is formatted properly.
|
||||
- The single space after `--` and before the newline **must** be included.
|
||||
|
||||
### `fmt` and Markdown Formatters Don't Work For Email
|
||||
|
||||
The `fmt` utility provides great wrapping and reflow capabilities — I use it all
|
||||
the time while writing LaTeX. However, it's syntax agnostic, and breaks
|
||||
Markdown. For example, it completely mangles fenced code blocks. I figured: hey,
|
||||
why not just use a Markdown formatter? It supports Markdown (obviously), _and_
|
||||
can reflow & wrap text! Here's the problem: it turns out treating your
|
||||
**entire** email as a Markdown document isn't ideal.
|
||||
|
||||
`mailfmt`'s approach is simple: detect when a line matches a known pattern of
|
||||
Markdown block element syntax, such as leading `#` for headings, `-` for lists,
|
||||
etc. If so, **leave the line untouched**. Similarly, **don't format anything
|
||||
inside fenced code blocks**.
|
||||
|
||||
#### Sign-Offs
|
||||
|
||||
Consider the following sign-off:
|
||||
|
||||
```
|
||||
Best wishes,
|
||||
Daniel
|
||||
```
|
||||
|
||||
A Markdown formatter considers this to be one paragraph, and reflows it
|
||||
accordingly, causing it to lost semantic meaning:
|
||||
|
||||
```
|
||||
Best wishes, Daniel
|
||||
```
|
||||
|
||||
Within the confines of Markdown, I counted three ways of dealing with the
|
||||
problem:
|
||||
|
||||
1. Put an empty line between the two parts:
|
||||
|
||||
```
|
||||
Best wishes,
|
||||
|
||||
Daniel
|
||||
```
|
||||
|
||||
> However, this empty line looks _awkward_ when viewed in plain-text.
|
||||
|
||||
2. Put a backslash after the intentional line break:
|
||||
|
||||
```
|
||||
Best wishes, \
|
||||
Daniel
|
||||
```
|
||||
|
||||
> Again, this looks bad when the Markdown isn't rendered.
|
||||
|
||||
3. Put two spaces after the intentional line break (• = space):
|
||||
|
||||
```
|
||||
Best•wishes,••
|
||||
Daniel
|
||||
```
|
||||
|
||||
> This syntax is **ambiguous, easy to forget**, and **not supported by editors
|
||||
> that trim trailing whitespace.**
|
||||
|
||||
`mailfmt` detects sign-offs using a very simple heuristic. First, we check if a
|
||||
line has _5 or less_ words, and **ends with a comma**. If we find such a line,
|
||||
we check the _next_ line. If it has 5 or less words **that all begin with an
|
||||
uppercase letter**, then we assume these two lines are a _sign-off_, and we
|
||||
don't reflow or wrap them. The heuristic matches a very simple pattern:
|
||||
|
||||
```
|
||||
A courteous greeting,
|
||||
First Middle Last Name
|
||||
```
|
||||
|
||||
#### Signature Block
|
||||
|
||||
The convention for signature blocks is as follows:
|
||||
|
||||
1. Begins with two `-` characters followed by a single space, then a newline.
|
||||
2. Everything that follows until the EOF is part of the signature.
|
||||
|
||||
Here's an example (note the • = space):
|
||||
|
||||
```
|
||||
--•
|
||||
Daniel
|
||||
|
||||
Software•Developer,•Company
|
||||
email@website.com
|
||||
```
|
||||
|
||||
As with sign-offs, such a signature block gets mangled by Markdown formatters.
|
||||
Furthermore, the single space after the `--` token is important: if it's
|
||||
missing, some clients won't recognize it is a valid signature — our formatter
|
||||
should address this too.
|
||||
|
||||
`mailfmt` detects when a line's _only_ content is `--`. It adds the required
|
||||
trailing space if it's missing, and it treats the rest of the input as part of
|
||||
the signature, leaving it completely untouched.
|
||||
|
||||
### Consistent Multipart Emails
|
||||
|
||||
Something you may want to do is generate a `multipart` email. This means that
|
||||
_both_ an HTML **and** plain-text representation of the _same_ email are
|
||||
included in the file — leaving it up to the reader's client to pick which one to
|
||||
display.
|
||||
|
||||
The plain-text email **must** be able to stand on its own, and _also_ render to
|
||||
decent-looking HTML. Essentially, you want to write your email in plain-text
|
||||
once, ensuring it has proper formatting, and then use a command to generate an
|
||||
HTML email from it. For this, `mailfmt` provides the `--markdown-safe` flag,
|
||||
which appends backslashes to the formatted output, making it safe for Markdown
|
||||
parsing without messing up the line breaks after sign-offs and signature blocks.
|
||||
|
||||
For example, I use the following in [aerc](https://aerc-mail.org/) to generate
|
||||
an HTML multipart email whenever I want:
|
||||
|
||||
```ini
|
||||
[multipart-converters]
|
||||
text/html=mailfmt --markdown-safe | pandoc -f markdown -t html --standalone
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
If you've made it this far, thanks for sticking with me and reading to the end!
|
||||
Even if you don't plan to write plain-text email or use `mailfmt` at all, I hope
|
||||
you learned something interesting.
|
|
@ -3,24 +3,18 @@ title: Writing Emails In Kakoune
|
|||
date: 2025-06-01
|
||||
---
|
||||
|
||||
This post will guide you through my setup for using Kakoune as an email composer
|
||||
inside `aerc`. I'll also explain how to configure Kakoune to act as the _pager_
|
||||
for reading `text/plain` emails. If you only care about the final config, feel
|
||||
free to skip to it [here](#final-configuration).
|
||||
This post will guide you through my setup for using Kakoune as an email
|
||||
composer inside `aerc`. I'll also explain how to configure Kakoune to act
|
||||
as the _pager_ for reading `text/plain` emails. If you only care about the
|
||||
final config, feel free to skip to it [here](#final-configuration).
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [Naive Approach](#naive-approach)
|
||||
- [Composer Setup](#composer-setup)
|
||||
- [Reader Setup](#reader-setup)
|
||||
- [Final Configuration](#final-configuration)
|
||||
|
||||
<!--toc:end-->
|
||||
[TOC]
|
||||
|
||||
## Naive Approach
|
||||
|
||||
Since `aerc` uses your `$EDITOR` for composition, you don't technically have to
|
||||
do anything. I prefer setting it explicitly in `aerc.conf`, for good measure:
|
||||
Since `aerc` uses your `$EDITOR` for composition, you don't technically
|
||||
have to do anything. I prefer setting it explicitly in `aerc.conf`, for
|
||||
good measure:
|
||||
|
||||
```ini
|
||||
[compose]
|
||||
|
@ -31,8 +25,9 @@ The rest of the magic happens in your `kakrc`.
|
|||
|
||||
## Composer Setup
|
||||
|
||||
Essentially, we want to hook `filetype=mail` and set our buffer configuration
|
||||
there. I'll share a recommended configuration with some explanation.
|
||||
Essentially, we want to hook `filetype=mail` and set our buffer
|
||||
configuration there. I'll share a recommended configuration with some
|
||||
explanation.
|
||||
|
||||
```kak
|
||||
hook global WinSetOption filetype=mail %~
|
||||
|
@ -47,51 +42,53 @@ hook global WinSetOption filetype=mail %~
|
|||
~
|
||||
```
|
||||
|
||||
I use a custom formatter to format emails. It automatically hard-wraps lines
|
||||
while preserving certain markup elements, code blocks, sign-offs, and signature
|
||||
blocks. For more details, check the formatting section of my post on
|
||||
[Helix](/blog/email/helix#formatting).
|
||||
I use a custom formatter to format emails. It automatically hard-wraps
|
||||
lines while preserving certain markup elements, code blocks, sign-offs,
|
||||
and signature blocks. For more details, check the formatting section of my
|
||||
post on [Helix](/blog/email/helix#formatting).
|
||||
|
||||
I find that setting `>` as the `comment_line` token is convenient for working
|
||||
with quotes in replies.
|
||||
I find that setting `>` as the `comment_line` token is convenient for
|
||||
working with quotes in replies.
|
||||
|
||||
The `try autospell-enable` enables my
|
||||
[kak-autospell](https://codeberg.org/ficd/kak-autospell) plugin for the buffer.
|
||||
Essentially, it provides spellchecking that's continuously refreshed and hidden
|
||||
in insert mode.
|
||||
[kak-autospell](https://codeberg.org/ficd/kak-autospell) plugin for the
|
||||
buffer. Essentially, it provides spellchecking that's continuously
|
||||
refreshed and hidden in insert mode.
|
||||
|
||||
The remaining commands configure auto-formatting on save. I always prefer having
|
||||
this on so I never forget to format my message before sending it.
|
||||
The remaining commands configure auto-formatting on save. I always prefer
|
||||
having this on so I never forget to format my message before sending it.
|
||||
|
||||
## Reader Setup
|
||||
|
||||
I find that using Kakoune to **read** emails is helpful because of how easy it
|
||||
is to copy quotes, open links, etc. Configuring this is a tad hackier, however.
|
||||
The basic idea is to set Kakoune as the viewer `pager` in `aerc.conf`.
|
||||
I find that using Kakoune to **read** emails is helpful because of how
|
||||
easy it is to copy quotes, open links, etc. Configuring this is a tad
|
||||
hackier, however. The basic idea is to set Kakoune as the viewer `pager`
|
||||
in `aerc.conf`.
|
||||
|
||||
However, all this does is pipe the email to `kak` through standard input, so we
|
||||
need to tell the editor to treat it like an email:
|
||||
However, all this does is pipe the email to `kak` through standard input,
|
||||
so we need to tell the editor to treat it like an email:
|
||||
|
||||
```ini
|
||||
[viewer]
|
||||
pager=kak -e 'set buffer filetype mail'
|
||||
```
|
||||
|
||||
When you're using Kakoune as a pager, you'll probably want to configure some
|
||||
things differently. In my case, I like to set the buffer as `readonly`, remove
|
||||
the `number-lines` and `show-whitespaces` highlighters, disable soft-wrap & my
|
||||
scrolloff settings, and _not_ set any formatters.
|
||||
When you're using Kakoune as a pager, you'll probably want to configure
|
||||
some things differently. In my case, I like to set the buffer as
|
||||
`readonly`, remove the `number-lines` and `show-whitespaces` highlighters,
|
||||
disable soft-wrap & my scrolloff settings, and _not_ set any formatters.
|
||||
|
||||
The `pager` command above sets the filetype, but we need to distinguish between
|
||||
_composing_ and _reading_ in our Kakoune hook. When Kakoune is opened with input
|
||||
through standard input, it loads a buffer that's conveniently named `*stdin*`.
|
||||
Thus, we can check the buffer name before continuing.
|
||||
The `pager` command above sets the filetype, but we need to distinguish
|
||||
between _composing_ and _reading_ in our Kakoune hook. When Kakoune is
|
||||
opened with input through standard input, it loads a buffer that's
|
||||
conveniently named `*stdin*`. Thus, we can check the buffer name before
|
||||
continuing.
|
||||
|
||||
If we're in "reading mode", we define a hidden command called `ismailreader`
|
||||
which doesn't do anything. Why? If the command is defined, and we try to invoke
|
||||
it... well, nothing happens! But if it's **not** defined, we get an error
|
||||
instead. We can combine this with the `try` command to for some simple boolean
|
||||
logic.
|
||||
If we're in "reading mode", we define a hidden command called
|
||||
`ismailreader` which doesn't do anything. Why? If the command is defined,
|
||||
and we try to invoke it... well, nothing happens! But if it's **not**
|
||||
defined, we get an error instead. We can combine this with the `try`
|
||||
command to for some simple boolean logic.
|
||||
|
||||
```kak
|
||||
evaluate-commands %sh{
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
:root {
|
||||
--main-text-color: #b4b4b4;
|
||||
--main-bg-color: #121212;
|
||||
--main-link-color: #df6464;
|
||||
--main-heading-color: #df6464;
|
||||
--main-bullet-color: #d87c4a;
|
||||
--main-transparent: rgba(255, 255, 255, 0.15);
|
||||
--main-small-text-color: rgba(255, 255, 255, 0.45);
|
||||
--main-text-color: #b4b4b4;
|
||||
--main-bg-color: #121212;
|
||||
--main-link-color: #df6464;
|
||||
--main-heading-color: #df6464;
|
||||
--main-bullet-color: #d87c4a;
|
||||
--main-transparent: rgba(255, 255, 255, 0.15);
|
||||
--main-small-text-color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.6;
|
||||
font-size: 18px;
|
||||
font-family: sans-serif;
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
padding-left: calc(100vw - 100%);
|
||||
line-height: 1.6;
|
||||
font-size: 18px;
|
||||
font-family: sans-serif;
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
padding-left: calc(100vw - 100%);
|
||||
}
|
||||
|
||||
.toclink {
|
||||
|
@ -63,277 +63,312 @@ h3,
|
|||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: var(--main-heading-color);
|
||||
color: var(--main-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-block-start: 0.67rem;
|
||||
margin-block-end: 0.67rem;
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
margin-block-start: 0.67rem;
|
||||
margin-block-end: 0.67rem;
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
article h1:first-of-type {
|
||||
margin-block-start: 1.67rem;
|
||||
margin-block-start: 1.67rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-block-start: 0.83rem;
|
||||
margin-block-end: 0.83rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-block-start: 0.83rem;
|
||||
margin-block-end: 0.83rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-block-start: 1rem;
|
||||
margin-block-end: 1rem;
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
margin-block-start: 1rem;
|
||||
margin-block-end: 1rem;
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-block-start: 1.33rem;
|
||||
margin-block-end: 1.33rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-block-start: 1.33rem;
|
||||
margin-block-end: 1.33rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
article h1+h4:first-of-type {
|
||||
margin-block-start: 0rem;
|
||||
article h1 + h4:first-of-type {
|
||||
margin-block-start: 0rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-block-start: 1.67rem;
|
||||
margin-block-end: 1.67rem;
|
||||
font-size: 0.83rem;
|
||||
font-weight: bold;
|
||||
margin-block-start: 1.67rem;
|
||||
margin-block-end: 1.67rem;
|
||||
font-size: 0.83rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin-block-start: 2.33rem;
|
||||
margin-block-end: 2.33rem;
|
||||
font-size: 0.67rem;
|
||||
font-weight: bold;
|
||||
margin-block-start: 2.33rem;
|
||||
margin-block-end: 2.33rem;
|
||||
font-size: 0.67rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
/* or any other list style */
|
||||
list-style-type: disc;
|
||||
/* or any other list style */
|
||||
}
|
||||
|
||||
li::marker {
|
||||
color: var(--main-bullet-color);
|
||||
/* Change this to your desired color */
|
||||
color: var(--main-bullet-color);
|
||||
/* Change this to your desired color */
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--main-link-color);
|
||||
color: var(--main-link-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--main-transparent);
|
||||
background: var(--main-transparent);
|
||||
}
|
||||
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: var(--main-small-text-color);
|
||||
border-left: 3px solid var(--main-transparent);
|
||||
padding: 0 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
color: var(--main-small-text-color);
|
||||
border-left: 3px solid var(--main-transparent);
|
||||
padding: 0 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
background: var(--main-small-text-color);
|
||||
border: none;
|
||||
height: 1px;
|
||||
background: var(--main-small-text-color);
|
||||
}
|
||||
|
||||
code {
|
||||
background: var(--main-transparent);
|
||||
border-radius: 0.1875rem;
|
||||
/* padding: .0625rem .1875rem; */
|
||||
/* margin: 0 .1875rem; */
|
||||
background: var(--main-transparent);
|
||||
border-radius: 0.1875rem;
|
||||
/* padding: .0625rem .1875rem; */
|
||||
/* margin: 0 .1875rem; */
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
white-space: pre;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
font-family: monospace;
|
||||
font-size: 0.95em;
|
||||
white-space: pre;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #151515;
|
||||
color: #d5d5d5;
|
||||
padding: 1em;
|
||||
border-radius: 5px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
background-color: #151515;
|
||||
color: #d5d5d5;
|
||||
padding: 1em;
|
||||
border-radius: 5px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Inline code styling */
|
||||
:not(pre) > code {
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 3px;
|
||||
background-color: #1d1d1d;
|
||||
color: #d5d5d5;
|
||||
font-size: 0.85em;
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 0.85em;
|
||||
line-height: 1;
|
||||
background-color: #1d1d1d;
|
||||
border-radius: 6px;
|
||||
vertical-align: middle;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* Block code styling (inherits from pre) */
|
||||
pre code {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.95rem;
|
||||
color: var(--main-small-text-color);
|
||||
font-size: 0.95rem;
|
||||
color: var(--main-small-text-color);
|
||||
}
|
||||
|
||||
small a {
|
||||
color: inherit;
|
||||
/* Inherit the color of the surrounding <small> text */
|
||||
text-decoration: underline;
|
||||
/* Optional: Keep the underline to indicate a link */
|
||||
color: inherit;
|
||||
/* Inherit the color of the surrounding <small> text */
|
||||
text-decoration: underline;
|
||||
/* Optional: Keep the underline to indicate a link */
|
||||
}
|
||||
|
||||
.title-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-container h1 {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
/* Optional: add some spacing around the image container */
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
/* Optional: add some spacing around the image container */
|
||||
}
|
||||
|
||||
.image-container img {
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
max-height: 100%;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
max-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.fixed .image-container img {
|
||||
max-width: 308px;
|
||||
max-height: 308px;
|
||||
max-width: 308px;
|
||||
max-height: 308px;
|
||||
}
|
||||
|
||||
.image-container small {
|
||||
display: block;
|
||||
/* Ensure the caption is on a new line */
|
||||
margin-top: 5px;
|
||||
/* Optional: adjust spacing between image and caption */
|
||||
display: block;
|
||||
/* Ensure the caption is on a new line */
|
||||
margin-top: 5px;
|
||||
/* Optional: adjust spacing between image and caption */
|
||||
}
|
||||
|
||||
.image-container small a {
|
||||
color: inherit;
|
||||
/* Ensure the link color matches the small text */
|
||||
text-decoration: underline;
|
||||
/* Optional: underline to indicate a link */
|
||||
color: inherit;
|
||||
/* Ensure the link color matches the small text */
|
||||
text-decoration: underline;
|
||||
/* Optional: underline to indicate a link */
|
||||
}
|
||||
|
||||
#header ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#header li {
|
||||
display: inline;
|
||||
font-size: 1.2rem;
|
||||
margin-right: 1.2rem;
|
||||
display: inline;
|
||||
font-size: 1.2rem;
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: 2.5rem auto;
|
||||
width: 90%;
|
||||
max-width: 60ch;
|
||||
margin: 2.5rem auto;
|
||||
width: 90%;
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
#postlistdiv ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.moreposts {
|
||||
font-size: 0.95rem;
|
||||
padding-left: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
#nextprev {
|
||||
text-align: center;
|
||||
margin-top: 1.4rem;
|
||||
font-size: 0.95rem;
|
||||
text-align: center;
|
||||
margin-top: 1.4rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
#footer {
|
||||
color: var(--main-small-text-color);
|
||||
color: var(--main-small-text-color);
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 1.5rem auto;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
font-size: 0.85rem;
|
||||
text-align: left; /* Use center if you prefer */
|
||||
border-collapse: collapse;
|
||||
margin: 1.5rem auto;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
font-size: 0.85rem;
|
||||
text-align: left; /* Use center if you prefer */
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid var(--main-transparent);
|
||||
/*border: 1px solid var(--main-bullet-color);*/
|
||||
padding: 0.4rem 0.8rem;
|
||||
vertical-align: middle;
|
||||
border: 1px solid var(--main-transparent);
|
||||
/*border: 1px solid var(--main-bullet-color);*/
|
||||
padding: 0.4rem 0.8rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: bold;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--main-text-color);
|
||||
font-weight: bold;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
table code {
|
||||
font-family: monospace;
|
||||
font-size: 0.85em;
|
||||
background: #1d1d1d;
|
||||
padding: 0.1em 0.25em;
|
||||
border-radius: 3px;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
font-size: 0.85em;
|
||||
background: #1d1d1d;
|
||||
padding: 0.1em 0.25em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
caption {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--main-small-text-color);
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--main-small-text-color);
|
||||
}
|
||||
|
||||
a > code {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a:has(> code) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover > code {
|
||||
background-color: var(--main-transparent);
|
||||
}
|
||||
|
|
2
justfile
2
justfile
|
@ -1,4 +1,4 @@
|
|||
zonaref := `rg "^.*(git\+.*).*$" -r '$1' .build.yml`
|
||||
zonaref := `rg "^.*(git\+.*).*$" -r '$1' .forgejo/workflows/deploy.yml`
|
||||
|
||||
echo:
|
||||
echo {{zonaref}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue