summaryrefslogtreecommitdiffstats
path: root/site/cleopatra
diff options
context:
space:
mode:
Diffstat (limited to 'site/cleopatra')
-rw-r--r--site/cleopatra/commands.org36
-rw-r--r--site/cleopatra/coq.org47
-rw-r--r--site/cleopatra/dependencies.org95
-rw-r--r--site/cleopatra/literate-programming.org82
-rw-r--r--site/cleopatra/org.org134
-rw-r--r--site/cleopatra/soupault.org698
-rw-r--r--site/cleopatra/theme.org534
7 files changed, 0 insertions, 1626 deletions
diff --git a/site/cleopatra/commands.org b/site/cleopatra/commands.org
deleted file mode 100644
index fbf0430..0000000
--- a/site/cleopatra/commands.org
+++ /dev/null
@@ -1,36 +0,0 @@
-#+TITLE: Adhoc *~cleopatra~* commands
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./soupault.html
-
-In this generation process, we provide adhoc commands to ease the
-authoring experience. A given command ~<cmd>~ is implemented as a
-~makefile~ rule, and can be called with ~cleopatra <cmd>~.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/commands.org</div>
-#+END_EXPORT
-
-* ~serve~
-
- This command spawns a simple HTTP server which allows us to navigate
- the website more easily.
-
- #+begin_src makefile :tangle commands.mk
-serve :
- @cleopatra echo Spwaning "HTTP server"
- @cd out && python -m http.server
- #+end_src
-
-* ~update~
-
- This commands updates the various dependencies locally installed to
- build this website, such as ~soupault~ for instance.
-
- #+begin_src makefile :tangle commands.mk
-update :
- @cleopatra echo "Updating" "OCaml dependencies"
- @opam update
- @opam upgrade -y
- #+end_src
diff --git a/site/cleopatra/coq.org b/site/cleopatra/coq.org
deleted file mode 100644
index 81f3d27..0000000
--- a/site/cleopatra/coq.org
+++ /dev/null
@@ -1,47 +0,0 @@
-#+TITLE: Authoring Content with Coq
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./dependencies.html
-#+SERIES_NEXT: ./org.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/coq.org</div>
-#+END_EXPORT
-
-
-* Author Guidelines
-
-* Under the Hood
-
-#+BEGIN_SRC makefile :tangle coq.mk
-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
- @cleopatra echo Exporting "$*.v"
- @coqc ${COQCARG} $<
- @coqdoc ${COQDOCARG} -d $(shell dirname $<) $<
- @rm -f $(shell dirname $<)/coqdoc.css
-#+END_SRC
diff --git a/site/cleopatra/dependencies.org b/site/cleopatra/dependencies.org
deleted file mode 100644
index 6cce498..0000000
--- a/site/cleopatra/dependencies.org
+++ /dev/null
@@ -1,95 +0,0 @@
-#+TITLE: Installing Dependencies
-
-#+SERIES: ../cleopatra.html
-#+SERIES_NEXT: ./coq.html
-
-* OCaml and Coq
-
- #+caption: Dependencies for Coq articles
- #+name: coq-deps
- | Package | Version |
- |--------------+---------|
- | coq | 8.13.2 |
- | coq-compcert | 3.8 |
-
- #+caption: Dependencies for the ~coqffi~ series
- #+name: lp-deps
- | Package | Version |
- |---------------+-------------|
- | dune | 2.9.0 |
- | coq-coqffi | 1.0.0~beta7 |
- | coq-simple-io | 1.5.0 |
-
- #+caption: Soupault
- #+name: soupault-deps
- | Package | Version |
- |----------+---------|
- | soupault | 3.1.0 |
-
- #+name: deps-listing
- #+begin_src emacs-lisp :noweb yes :var coq-deps=coq-deps :var lp-deps=lp-deps :var soupault-deps=soupault-deps :results value raw :exports none
-;; We use this Emacs Lisp snippet to generate the list of dependencies
-;; we have to install with Opam
-(defun fmt-deps (d)
- (mapconcat (lambda (d) (format "%s" d)) d "."))
-
-(string-join
- (append (mapcar 'fmt-deps lp-deps)
- (mapcar 'fmt-deps soupault-deps)
- (mapcar 'fmt-deps coq-deps))
- " ")
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-OCAML_VERSION := 4.12.0
-OCAML := ocaml-base-compiler.${OCAML_VERSION}
-
-_opam/init :
- @cleopatra echo "Creating" "a local Opam switch"
- @opam switch create . ${OCAML} --repos default,coq-released || true
- @cleopatra echo "Installing" "OCaml dependencies"
- @opam install <<deps-listing()>> -y
- @touch $@
-
-CONFIGURE += _opam
- #+end_src
-
-* Frontend
-
- #+caption: Frontend dependencies
- #+name: frontend-deps
- | Package | Version |
- |---------------+---------|
- | katex | 0.13.13 |
- | minify | 7.0.2 |
- | normalize.css | 8.0.1 |
-
- #+name: frontend-listing
- #+begin_src emacs-lisp :var frontend-deps=frontend-deps :exports none
-;; We use this Emacs Lisp snippet to generate the list of dependencies
-;; we have to install with npm
-(defun fmt-deps (d)
- (format " \"%s\": \"^%s\"" (nth 0 d) (nth 1 d)))
-
-(string-join (mapcar 'fmt-deps frontend-deps) ",\n")
- #+end_src
-
- #+begin_src json :tangle package.json :noweb yes
-{
- "dependencies": {
- <<frontend-listing()>>
- }
-}
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-package-lock.json : package.json
- @cleopatra echo "Installing" "frontend dependencies"
- @npm install
-
-CONFIGURE += package-lock.json
- #+end_src
-
- #+begin_src makefile :tangle dependencies.mk :noweb yes
-dependencies-prebuild : _opam/init package-lock.json
- #+end_src
diff --git a/site/cleopatra/literate-programming.org b/site/cleopatra/literate-programming.org
deleted file mode 100644
index 63e0b02..0000000
--- a/site/cleopatra/literate-programming.org
+++ /dev/null
@@ -1,82 +0,0 @@
-#+TITLE: Literate Programming Projects
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./org.html
-#+SERIES_NEXT: ./theme.html
-
-Literate programming is an interesting exercice. It forces programmers
-to think about how to present their code for other people to
-understand it. It poses several significant challenges, in particular
-in terms of code refactoring. If a given piece of code is too much
-entangled with proses explaining it, rewriting it becomes cumbersome.
-
-That being said, literate programming is particularly well-suited for
-blog posts, since at the very least it provides the tool to enforce
-the code presented to readers is correct.
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/literate-programming.org</div>
-#+END_EXPORT
-
-* Tangling
-
-We use Emacs and ~org-mode~ to tangle the literate programming
-projects present in the ~posts/~ directory of this website. This is
-done with the following emacs lisp script.
-
-#+BEGIN_SRC emacs-lisp :tangle export-lp.el
-;; 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)))
-
-;; scan the posts/ directory and tangled it into lp/
-(setq org-publish-project-alist
- '(("lp"
- :base-directory "site/posts"
- :publishing-directory "lp"
- :recursive t
- :publishing-function cleopatra:tangle-publish)))
-
-(org-publish-all)
-#+END_SRC
-
-Tangling literate programming is done in the =prebuild= phase of
-*~cleopatra~*.
-
-#+BEGIN_SRC makefile :tangle literate-programming.mk
-literate-programming-prebuild :
- @cleopatra echo "Tangling" "literate programming project"
- @cleopatra exec -- cleopatra-run-elisp export-lp.el \
- >> build.log 2>&1
-
-ARTIFACTS += lp/ site/posts/deps.svg
-#+END_SRC
-
-* Building
-
-In the =build= phase, we actually try to compile the tangled projects.
-As of now, there is only one literate program: [[../posts/CoqffiEcho.org][the Echo server
-implemented in Coq]] which demonstrates how ~coqffi~ can be used to
-implement realistic software projects.
-
-#+BEGIN_SRC makefile :tangle literate-programming.mk
-COQFFI_ARCHIVE := site/files/coqffi-tutorial.tar.gz
-
-coqffi-tutorial-build : literate-programming-prebuild _opam/init
- @cleopatra echo "Building" "coqffi tutorial"
- @cd lp/coqffi-tutorial; dune build --display quiet
- @cleopatra echo "Archiving" "coqffi tutorial"
- @rm -f ${COQFFI_ARCHIVE}
- @tar --exclude="_build" -C lp/ -czvf ${COQFFI_ARCHIVE} coqffi-tutorial \
- 2>&1 >> build.log
-
-site/posts/CoqffiEcho.html : coqffi-tutorial-build
-literate-programming-build : coqffi-tutorial-build
-
-ARTIFACTS += ${COQFFI_ARCHIVE}
-#+END_SRC
diff --git a/site/cleopatra/org.org b/site/cleopatra/org.org
deleted file mode 100644
index 829272b..0000000
--- a/site/cleopatra/org.org
+++ /dev/null
@@ -1,134 +0,0 @@
-#+TITLE: Authoring Content with ~org-mode~
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./coq.html
-#+SERIES_NEXT: ./literate-programming.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/org.org</div>
-#+END_EXPORT
-
-* Author Guidelines
-
-* Implementation
-
-#+begin_src makefile :tangle org.mk
-EMACS := cleopatra-emacs
-
-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}
-CONFIGURE += .emacs
-
-EXPORT := --batch \
- --load="${ROOT}/scripts/packages.el" \
- --load="${ROOT}/scripts/export-org.el" \
- 2>> build.log
-
-INIT := --batch --load="${ROOT}/scripts/packages.el" \
- 2>> build.log
-
-.emacs : scripts/packages.el
- @cleopatra echo Initiating "Emacs configuration"
- @${EMACS} ${INIT}
- @touch .emacs
-
-%.html : %.org scripts/packages.el scripts/export-org.el \
- .emacs org.mk
- @cleopatra echo Exporting "$*.org"
- @${EMACS} $< ${EXPORT}
-#+end_src
-
-#+begin_src emacs-lisp :tangle scripts/packages.el
-(use-package ox-tufte :ensure t)
-#+end_src
-
-#+begin_src emacs-lisp :tangle scripts/export-org.el
-(cleopatra:configure)
-
-(org-babel-do-load-languages
- 'org-babel-load-languages
- '((dot . t)
- (shell . 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))
-#+end_src
diff --git a/site/cleopatra/soupault.org b/site/cleopatra/soupault.org
deleted file mode 100644
index 19a1096..0000000
--- a/site/cleopatra/soupault.org
+++ /dev/null
@@ -1,698 +0,0 @@
-#+TITLE: ~soupault~
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./theme.html
-#+SERIES_NEXT: ./commands.html
-
-We use ~soupault~ to build this website[fn::~soupault~ is an awesome
-free software project, with a unique approach to static website
-generation. You should definitely [[https://soupault.app][check out their website]]!].
-
-#+begin_export html
-<nav id="generate-toc"></nav>
-#+end_export
-
-* Installation
-
- We install ~soupault~ in a local switch. We use a witness file
- ~_opam/.init~ to determine whether or not our switch has always been
- created during a previous invocation of *~cleopatra~*.
-
- #+begin_src makefile :tangle soupault.mk
-CONFIGURE += _opam rss.json
-ARTIFACTS += out
-
-soupault-prebuild : _opam/init
- #+end_src
-
- Using ~soupault~ is as simple as calling it, without any particular
- command-line arguments.
-
- #+begin_src makefile :tangle soupault.mk
-soupault-build : dependencies-prebuild style.min.css
- @cleopatra echo "Executing" "soupault"
- @soupault
- #+end_src
-
- We now describe our configuration file for ~soupault~.
-
-* Configuration
-
- #+name: base-dir
- #+begin_src verbatim :noweb yes :exports none
-~lthms
- #+end_src
-
-** Global Settings
-
- The options of the ~[settings]~ section of a ~soupault~
- configuration are often self-explanatory, and we do not spend too
- much time to detaul them.
-
- #+begin_src toml :tangle soupault.conf :noweb yes
-[settings]
-strict = true
-site_dir = "site"
-build_dir = "out/<<base-dir>>"
-doctype = "<!DOCTYPE html>"
-clean_urls = false
-generator_mode = true
-complete_page_selector = "html"
-default_content_selector = "main"
-page_file_extensions = ["html"]
-ignore_extensions = [
- "v", "vo", "vok", "vos", "glob",
- "html~", "org"
-]
-default_template_file = "templates/main.html"
-pretty_print_html = false
- #+end_src
-
-** Setting Page Title
-
- We use the “page title” widget to set the title of the webpage
- based on the first (and hopefully the only) ~<h1>~ tag of the
- page.
-
- #+begin_src toml :tangle soupault.conf
-[widgets.page-title]
-widget = "title"
-selector = "h1"
-default = "~lthms"
-prepend = "~lthms: "
- #+end_src
-
-** Acknowledging ~soupault~
-
- When creating a new ~soupault~ project (using ~soupault --init~),
- the default configuration file suggests advertising the use of
- ~soupault~. Rather than hard-coding the used version of ~soupault~
- (which is error-prone), we rather determine the version of
- ~soupault~ with the following script.
-
- #+NAME: soupault-version
- #+begin_src bash :results verbatim output
-soupault --version | head -n 1 | tr -d '\n'
- #+end_src
-
- The configuration of the widget ---initially provided by
- ~soupault~--- becomes less subject to the obsolescence[fn::That
- is, as long as ~soupault~ does not change the output of its
- ~--version~ option.].
-
- #+begin_src toml :tangle soupault.conf :noweb yes
-[widgets.generator-meta]
-widget = "insert_html"
-html = """<meta name="generator" content="<<soupault-version()>>">"""
-selector = "head"
- #+end_src
-
-** Prefixing Internal URLs
-
- On the one hand, internal links can be absolute, meaning they
- start with a leading ~/~, and therefore are relative to the
- website root. On the other hand, website (especially static
- website) can be placed in larger context. For instance, my
- personal website lives inside the ~~lthms~ directory of the
- ~soap.coffee~ domain[fn::To my experience in hosting webapps and
- websites, this set-up is way harder to get right than I initially
- expect.].
-
- The purpose of this plugin is to rewrite internal URLs which are relative to the
- root, in order to properly prefix them.
-
- From a high-level perspective, the plugin structure is the following.
-
- First, we validate the widget configuration.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-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
- #+END_SRC
-
- Then, we propose a generic function to enumerate and rewrite tags
- which can have.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-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
- #+END_SRC
-
- Finally, we use this generic function for relevant tags.
-
- #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua
-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)
- #+END_SRC
-
- Again, configuring soupault to use this plugin is relatively
- straightforward.
-
- #+BEGIN_SRC toml :tangle soupault.conf :noweb yes
-[widgets.urls-rewriting]
-widget = "urls-rewriting"
-prefix_url = "<<base-dir>>"
-after = "mark-external-urls"
- #+END_SRC
-
-** Marking External Links
-
- #+BEGIN_SRC lua :tangle plugins/external-urls.lua
-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
- #+END_SRC
-
- #+BEGIN_SRC toml :tangle soupault.conf
-[widgets.mark-external-urls]
-after = "generate-history"
-widget = "external-urls"
- #+END_SRC
-
-** Generating a Table of Contents
-
- The ~toc~ widget allows for generating a table of contents for
- HTML files which contains a node matching a given ~selector~ (in
- the case of this document, ~#generate-toc~).
-
- #+begin_src toml :tangle soupault.conf
-[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"
- #+end_src
-
-** Generating Per-File Revisions Tables
-
-*** Users Instructions
-
- This widgets allows to generate a so-called “revisions table” of
- the filename contained in a DOM element of id ~history~, based on
- its history. Paths should be relative to the directory from which
- you start the build process (typically, the root of your
- repository). The revisions table notably provides hyperlinks to a
- ~git~ webview for each commit.
-
- For instance, considering the following HTML snippet
-
- #+begin_src html
-<div id="history">
- site/posts/FooBar.org
-</div>
- #+end_src
-
- This plugin will replace the content of this ~<div>~ with the
- revisions table of ~site/posts/FooBar.org~.
-
-*** Customization
-
- The base of the URL webview for the document you are currently
- reading is src_verbatim[:noweb yes :exports code]{<<repo>>}.
-
- #+name: repo
- #+begin_src verbatim :exports none
-https://labs.soap.coffee/soap.coffee/lthms.git
- #+end_src
-
- The template used to generate the revision table is the following.
-
- #+begin_src html :tangle templates/history.html :noweb yes
-<details id="history">
- <summary>Revisions</summary>
- <p>
- This revisions table has been automatically generated
- from <a href="<<repo>>">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="<<repo>>/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="<<repo>>/commit/{{filename}}/?id={{hash}}">{{abbr_hash}}</a>
- </td>
- </tr>
- {{/history}}
- </table>
-</details>
- #+end_src
-
-*** Implementation
-
- We use the built-in [[https://soupault.neocities.org/reference-manual/#widgets-preprocess-element][=preprocess_element=]] to implement, which
- means we need a script which gets its input from the standard
- input, and echoes its output to the standard input.
-
- #+begin_src toml :tangle soupault.conf
-[widgets.generate-history]
-widget = "preprocess_element"
-selector = "#history"
-command = 'scripts/history.sh templates/history.html'
-action = "replace_element"
- #+end_src
-
- This plugin proceeds as follows:
-
- 1. Using an ad-hoc script, it generates a JSON containing for each revision
- - The subject, date, hash, and abbreviated hash of the related commit
- - The name of the file at the time of this commit
- 2. This JSON is passed to a mustache engine (~haskell-mustache~) with a
- proper template
- 3. The content of the selected DOM element is replaced with the output of
- ~haskell-mustache~
-
- This translates in Bash like this.
-
- #+begin_src bash :tangle scripts/history.sh :shebang "#!/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}
-}
- #+end_src
-
- Generating the expected JSON is therefore as simple as:
-
- - Fetching the logs
- - Reading 8 line from the logs, parse the filename from the 6th
- line
- - Outputing the JSON
-
- We will use ~git~ to get the information we need. By default,
- ~git~ subcommands use a pager when its output is likely to be
- long. This typically includes ~git-log~. To disable this
- behavior, ~git~ exposes the ~--no-pager~ command. Besides, we
- also need ~--follow~ and ~--stat~ to deal with file
- renaming. Without this option, ~git-log~ stops when the file
- first appears in the repository, even if this “creation” is
- actually a renaming. Therefore, the ~git~ command line we use to
- collect our history is
-
- #+name: gitlog
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function gitlog () {
- local file="${1}"
- git --no-pager log \
- --follow \
- --stat=10000 \
- --pretty=format:'%s%n%h%n%H%n%cs%n' \
- "${file}"
-}
- #+end_src
-
- This function will generate a sequence of 8 lines containing all
- the relevant information we are looking for, for each commit,
- namely:
-
- - Subject
- - Abbreviated hash
- - Full hash
- - Date
- - Empty line
- - Change summary
- - Shortlog
- - Empty line
-
- For instance, the =gitlog= function will output the following
- lines for the last commit of this very file:
-
- #+begin_src bash :results verbatim :exports results :noweb yes
-<<gitlog>>
-gitlog "soupault.org" | head -n8
- #+end_src
-
- Among other things, the 6th line contains the filename. We need
- to extract it, and we do that with ~sed~. In case of file
- renaming, we need to parse something of the form ~both/to/{old =>
- new}~.
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-function parse_filename () {
- local line="${1}"
- local shrink='s/ *\(.*\) \+|.*/\1/'
- local unfold='s/\(.*\){\(.*\) => \(.*\)}/\1\3/'
-
- echo ${line} | sed -e "${shrink}" | sed -e "${unfold}"
-}
- #+end_src
-
- The next step is to process the logs to generate the expected
- JSON. We have to deal with the fact that JSON does not allow the
- last item of an array to be concluded by ",". Besides, we also
- want to indicate which commit is responsible for the creation of
- the file. To do that, we use two variables: =idx= and
- =last_entry=. When =idx= is equal to 0, we know it is the latest
- commit. When =idx= is equal to =last_entry=, we know we are
- looking at the oldest commit for that file.
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-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 "]}"
-}
- #+end_src
-
- Generating the JSON object for a given commit is as simple as
-
- #+begin_src bash :tangle scripts/history.sh :noweb yes
-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 "}"
-}
- #+end_src
-
- And we are done! We can safely call the =main= function to generate
- our revisions table.
-
- #+begin_src bash :tangle scripts/history.sh
-main "$(cat)" "${1}"
- #+end_src
-
-** Rendering Equations Offline
-
-*** Users instructions
-
- Inline equations written in the DOM under the class
- src_css{.imath} and using the \im \LaTeX \mi syntax can be
- rendered once and for all by ~soupault~. User For instance,
- ~<span class="imath">\LaTeX</span>~ is rendered \im \LaTeX \mi as
- expected.
-
- Using this widgets requires being able to inject raw HTML in
- input files.
-
-*** Implementation
-
- #+begin_src js :tangle scripts/render-equations.js
-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)
- #+end_src
-
- We reuse once again the =preprocess_element= widget. The selector
- is ~.imath~ (~i~ stands for inline in this context), and we
- replace the previous content with the result of our script.
-
- #+begin_src toml :tangle soupault.conf
-[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"
- #+end_src
-
-** RSS Feed
-
- #+begin_src toml :tangle soupault.conf
-[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"] }
- #+end_src
-
-** Series Navigation
-
- #+begin_src lua :tangle plugins/series.lua
-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
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-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
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-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
- #+end_src
-
- #+begin_src lua :tangle plugins/series.lua
-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)
- #+end_src
-
-#+begin_src toml :tangle soupault.conf
-[widgets.series]
-widget = "series"
-#+end_src
-
-** Injecting Minified CSS
-
- #+begin_src lua :tangle plugins/css.lua
-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
- #+end_src
-
- #+begin_src toml :tangle soupault.conf
-[widgets.css]
-widget = "css"
- #+end_src
-
-** Cleaning-up
-
- #+begin_src lua :tangle plugins/clean-up.lua
-function remove_if_empty(html)
- if String.trim(HTML.inner_html(html)) == "" then
- HTML.delete(html)
- end
-end
- #+end_src
-
- #+begin_src lua :tangle plugins/clean-up.lua
-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
- #+end_src
-
- #+begin_src lua :tangle plugins/clean-up.lua
-remove_all_if_empty("p") -- introduced by org-mode
-remove_all_if_empty("div.code") -- introduced by coqdoc
- #+end_src
-
-#+begin_src toml :tangle soupault.conf
-[widgets.clean-up]
-widget = "clean-up"
-#+end_src
diff --git a/site/cleopatra/theme.org b/site/cleopatra/theme.org
deleted file mode 100644
index 628baf8..0000000
--- a/site/cleopatra/theme.org
+++ /dev/null
@@ -1,534 +0,0 @@
-#+TITLE: Layout and Style
-
-#+SERIES: ../cleopatra.html
-#+SERIES_PREV: ./literate-programming.html
-#+SERIES_NEXT: ./soupault.html
-
-#+BEGIN_EXPORT html
-<nav id="generate-toc"></nav>
-<div id="history">site/cleopatra/theme.org</div>
-#+END_EXPORT
-
-* Setup
-
- As often when it comes to frontend development, we will use several
- tools hosted in the ~npm~ packages repository. ~npm~ is infamous
- for downloading lots of files and to store it in the ~node_modules/~
- directory. We configure *~cleopatra~* accordingly.
-
- #+begin_src makefile :tangle theme.mk
-CONFIGURE += package.json package-lock.json node_modules
- #+end_src
-
-* Base CSS
-
- We know construct piece by piece the “base” CSS layout which we will
- inject inside a ~<style>~ tag in each web page.
-
-** Layout
-
- Our goal is to have a three columns layout: one aside menu, with
- the top-level navigation links (technical articles, news, etc.) and
- the table of contents of the current pages if relevant, one main
- area for the webpage content, and a margin column with side notes
- and margin notes.
-
- #+caption: Widths of page components (in ~rem~)
- #+name: widths
- | Content | 35 |
- | Gutter | 3 |
- | Margin | 13 |
-
- #+name: main-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 0 widths)
- #+end_src
-
- #+name: gutter-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 1 widths)
- #+end_src
-
- #+name: margin-width
- #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
-(nth 2 widths)
- #+end_src
-
- #+begin_src css :tangle style.css :noweb yes
-:root {
- --main-width: <<main-width()>>rem;
- --gutter-width: <<gutter-width()>>rem;
- --margin-width: <<margin-width()>>rem;
- --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)));
-}
- #+end_src
-
- According to CSS’ own [[https://www.w3.org/TR/css-variables-1/#using-variables][specification]], you cannot use ~var()~ inside
- media queries. As a consequnece, for our theme to be responsive,
- the full width of the page content (\im 2 \times (that is,
- \mathrm{margin\_width} + \mathrm{gutter\_width}) +
- \mathrm{content\_width} \mi or call_body-width[:results raw]()rem)
- has to be hard-coded[fn::Fortunately, this is a literate
- program. This value is actually programmatically computed, so that
- we do not have to worry about forgetting to update it].
-
- #+name: body-width
- #+begin_src bash :exports none :noweb yes
-echo $((2 * (<<margin-width()>> + <<gutter-width()>>) + <<main-width()>>))
- #+end_src
-
- #+begin_src css :tangle style.css :noweb yes
-@media (max-width: <<body-width()>>rem) {
- :root {
- --body-width: var(--main-width);
- --code-width: var(--main-width);
- }
-}
- #+end_src
-
- And now, we are free to actually implement the layout.
-
- #+begin_src css :tangle style.css :noweb yes
-,* {
- box-sizing: border-box;
-}
-
-.fullwidth {
- width: var(--body-width);
-}
-
-@media (min-width: <<body-width()>>rem) {
- .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%;
-}
-
-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 {
- font-family: serif;
- font-weight: normal;
-}
-
-dt {
- font-weight: bold;
-}
-
-div.code,
-span.inlinecode,
-code,
-.doc pre,
-tt,
-.dmath,
-.imath {
- font-family: monospace;
- font-size: 80%;
-}
-
-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;
-}
-
-/* max-width has to be equal to --body-width */
-@media (max-width: <<body-width()>>rem) {
- 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 {
- width: 100%;
- }
-}
- #+end_src
-
-** Colors
-
- #+begin_src css :tangle style.css
-:root {
- --bg: white;
- --bg-plus: #f9f8f4;
- --current-line: #fbfbfb;
- --fade: #cfcecb;
- --fg: #3c3c3c;
- --fg-plus: #575757;
- --doc: #91003e;
- --warning: #bd745e;
- --red: #b3534b;
- --green: #6d9319;
- --yellow: #d4b100;
-}
- #+end_src
-
- #+begin_src css :tangle style.css
-body {
- font-family: sans-serif;
- color: var(--fg);
- background: var(--bg);
-}
-
-a[href] {
- color: inherit;
- text-decoration-color: var(--doc);
-}
-
-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);
-}
-
-.sidenote-number:after,
-.sidenote:before,
-pre,
-code,
-div.code,
-span.inlinecode,
-tt {
- color: var(--doc);
-}
- #+end_src
-
-** Coq
-
- #+begin_src css :tangle style.css
-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;
-}
-#+END_SRC
-
-** Icons
-
- #+begin_src css :tangle style.css
-.icon svg {
- fill: var(--doc);
- display: inline;
- width: 1em;
- height: .9em;
- vertical-align: text-top;
-}
-
-.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";
-}
- #+end_src
-
-** Minify CSS
-
- #+begin_src shell :shebang #!/bin/bash :tangle scripts/css.sh
-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
- #+end_src
-
- #+begin_src makefile :tangle theme.mk
-style.min.css : style.css dependencies-prebuild
- @cleopatra echo "Minifying" "CSS"
- @scripts/css.sh
-
-ARTIFACTS += style.min.css
-
-theme-build : style.min.css
- #+end_src
-
-* HTML Templates
-
- It would be best if we had a preprocessing step to inject the
- minified style, rather than using ~soupault~ to do the work once per
- page.
-
- #+begin_src html :tangle templates/main.html :noweb yes
-<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>
- </main>
- </body>
-</html>
- #+end_src