diff options
Diffstat (limited to 'site/cleopatra')
-rw-r--r-- | site/cleopatra/commands.org | 36 | ||||
-rw-r--r-- | site/cleopatra/coq.org | 47 | ||||
-rw-r--r-- | site/cleopatra/dependencies.org | 95 | ||||
-rw-r--r-- | site/cleopatra/literate-programming.org | 82 | ||||
-rw-r--r-- | site/cleopatra/org.org | 134 | ||||
-rw-r--r-- | site/cleopatra/soupault.org | 698 | ||||
-rw-r--r-- | site/cleopatra/theme.org | 534 |
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 |