summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <lthms@soap.coffee>2022-08-13 17:05:37 +0200
committerThomas Letan <lthms@soap.coffee>2022-08-13 17:11:58 +0200
commitde83a3ea99275de9887618f1ea2f9d468a0d4e6a (patch)
tree6a8066ac9414b09b353d810793f7d041c4242172
parentSome minor CSS tweak (diff)
Goodbye, cleopatra
As it is, it is just too slow. Plus, the literate program that was my toolchain is actually not documented, so it makes no sense. I’m sad, though.
-rw-r--r--.gitignore34
-rw-r--r--cleopatra.toml1
-rw-r--r--commands.mk8
-rw-r--r--coq.mk29
-rw-r--r--dependencies.mk19
-rw-r--r--literate-programming.mk19
-rw-r--r--makefile31
-rw-r--r--org.mk21
-rw-r--r--package.json7
-rw-r--r--plugins/clean-up.lua19
-rw-r--r--plugins/css.lua6
-rw-r--r--plugins/external-urls.lua25
-rw-r--r--plugins/series.lua51
-rw-r--r--plugins/urls-rewriting.lua37
-rw-r--r--scripts/cleopatra.el67
-rwxr-xr-xscripts/css.sh12
-rw-r--r--scripts/export-lp.el18
-rw-r--r--scripts/export-org.el81
-rwxr-xr-xscripts/gen-deps.sh14
-rwxr-xr-xscripts/history.sh106
-rw-r--r--scripts/init.el66
-rw-r--r--scripts/packages.el5
-rwxr-xr-xscripts/pretty-echo.sh6
-rw-r--r--scripts/render-equations.js11
-rwxr-xr-xscripts/update-gitignore.sh16
-rw-r--r--site/cleopatra.org27
-rw-r--r--site/meta.org6
-rw-r--r--site/news/index.html10
-rw-r--r--soupault.conf94
-rw-r--r--soupault.mk8
-rw-r--r--style.css413
-rw-r--r--templates/history.html33
-rw-r--r--templates/main.html47
-rw-r--r--theme.mk7
34 files changed, 1317 insertions, 37 deletions
diff --git a/.gitignore b/.gitignore
index 8dcd287..816f58d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,33 +1,23 @@
*~
# begin generated files
-.cleopatra
+.commands.deps
+.coq.deps
+.dependencies.deps
.emacs
+.emacs.d
+.emacs.d/cache
.lia.cache
+.literate-programming.deps
+.org.deps
+.soupault.deps
+.theme.deps
_opam
-build.log
-commands.mk
-coq.mk
-dependencies.mk
-export-lp.el
-literate-programming.mk
lp/
node_modules
-org.mk
out
package-lock.json
-package.json
-plugins/clean-up.lua
-plugins/css.lua
-plugins/external-urls.lua
-plugins/series.lua
-plugins/urls-rewriting.lua
rss.json
-scripts/css.sh
-scripts/export-org.el
-scripts/history.sh
-scripts/packages.el
-scripts/render-equations.js
site/cleopatra.html
site/cleopatra/commands.html
site/cleopatra/coq.html
@@ -106,11 +96,5 @@ site/posts/StronglySpecifiedFunctionsRefine.vos
site/posts/Thanks.html
site/posts/deps.svg
site/projects/index.html
-soupault.conf
-soupault.mk
-style.css
style.min.css
-templates/history.html
-templates/main.html
-theme.mk
# end generated files
diff --git a/cleopatra.toml b/cleopatra.toml
deleted file mode 100644
index b044b35..0000000
--- a/cleopatra.toml
+++ /dev/null
@@ -1 +0,0 @@
-generation_processes = "site/cleopatra/" \ No newline at end of file
diff --git a/commands.mk b/commands.mk
new file mode 100644
index 0000000..1554901
--- /dev/null
+++ b/commands.mk
@@ -0,0 +1,8 @@
+serve :
+ @cleopatra echo Spwaning "HTTP server"
+ @cd out && python -m http.server
+
+update :
+ @scripts/pretty-echo "Updating" "OCaml dependencies"
+ @opam update
+ @opam upgrade -y
diff --git a/coq.mk b/coq.mk
new file mode 100644
index 0000000..c55526e
--- /dev/null
+++ b/coq.mk
@@ -0,0 +1,29 @@
+COQ_POSTS := $(shell find site/ -name "*.v")
+COQ_HTML := $(COQ_POSTS:.v=.html)
+COQ_ARTIFACTS := $(COQ_POSTS:.v=.vo) \
+ $(COQ_POSTS:.v=.vok) \
+ $(COQ_POSTS:.v=.vos) \
+ $(COQ_POSTS:.v=.glob) \
+ $(join $(dir ${COQ_POSTS}),$(addprefix ".",$(notdir $(COQ_POSTS:.v=.aux))))
+
+coq-build : ${COQ_HTML}
+
+soupault-build : coq-build
+
+ARTIFACTS += ${COQ_ARTIFACTS} .lia.cache
+ARTIFACTS += ${COQ_HTML}
+
+COQLIB := "https://coq.inria.fr/distrib/current/stdlib/"
+COQCARG := -async-proofs-cache force \
+ -w -custom-entry-overriden
+COQDOCARG := --no-index --charset utf8 --short \
+ --body-only --coqlib "${COQLIB}" \
+ --external "https://coq-community.org/coq-ext-lib/v0.11.2/" ExtLib \
+ --external "https://compcert.org/doc/html" compcert \
+ --external "https://lysxia.github.io/coq-simple-io" SimpleIO
+
+%.html : %.v coq.mk _opam/init
+ @scripts/pretty-echo.sh Exporting "$*.v"
+ @coqc ${COQCARG} $<
+ @coqdoc ${COQDOCARG} -d $(shell dirname $<) $<
+ @rm -f $(shell dirname $<)/coqdoc.css
diff --git a/dependencies.mk b/dependencies.mk
new file mode 100644
index 0000000..f79c5ad
--- /dev/null
+++ b/dependencies.mk
@@ -0,0 +1,19 @@
+OCAML_VERSION := 4.12.0
+OCAML := ocaml-base-compiler.${OCAML_VERSION}
+
+_opam/init :
+ @scripts/pretty-echo.sh "Creating" "a local Opam switch"
+ @opam switch create . ${OCAML} --repos default,coq-released || true
+ @scripts/pretty-echo.sh "Installing" "OCaml dependencies"
+ @opam install dune.2.9.0 coq-coqffi.1.0.0~beta7 coq-simple-io.1.5.0 soupault.4.0.1 coq.8.13.2 coq-compcert.3.8 -y
+ @touch $@
+
+CONFIGURE += _opam
+
+package-lock.json : package.json
+ @scripts/pretty-echo.sh "Installing" "frontend dependencies"
+ @npm install
+
+CONFIGURE += package-lock.json node_modules
+
+dependencies-prebuild : _opam/init package-lock.json
diff --git a/literate-programming.mk b/literate-programming.mk
new file mode 100644
index 0000000..42b1bab
--- /dev/null
+++ b/literate-programming.mk
@@ -0,0 +1,19 @@
+literate-programming-prebuild : org-prebuild
+ @scripts/pretty-echo.sh "Tangling" "literate programming project"
+ @${EMACS} --"load=scripts/export-lp.el"
+
+ARTIFACTS += lp/ site/posts/deps.svg
+
+COQFFI_ARCHIVE := site/files/coqffi-tutorial.tar.gz
+
+coqffi-tutorial-build : literate-programming-prebuild _opam/init
+ @scripts/pretty-echo.sh "Building" "coqffi tutorial"
+ @cd lp/coqffi-tutorial; dune build --display quiet
+ @scripts/pretty-echo.sh "Archiving" "coqffi tutorial"
+ @rm -f ${COQFFI_ARCHIVE}
+ @tar --exclude="_build" -C lp/ -czvf ${COQFFI_ARCHIVE} coqffi-tutorial
+
+site/posts/CoqffiEcho.html : coqffi-tutorial-build
+literate-programming-build : coqffi-tutorial-build
+
+ARTIFACTS += ${COQFFI_ARCHIVE}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..8ab0819
--- /dev/null
+++ b/makefile
@@ -0,0 +1,31 @@
+ARTIFACTS :=
+CONFIGURE := .emacs.d
+
+PROCS := $(wildcard *.mk)
+PROCS_DEPS := $(foreach proc,$(PROCS:.mk=.deps),.${proc})
+
+CMD ?= postbuild
+
+EMACS := ROOT=$(shell pwd) emacs -Q --load="scripts/init.el" --load="scripts/packages.el" --batch
+
+init : ${PROCS_DEPS}
+ make ${CMD}
+
+.%.deps : %.mk makefile
+ @scripts/gen-deps.sh $< $@
+
+-include ${PROCS_DEPS}
+
+prebuild :
+build : prebuild
+postbuild : build
+
+postbuild :
+ @scripts/update-gitignore.sh $(sort ${CONFIGURE} ${ARTIFACTS} ${PROCS_DEPS})
+ @rm -f $(wildcard .*.deps)
+
+clean :
+ @rm -rf ${ARTIFACTS}
+
+cleanall : clean
+ @rm -rf ${CONFIGURE}
diff --git a/org.mk b/org.mk
new file mode 100644
index 0000000..2d993f1
--- /dev/null
+++ b/org.mk
@@ -0,0 +1,21 @@
+ORG_IN := $(shell find site/ -name "*.org")
+ORG_OUT := $(ORG_IN:.org=.html)
+
+org-prebuild : .emacs
+org-build : ${ORG_OUT}
+
+soupault-build : org-build
+
+ARTIFACTS += ${ORG_OUT} .emacs.d/cache
+CONFIGURE += .emacs
+
+.emacs : scripts/packages.el
+ @scripts/pretty-echo.sh echo Initiating "Emacs configuration"
+ @${EMACS}
+ @touch .emacs
+
+site/index.org : site/haskell.org site/miscellaneous.org site/meta.org site/coq.org
+
+%.html : %.org scripts/packages.el scripts/export-org.el .emacs org.mk
+ @scripts/pretty-echo.sh Exporting "$*.org"
+ @${EMACS} $< --load="$(shell pwd)/scripts/export-org.el"
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..9b7d95c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,7 @@
+{
+ "dependencies": {
+ "katex": "^0.13.13",
+ "minify": "^7.0.2",
+ "normalize.css": "^8.0.1"
+ }
+}
diff --git a/plugins/clean-up.lua b/plugins/clean-up.lua
new file mode 100644
index 0000000..6325e59
--- /dev/null
+++ b/plugins/clean-up.lua
@@ -0,0 +1,19 @@
+function remove_if_empty(html)
+ if String.trim(HTML.inner_html(html)) == "" then
+ HTML.delete(html)
+ end
+end
+
+function remove_all_if_empty(cls)
+ local elements = HTML.select(page, cls)
+
+ local i = 1
+ while elements[i] do
+ local element = elements[i]
+ remove_if_empty(element)
+ i = i + 1
+ end
+end
+
+remove_all_if_empty("p") -- introduced by org-mode
+remove_all_if_empty("div.code") -- introduced by coqdoc
diff --git a/plugins/css.lua b/plugins/css.lua
new file mode 100644
index 0000000..31a422c
--- /dev/null
+++ b/plugins/css.lua
@@ -0,0 +1,6 @@
+style = HTML.select_one(page, "style")
+
+if style then
+ css = HTML.create_text(Sys.read_file("style.min.css"))
+ HTML.replace_content(style, css)
+end
diff --git a/plugins/external-urls.lua b/plugins/external-urls.lua
new file mode 100644
index 0000000..afd8a1a
--- /dev/null
+++ b/plugins/external-urls.lua
@@ -0,0 +1,25 @@
+function mark(name)
+ return '<span class="icon"><svg><use href="/img/icons.svg#'
+ .. name ..
+ '"></use></svg></span>'
+end
+
+links = HTML.select(page, "a")
+
+index, link = next(links)
+
+while index do
+ href = HTML.get_attribute(link, "href")
+
+ if href then
+ if Regex.match(href, "^https?://github.com") then
+ icon = HTML.parse(mark("github"))
+ HTML.append_child(link, icon)
+ elseif Regex.match(href, "^https?://") then
+ icon = HTML.parse(mark("external-link"))
+ HTML.append_child(link, icon)
+ end
+ end
+
+ index, link = next(links, index)
+end
diff --git a/plugins/series.lua b/plugins/series.lua
new file mode 100644
index 0000000..37d0e05
--- /dev/null
+++ b/plugins/series.lua
@@ -0,0 +1,51 @@
+function get_title_from_path (path)
+ if Sys.is_file(path) then
+ local content_raw = Sys.read_file(path)
+ local content_dom = HTML.parse(content_raw)
+ local title = HTML.select_one(content_dom, "h1")
+
+ if title then
+ return String.trim(HTML.inner_html(title))
+ else
+ Plugin.fail(path .. ' has no <h1> tag')
+ end
+ else
+ Plugin.fail(path .. ' is not a file')
+ end
+end
+
+function generate_nav_item_from_title (title, url, template)
+ local env = {}
+ env["url"] = url
+ env["title"] = title
+ local new_content = String.render_template(template, env)
+ return HTML.parse(new_content)
+end
+
+function generate_nav_items (cwd, cls, template)
+ local elements = HTML.select(page, cls)
+
+ local i = 1
+ while elements[i] do
+ local element = elements[i]
+ local url = HTML.strip_tags(element)
+ local path = Sys.join_path(cwd, url)
+ local title_str = get_title_from_path(path)
+
+ HTML.replace_content(
+ element,
+ generate_nav_item_from_title(title_str, url, template)
+ )
+
+ i = i + 1
+ end
+end
+
+cwd = Sys.dirname(page_file)
+
+home_template = 'This article is part of the series “<a href="{{ url }}">{{ title }}</a>.”'
+nav_template = '<a href="{{ url }}">{{ title }}</a>'
+
+generate_nav_items(cwd, ".series", home_template)
+generate_nav_items(cwd, ".series-prev", nav_template)
+generate_nav_items(cwd, ".series-next", nav_template)
diff --git a/plugins/urls-rewriting.lua b/plugins/urls-rewriting.lua
new file mode 100644
index 0000000..82e4692
--- /dev/null
+++ b/plugins/urls-rewriting.lua
@@ -0,0 +1,37 @@
+prefix_url = config["prefix_url"]
+
+if not prefix_url then
+ Plugin.fail("Missing mandatory field: `prefix_url'")
+end
+
+if not Regex.match(prefix_url, "^/(.*)") then
+ prefix_url = "/" .. prefix_url
+end
+
+if not Regex.match(prefix_url, "(.*)/$") then
+ prefix_url = prefix_url .. "/"
+end
+
+function prefix_urls (links, attr, prefix_url)
+ index, link = next(links)
+
+ while index do
+ href = HTML.get_attribute(link, attr)
+
+ if href then
+ if Regex.match(href, "^/") then
+ href = Regex.replace(href, "^/*", "")
+ href = prefix_url .. href
+ end
+
+ HTML.set_attribute(link, attr, href)
+ end
+ index, link = next(links, index)
+ end
+end
+
+prefix_urls(HTML.select(page, "a"), "href", prefix_url)
+prefix_urls(HTML.select(page, "link"), "href", prefix_url)
+prefix_urls(HTML.select(page, "img"), "src", prefix_url)
+prefix_urls(HTML.select(page, "script"), "src", prefix_url)
+prefix_urls(HTML.select(page, "use"), "href", prefix_url)
diff --git a/scripts/cleopatra.el b/scripts/cleopatra.el
new file mode 100644
index 0000000..5977536
--- /dev/null
+++ b/scripts/cleopatra.el
@@ -0,0 +1,67 @@
+;;; cleopatra.el --- The cleopatra Emacs Library
+;;; Commentary:
+;;; Code:
+(require 'package)
+
+(defun cleopatra:ensure-package-installed (&rest packages)
+ "Ensure every PACKAGES is installed."
+ (mapcar
+ (lambda (package)
+ (if (package-installed-p package)
+ nil
+ (package-install package))
+ package)
+ packages))
+
+(defvar cleopatra:*emacs-dir* (concat (getenv "ROOT") "/.cleopatra/emacs.d/"))
+
+(setq user-emacs-directory cleopatra:*emacs-dir*)
+(setq package-user-dir (concat cleopatra:*emacs-dir* "packages"))
+
+(setq package-archives
+ '(("gnu" . "https://elpa.gnu.org/packages/")
+ ("melpa" . "https://melpa.org/packages/")
+ ("org" . "https://orgmode.org/elpa/")))
+
+(package-initialize)
+
+(or (file-exists-p package-user-dir)
+ (package-refresh-contents))
+
+(cleopatra:ensure-package-installed 'use-package)
+
+(require 'use-package)
+
+(use-package org :ensure org-plus-contrib :pin org)
+(use-package htmlize :ensure t)
+
+(defun cleopatra:configure ()
+ (setq backup-inhibited t)
+ (setq org-html-doctype "html5")
+ (setq org-html-html5-fancy t)
+ (setq org-src-fontify-natively t)
+ (setq org-export-with-sub-superscripts nil)
+ (setq org-confirm-babel-evaluate nil)
+ (setq org-publish-timestamp-directory
+ (concat cleopatra:*emacs-dir* "cache/"))
+ (setq org-confirm-babel-evaluate nil)
+ (setq org-src-preserve-indentation t)
+ (add-to-list 'org-babel-default-header-args
+ '(:mkdirp . "yes"))
+ (add-to-list 'org-babel-default-header-args
+ '(:noweb-sep . "\n\n")))
+
+(defun cleopatra:tangle-publish (conf filename _pub-dir)
+ (let ((pub-dir (plist-get conf :publishing-directory)))
+ (if pub-dir
+ (with-temp-buffer
+ (find-file-read-only filename)
+ (cd (getenv "ROOT"))
+ (unless (file-exists-p pub-dir)
+ (make-directory pub-dir))
+ (cd pub-dir)
+ (org-babel-tangle))
+ (error "cleopatra: missing :publishing-directory option"))))
+
+(provide 'cleopatra)
+;;; cleopatra.el ends here
diff --git a/scripts/css.sh b/scripts/css.sh
new file mode 100755
index 0000000..753bc8c
--- /dev/null
+++ b/scripts/css.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+minify="$(npm bin)/minify"
+normalize="$(npm root)/normalize.css/normalize.css"
+style="style.css"
+
+# minify add a newline character at the end of its input
+# we remove it using `head'
+echo "
+@charset \"UTF-8\";
+$(cat ${normalize})
+$(cat ${style})
+" | ${minify} --css | head -c -1 > style.min.css
diff --git a/scripts/export-lp.el b/scripts/export-lp.el
new file mode 100644
index 0000000..bd17b0d
--- /dev/null
+++ b/scripts/export-lp.el
@@ -0,0 +1,18 @@
+;; opinionated configuration provided by cleopatra
+(cleopatra:configure)
+
+;; allow the execution of shell block code
+(org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((shell . t)))
+
+(setq org-publish-project-alist
+ '(("lp"
+ :base-directory "site/posts"
+ :publishing-directory "lp"
+ ;; hand-pick which files to tangle (this save a lots of time)
+ :exclude ".*"
+ :include ("CoqffiEcho.org")
+ :publishing-function cleopatra:tangle-publish)))
+
+(org-publish-all)
diff --git a/scripts/export-org.el b/scripts/export-org.el
new file mode 100644
index 0000000..1b20265
--- /dev/null
+++ b/scripts/export-org.el
@@ -0,0 +1,81 @@
+(cleopatra:configure)
+
+(org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((dot . t)
+ (shell . t)
+ (ocaml . t)))
+
+(setq org-export-with-toc nil
+ org-html-htmlize-output-type nil
+ org-export-with-section-numbers nil)
+
+(add-to-list 'org-entities-user
+ '("im" "\\(" nil "<span class=\"imath\">" "" "" ""))
+(add-to-list 'org-entities-user
+ '("mi" "\\)" nil "</span>" "" "" ""))
+
+(defun with-keyword (keyword k)
+ "Look-up for keyword KEYWORD, and call continuation K with its value."
+ (pcase (org-collect-keywords `(,keyword))
+ (`((,keyword . ,kw))
+ (when kw (funcall k (string-join kw " "))))))
+
+(defun get-keyword (keyword)
+ "Look-up for keyword KEYWORD, and returns its value"
+ (with-keyword keyword (lambda (x) x)))
+
+(defun get-org-title (path)
+ "Fetch the title of an Org file whose path is PATH."
+ (with-temp-buffer
+ (find-file-read-only path)
+ (get-keyword "TITLE")))
+
+(defun insert-title ()
+ "Insert the title of the article."
+ (with-keyword
+ "TITLE"
+ (lambda (title)
+ (insert
+ (format "\n\n@@html:<h1>@@ %s @@html:</h1>@@\n\n" title)))))
+
+(defun insert-series ()
+ "Insert the series root link."
+ (with-keyword
+ "SERIES"
+ (lambda (series)
+ (insert "\n\n#+attr_html: :class series\n")
+ (insert series))))
+
+(defun insert-series-prev ()
+ "Insert the series previous article link."
+ (with-keyword
+ "SERIES_PREV"
+ (lambda (series-prev)
+ (insert "\n\n#+attr_html: :class series-prev\n")
+ (insert series-prev))))
+
+(defun insert-series-next ()
+ "Insert the series next article link."
+ (with-keyword
+ "SERIES_NEXT"
+ (lambda (series-next)
+ (insert "\n\n#+attr_html: :class series-next\n")
+ (insert series-next))))
+
+(defun insert-nav ()
+ "Insert the navigation links."
+ (when (get-keyword "SERIES")
+ (insert "\n\n#+begin_nav\n")
+ (insert-series)
+ (insert-series-prev)
+ (insert-series-next)
+ (insert "\n\n#+end_nav\n")))
+
+(beginning-of-buffer)
+(insert-nav)
+(insert-title)
+
+(let ((outfile (org-export-output-file-name ".html"))
+ (org-html-footnotes-section "<!-- %s --><!-- %s -->"))
+ (org-export-to-file 'tufte-html outfile nil nil nil t))
diff --git a/scripts/gen-deps.sh b/scripts/gen-deps.sh
new file mode 100755
index 0000000..2283f9b
--- /dev/null
+++ b/scripts/gen-deps.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+input=${1}
+output=${2}
+
+proc="$(basename ${input} | cut -f 1 -d '.')"
+
+echo "include ${input}" > ${output}
+echo "prebuild : ${proc}-prebuild" >> ${output}
+echo "build : ${proc}-build" >> ${output}
+echo "postbuild : ${proc}-postbuild" >> ${output}
+echo "${proc}-build : ${proc}-prebuild" >> ${output}
+echo "${proc}-postbuild : ${proc}-build" >> ${output}
+echo ".PHONY : ${proc}-prebuild ${proc}-build ${proc}-postbuild" >> ${output}
diff --git a/scripts/history.sh b/scripts/history.sh
new file mode 100755
index 0000000..1594714
--- /dev/null
+++ b/scripts/history.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/bash
+function main () {
+ local file="${1}"
+ local template="${2}"
+
+ tmp_file=$(mktemp)
+ generate_json ${file} > ${tmp_file}
+ haskell-mustache ${template} ${tmp_file}
+ rm ${tmp_file}
+}
+
+function gitlog () {
+ local file="${1}"
+ git --no-pager log \
+ --follow \
+ --stat=10000 \
+ --pretty=format:'%s%n%h%n%H%n%cs%n' \
+ "${file}"
+}
+
+function parse_filename () {
+ local line="${1}"
+ local shrink='s/ *\(.*\) \+|.*/\1/'
+ local unfold='s/\(.*\){\(.*\) => \(.*\)}/\1\3/'
+
+ echo ${line} | sed -e "${shrink}" | sed -e "${unfold}"
+}
+
+function generate_json () {
+ local input="${1}"
+ local logs="$(gitlog ${input})"
+
+ if [ ! $? -eq 0 ]; then
+ exit 1
+ fi
+
+ let "idx=0"
+ let "last_entry=$(echo "${logs}" | wc -l) / 8"
+
+ local subject=""
+ local abbr_hash=""
+ local hash=""
+ local date=""
+ local file=""
+ local created="true"
+ local modified="false"
+
+ echo -n "{"
+ echo -n "\"file\": \"${input}\""
+ echo -n ",\"history\": ["
+
+ while read -r subject; do
+ read -r abbr_hash
+ read -r hash
+ read -r date
+ read -r # empty line
+ read -r file
+ read -r # short log
+ read -r # empty line
+
+ if [ ${idx} -ne 0 ]; then
+ echo -n ","
+ fi
+
+ if [ ${idx} -eq ${last_entry} ]; then
+ created="true"
+ modified="false"
+ else
+ created="false"
+ modified="true"
+ fi
+
+ output_json_entry "${subject}" \
+ "${abbr_hash}" \
+ "${hash}" \
+ "${date}" \
+ "$(parse_filename "${file}")" \
+ "${created}" \
+ "${modified}"
+
+ let idx++
+ done < <(echo "${logs}")
+
+ echo -n "]}"
+}
+
+function output_json_entry () {
+ local subject="${1}"
+ local abbr_hash="${2}"
+ local hash="${3}"
+ local date="${4}"
+ local file="${5}"
+ local created="${6}"
+ local last_entry="${7}"
+
+ echo -n "{\"subject\": \"${subject}\""
+ echo -n ",\"created\":${created}"
+ echo -n ",\"modified\":${modified}"
+ echo -n ",\"abbr_hash\":\"${abbr_hash}\""
+ echo -n ",\"hash\":\"${hash}\""
+ echo -n ",\"date\":\"${date}\""
+ echo -n ",\"filename\":\"${file}\""
+ echo -n "}"
+}
+
+main "$(cat)" "${1}"
diff --git a/scripts/init.el b/scripts/init.el
new file mode 100644
index 0000000..f6f069c
--- /dev/null
+++ b/scripts/init.el
@@ -0,0 +1,66 @@
+;;; cleopatra.el --- The cleopatra Emacs Library
+;;; Commentary:
+;;; Code:
+(require 'package)
+
+(defun cleopatra:ensure-package-installed (&rest packages)
+ "Ensure every PACKAGES is installed."
+ (mapcar
+ (lambda (package)
+ (if (package-installed-p package)
+ nil
+ (package-install package))
+ package)
+ packages))
+
+(defvar cleopatra:*emacs-dir* (concat (getenv "ROOT") "/.emacs.d/"))
+
+(setq user-emacs-directory cleopatra:*emacs-dir*)
+(setq package-user-dir (concat cleopatra:*emacs-dir* "packages"))
+
+(setq package-archives
+ '(("gnu" . "https://elpa.gnu.org/packages/")
+ ("melpa" . "https://melpa.org/packages/")))
+
+(package-initialize)
+
+(or (file-exists-p package-user-dir)
+ (package-refresh-contents))
+
+(cleopatra:ensure-package-installed 'use-package)
+
+(require 'use-package)
+
+(use-package org :ensure org-plus-contrib)
+(use-package htmlize :ensure t)
+
+(defun cleopatra:configure ()
+ (setq backup-inhibited t)
+ (setq org-html-doctype "html5")
+ (setq org-html-html5-fancy t)
+ (setq org-src-fontify-natively t)
+ (setq org-export-with-sub-superscripts nil)
+ (setq org-confirm-babel-evaluate nil)
+ (setq org-publish-timestamp-directory
+ (concat cleopatra:*emacs-dir* "cache/"))
+ (setq org-confirm-babel-evaluate nil)
+ (setq org-src-preserve-indentation t)
+ (add-to-list 'org-babel-default-header-args
+ '(:mkdirp . "yes"))
+ (add-to-list 'org-babel-default-header-args
+ '(:noweb-sep . "\n\n")))
+
+(defun cleopatra:tangle-publish (conf filename _pub-dir)
+ (let ((pub-dir (plist-get conf :publishing-directory)))
+ (if pub-dir
+ (with-temp-buffer
+ (find-file-read-only filename)
+ (cd (getenv "ROOT"))
+ (unless (file-exists-p pub-dir)
+ (make-directory pub-dir))
+ (cd pub-dir)
+ (org-babel-tangle))
+ (error "cleopatra: missing :publishing-directory option"))))
+
+(provide 'cleopatra)
+;;; cleopatra.el ends here
diff --git a/scripts/packages.el b/scripts/packages.el
new file mode 100644
index 0000000..9ed944a
--- /dev/null
+++ b/scripts/packages.el
@@ -0,0 +1,5 @@
+(use-package ox-tufte :ensure t)
+
+(use-package tuareg :ensure t
+ :config
+ (require 'ob-ocaml))
diff --git a/scripts/pretty-echo.sh b/scripts/pretty-echo.sh
new file mode 100755
index 0000000..8b4e149
--- /dev/null
+++ b/scripts/pretty-echo.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+title=${1}
+message=${2}
+
+printf "\033[0;32m%12s \e[0m%s\n" "${title}" "${message}"
diff --git a/scripts/render-equations.js b/scripts/render-equations.js
new file mode 100644
index 0000000..cae348a
--- /dev/null
+++ b/scripts/render-equations.js
@@ -0,0 +1,11 @@
+var katex = require("katex");
+var fs = require("fs");
+var input = fs.readFileSync(0);
+var displayMode = process.env.DISPLAY != undefined;
+
+var html = katex.renderToString(String.raw`${input}`, {
+ throwOnError : false,
+ displayModed : displayMode
+});
+
+console.log(html)
diff --git a/scripts/update-gitignore.sh b/scripts/update-gitignore.sh
new file mode 100755
index 0000000..7b9fafe
--- /dev/null
+++ b/scripts/update-gitignore.sh
@@ -0,0 +1,16 @@
+#+/bin/bash
+BEGIN_MARKER="# begin generated files"
+END_MARKER="# end generated files"
+
+# remove the previous list of generated files to ignore
+sed -i -e "/${BEGIN_MARKER}/,/${END_MARKER}/d" .gitignore
+# remove trailing empty lines
+sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' .gitignore
+
+# output the list of files to ignore
+echo "" >> .gitignore
+echo ${BEGIN_MARKER} >> .gitignore
+for f in $@; do
+ echo "${f}" >> .gitignore
+done
+echo ${END_MARKER} >> .gitignore
diff --git a/site/cleopatra.org b/site/cleopatra.org
index 0831621..c926c5a 100644
--- a/site/cleopatra.org
+++ b/site/cleopatra.org
@@ -1,4 +1,4 @@
-#+TITLE: A Series on Generating this Static Website
+#+TITLE: An Unfinished Series on How This Static Website Used to be Generating
#+SERIES: ./meta.html
#+SERIES_PREV: ./posts/Thanks.html
@@ -10,11 +10,12 @@ become out-dated. This is reminescent of documenting any software
project, and I was aware at that time of a dedicated paradigm to
prevent these kind of issues: [[http://www.literateprogramming.com/][literate programming]].
-I spent quite some time turning my custom toolchain into a literate program, so
-that its actual code source would actually be the write-ups I wanted to add to
-my website. This was an interesting challenge, since it meant *~cleopatra~*
-would have to generate itself before it could build my website. In other words,
-*~cleopatra~* achieves the bootstsrapping challenge!
+I spent quite some time turning my custom toolchain into a literate
+program, so that its actual code source would actually be the
+write-ups I wanted to add to my website. This was an interesting
+challenge, since it meant *~cleopatra~* would have to generate itself
+before it could build my website. In other words, *~cleopatra~*
+achieves the bootstsrapping challenge!
I really enjoyed this first experiment with literate programming, and
I started using *~cleopatra~* for other projects of mine where
@@ -28,9 +29,17 @@ bootstrap jump.”]. But the so-called generation processes I had
written for *~cleopatra~* the first basically “just worked” with
*~cleopatra~* the second.
-So, coming back to this series, it is just the very reason why I started using
-*~cleopatra~* in the first place: the generation processes used by this website,
-written as literate programs.
+Now, I don’t use *~cleopatra~* anymore. Literate programming is a fun
+paradigm, but I never took the time to actually document in depth most
+of the bits on how this website is built. So I took the various
+scripts extracted by *~cleopatra~*, and recreated a straightforward
+~makefile~ file on top of it. The nice thing is, it now takes way less
+time to build!
+
+Anyway, coming back to this series, it is just the very reason why I
+started using *~cleopatra~* in the first place: the generation
+processes I was using to generate this website, written as literate
+programs. If you are curious, you can have a look.
- [[./cleopatra/dependencies.org][Installing Dependencies]] ::
diff --git a/site/meta.org b/site/meta.org
index d5ce4b4..318a6b7 100644
--- a/site/meta.org
+++ b/site/meta.org
@@ -15,9 +15,11 @@ pages you read.
desire is at least to try keeping up-to-date a curated description
of the most significant ones.
-- [[./cleopatra.org][A Series on Generating this Website]] ::
+- [[./cleopatra.org][An Unfinished Series on How This Static Website Used to be Generating]] ::
At some point, I felt like the whole process of generating this
website was interesting enough so that it would deserve a write-up
of its own. As a result, I spent quite some time turning my custom
toolchain into a literate program, so that its actual code source
- would actually be the write-ups I wanted to add to my website.
+ would actually be the write-ups I wanted to add to my website. I
+ don’t use said toolchain anymore, but the article remains, though
+ they are doomed to become more and more outdated.
diff --git a/site/news/index.html b/site/news/index.html
index 3e8842c..e21c51f 100644
--- a/site/news/index.html
+++ b/site/news/index.html
@@ -1,5 +1,15 @@
<h1>News</h1>
+<h2>2022</h2>
+
+<ul>
+ <li>
+ On <strong>August 13, 2022</strong>, after more than two years of good services,
+ <a href="https://cleopatra.soap.coffee"><strong><code>cleopatra</code></strong></a>
+ is no longer used to build this website.
+ </li>
+</ul>
+
<h2>2021</h2>
<ul>
diff --git a/soupault.conf b/soupault.conf
new file mode 100644
index 0000000..ad34170
--- /dev/null
+++ b/soupault.conf
@@ -0,0 +1,94 @@
+[settings]
+strict = true
+site_dir = "site"
+build_dir = "out/~lthms"
+doctype = "<!DOCTYPE html>"
+clean_urls = false
+generator_mode = true
+complete_page_selector = "html"
+default_content_selector = "main"
+default_content_action = "append_child"
+page_file_extensions = ["html"]
+ignore_extensions = [
+ "v", "vo", "vok", "vos", "glob",
+ "html~", "org"
+]
+default_template_file = "templates/main.html"
+pretty_print_html = false
+
+[widgets.page-title]
+widget = "title"
+selector = "h1"
+default = "~lthms"
+prepend = "~lthms: "
+
+[widgets.generator-meta]
+widget = "insert_html"
+html = """<meta name="generator" content="soupault 4.0.1">"""
+selector = "head"
+
+[widgets.urls-rewriting]
+widget = "urls-rewriting"
+prefix_url = "~lthms"
+after = "mark-external-urls"
+
+[widgets.mark-external-urls]
+after = "generate-history"
+widget = "external-urls"
+
+[widgets.table-of-contents]
+widget = "toc"
+selector = "#generate-toc"
+action = "replace_content"
+valid_html = true
+min_level = 2
+max_level = 3
+numbered_list = false
+heading_links = true
+heading_link_text = " §"
+heading_links_append = true
+heading_link_class = "anchor-link"
+
+[widgets.append-toc-title]
+widget = "insert_html"
+selector = "#generate-toc"
+action = "prepend_child"
+html = '<h2>Table of Contents</h2>'
+after = "table-of-contents"
+
+[widgets.generate-history]
+widget = "preprocess_element"
+selector = "#history"
+command = 'scripts/history.sh templates/history.html'
+action = "replace_element"
+
+[widgets.inline-math]
+widget = "preprocess_element"
+selector = ".imath"
+command = "node scripts/render-equations.js"
+action = "replace_content"
+
+[widgets.display-math]
+widget = "preprocess_element"
+selector = ".dmath"
+command = "DISPLAY=1 node scripts/render-equations.js"
+action = "replace_content"
+
+[index]
+index = true
+dump_json = "rss.json"
+extract_after_widgets = ["urls-rewriting"]
+
+[index.fields]
+title = { selector = ["h1"] }
+modified-at = { selector = ["#modified-at"] }
+created-at = { selector = ["#created-at"] }
+
+[widgets.series]
+widget = "series"
+
+[widgets.css]
+widget = "css"
+
+[widgets.clean-up]
+widget = "clean-up"
diff --git a/soupault.mk b/soupault.mk
new file mode 100644
index 0000000..a30e541
--- /dev/null
+++ b/soupault.mk
@@ -0,0 +1,8 @@
+CONFIGURE += _opam rss.json
+ARTIFACTS += out
+
+soupault-prebuild : _opam/init
+
+soupault-build : dependencies-prebuild style.min.css
+ @scripts/pretty-echo.sh "Executing" "soupault"
+ @soupault
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..f93664a
--- /dev/null
+++ b/style.css
@@ -0,0 +1,413 @@
+:root {
+ --main-width: 35rem;
+ --gutter-width: 3rem;
+ --margin-width: 13rem;
+ --code-width: calc(var(--main-width) + var(--gutter-width) + var(--margin-width));
+ --body-width: calc(var(--main-width) + 2 * (var(--gutter-width) + var(--margin-width)));
+}
+
+/*
+
+ {{Note: width}}
+
+ According to CSS’ own specification, you cannot use 'var()' inside
+ media queries. As a consequnece, for our theme to be responsive,
+ the full width of the page content is
+
+ 2 * (margin_width + gutter_width) + content_width = 67rem
+
+ */
+
+/* See {{Note: width}} */
+@media (max-width: 67rem) {
+ :root {
+ --body-width: var(--main-width);
+ --code-width: var(--main-width);
+ }
+}
+
+* {
+ box-sizing: border-box;
+}
+
+.fullwidth {
+ width: var(--body-width);
+}
+
+/* See {{Note: width}} */
+@media (min-width: 67rem) {
+ .fullwidth {
+ margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+ }
+}
+
+html {
+ font-size: 1rem;
+}
+
+body {
+ line-height: 1.4;
+ max-width: var(--body-width);
+ margin-left: auto;
+ margin-right: auto;
+}
+
+aside {
+ background: var(--bg);
+ z-index: 9999;
+ width: var(--body-width);
+ align-self: flex-start;
+ position: sticky;
+ top: 0;
+}
+
+aside nav {
+ text-align: center;
+ border-bottom: 1px solid var(--fade);
+}
+
+aside nav ul {
+ list-style: none;
+ padding: 1rem 0;
+ margin: 0;
+}
+
+aside nav li {
+ display: inline;
+}
+
+aside nav li:not(:first-of-type)::before {
+ content: " · ";
+}
+
+main {
+ counter-reset: sidenote-counter;
+ max-width: var(--main-width);
+ margin: auto;
+}
+
+main nav {
+ font-style: italic;
+ color: var(--fg-plus);
+ background: var(--current-line);
+ padding: .5rem 1rem;
+}
+
+main nav .series-next {
+ text-align: right;
+}
+
+main nav p.series-next::after {
+ content: " →";
+}
+
+main nav p.series-prev::before {
+ content: "← ";
+}
+
+img {
+ max-width: 100%;
+}
+
+#whoami.marginnote {
+ color: var(--fg);
+ margin-bottom: 2em;
+}
+
+img.avatar {
+ border-radius: 20px;
+ display: block;
+ max-width: 90%;
+ margin: auto;
+}
+
+dd {
+ margin-left: 0;
+ margin-bottom: 0.5rem;
+}
+
+.sidenote,
+.marginnote {
+ font-size: smaller;
+ position: relative;
+ width: var(--margin-width);
+}
+
+.sidenote {
+ margin-right: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+ float: right;
+ clear: right;
+}
+
+.marginnote {
+ float: left;
+ clear: left;
+ margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+}
+
+input.margin-toggle {
+ display: none;
+}
+
+label.sidenote-number {
+ display: inline;
+}
+
+label.margin-toggle:not(.sidenote-number) {
+ display: none;
+}
+
+.sidenote-number:after,
+.sidenote:before {
+ position: relative;
+ vertical-align: baseline;
+}
+
+.sidenote-number {
+ counter-increment: sidenote-counter;
+}
+
+.sidenote-number::after {
+ content: "(" counter(sidenote-counter, lower-greek) ")";
+ font-size: 60%;
+ top: -0.4rem;
+ left: 0.1rem;
+}
+
+.sidenote::before {
+ content: "(" counter(sidenote-counter, lower-greek) ")";
+ font-size: 70%;
+ top: -0.5rem;
+ right: 0.1rem;
+}
+
+div.code,
+pre {
+ width: var(--code-width);
+ overflow-x: auto;
+ overflow-y: hidden;
+ padding: 1rem 2rem;
+}
+
+main {
+ padding-top: 4.2rem;
+ padding-bottom: 4.2rem;
+}
+
+h1 {
+ text-align: center;
+}
+
+h2, h3, h4 {
+ font-style: italic;
+}
+
+h1, h2, h3, h4 {
+ color: var(--doc);
+ font-family: serif;
+ font-weight: normal;
+}
+
+dt {
+ font-weight: bold;
+}
+
+div.code,
+span.inlinecode,
+pre,
+tt,
+.dmath,
+.imath {
+ font-family: monospace;
+ font-size: 85%;
+}
+
+details {
+ margin: 1.5rem 0;
+}
+
+table {
+ border-top: 2px solid var(--fg);
+ border-bottom: 2px solid var(--fg);
+ border-collapse: collapse;
+ width: 100%;
+ margin: 1.5rem 0;
+}
+
+th {
+ font-weight: normal;
+ text-transform: uppercase;
+}
+
+td,
+th {
+ border-top: 1px solid var(--fade);
+ height: 2em;
+ padding: 0 1em;
+}
+
+td.date,
+td.commit {
+ text-align: center;
+ font-size: 0.75em;
+ font-family: monospace;
+}
+
+/* See {{Note: width}} */
+@media (max-width: 67rem) {
+ body {
+ padding: 2rem;
+ margin: auto;
+ display: block;
+ }
+
+ aside {
+ width: var(--main-width);
+ margin: auto;
+ }
+
+ label.margin-toggle:not(.sidenote-number) {
+ display: inline;
+ }
+
+ .sidenote,
+ .marginnote {
+ display: none;
+ }
+
+ .margin-toggle:checked + .sidenote,
+ .margin-toggle:checked + .marginnote {
+ display: block;
+ float: left;
+ left: 1rem;
+ clear: both;
+ width: 95%;
+ margin: 1rem 2.5%;
+ vertical-align: baseline;
+ position: relative;
+ }
+
+ label {
+ cursor: pointer;
+ }
+
+ pre, aside, div.code {
+ width: 100%;
+ }
+}
+
+:root {
+ --bg: white;
+ --bg-plus: #f7f7f7;
+ --current-line: #fbfbfb;
+ --fade: #cfcecb;
+ --fg: #3c3c3c;
+ --fg-plus: #575757;
+ --doc: black;
+ --warning: #bd745e;
+ --red: #b3534b;
+ --green: #6d9319;
+ --yellow: #d4b100;
+}
+
+body {
+ font-family: sans-serif;
+ color: var(--fg);
+ background: var(--bg);
+}
+
+h2 a.anchor-link,
+h3 a.anchor-link,
+h4 a.anchor-link {
+ display: none;
+ font-style: normal;
+ text-decoration: none;
+ font-family: monospace;
+ font-size: smaller;
+ color: var(--doc);
+}
+
+[id] {
+ scroll-margin-top: 4rem;
+}
+
+h2:hover a.anchor-link,
+h3:hover a.anchor-link,
+h4:hover a.anchor-link {
+ display: inline;
+}
+
+.sidenote,
+.marginnote {
+ color: var(--fg-plus);
+}
+
+pre,
+code,
+div.code,
+span.inlinecode,
+tt {
+ color: var(--doc);
+}
+
+div.code {
+ white-space: nowrap;
+}
+
+div.code,
+span.inlinecode {
+ font-family : monospace;
+}
+
+.paragraph {
+ margin-bottom : .8em;
+}
+
+.code a[href] {
+ color : inherit;
+ text-decoration : none;
+ background : var(--bg-plus);
+ padding : .1rem .15rem .1rem .15rem;
+ border-radius : 15%;
+}
+
+.code .icon {
+ display: none;
+}
+
+.icon svg {
+ display: inline;
+ width: 1em;
+ height: .9em;
+ vertical-align: text-top;
+}
+
+a[href], .margin-toggle {
+ color: #0000ee;
+}
+
+a[href] .icon svg {
+ fill: #0000ee;
+}
+
+a[href]:visited {
+ color: #25008b;
+}
+
+a[href]:visited .icon svg {
+ fill: #25008b;
+}
+
+.url-mark.fa {
+ display: inline;
+ font-size: 90%;
+ width: 1em;
+}
+
+.url-mark.fa-github::before {
+ content: "\00a0\f09b";
+}
+
+.url-mark.fa-external-link::before {
+ content: "\00a0\f08e";
+}
diff --git a/templates/history.html b/templates/history.html
new file mode 100644
index 0000000..b106c4f
--- /dev/null
+++ b/templates/history.html
@@ -0,0 +1,33 @@
+<details id="history">
+ <summary>Revisions</summary>
+ <p>
+ This revisions table has been automatically generated
+ from <a href="https://src.soap.coffee/soap.coffee/lthms.git">the
+ <code>git</code> history of this website repository</a>, and the
+ change descriptions may not always be as useful as they should.
+ </p>
+
+ <p>
+ You can consult the source of this file in its current version
+ <a href="https://src.soap.coffee/soap.coffee/lthms.git/tree/{{file}}">here</a>.
+ </p>
+
+ <table class="fullwidth">
+ {{#history}}
+ <tr>
+ <td class="date"
+{{#created}}
+ id="created-at"
+{{/created}}
+{{#modified}}
+ id="modified-at"
+{{/modified}}
+ >{{date}}</td>
+ <td class="subject">{{subject}}</td>
+ <td class="commit">
+ <a href="https://src.soap.coffee/soap.coffee/lthms.git/commit/{{filename}}/?id={{hash}}">{{abbr_hash}}</a>
+ </td>
+ </tr>
+ {{/history}}
+ </table>
+</details>
diff --git a/templates/main.html b/templates/main.html
new file mode 100644
index 0000000..6f439ba
--- /dev/null
+++ b/templates/main.html
@@ -0,0 +1,47 @@
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style></style>
+ <link href="https://soap.coffee/+vendors/katex.0.11.1+swap/katex.css" rel="stylesheet" media="none" onload="if(media!='all')media='all'">
+ <title></title>
+ </head>
+ <body>
+ <aside>
+ <nav>
+ <ul>
+ <li>
+ <a href="/">Technical Posts</a>
+ </li>
+ <li>
+ <a href="/opinions">Opinions</a>
+ </li>
+ <li>
+ <a href="/news">News</a>
+ </li>
+ </ul>
+ </nav>
+ </aside>
+ <main>
+ <span id="whoami" class="marginnote">
+ <img class="avatar" src="/img/vampy.jpg" />
+
+ <p>
+ Hi, I’m <strong>lthms</strong>.
+ </p>
+
+ <p>
+ I don’t like syntax highlighting, but I like
+ types and functional programming languages.
+ He/him.
+ </p>
+
+ <p>
+ Interested in starting a discussion? Don’t hesitate to <a
+ href="mailto:~lthms/public-inbox@lists.sr.ht">shoot me an
+ email</a>.
+ </p>
+ </span>
+ </main>
+ </body>
+</html>
diff --git a/theme.mk b/theme.mk
new file mode 100644
index 0000000..3d26423
--- /dev/null
+++ b/theme.mk
@@ -0,0 +1,7 @@
+style.min.css : style.css dependencies-prebuild
+ @scripts/pretty-echo.sh "Minifying" "CSS"
+ @scripts/css.sh
+
+ARTIFACTS += style.min.css
+
+theme-build : style.min.css