9932a47f433d98ff34190c8fd49f9893ceb24cd9 — Andrew Tropin 4 months ago f06135e
Add pages
M assets/paper.css => assets/paper.css +0 -7
@@ 24,10 24,3 @@ body{counter-reset: section}
    counter-increment: detail;
    content: counter(section) "." counter(sub-section) "." counter(composite) "." counter(detail) " ";

a {
    color: black;
a:visited {
    color: grey;

M assets/simple.css => assets/simple.css +56 -32
@@ 17,7 17,10 @@
  --text-light: #585858;
  --border: #898EA4;
  --accent: #0d47a1;
  --code: #d81b60;
  --link: black;
  --link-visited: gray;
  /* --code: #d81b60; */
  --code: #444;
  --preformatted: #444;
  --marked: #ffdd33;
  --disabled: #efefef;

@@ 33,7 36,10 @@
    --text: #dcdcdc;
    --text-light: #ababab;
    --accent: #ffb300;
    --code: #f06292;
    --link: white;
    --link-visited: gray;
    /* --code: #f06292; */
    --code: #ccc;
    --preformatted: #ccc;
    --disabled: #111;

@@ 112,6 118,19 @@ body > footer {
  border-top: 1px solid var(--border);

/* Prevent long strings from overflowing container */
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;

/* Fix line height when title wraps */
h3 {
  line-height: 1.1;

/* Format headers */
/* h1 { */
/*   font-size: 3rem; */

@@ 139,36 158,24 @@ body > footer {
/*   font-size: 0.96rem; */
/* } */

/* Prevent long strings from overflowing container */
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;

/* Fix line height when title wraps */
h3 {
  line-height: 1.1;

/* Reduce header size on mobile */
@media only screen and (max-width: 720px) {
  h1 {
    font-size: 2.5rem;

  h2 {
    font-size: 2.1rem;

  h3 {
    font-size: 1.75rem;

  h4 {
    font-size: 1.25rem;
/* @media only screen and (max-width: 720px) { */
/*   h1 { */
/*     font-size: 2.5rem; */
/*   } */

/*   h2 { */
/*     font-size: 2.1rem; */
/*   } */

/*   h3 { */
/*     font-size: 1.75rem; */
/*   } */

/*   h4 { */
/*     font-size: 1.25rem; */
/*   } */
/* } */

/* Format links & buttons */
/* a, */

@@ 571,7 578,7 @@ pre span,
samp {
  font-family: var(--mono-font);
  /* color: var(--code); */
  color: var(--code);

kbd {

@@ 590,6 597,10 @@ pre {
  color: var(--preformatted);

code {
    font-size: 1rem;

/* Fix embedded code within pre */
pre code {
  /* color: var(--preformatted); */

@@ 674,3 685,16 @@ dialog::backdrop {
  padding: 1.5rem;
  margin: 2rem 0;

a.invisible-link, a.invisible-link:visited {
    text-decoration: none;
    color: var(--link);

a {
    color: var(--link);

a:visited {
    color: var(--link-visited);

A pages/contact.md => pages/contact.md +16 -0
@@ 0,0 1,16 @@
title: contact
# Contact

Here are my contacts:

- **email:** andrew [-at-] this domain
- **mastodon:** [@abcdw@fosstodon.org](https://fosstodon.org/@abcdw)
- **tg:** @andrewtropin
- **matrix:** [@abcdw:attendees.fosdem.org](https://matrix.to/#/@abcdw:attendees.fosdem.org)
- **OpenPGP:** [D963A5A38A803D524461F91474830A276C328EC2](https://meta.sr.ht/~abcdw.pgp)

For bug reports, patches and user support use mailing lists and chats
of respective projects, please.  This way more people can
help/review/answer and more people can benefit having public access to
those conversations.

A pages/guix.md => pages/guix.md +49 -0
@@ 0,0 1,49 @@
title: guix
## Guix Installation ISO

This is a basic installation image, but ci.guix.gnu.org substitute
server is changed to ci.guix.trop.in mirror.


Built using [this](https://git.sr.ht/~abcdw/rde/commit/5dd6470437113f24cc7934f3a47f911fb768a328) simple operating-system record definition and my
local guix checkout.

To build a similiar image yourself, [add](http://guix.trop.in/en/manual/devel/en/guix.html#Specifying-Additional-Channels) rde [channel](https://trop.in/rde/manual#rde-as-a-Channel) and do:

guix system image -t iso9660 \
-e '(@ (rde system install) guix-with-substitute-mirror)'

If you use graphical installer and network check fails, `ctrl-alt-f3`:

touch /tmp/installer-assume-online

## Guix Mirror

If \*guix.gnu.org is blocked for your network by any reason[^1], you
can use a reverse proxy, which mirrors various guix services.  Please
don't use it if the upstream services is available for you.

All services are available in clearnet and yggdrasil.  All services
are exposed by **HTTP only**, if you want to have the traffic to be end to
end encrypted use yggdrasil.  The instruction about yggdrasil setup
should appear here in some future.

-   **site:** [guix.trop.in](http://guix.trop.in), [guix.ygg.trop.in](http://guix.ygg.trop.in), [clearnet
    ip](, [yggdrasil ip](http://[200:554d:3eb1:5bc5:6d7b:42f4:8792:efb8]/).
-   **bugtracker:** [issues.guix.trop.in](http://issues.guix.trop.in),
-   **substitutes:** [http://ci.guix.trop.in](http://ci.guix.trop.in),
    [http://ci.guix.ygg.trop.in](http://ci.guix.ygg.trop.in), the protocol is http!

It's also possible to obtain [substitutes via Tor](http://guix.trop.in/en/cookbook/en/html_node/Getting-substitutes-from-Tor.html#Getting-substitutes-from-Tor), directly from
ci.guix.gnu.org host and there is another mirror[^2].

[^1]: https://lists.gnu.org/archive/html/help-guix/2022-03/msg00004.html

[^2]: http://issues.guix.trop.in/54370#2

A pages/index.html => pages/index.html +22 -0
@@ 0,0 1,22 @@
title: main
  <h1 class="title">trop.in</h1>
Probably you are interested in yt <a href="https://youtube.com/@abcdw">channel</a>.
rde project is <a href="./rde/ ">here</a>.
You can find Andrew's contacts <a href="./contact">here</a>.
  <a rel="me" href="https://fosstodon.org/@abcdw" hidden="hidden">Mastodon</a>
  <h2 id="guix-mirror">Guix Mirror and Installation Image</h2>
      <a href="./guix.html">Here</a> you can find information about guix mirror and
link to custom installation ISO.

A pages/posts/2023-05-10-scheme-ssgs-review.md => pages/posts/2023-05-10-scheme-ssgs-review.md +374 -0
@@ 0,0 1,374 @@
title: Scheme Static Site Generators Review
date: 2023-05-10 12:00
tags: architecture, tech
abstract: An overview of Scheme ecosystem in the field of static site generators (SSGs), review of the Haunt SSG architecture and possible ways to improve it and Guile ecosystem.

## Introduction
Static site generator is a program, which accepts text files as input
and produces static web pages as output.  It can be useful in various
scenarios: for building blog, book, documentation, project or personal
page for example.

There are a few SSGs
in Scheme available in the wild, namely
[Skribilo](https://www.nongnu.org/skribilo/) and
[Hyde](http://wiki.call-cc.org/eggref/5/hyde) and two more for Racket:
[Pollen](https://docs.racket-lang.org/pollen/) and
[Frog](https://docs.racket-lang.org/frog/index.html), which are
outside of Guile ecosystem, but still quite close and can be taken as
a source of inspiration.

Hyde doesn't seem to be maintained, but the [source
still available and even [an
attempt](https://bitbucket.org/DerGuteMoritz/ate) to go
further/reincarnate the project exists.

Skribilo is documentation production toolkit and capable of much more
and provides a lot of functionality outside of SSG scope, so we don't
cover it in this writing.

Basically we have only one option left at the moment: Haunt and the
further discussion will be ralated to it, but before exploring it, we
need to get to common ground and cover the topic of different markup

## Markup Languages
Markup languages are used for defining documentation structure,
formatting, and relationship between its parts.  They play an
important role in SSGs, different languages can suite better for
different tasks: simple and expressive for human convinience, powerful
and capable for intermediate representation and manipulation,
compatible and wide-spread for distribution.

This is a probably most widespread family of markup languages,
currently used all over the web.  Not always, but usually SSGs create
HTML or XHTML documents as an output.  Also, it is good to know the
relationship between those languages to understand some technical
issues we will face later.

SGML (Standard Generalized Markup Language) appeared in 1986 and
highly influenced HTML and XML.

XML (Extensible Markup Language) is a meta language, which allows to
create new languages (like XHTML), originially developed as a
simplification of SGML with rigid and not open for confusion syntax,
it's defined in the SGML Doctype language.  Often used for
representing and exchange data.

HTML is a more user friendly markup language, it's defined in plain
english, has more forgiving parses and interpreters, allows things
like uppercased tags, tags without matching closing tag.  Such
flexibilities can be convinient for users, but it makes it harder to
programmaticaly operate on it (parse, process and serialize).

XHTML (XML serialization of HTML) is a version of HTML, which is
compliant with an XML grammar.  XHTML can be used with XML parsers,
tools for querying, transformation should work as well.

While both HTML and XML are influenced by SGML, there is no direct
relationship between them and tools for XML can't be used for HTML in
general case.

### Lightweight Markup Languages
This is another family of markup languages, which are simplier, less
verbose, and more human-oriented in general.  The notable members are
Wiki, Markdown, Org-mode, reStructuredText, BBCode, AsciiDoc.

Often SSGs use those languages for representing the content of pages,
posts, etc.  Later it is combined with other parts and templates and
final output is produced, usually in the form of (X)HTML documents.

### Other Markup Languages
There are a number of languages and typesetting systems, which are not
covered by previous two sections: Texinfo, LaTeX, Skribe, Hiccup,
SXML.  The goals for them can be different: preparing hardcopies,
using as intermediate format, or just more suitable for specific needs
like writing documentation.

## Haunt Overview
Haunt is a simple and hackable SSG written in Scheme, it tries to
apply functional programming ideas and the usual approach for building
a site with it: prepare SXML page templates, read the content from
HTML, Markdown or any other markup files and convert it to SXML,
insert the content into templates and serialize resulting pages to
HTML.  Let's discuss various parts of this process in more details.

### SXML
SXML is a representation of XML using S-expressions: lists, symbols
and strings, which can be less verbose than original representation
and much easier to work with in Scheme.

[SXML](https://okmij.org/ftp/Scheme/xml.html#SXML-spec) is used as
intermediate format for pages and their parts in Haunt, which
relatively easy to process, manipulate and later serialize to target
formats like XHTML.  It can be crafted by creating s-expression from
Scheme code manually, or programmatically, or with a mix of both.  It
looks like this:

(define n 3)

(define slide-content
  (get-html-part "./slide3.html" "body>div.content"))

(define (sxml-slide n slide-content)
  `((h2 ,(format #f "Slide number: ~a" n))
    (div (@ (class "slide-content"))
         (p "the additional text of the slide"))))))

As it was mentioned in the introduction there is no direct
relationship between XML and HTML, and while we usually can parse
arbitrary HTML and convert it to SXML without losing significant
information, we can't directly use XML parses for that.  For example
this HTML is not valid XML:

<input type="checkbox" checked />

Luckily, we can present boolean attributes in full form as
`hidden="hidden"`, which is valid both in HTML[^4] and XML.

Most lightweight markup languages as well as SSGs usually targeting
HTML, but SSG needs to combine the content, templates and data from
various sources and merge them together, so SXML looks as a solid
choice for intermediate representation.

### The Transformation Workflow
Each site page is built out of a series of consequently applied
trasformations, the transformation is basically a function, which
accepts some metadata and data and returns another data (usually SXML)
and sometimes additional metadata.  Because transformation is a basic
pure function, a few transformations can be composed in one bigger

We will cover it in more details in the next section, but readers,
templates, layouts, serializers, builders are all just
transformations.  For example the top level template, called layout
just produces SXML for the final page, which can be serialized to the
target format.  To demonstrate the workflow we will go bottom up.

Let's take a simple Markdown file, where one wants to write the
content of a blog post in human-friendly markup langugage and let's
add a metadata to the top of this file: title, publish date, tags.

title: Hello, CommonMark!
date: 2023-05-09 12:00
tags: markdown, commonmark

## This is a CommonMark post

CommonMark is a **strongly** defined, *highly* compatible
specification of Markdown, learn more about CommomMark

It can be parsed into metadata
([alist](https://www.gnu.org/software/guile/manual/html_node/Association-Lists.html)) +
data (SXML).

=> ((tags "markdown" "commonmark")
    (date . #<date nanosecond: 12 day: 9 month: 5 year: 2023 zone-offset: 14400>)
    (title . "Hello, CommonMark!"))
=> ((h2 "This is a CommonMark post")
    (p "CommonMark is a " (strong "strongly") " defined, "
       (em "highly") " compatible" "\n"
       "specification of Markdown, learn more about CommomMark" "\n"
       (a (@ (href "http://commonmark.org/")) "here") "."))

Metadata+data representing one post is a good unit of operation.  With
one more transformation (it can be just a template, function adding
`html`, `head`, `body` tags and a few more minor things) SSG can
produce almost ready for serialization SXML.  Decide on resulting file
name, one more serialization step and final HTML is here.

Some additional transformation can be desirable in between: substitute
relative links to source markup files to finally generated html files
or something else, but overall it fits this general trasformation
workflow well.

Let's zoom out a little and take a look at the directory, rather than
a single file.  Usually, SSGs operate on a number of files and in
addition to simple pages can generate composite pages like a list of
articles, rss feeds or something else.  For this purpose our unit of
operation becomes a list of data+metadata objects: instead of parsing
one markup file SSG traverses the whole directory and generates a list
of objects for future transformation, overall idea still the same, but
instead many output files for many input files, SSG produces a list
containing only a few or even one output file.

### The Implementation

#### The Entry Point
The entry point is a `site` record, which can be created with a
function having the following docstring:

Create a new site object.  All arguments are optional:

TITLE: The name of the site
DOMAIN: The domain that will host the site
SCHEME: Either 'https' or 'http' ('https' by default)
POSTS-DIRECTORY: The directory where posts are found
FILE-FILTER: A predicate procedure that returns #f when a post file
should be ignored, and #f otherwise.  Emacs temp files are ignored by
BUILD-DIRECTORY: The directory that generated pages are stored in
DEFAULT-METADATA: An alist of arbitrary default metadata for posts
whose keys are symbols
MAKE-SLUG: A procedure generating a file name slug from a post
READERS: A list of reader objects for processing posts
BUILDERS: A list of procedures for building pages from posts

The primary thing here is a list of builders, as previously mentioned
a builder is a special case of complex transformation, it's a thing,
which do all the work including parsing, templating, generating
collections, serialization, etc.

The rest of the list is basically metadata or auxiliary functions,
while many of those values can be useful, almost none of them are
needed in many cases.  `scheme` and `domain` used for rss/atom feeds,
which are rare for personal or landing pages, the similiar logic is
applicable for the rest of function arguments, except maybe
`build-directory`, which almost always make sense.

Providing default values for them is convinient, but making them to be
fields of `site` records incorporates unecessary assumptions about
blog nature of the site, which can negatively impact the rest of the
implementation by adding unwanted coupling and reducing composability.

TODO: Write about possible alternative for site fields.

#### Builders
Builders are functions, which accept `site` and `posts` and returns a
list of artifacts.  Artifacts are records, which have
`artifact-writer` field, containing a closure writing actual output
file.  There are a number of different builders provided out of the
box, but the most basic one (static-page) is missing, luckily it's not
hard to implement it, so let's do it.

(define* (page-theme #:key (footer %default-footer))
   (lambda (site title body)
     `((doctype "html")
        (meta (@ (charset "utf-8")))
        (title ,(string-append title " — " (site-title site))))
        (div (@ (class "container"))
   (lambda (post)
     `((div ,(post-sxml post))))))

(define* (static-page file destination
                      (theme (page-theme))
                      (reader commonmark-reader))
  "Return a builder procedure that reads FILE into SXML, adjusts it
according to the THEME and serialize to HTML and put it to
build-directory of the site.  DESTINATION is a relative resulting file
  (lambda (site posts)
      (render-post theme site (read-post reader file '()))

As described in a section about transformations, the series of
transmorations happens here:
- `read-post` basically prases markdown and returns SXML + metadata.
- `render-post` uses post-template from `theme` to produce SXML post body.
- `render-post` uses layout from `theme` to produce SXML post body.
- `serialized-artifact` creates a closure, which wraps `sxml->html`
  and will later serialize obtained SXML for the page to HTML.

### Readers
There is a concept of readers, small functions

### Guile-Commonmark
It used in haunt by default to parse markdown files in SXML, it
doesn't support embeded html, tables, footnotes and comments, so it
can be quite inconvinient for many use cases.

TODO: It was something else important that was missing in

### Mix of imlicit and explicit things

### Metadata
Accepts only one-line metadata.  Doesn't accept files without metadata.
Metadata is not a part of the html grammar -> post is not a valid html.

register-metadata-parser! is a reimplementation of multimethods.

### Site Alist

`((posts-directory . "pages/posts")
  (build-directory . "target/site"))

### Theme
Layout for posts and collections is the same. the same layout is
coupled to both of them.

### TODOs
Linking to md files are not converted to apropriate urls, do we want
to implement it and if want then how?

Org-roam workflow

### Workflows
- ox-haunt
- md
- citations
- one file multiple post

### Build/Deploy
Rebuild and redeploy do it for the whole site every time.


How to build page with links to rss?
How to build a collection with different template?

## Conclusion
The conclusion here

### Future Work
Possible future steps are improving SXML/HTML ecosystem in Guile,
producing tree-sitter based parsers for various formats

**Aknowledgments.** Thank you to [David
Thompson](https://dthompson.us/about.html) for making Haunt.

[^1]: https://jamstack.org/generators/haunt/

[^2]: https://github.com/pinceladasdaweb/Static-Site-Generators#scheme

[^3]: https://stackoverflow.com/questions/1429065/compare-contrast-html-xhtml-xml-and-html5

[^4]: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML

A pages/rde/index.md => pages/rde/index.md +16 -0
@@ 0,0 1,16 @@
title: trop.in/rde
# trop.in/rde

Developers and power user friendly GNU/Linux distribution.

Build [status](https://builds.sr.ht/~abcdw/rde?).

The documentation: [manual](/rde/manual).

The source code: [sr.ht](https://git.sr.ht/~abcdw/rde),
[github](https://github.com/abcdw/rde) (mirror).

There is a [video](https://youtu.be/6yrYWjjuIOs) about rde.

[Contact](/contact) the author for more details.

A pages/sport.md => pages/sport.md +24 -0
@@ 0,0 1,24 @@
title: sport
# Sport

This page is related to trop.in/sport [chat](https://t.me/joinchat/PEtuyKqvlsQ3NzJi), where different
activities, workouts and events are organized:

-   Climbing workouts.
-   Kayking workouts.
-   Wakeboard camps.
-   Snowboard trips.
-   And some others.

If you want to participate, join the chat or [contact](contact) Andrew via email
or any other way.

## Climbing

-   **[Belay Masterclass](https://www.youtube.com/playlist?list=PL5FEOhiQGSo8PBwTZPeiwQGcxQ0xB99Gt):** a series of videos, all about belaying: ropes,
    carabiners, harness, quickdraws and of course techniques.
-   **[Response to Belay Masterclass Ep. 6](https://youtu.be/H63pxyXHP50):** explanation of different
    belaying techniques, including PBUS (the one usually we use in our
-   **[How to Tie Figure 8](https://youtu.be/PJkCaUUhqgs):** a tutorial on Figure 8 knot.

A pages/stream.html => pages/stream.html +16 -0
@@ 0,0 1,16 @@
title: stream
plain: #t
<div id="content" class="content">
<h1 class="title" align="center">trop.in/stream</h1>
  Hi!  I'm Andrew Tropin and I stream from time to time. You can find recordings of the streams at
  <a href="https://diode.zone/c/andrewtropin/videos">peertube</a> and
  <a href="https://youtube.com/@abcdw">youtube</a>.
<!-- <p><code>mpv https://diode.zone/w/uwH9rPkGLGesfp2GCd7sHT</code></p> -->
<iframe width="800" height="450" src="https://diode.zone/videos/embed/e7098d6e-0314-4ff2-835f-d01bab54dad5" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups"></iframe>
<iframe src="https://web.libera.chat/gamja/?nick=guest#tropin" style="border:0; height:450px; width:400px"></iframe>