Here, on this blueprints page, the configuration of the Siege Tower itself are declared.
In my org-roam
wiki this is just an Org-mode document with code blocks, exported
for the blog likewise the engine that powers the following declaration forms of the
website's sections.
Therefore, it's a live document. Layout edits and new sections will be reflected here once, and then appear within the walls of the Tower.
§ SiegeTower appears
First a blog site
instance must be instantiated.
A sample definition may go as follows:
(use-package org-roam-blog ; load blogware engine :ensure nil :load-path "/path/to/local/org-roam-blog.el") (org-roam-blog-load-dynamic-module) ; load the rust dynamic module (setf siegetower ; bind instance to variable (org-roam-blog-site-create :staging-dir "/path/to/staging/dir" ; where the resulting website will be generated :src-root-dir "/path/to/root/dir" ; with the pre-generated root files, e.g. stylesheets, js, etc :src-template-dir "/path/to/template/dir")) ; where mustache base templates are visible
§ Templates
For clarity, generators of "standard" (= most used) custom templates are defined here.
The org-roam-blog
sites are static sets of pages by default, meaning all template
transforms, e.g. changing cover image on a "standard" template for different index
sections, must be done upon registration of indexes.
The following function returns a mustache
template (as a string) that is reused
in several of the entries categories later, with only a small change (of a cover image).
(defsubst siegetower/st-entry-template (cover-image) "Return a standard SiegeTower entry material template with selected COVER-IMAGE." (format "<div class=\"columns\"> <div class=\"column is-three-quarters\"> <figure class=\"image \"><img src=\"/image/%s\"></figure> </div> </div> <div class=\"columns\"> <div class=\"column is-three-quarters\"> {{> standard-title}} {{& main}} </div> <div class=\"column is-one-quarter\"> {{> toc}} {{> backlinks}} {{> mt-timeline}} </div> </div>" cover-image))
This short template part itself includes some other templates located in :src-template-dir
of the site instance: standard-title.mustache
, backlincks.mustache
. In the :src-template-dir
there can also be found wrapper-top.mustache
and wrapper-bottom.mustache
.
At the moment, the default (and only) behavior of the site generator is to "sandwich" index
:template
and :entry-template
between wrapper-top
and wrapper-bottom
templates and
populate the resulting template page with index or entry context.
§ Index Registry
For declaring org-roam-blog
index
sections for a site
instance there is
org-roam-blog-register-index
function and its shorter macro version
org-roam-blog-reg
.
§ Literate Style Org-Roam-Blog Engine
The first section to appear within the Tower - its own engine,
programmed in a "literate" style inside emacs-org
!
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "orb" tags) (member "source" tags) (not (member "noexport" tags))))) :title "Orb's Engine" :entry-template (siegetower/st-entry-template "orbs_engine_cover.png"))
The registration form says: "From all the org-roam
nodes take those
that have orb
and source
tags attached to them but not noexport
thus form an Orb's Engine section index". Only :entry-template
and
not just (index) :template
defined means only generate individual entry
pages. :entry-dir
set to nil
means the entry files will go directly
under the index route, which is by default a slugified :title
(in this
case it will be /orbs-engine/
).
§ the Siege Tower Blog Setup
Likewise, this very section of the Siege Tower website configuration has its own declaration (resulting, at the moment, in a single page placed under specific route).
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (member (org-roam-node-id node) '("siegetower-blog-setup" "siegetower-styles"))) :title "SiegeTower Blueprints" :entry-template (siegetower/st-entry-template "torre_sketch.png"))
§ Emacs Literate Config
Foundation of my Digital Garden of Words and Instructions.
Includes literate source blocks that compose my init.el
configuration, as well as the useful definitions library.
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (member (org-roam-node-id node) '("emacs-literate-config" "setup-my-elisp"))) :title "Emacs Literate Config" :entry-template (siegetower/st-entry-template "emacs_node_cover.png"))
§ StumpWM Literate Config
Part of public configs of my work environment, that section also fits on a single page since the StumpWM config is quite short.
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (string-equal (org-roam-node-id node) "project-stumpwm-literate-config")) :title "StumpWM Config" :entry-template (siegetower/st-entry-template "stump_desk.png"))
§ General Info Pages
Lonely static pages have st_keepers_notes
tag attached.
There are very few of them.
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_keepers_notes" tags) (not (member "noexport" tags))))) :title "Keeper's Notes" :entry-template (siegetower/st-entry-template "keepers_table.png"))
§ FTL:Nomad RPG Generators
An interactive leisure tool - first of a kind - on my homepage.
Speed up preparation to ttRPG sessions with a fast and furious SciFi System.
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_ftl_nomad_generators" tags) (not (member "noexport" tags))))) :title "Trident Nexus" :entry-template (siegetower/st-entry-template "trident_nexus.png"))
§ PaperGraph Section
The antique alien device that requires widescreen to work properly.
(org-roam-blog-reg siegetower :root-spec '(all) :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_papergraph" tags) (not (member "noexport" tags))))) :title "Papergraph" :entry-template "<h1 class=\"title has-text-danger\"> Ether Gate: PaperGraph </h1> {{& main}} {{> backlinks}} ")
§ Blog posts and the Feed of the front page
The Infinite Staircase on the front is a mixture of standalone blog posts and also some other materials, sorted by date (newest first).
Later I added some other indexes: Parapet a.k.a Dev Pages for anything development-related, Lounge a.k.a Floating Gardens for leisure and hobby notes. Everything else either ends up in Posts (e.g. personal notes) or lives in different index.
One template to rule them all the post feeds:
(defsubst siegetower/st-feed-template (cover-image) "Return a standard SiegeTower materials feed template with selected COVER-IMAGE." (format "<div class=\"columns\"> <div class=\"column is-four-fifths\"> <figure class=\"image \"><img src=\"/image/%s\"></figure> </div> </div> <div class=\"columns\"> <div class=\"column is-three-quarters\"> <h2 class=\"title has-text-danger\">{{title}}</h2> <ul class=\"has-text-info\"> {{#entries}} <li> {{#show-date}}<h5 class=\"has-text-info\" style=\"display: inline;\">{{date}}</h5>{{/show-date}} <h3 style=\"display: inline;\"><a href=\"{{self-url}}\">{{title}}</a></h3> </li> {{/entries}} </ul> <p> {{#has-prev-page}} <a href=\"{{prev-page}}\"> <button class=\"button is-small is-primary\">< previous</button> </a> {{/has-prev-page}} page <code>{{page}}</code> of <code>{{page-max}}</code> {{#has-next-page}} <a href=\"{{next-page}}\"> <button class=\"button is-small is-primary\">next ></button> </a> {{/has-next-page}} </p> </div> <div class=\"column is-one-quarter\"> {{> mt-timeline}} </div> </div>" cover-image))
Parapet and Lounge will be leading indexes for corresponding post
materials (note alternative entry-fname-fn
that will prepend date to
the slug string of the entries):
(org-roam-blog-reg siegetower :root-spec '(all) :group-by 30 :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_dev" tags) (not (member "noexport" tags))))) :title "Parapet: Dev Pages" :slug "dev" :entry-fname-fn #'org-roam-blog--entry-fname-with-date :template (siegetower/st-feed-template "parapet.png") :entry-template (siegetower/st-entry-template "parapet.png"))
(org-roam-blog-reg siegetower :root-spec '(all) :group-by 30 :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_lounge" tags) (not (member "noexport" tags))))) :title "Floating Gardens: Lounge" :slug "lounge" :entry-fname-fn #'org-roam-blog--entry-fname-with-date :template (siegetower/st-feed-template "floating_gardens.png") :entry-template (siegetower/st-entry-template "floating_gardens.png"))
The post entries definition is similar to what already appeared above,
only it does not define it's own :template
, therefore there is no dedicated
list of standalone posts:
(org-roam-blog-reg ; siegetower :root-spec '(all) :group-by 30 :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (member "st_post" tags) (not (member "noexport" tags))))) :title "Posts" :entry-fname-fn #'org-roam-blog--entry-fname-with-date :entry-template (siegetower/st-entry-template "infinite_staircase.png"))
However, the Staircase feed itself is trickier:
(org-roam-blog-reg siegetower :leading nil :root-spec '(all) :group-by 30 :entry-dir nil :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (and (or (member "st_dev" tags) ;; (member "st_lounge" tags) (member "st_post" tags) (member "st_feed" tags)) (not (member "noexport" tags))))) :title "Infinite Staircase: Journal" :slug "infinite-staircase" :template (siegetower/st-feed-template "infinite_staircase.png"))
So, this is an index
built from materials of other sections -
:leading
set to nil
will make the links to other roam
nodes
within the site point to corresponding (correct) section routes.
That's why for the Staircase index I only define index :template
and omit :entry-template
it is only an aggregator section and does
not export individual entry pages of its own. And in order to appear
on the Staircase, a node must be tagged as st_lounge
, st_dev
st_post
(for corresponding lead indexes) or st_feed
(for other
materials that I would like to drag onto the front page).
On a second thought, I've removed the Lounge from the Staircase.
§ Library Hall - BookTracker Section
This is a reading tracking section built inside my Org-Roam with help of org-books. At the moment, the source is just one file with short review reflections (sometimes one-liners).
I want to display that section as a set of books category indexes (roughly five), joined as one general index.
§ BookTracker Styles and Templates
Booktracker title colors:
(defsubst siegetower/booktracker-color (index-slug) (pcase index-slug ("booktracker-fiction" "#7ec7ff") ("booktracker-cs" "#9cffca") ("booktracker-science" "#ff6a6a") ("booktracker-social" "#d57dff") ("booktracker-gaming" "#fcff93") (default "#bdddda")))
Index template for booktracker sections:
(defsubst siegetower/booktracker-index-template (cover-image section-title index-slug &optional cover-text) (format "<div class=\"columns\"> <div class=\"column is-four-fifths\"> <figure class=\"image \"><img src=\"/image/%s\"></figure> <h2 class=\"title\" style=\"color: %s;\">%s</h2> %s </div> </div> <div class=\"columns\"> <div class=\"column is-three-quarters\"> <ul class=\"has-text-info\"> {{#entries}} <li> {{#show-date}}<h5 class=\"has-text-info\" style=\"display: inline;\">{{date}}</h5>{{/show-date}} <h3 style=\"display: inline; color: {{title-color}};\"> <a href=\"{{self-url}}\" style=\"color: inherit;\">{{title}}</a> </h3> <div style=\"display: inline;\"> by {{author}}</div> </li> {{/entries}} </ul> <p> {{#has-prev-page}} <a href=\"{{prev-page}}\"> <button class=\"button is-small is-primary\">< previous</button> </a> {{/has-prev-page}} page <code>{{page}}</code> of <code>{{page-max}}</code> {{#has-next-page}} <a href=\"{{next-page}}\"> <button class=\"button is-small is-primary\">next ></button> </a> {{/has-next-page}} </p> </div> <div class=\"column is-one-quarter\"> {{> mt-timeline}} </div> </div>" cover-image (siegetower/booktracker-color index-slug) section-title (or cover-text "")))
Each booktracker entry knows its category color. When in the general library hall, they will be of nice different colors:
(defun siegetower/booktracker-entry-context (node) (let ((context-ht (org-roam-blog--entry-context-default node))) (let* ((lead-index (org-roam-node--lead-index-for (org-roam-node-id node))) (lead-index-slug (org-roam-blog-index-slug lead-index)) (title-color (siegetower/booktracker-color lead-index-slug)) (author (cdr (assoc "AUTHOR" (org-roam-node-properties node)))) (catalog-url (cdr (assoc "GOODREADS" (org-roam-node-properties node))))) (ht-merge context-ht (ht ("title-color" title-color) ("author" author) ("catalog-url" catalog-url))))))
Booktracker entry template:
(defsubst siegetower/booktracker-entry-template (cover-image) (format "<div class=\"columns\"> <div class=\"column is-three-quarters\"> <figure class=\"image \"><img src=\"/image/%s\"></figure> </div> </div> <div class=\"columns\"> <div class=\"column is-three-quarters\"> {{> booktracker-title}} {{& main}} </div> <div class=\"column is-one-quarter\"> {{> backlinks}} {{> mt-timeline}} </div> </div>" cover-image))
§ BookTracker Categories
Books category registration macro shortcut:
(defmacro siegetower/booktracker-cat-reg (spec-id index-title cover-image cover-title) `(org-roam-blog-reg siegetower :root-spec '(node ,spec-id) :group-by 30 :filter-fn (lambda (node) (let ((tags (org-roam-node-tags node))) (not (member "noexport" tags)))) :title ,index-title :entry-context-fn #'siegetower/booktracker-entry-context :template (siegetower/booktracker-index-template ,cover-image ,cover-title (org-roam-blog-slugify ,index-title)) :entry-template (siegetower/booktracker-entry-template ,cover-image)))
Register site index for each of the books category:
(siegetower/booktracker-cat-reg "books-fiction" "BookTracker Fiction" "libhall_fiction.png" "Library Hall: Fiction") (siegetower/booktracker-cat-reg "books-computer" "BookTracker CS" "libhall_compsci.png" "Library Hall: Computer Science & IT") (siegetower/booktracker-cat-reg "books-natural" "BookTracker Science" "libhall_natural.png" "Library Hall: (Popular) Science") (siegetower/booktracker-cat-reg "books-social" "BookTracker Social" "libhall_social.png" "Library Hall: Psy, Soc and Economics") (siegetower/booktracker-cat-reg "books-gaming" "BookTracker Gaming" "libhall_gaming.png" "Library Hall: Narrative Gaming")
The topmost (and currently only visible in the nav menu) Library Hall book tracker index:
(let ((index-title "BookTracker") (cover-text (format "<p> In this dusty hall I track my readings with <code>org-books</code> extension for <code>emacs</code>, leaving short comments upon completion of each tome. </p> <p> Almost all the books I do classify in five categories: <span style=\"color: %s\">fiction</span>, (popular) science of <span style=\"color: %s\">natural phenomena</span>, <span style=\"color: %s\">sociology and economics</span>, <span style=\"color: %s\">IT & computer science</span>, and <span style=\"color: %s\">tabletop rpg-related</span> materials. Each category assignment may be quite arbitrary though. </p><hr/>" (siegetower/booktracker-color "booktracker-fiction") (siegetower/booktracker-color "booktracker-science") (siegetower/booktracker-color "booktracker-social") (siegetower/booktracker-color "booktracker-cs") (siegetower/booktracker-color "booktracker-gaming")))) (org-roam-blog-reg siegetower :root-spec '(all) :group-by 30 :filter-fn (lambda (node) (let ((id (org-roam-node-id node)) (tags (org-roam-node-tags node))) (and (member "st_library_hall" tags) (not (member id '("books-revised" "books-fiction" "books-computer" "books-social" "books-natural" "books-gaming"))) (not (member "noexport" tags))))) :title index-title :entry-context-fn #'siegetower/booktracker-entry-context :template (siegetower/booktracker-index-template "libhall.png" "Library Hall: BookTracker" (org-roam-blog-slugify index-title) cover-text) :entry-template (siegetower/booktracker-entry-template "libhall.png")))
§ Siegetower Org-Capture Template
(defun my-init/siegetower-post-file () "Helper file finding Org-capture shortcut for Siegetower posts." (expand-file-name (format "%s/projects/siegetower/infinite_staircase/%s.org" my-init/org-roam-dir (read-minibuffer "post filename: ")))) (add-to-list 'org-capture-templates '("s" "Siegetower Staircase" plain (function (lambda () (find-file (my-init/siegetower-post-file)) (goto-char 0))) ":PROPERTIES: :ID: %(org-id-uuid) :ADDED: [%<%Y-%m-%d>] :TOC_LEVEL: 1 :END: ,#+title: %^{Title} ,#+filetags: %^{Tags} %?") t) ;; append