diff options
author | Thomas Letan <lthms@soap.coffee> | 2022-08-13 17:05:37 +0200 |
---|---|---|
committer | Thomas Letan <lthms@soap.coffee> | 2022-08-13 17:11:58 +0200 |
commit | de83a3ea99275de9887618f1ea2f9d468a0d4e6a (patch) | |
tree | 6a8066ac9414b09b353d810793f7d041c4242172 | |
parent | Some 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-- | .gitignore | 34 | ||||
-rw-r--r-- | cleopatra.toml | 1 | ||||
-rw-r--r-- | commands.mk | 8 | ||||
-rw-r--r-- | coq.mk | 29 | ||||
-rw-r--r-- | dependencies.mk | 19 | ||||
-rw-r--r-- | literate-programming.mk | 19 | ||||
-rw-r--r-- | makefile | 31 | ||||
-rw-r--r-- | org.mk | 21 | ||||
-rw-r--r-- | package.json | 7 | ||||
-rw-r--r-- | plugins/clean-up.lua | 19 | ||||
-rw-r--r-- | plugins/css.lua | 6 | ||||
-rw-r--r-- | plugins/external-urls.lua | 25 | ||||
-rw-r--r-- | plugins/series.lua | 51 | ||||
-rw-r--r-- | plugins/urls-rewriting.lua | 37 | ||||
-rw-r--r-- | scripts/cleopatra.el | 67 | ||||
-rwxr-xr-x | scripts/css.sh | 12 | ||||
-rw-r--r-- | scripts/export-lp.el | 18 | ||||
-rw-r--r-- | scripts/export-org.el | 81 | ||||
-rwxr-xr-x | scripts/gen-deps.sh | 14 | ||||
-rwxr-xr-x | scripts/history.sh | 106 | ||||
-rw-r--r-- | scripts/init.el | 66 | ||||
-rw-r--r-- | scripts/packages.el | 5 | ||||
-rwxr-xr-x | scripts/pretty-echo.sh | 6 | ||||
-rw-r--r-- | scripts/render-equations.js | 11 | ||||
-rwxr-xr-x | scripts/update-gitignore.sh | 16 | ||||
-rw-r--r-- | site/cleopatra.org | 27 | ||||
-rw-r--r-- | site/meta.org | 6 | ||||
-rw-r--r-- | site/news/index.html | 10 | ||||
-rw-r--r-- | soupault.conf | 94 | ||||
-rw-r--r-- | soupault.mk | 8 | ||||
-rw-r--r-- | style.css | 413 | ||||
-rw-r--r-- | templates/history.html | 33 | ||||
-rw-r--r-- | templates/main.html | 47 | ||||
-rw-r--r-- | theme.mk | 7 |
34 files changed, 1317 insertions, 37 deletions
@@ -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 @@ -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} @@ -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 |