Ξ
- 1. Typical workflow: How do I publish an article?
- 2. Why not use an existing blogging platform?
- 3. “Goal-driven development” —or, Getting Started: doc:blog/new-article
- 4. Seamlessly Previewing Articles within Emacs 😲
- 5. Style! ✨ What do we want to be inserted into the head of every page?
- 6. Ξ: Floating Table of Contents
- 7. Clickable Sections with Sensible Anchors
- 8. MathJax Support — \(e^{i \cdot \pi} + 1 = 0\)
- 9. Arabic Font Setup
- 10. Actually publishing an article
- 11. The name: al-bas-mala
- Appendix: Using a Custom Domain:
alhassy.com
AlBasmala:
Blogging with Emacs & Org-mode (•̀ᴗ•́)و
Image Org Link
A quick way to embed clickable images, along with tooltip credits and other configs.
(org-deflink image "Provide a quick way to insert images along with credits via tooltips. Example usage: image:https://upload.wikimedia.org/wikipedia/commons/3/33/Heisokudachi.svg|100|100 image:URL|WIDTH|HEIGHT|CENTER?|CREDIT? " ;; (upcase (or o-description o-label)) (-let [(image width height center? credit?) (s-split "|" o-label)] (-let [unsplash (cl-second (s-match ".*unsplash.com/photos/\\(.*\\)" image))] (let* ((href (if unsplash (concat "https://unsplash.com/photos/" unsplash) image)) (title (format "Image credit “%s”" (or credit? (if unsplash (concat "https://unsplash.com/photos/" unsplash) image)))) (src (if unsplash (format "https://source.unsplash.com/%s/%sx%s" unsplash width height) image)) (it (format "<a href=\"%s\" class=\"tooltip\" title=\"%s\"><img src=\"%s\" alt=\"Article image\" width=\"%s\" height=\"%s\" align=\"top\"/></a>" href title src width height))) (if center? (format "<center> %s </center>" it) it)))))
This will eventually be part of org-special-block-extras.
Redefining Org Section for purposes of blocks
This will eventually be part of org-special-block-extras.
(defmacro org-deftag (name args docstring &rest body) "Re-render an Org section in any way you like, by tagging the section with NAME. That is to say, we essentially treat tags as functions that act on Org headings: We redefine Org sections for the same purposes as Org special blocks. Anyhow: ARGS are the sequence of items seperated by underscores after the NAME of the new tag. BODY is a form that may anaphorically mention: - O-BACKEND: The backend we are exporting to, such as `latex' or `html'. - O-HEADING: The string denoting the title of the tagged section heading. DOCSTRING is mandatory; everything should be documented for future maintainability. The result of this anaphoric macro is a symbolic function name `org-deftag/NAME', which is added to `org-export-before-parsing-hook'. ---------------------------------------------------------------------- Below is the motivating reason for inventing this macro. It is used: ** Interesting, but low-priority, content :details_red: Blah blah blah blah blah blah blah blah blah blah blah. Blah blah blah blah blah blah blah blah blah blah blah. Here is the actual implementation: (org-deftag details (color) \"HTML export a heading as if it were a <details> block; COLOR is an optional argument indicating the background colour of the resulting block.\" (insert \"\n#+html:\" (format \"<details style=\\\"background-color: %s\\\">\" color) \"<summary>\" (s-replace-regexp \"^\** \" \"\" heading) \"</summary>\") (org-next-visible-heading 1) (insert \"#+html: </details>\")) " (let ((func-name (intern (format "org-deftag/%s" name)))) `(progn (cl-defun ,func-name (o-backend) ,docstring (outline-show-all) (org-map-entries (lambda () (kill-line) (let ((o-heading (car kill-ring))) (if (not (s-contains? (format ":%s" (quote ,name)) o-heading 'ignoring-case)) (insert o-heading) (-let [,args (cdr (s-split "_" (car (s-match (format "%s[^:]*" (quote ,name)) o-heading))))] (setq o-heading (s-replace-regexp (format ":%s[^:]*:" (quote ,name)) "" o-heading)) ,@body) ;; Otherwise we impede on the auto-inserted “* footer :ignore:” (insert "\n")))))) (add-hook 'org-export-before-parsing-hook (quote ,func-name)) (quote ,func-name))))
(org-deftag details (anchor color) "HTML export a heading as if it were a <details> block; ANCHOR & COLOR are optional arguments indicating the anchor for this block as well as the background colour of the resulting block. For example, in my blog, I would use :details_rememberthis_#F47174: to mark a section as friendly-soft-red to denote it as an “advanced” content that could be ignored on a first reading of my article. Incidentally, `orange' and `#f2b195' are also nice ‘warning’ colours." (insert "\n#+html:" (format "<div>%s <details class=\"float-child\" style=\"background-color: %s\">" (if anchor (format "<a style=\"width: 1%%;float: left; padding: 0px\" id=\"%s\" href=\"#%s\">🔗</a>" anchor anchor) "") color) "<summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\">" (s-replace-regexp "^\** " "" o-heading) "</font> </strong> </summary>") (org-next-visible-heading 1) (insert "#+html: </details> </div>"))
1. Typical workflow: How do I publish an article?
- Open an Org-mode buffer —or invoke blog/new-article.
(org-babel-load-file "~/blog/AlBasmala.org")
- Invoke blog/preview to get live WYSIWYG in an adjacent buffer after every save C-x C-s.
Until content:
- Write, write, and write!
- C-x C-s
- Preview
🤔 Consider using C-x n s, or C-x n n, to focus your attention on a particular section, thereby dramatically increasing the speed at which the preview renders.
- Execute blog/publish-current-article when you're done.
- This gets the new article showing up in index, RSS, archive, and updates the tags.
- NOTE: It takes about 20secs ~ 1min for the changes to be live on github pages.
2. Why not use an existing blogging platform?
I dislike coding in any website's primitive textarea, likewise for general writing.
For a brief period, I used Hashnode: I'd write in Emacs, then C-c C-e C-b h h to export current body as HTML, then paste that into Hashnode. However, Hashnode does not respect my CSS nor my inline JS. This was problematic, since I wanted to write a tiny root-meaning program to learn Arabic and to have a tiny web-app for Arabic shows for my kids.
3. “Goal-driven development” —or, Getting Started: blog/new-article
Here's what an example article source looks like:
#+title: Example Article #+author: Musa Al-hassy #+email: alhassy@gmail.com #+filetags: demo math #+fileimage: emacs-birthday-present.png #+description: This is an example article. * Abstract :ignore: Here is the extended abstract, which is rather concrete, regarding the goals of this article. * Body I like maths. * Conclusion I like programming.
Almost all #+keyword:⋯
lines become part of the JSON file
https://alhassy.com/posts.json, that I'll use to generate the index landing
page.
[ { "file": "java-cheat-sheet", "title": "Java CheatSheet", "date": "2023-12-25 Mon", "image": "modern-java.png 88% 88%", "description": "Quick reference for an old-school-cool high-level language ^_^", "tags": "java cheat-sheet", "url": "https://alhassy.com/java-cheat-sheet", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/java-cheat-sheet.org", "abstract": "\n#+begin_center\n#+html: This is a quick reference of concepts in modern Java.\n\nbadge:PDF|colorful_cheat_sheet|success|https://alhassy.com/java-cheat-sheet.pdf|read-the-docs\n\n# badge:license|GNU_3|informational|https://www.gnu.org/licenses/gpl-3.0.en.html|read-the-docs\ntweet:https://alhassy.com/java-cheat-sheet\nbadge:|buy_me_a_coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee\nbadge:contributions|welcome|green|https://github.com/alhassy/alhassy.github.io/issues\n# badge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds\n# badge:Warning|Incomplete_DRAFT|red||codeigniter\n#+end_center\n\n# @@html: <br> @@\n\nModern Java is a strongly-typed, eagery evaluated, case sensative, yet\nwhitespace insensative language. It uses hierarchies of classes/types\nto structure data, but also has first-class support for\nfunctional-style algebraic datatypes.\n\nJava programs are made up of ‘classes’, classes contain methods, and methods contain commands. To just try out a\nsnippet of code, we can\n+ Open a terminal and enter ~jshell~; then enter:\n #+begin_src java\n1 + 2 // The jshell lets you try things out!\n\n// Say hello in a fancy way\nimport javax.swing.*;\nJOptionPane.showMessageDialog(new JFrame(), \"Hello, World!\");\n\n#+end_src\n\n+ Alternatively, in IntelliJ, click /Tools/ then /Groovy Console/ to try things out!\n+ Finally, [[http://alhassy.com/making-vscode-itself-a-java-repl.html][VSCode]] allows arbitrary Java code to be sent to a ~jshell~\n in the background(!) and it echoes the result in a friendly way.\n\n# A program cannot consist of only commands. Java commands must be inside functions, and functions must be inside classes.\n#\n# Imagine a sofa. A sofa cannot exist on its own. It exist in a room somewhere. And a room also cannot exist on its own. A room is located in some house. Or, you could say that the house is divided into rooms, and those rooms contain things.\n#\n# Java programs are made up of classes, classes contain methods, and methods contain commands.\n\n# A minimal program must consist of at least one class, which must have at least\n# one method (function) that marks the program's starting point. This method must\n# be named main.\n\n:MWE:\nIn order to run a java program, it must have a main method as an entry point.\n\n#+begin_src java\n public class LearnJava {\n // In order to run a java program, it must have a main method as an entry\n // point.\n public static void main(String[] args) {\n System.out.println(\"Hello World!\");\n\n // Use System.out.printf() for easy formatted printing.\n System.out.printf(\"pi = %.5f\", Math.PI); // => pi = 3.14159\n }\n }\n#+end_src\n:End:\n\n** Web reference :ignore:\n\n#+macro: begin-ignore-html #+html: <!--\n#+macro: end-ignore-html #+html: -->\n\n#+latex: \\vspace{-1em}\n{{{begin-ignore-html()}}}\nTo be terse, lots of content is not shown in this [[http://alhassy.com/java-cheat-sheet.pdf][PDF]], but is shown in the *[[https://alhassy.com/java-cheat-sheet][HTML]]*\nversion.\n{{{end-ignore-html()}}}\n" }, { "file": "repl-driven-development", "title": "💐 Repl Driven Development: /Editor Integrated REPLs for all languages/ 🔁", "date": "2023-09-08 Fri", "image": "rdd-benefits.png", "description": "Press “C-x C-e” to send any piece of code (in any language) to a REPL in the background, within Emacs!", "tags": "repl-driven-development vscode emacs javascript java python lisp clojure haskell arend purescript idris racket", "url": "https://alhassy.com/repl-driven-development", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/repl-driven-development.org", "abstract": "\n#+begin_center\nbadge:Warning|Incomplete_DRAFT|red||codeigniter\n#+end_center\n\nThe melpa:repl-driven-development package makes the philosophy of REPL Driven\nDevelopment (RDD) accessible to any language that has a primitive CLI repl: /The\nresult is an Emacs interface for the language, where code of your choosing is/\n/evaluated, and results are echoed at your cursor in overlays./\n\nThat is, with Repl *green:aided* development, you make software by starting with\nan already working program (i.e., the repl) then *green:incrementlly* “teach it”\nto be the program you want, by defining & redefining things. Until satisfied,\nloop: Type/modify code *[[green:in your editor]]*, press some keys to evaluate what you\nwrote/modified /in the currently running system/, and explore/test the resulting\nruntime.\n# Eventually, save your code as a clean text file.\n\n/RDD is programming emphasising fast & rich feedback from a running system./ RDD\nis fantastic for quickly /teaching/exploring/ an idea; as such, the running\nexample of this article will be on servers ---no prior experience with servers\nis assumed.\nThe main examples will be in JavaScript, Python, and Java. (Since /JavaScript is\njust Lisp in C clothing/, we will not discuss Lisp.) Since Java is verbose, the\npower of REPLs really pays off when exploring a new idea. We see how many\nimports and setup-code simply disappear in the RDD approach, letting you focus\non the core idea you're exploring/teaching. For comparison, a traditional\nself-contained Java server program is ~30 lines long whereas the focused RDD\napproach is ~4 lines long.\n#\n# + We begin with JavaScript: Write some code, and see it interact with your browser.\n# + Then Python: Write some code, and see it interact with the terminal.\n\n# badge:repl-driven-development|1.4|informational|https://github.com/alhassy/repl-driven-development|Gnu-Emacs\n\n#+begin_center\nbadge:license|GNU_3|informational|https://www.gnu.org/licenses/gpl-3.0.en.html|read-the-docs\ntweet:https://alhassy.com/repl-driven-development\nbadge:|buy_me_a_coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee\n@@TODO: FIX contributions URL@@\nbadge:contributions|welcome|green|https://github.com/alhassy/alhassy.github.io/issues\n#+end_center\n\n# @@html: <br> @@\n\n/tdlr:/ This library provides the Emacs built-in kbd:C-x_C-e behaviour for\narbitrary languages, provided they have a primitive cli REPL.\n" }, { "file": "arabic-cheat-sheet", "title": "Arabic CheatSheet", "date": "2023-06-14 Wed", "image": "arabic-irab.png 100% 100%", "description": "Quick reference for the Arabic language; Modern Standard Arabic", "tags": "arabic cheat-sheet", "url": "https://alhassy.com/arabic-cheat-sheet", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/arabic-cheat-sheet.org", "abstract": "\n#+latex: \\iffalse\n\n#+begin_center\n#+html: This is a quick reference of concepts in the Arabic language.\n\nbadge:PDF|colorful_cheat_sheet|success|https://alhassy.com/arabic-cheat-sheet.pdf|read-the-docs\n\n# badge:license|GNU_3|informational|https://www.gnu.org/licenses/gpl-3.0.en.html|read-the-docs\ntweet:https://alhassy.com/arabic-cheat-sheet\nbadge:|buy_me_a_coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee\n#+end_center\n\n# @@html: <br> @@\n\n#+latex: \\fi\n" }, { "file": "family-tree", "title": "My Family Tree", "date": "2023-02-02 Thu", "image": "../images/family-tree.png 88% 88%", "description": "من هو في شجرة العائلة القديمة", "tags": "family", "url": "https://alhassy.com/family-tree", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/family-tree.org", "abstract": "\n#+begin_center\nbadge:Warning|Incomplete_DRAFT|red||codeigniter\n#+end_center\n\nWho's who in the old family tree\n" }, { "file": "karate", "title": "A Brisk Introduction to Karate", "date": "2023-02-02 Thu", "image": "https://www.usadojo.com/wp-content/uploads/2013/08/Goju-Ryu-Karate-600x300.png 88% 88%", "description": "Discovering what be ka-ra-te", "tags": "karate", "url": "https://alhassy.com/karate", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/karate.org", "abstract": "\n#+begin_center\nbadge:Warning|Incomplete_DRAFT|red||codeigniter\n#+end_center\n\nWhat are the basic forms of Karate? What is Karate?\n\n/“The ultimate aim of karate lies not in victory or defeat but in the perfection\nof the character of its participants … to subdue the enemy without fighting is/\n/the highest skill, know your enemy and know yourself, in a hundred battles you\nwill not be defeated”/ says Gichin Funakoshi ---known as The Father of Modern Karate.\n\n/Karate/ means “empty hand” and was developed on the island of Okinawa ---part of\nmodern-day Japan. The major styles (“Ryu”) are Shotokan, Wado-ryu,\nShito-ryu, and Goju-ryu ---many other styles of Karate are derived from these\nfour. I'm focusing on Goju-Ryu in this article: Goju-Ryu was founded by Chojun\nMiyagi; whose colleague, Gichin Funakosi, founded Shotokan-Ryu.\n\n#+begin_center\nimage:http://www.traditionalshotokankarate.co.uk/kara-te-do.gif\n#+end_center\n\nOccasionally one sees /Karate-Do/, which means “the way of the empty hand”.\nThis usage is a reminder that Karate is not just about fighting, but is also\na spiritual discipline.\n\nThe basic form of Goju-Ryu karate is Sanchin, “3 battles”: The battles of the\nmind, the body, and the spirit. However, this was considered a bit difficult\nfor beginners, and so new forms were needed as a way of introducing fundamental\nkarate forms to a wider audience. There are the “peaceful and safe” forms known\nas Pinan/Heian, the “first course” or Taikyoku forms, the “popularising forms”\nknown as Fukyugata ---the second of which was rebranded as “attack & smash”,\nGekaisai--- and, finally, there is the so-called Dachi-waza form. This last one\nis relatively new, and aims to be a smooth introduction to the world of\nforms/Kata.\n\nIn this article, I'd like to discuss the basic forms and their relationships.\n#+begin_center\nimage:https://www.sullivanskarateschool.com/wp-content/uploads/2019/07/saifa.gif\n#+end_center\n" }, { "file": "arabic-word-order", "title": "A Brisk Introduction to the Fundamentals of Arabic Grammar, نحو", "date": "2022-11-03 Thu", "image": "arabic-irab.png 100% 100%", "description": "Discovering how to say “a/an/the” in Arabic leads onto a zany adventure into case markings, gender, annexation, non-verbal sentences, plurals, and concludes with whether “Muslims” is مسلمون or مسلمین ---it's both!", "tags": "arabic", "url": "https://alhassy.com/arabic-word-order", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/arabic-word-order.org", "abstract": "\n# I'd like to discuss the importance of Arabic's short vowels and their use to give Arabic flexible word order.\n\nIn short: In English sometimes we mess-up between “I/me/my”, likewise in Arabic we might mess up with “ابو / ابا / ابي”:\nThese are just اب followed by one of ا/ي/و (which are the pronounced case endings!)\n\n#+begin_center\n~ ~ ~ ~ ~ ~ ~\n#+end_center\n\n How do Arabs say the English “a/an/the”, as in “an apple” or “the chair”? Easy! By default, all words are /indefinite/\n (“a/an”); and made /definite/ (“the”) by adding الـ to the front of the word.\n\n But... there's some subtleties, which first require us to discuss vowel markings... which also change if the /feminine\n marker/ ة is used, so we also need to briefly discuss gender.\n\nEnglish relies on /word order/ for meaning; for example, /Jim hit Bob/ is a sentence where the person doing the action is\n/Jim/ and we know it has to be /Jim/, and not /Bob/, since /Jim/ is the word /before/ the action /hit/. However, in Arabic words can\nbe ordered in almost any way you like! Then how do we identifiy who does an action? We use *[[green:case markings]]*: We add small\nsymbols to the end of words to indiciate the role they play in a sentence.\n\nWith vowel markings, we can finally flesh-out the nature of\n“a/an/the” in Arabic... but then something wild happens if we stick\nan (in)definite /followed by/ a definite! We get the concepts of ownership and complete sentences that don't need a verb!\n\nFinally, we conclude with an explanation of why in the world English Qurans use the single word /muslim/ where's Arabic\nQurans use both مسلمون and مسلمين.\n" }, { "file": "arabic-roots", "title": "Arabic Roots: The Power of Patterns", "date": "2022-11-02 Wed", "image": "https://unsplash.com/photos/Ejdemp9O7Po", "description": "Let's learn about how the Arabic language makes use of “roots” to obtain various words", "tags": "arabic javascript emacs", "url": "https://alhassy.com/arabic-roots", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/arabic-roots.org", "abstract": "\nI want to quickly introduce the Arabic language, through its “root system” ---i.e., most words have 3-letters at their\ncore--- and how these roots can be placed in “patterns” to obtain new words.\n\nI'd like to take a glance at Arabic's Verb Forms: These give you 10 words for each root!\n\nSome *green:interesting* concepts will also be mentioned, for those curious, but should be ignored on a first\nreading. These will be hidden away in /clickable/foldable/ regions.\n\nThese are notes of things that I'm learning; there's likely errors.\n" }, { "file": "arabic-glossary", "title": "Glossary of Arabic Linguistic Terms", "date": "2022-11-01 Tue", "image": "arabic-irab.png 100% 100%", "description": "Definitions, and discussions, of jargon relating to learning the Arabic language.", "tags": "arabic", "url": "https://alhassy.com/arabic-glossary", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/arabic-glossary.org", "abstract": "Definitions, and discussions, of jargon relating to learning the Arabic language." }, { "file": "cartoon", "title": "Arabic Cartoons", "date": "2022-10-21 Fri 11:20", "image": "https://upload.wikimedia.org/wikipedia/en/6/64/Dora_and_Boots.jpg 350 300", "description": "A simple interface to watch the engaging Arabic cartoons", "tags": "family arabic javascript", "url": "https://alhassy.com/cartoon", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/cartoon.org", "abstract": "\nA simple interface to watch the engaging Arabic cartoons.\n" }, { "file": "making-vscode-itself-a-java-repl", "title": "💐 Making VSCode itself a Java REPL 🔁", "date": "2022-09-05 Mon", "image": "https://github.com/alhassy/easy-extensibility/blob/main/graphics/repl-java.gif?raw=true 90% 90%", "description": "VSCode evaluates Java code wherever it sees it, by sending it to a JShell in the background, and echos the results in a friendly way!", "tags": "repl-driven-development vscode emacs javascript java python ruby clojure typescript haskell lisp", "url": "https://alhassy.com/making-vscode-itself-a-java-repl", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/making-vscode-itself-a-java-repl.org", "abstract": "\nVSCode evaluates Java code wherever it sees it, by sending it to a JShell in the background, and echos the results in a\nfriendly way!\n\nThis is achieved with a [[https://github.com/alhassy/easy-extensibility][meta-extension for VSCode]] that makes VSCode into a living, breathing, JS interpreter: It can\nexecute arbitrary JS that alters VSCode on-the-fly. /(Inspired by using Emacs and Lisp!)/\n\nThe relevant docs show how to make a similar REPL for [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1417-L1420][Python]], [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1459-L1462][Ruby]], [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1475-L1479][Clojure]], [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1490-L1494][Common Lisp]], [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1509-L1515][JavaScript]], [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1527-L1536][Typescript]],\n[[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1553-L1557][Haskell]], and of-course [[https://github.com/alhassy/easy-extensibility/blob/65572a283e4864d9bef157b438c33f4e47276e3a/vscodejs/index.js#L1575-L1585][Java]].\n" }, { "file": "vscode-is-itself-a-javascript-repl", "title": "💐 VSCode is itself a JavaScript REPL 🔁", "date": "2022-08-17 Wed", "image": "https://raw.githubusercontent.com/alhassy/easy-extensibility/main/graphics/repl.gif 90% 90%", "description": "A meta-extension for VSCode that makes VSCode into a living, breathing, JS interpreter: It can execute arbitrary JS that alters VSCode on-the-fly. A gateway into the world of Editor Crafting!", "tags": "repl-driven-development vscode emacs javascript", "url": "https://alhassy.com/vscode-is-itself-a-javascript-repl", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/vscode-is-itself-a-javascript-repl.org", "abstract": "\nA meta-extension for VSCode that makes VSCode into a living, breathing, JS interpreter: It can execute arbitrary JS that\nalters VSCode on-the-fly. A gateway into the world of Editor Crafting!\n\n| /(Inspired by using Emacs and Lisp!)/ |\n" }, { "file": "TypedLisp", "title": "Typed Lisp, A Primer", "date": "2019-08-21 19:29", "image": "emacs-birthday-present.png", "description": "Exploring Lisp's fine-grained type hierarchy.", "tags": "types lisp program-proving emacs", "url": "https://alhassy.com/TypedLisp", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/TypedLisp.org", "abstract": "\n#+TOC: headlines 2\n\nLet's explore Lisp's fine-grained type hierarchy!\n\nWe begin with a shallow comparison to Haskell, a rapid tour of type theory,\ntry in vain to defend dynamic approaches, give a somewhat humorous account of history,\nnote that you've been bamboozled ---type's have always been there---,\nthen go into technical details of some Lisp types, and finally conclude by showing\nhow /macros permit typing/.\n\n# Lisp types are fine-grained; e.g., rather than ~int~ we may use a spefied range of numbers,\n# or a set of specfiied elements, intersections, unions, and complements of types, and\n# even arbitrary predicates!\n\nGoals for this article:\n\n1. Multiple examples of type constructions in Lisp.\n2. Comparing Lisp type systems with modern languages, such as Haskell.\n3. Show how algebraic polymorphic types like ~Pair~ and ~Maybe~ can be defined in Lisp.\n Including heterogeneously typed lists!\n4. Convey a passion for an elegant language.\n5. Augment Lisp with functional Haskell-like type declarations ;-)\n\nUnless suggested otherwise, the phrase “Lisp” refers to\n[[https://www.gnu.org/software/emacs/manual/html_mono/cl.html#index-cl_002ddeftype-14][Common Lisp as supported by Emacs Lisp]]. As such, the resulting discussion\nis applicable to a number of Lisp dialects\n---I'm ignoring editing types such as buffers and keymaps, for now.\n" }, { "file": "three-minute-thesis", "title": "Have you ever packaged anything?", "date": "2019-03-12 19:29", "image": "packages.png 250 250", "description": "I learned something neat, and wanted to share!", "tags": "packages dependent-types", "url": "https://alhassy.com/three-minute-thesis", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/three-minute-thesis.org", "abstract": "\n#+TOC: headlines 2\n\n# copied from the repo\n\nHerein I try to make my current doctoral research accessible to the average person:\nExtending dependently-typed languages to implement module system features in the core\nlanguage. It's something I can direct my family to, if they're inclined to know what it is\nI've been doing lately.\n\nThe technical matter can be seen at the associated website\n─[[https://alhassy.github.io/next-700-module-systems-proposal/][The Next 700 Module Systems]]─ which includes a poster, slides, and a demo.\n\nExcluding the abstract, this is my thesis proposal in /three minutes/ (•̀ᴗ•́)و\n" }, { "file": "InteractiveWayToC", "title": "An Interactive Way To C", "date": "2019-01-12 19:29", "image": "interactive_way_to_c.png 450 450", "description": "Learning C program proving using Emacs --reminiscent of Coq proving with Proof General.", "tags": "program-proving c emacs frama-c", "url": "https://alhassy.com/InteractiveWayToC", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/InteractiveWayToC.org", "abstract": "\n#+TOC: headlines 2\n\n# copied from the repo\n\nDo you know what the above program accomplishes?\nIf you do, did you also spot a special edge case?\n\nWe aim to present an approach to program proving in C using a minimal Emacs setup\nso that one may produce literate C programs and be able to prove them correct\n--or execute them-- using a single button press; moreover the output is again in Emacs.\n\nThe goal is to learn program proving using the Frama-C tool\n--without necessarily invoking its gui-- by loading the source of this file into\nEmacs then editing, executing, & proving as you read along.\nOne provides for the formal specification of properties of C programs --e.g., using ACSL--\n which can then be verified for the implementations using tools that interpret such annotation\n--e.g., Frama-C invoked from within our Emacs setup.\n\nRead on, and perhaps you'll figure out how to solve the missing ~FixMe~ pieces 😉\n\nThe intent is for rapid editing and checking.\nIndeed, the Frama-c gui does not permit editing in the gui, so one must switch between\ntheir text editor and the gui.\n[[https://orgmode.org/worg/org-tutorials/org4beginners.html][Org mode beginning at the basics]] is a brief tutorial that covers a lot of Org and,\nfrom the get-go, covers “the absolute minimum you need to know about Emacs!”\n\nIf anything, this effort can be construed as a gateway into interactive theorem proving\nsuch as with Isabelle, Coq, or Agda.\n\nThe article /aims/ to be self-contained ---not even assuming familiarity with any C!\n\n\n#+BEGIN_QUOTE\n The presentation and examples are largely inspired by\n\n + Gilles Dowek's exquisite text [[https://www.springer.com/gp/book/9781848820319][Principles of Programming Languages]].\n - It is tremendously accessible!\n\n + Allan Blanchard's excellent tutorial\n [[https://allan-blanchard.fr/publis/frama-c-wp-tutorial-en.pdf][Introduction to C Program Proof using Frama-C and its WP Plugin]].\n\n Another excellent and succinct tutorial is Virgile Prevosto's [[https://frama-c.com/download/acsl-tutorial.pdf][ACSL Mini-Tutorial]].\n In contrast, the tutorial [[https://www.cs.umd.edu/class/spring2016/cmsc838G/frama-c/ACSL-by-Example-12.1.0.pdf][ACSL By Example]] aims to provide a variety of algorithms\n rendered in ACSL.\n#+END_QUOTE\n\nThere are no solutions since it's too easy to give up and look at the solutions that're\nnearby. Moreover, I intend to use some of the exercises for a class I'm teaching ;-)\n" }, { "file": "PathCat", "title": "Graphs are to categories as lists are to monoids", "date": "2018-12-24 19:29", "image": "PathCat.png 300 300", "description": "A fast-paced introduction to Category Theory based on the notion of graphs.", "tags": "category-theory agda types", "url": "https://alhassy.com/PathCat", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/PathCat.org", "abstract": "\n#+TOC: headlines 2\n\nNumbers are the lengths of lists which are the flattenings of trees which are\nthe spannings of graphs.\nUnlike the first three, graphs have /two/ underlying types of interest\n--the vertices and the edges-- and it is getting to grips with this complexity\nthat we attempt to tackle by considering their ‘algebraic’ counterpart: Categories.\n\n# trees are just those graphs for which arbitrary points are connected by a unique undirected path.\n\nIn our exploration of what graphs could possibly be and their relationships to lists are,\nwe shall /mechanise,/ or /implement,/ our claims since there will be many details and it is easy\nto make mistakes --moreover as a self-learning project, I'd feel more confident to make" }, { "file": "HeytingAlgebra", "title": "Discovering Heyting Algebra", "date": "2018-11-14 19:29", "image": "HeytingAlgebra.png 350 350", "description": "How do friends communicate secretly using non-invertible operations such as minimum? An introduction to Heyting Algebra --an instance of Cartesian Closed Categories!", "tags": "order-theory category-theory", "url": "https://alhassy.com/HeytingAlgebra", "history": "https://github.com/alhassy/alhassy.github.io/commits/master/posts/HeytingAlgebra.org", "abstract": "\n#+toc: headlines 2\n\nWe attempt to motivate the structure of a Heyting Algebra\nby considering ‘inverse problems’.\n\nFor example,\n+ You have a secret number $x$ and your friend has a secret number $y$, which you've\n communicated to each other in person.\n+ You communicate a ‘message’ to each other\n by adding onto your secret number.\n+ Hence, if I receive a number $z$, then I can /undo/ the addition operation to find the ‘message’ $m = z - y$.\n\nWhat if we decided, for security, to change our protocol from using addition to using\nminimum. That is, we encode our message $m$ as $z = x ↓ m$. Since minimum is not\ninvertible, we decide to send our encoded messages with a ‘context’ $c$ as a pair $(z, c)$.\nFrom this pair, a unique number $m′$ can be extracted, which is not necessarily the original $m$.\nRead on, and perhaps you'll figure out which messages can be communicated 😉\n\n# For example, we wrote our message on a piece of paper and placed it on a cafe bulletin board --a context!\n\nThis exploration demonstrates that relative pseudo-complements\n+ Are admitted by the usual naturals precisely when infinity is considered a number;\n+ Are /exactly/ implication for the Booleans;\n+ /Internalises/ implication for sets;\n+ Yield /the largest complementary subgraph/ when considering subgraphs.\n# + Generalise the /deduction theorem/.\n\nIn some sense, the pseudo-complement is the “best approximate inverse” to forming meets, minima, intersections.\n\nAlong the way we develop a number of the theorems describing the relationships\nbetween different structural components of Heyting Algebras;\nmost notably the internalisation of much of its own structure.\n\nThe article aims to be self-contained, however it may be helpful to\nlook at [[https://alhassy.github.io/CatsCheatSheet/LatticesCheatSheet.pdf][this lattice cheat sheet]] (•̀ᴗ•́)و\n" } ]
The #+description
is exported, by standard Org-mode, as HTML meta-data which is
used to ‘unfurl’ a link to an article: When a link to an article is pasted in a
social media website, it unfurls into a little card showing some information
about the link, such as its image, description, and author.
- For long descriptions, one can use multiple
#+description
lines; I'd like to have a terse one-liner with a longer description in theAbstract
heading.
Below are the methods to make a new article, to get the meta-data about each article, to create the JSON file, and to load it.
Use-package declarations
1: (use-package org-static-blog) 2: (use-package lf) ;; So we can use `lf-string' for multi-line strings supporting interpolation: 3: ;; (lf-string "100/2 is ${ (/ 100 2) }; neato!") ;; ⇒ "100/2 is 50; neato!"
Basic facts (global variables) of my blog
(defvar blog/title "Life & Computing Science" "Title of the blog.") (defvar blog/url "https://alhassy.com" "URL of the blog.") (defvar blog/publish-directory "~/blog/" "Directory containing published HTML files.") (defvar blog/posts-directory "~/blog/posts" "Directory containing source Org files. When publishing, posts are rendered as HTML and included in the index and RSS feed. See `blog/make-index-page' and `blog/publish-directory'.")
blog/new-article: Helper function to make a new article
1: (defun blog/new-article () 2: "Make a new article for my blog; prompting for the necessary ingredients. 3: 4: If the filename entered already exists, we simply write to it. 5: The user notices this and picks a new name. 6: 7: This sets up a new article based on existing tags and posts. 8: + Use C-SPC to select multiple tag items 9: 10: Moreover it also enables `org-preview-html-mode' so that on every alteration, 11: followed by a save, C-x C-s, will result in a live preview of the blog article, 12: nearly instantaneously." 13: (interactive) 14: (let (file desc) 15: 16: (thread-last blog/posts-directory 17: f-entries 18: (mapcar #'f-filename) 19: (completing-read "Filename (Above are existing): ") 20: (concat blog/posts-directory) 21: (setq file)) 22: 23: ;; For some reason, ‘find-file’ in the thread above 24: ;; wont let the completing-read display the possible completions. 25: (find-file file) 26: 27: (insert "#+title: " (read-string "Title: ") 28: "\n#+author: " user-full-name 29: "\n#+email: " user-mail-address 30: ;; "\n#+date: " (format-time-string "<%Y-%m-%d %H:%M>") 31: "\n#+filetags: " (s-join " " (helm-comp-read "Tags: " 32: blog/tags 33: :marked-candidates t)) 34: "\n#+fileimage: emacs-birthday-present.png" 35: ;; "\n#+fileimage: " (completing-read 36: ;; "Image: " 37: ;; (mapcar #'f-filename (f-entries "~/blog/images/"))) 38: ;; "\n#+include: ../MathJaxPreamble.org" ;; TODO. Is this someting I actually want here? If so, then consider tangling it from AlBasmala! (and add the whitespace-MathJax setup from above!) 39: "\n#+description: " 40: (setq desc (read-string "Article Purpose: ")) 41: "\n\n* Abstract :ignore: \n" desc 42: "\n\n* ???") 43: (save-buffer) 44: (blog/preview)))
blog/create-posts-json-file
1: (defun blog/create-posts-json-file () 2: "Create cache info about posts." 3: (interactive) 4: (require 'json) 5: (cl-loop for file in (f-files "~/blog/posts") 6: when (s-ends-with? ".org" file) 7: collect (blog/info file) into posts 8: finally 9: ;; Sorted in descending time; i.e., the latest article should be first 10: (setq posts (sort posts (lambda (newer older) (time-less-p (date-to-time (@date older)) (date-to-time (@date newer)))))) 11: (f-write-text (json-encode posts) 'utf-8 (f-expand "~/blog/posts.json")) 12: (find-file "~/blog/posts.json") 13: (json-pretty-print-buffer) 14: (write-file "~/blog/posts.json"))) 15: 16: 17: (defvar blog/posts (with-temp-buffer (insert-file-contents "~/blog/posts.json") (json-parse-buffer)) 18: "Load cached info about posts") 19: 20: 21: (defvar blog/tags (sort (seq-uniq (-flatten (seq-map (lambda (it) (s-split " " (map-elt it "tags"))) blog/posts))) #'string<) 22: "Tags for my blog articles.")
Convenient accessor methods: Given a JSON hashmap, get the specified key values
Since “accessor” begins with ‘a’, and ‘@’ looks like an ‘a’, all these methods start with ‘@’.
- The final 3 below not only access, but also produce HTMLized renditions of what they access. This is useful for when we want to organise an index landing page of all of my posts.
;; Convenient accessor methods: Given a JSON hashmap, get the specified key values. ;; Later, we redefine these, for example `@image' will actually produces the HTML for the image. ;; Example usage: (@title (seq-elt posts 0)) ⇒ "Java CheatSheet" ;; Extract the ‘#+title:‘ from POST-FILENAME. (defun @title (json) (map-elt json "title")) ;; TODO: Consider using: (format-time-string "%d %b %Y" ⋯) to have the same format across all articles. (defun @date (json) "Extract the “#+date:” from JSON." (map-elt json "date")) (defun @file (json) (map-elt json "file")) (defun @description (json) (map-elt json "description")) (defun @abstract (json) (map-elt json "abstract")) ;; Returns absolute URL to the published POST-FILENAME. ;; ;; This function concatenates publish URL and generated custom filepath to the ;; published HTML version of the post. ;; (defun @url (json) (map-elt json "url"))
@history: Get an HTML badge that points to the Github history of a given file name, in my blog
(defun @history (json) "Get an HTML badge that points to the Github history of a given file name, in my blog." (concat "<a class=\"tooltip\" title=\"See the various edits to this article over time\" href=\"" (map-elt json "history") "\"><img src=\"https://img.shields.io/badge/-History-informational?logo=github\"></a>"))
@tags: Get an HTML listing of tags, as shields.io bages, associated with the given file
(defun @tags (json) "Get an HTML listing of tags, as shields.io bages, associated with the given file. Example use: (@tags (seq-elt blog/posts 0)) " (concat ;; Straightforward implementation. ;; "<div class=\"taglist\">" ;; (org-static-blog-post-taglist file-name) ;; "</div>" ;; Badges implementation (concat (format "<a href=\"https://alhassy.github.io/tags.html\"> %s </a>" (org-link/octoicon "tag" nil 'html)) (s-join " " (--map (org-link/badge (format "|%s|grey|%stag-%s.html" (s-replace "-" "_" it) "https://alhassy.com/" it) nil 'html) (s-split " " (map-elt json "tags")))))))
@image
Every article declaratively has an associated image ^_^
- Images are loaded from the
~/blog/images/
directory, but may be explicit paths or URLs. If none declared, we use
emacs-birthday-present.png
:-)
(cl-defun @image (json &optional explicit-image-path-prefix) "Assemble the value of ‘#+fileimage: image width height border?’ as an HTML form. By default, the image should be located in the top-level `images/' directory. If the image is located elsewhere, or is a URL, is dictated by the presence of a `/' in the image path. Example use: (@image (seq-elt blog/posts 0)) Here are 4 example uses: #+fileimage: emacs-birthday-present.png #+fileimage: ../images/emacs-birthday-present.png #+fileimage: https://upload.wikimedia.org/wikipedia/en/6/64/Dora_and_Boots.jpg 350 300 #+fileimage: https://unsplash.com/photos/Vc2dD4l57og + Notice that the second indicates explicit width and height. + (To make the first approach work with local previews, we need the variable EXPLICIT-IMAGE-PATH-PREFIX which is used for local previews in `my/blog/style-setup'. This requires a slash at the end.) + The unsplash approach is specific: It shows the *main* image in the provided URL, and links to the provided URL. " (-let [(image width height no-border?) (s-split " " (map-elt json "image"))] (setq width (or width 350)) (setq height (or height 350)) (setq no-border? (if no-border? "" "style=\"border: 2px solid black;\"")) (cond ((s-contains? "/" image) t) ;; It's a URL, or explicit path, do nothing to it. (explicit-image-path-prefix (setq image (format "%s%s" explicit-image-path-prefix image))) ((not (s-contains? "/" image)) (setq image (format "images/%s" image)))) (-let [unsplash (cl-second (s-match ".*unsplash.com/photos/\\(.*\\)" image))] (setq href (if unsplash (concat "https://unsplash.com/photos/" unsplash) image)) (setq title (format "Image credit “%s”" (if unsplash (concat "https://unsplash.com/photos/" unsplash) image))) (setq src (if unsplash (format "https://source.unsplash.com/%s/%sx%s" unsplash width height) image)) (s-collapse-whitespace (format "<center class=\"post-image\"><a href=\"%s\" class=\"tooltip\" title=\"%s\"><img src=\"%s\" alt=\"Article image\" %s width=\"%s\" height=\"%s\" align=\"top\"/></a></center>" href title src no-border? width height)))))
blog/info: Core helper to get the plist/JSON metadata about each post
(defun blog/info (post-filename) "Extract the `#+BLOG_KEYWORD: VALUE` pairs from POST-FILENAME. Example use: (blog/info \"~/blog/posts/HeytingAlgebra.org\") " (let ((case-fold-search t)) (with-temp-buffer (insert-file-contents post-filename) (-snoc (cons (cons "file" (f-base post-filename)) (cl-loop for (prop.name prop.regex prop.default) on `("title" "^\\#\\+title:[ ]*\\(.+\\)$" ,post-filename "date" "^\\#\\+date:[ ]*<\\([^]>]+\\)>$" ,(time-since 0) "image" "^\\#\\+fileimage: \\(.*\\)" "emacs-birthday-present.png 350 350" "description" "^\\#\\+description:[ ]*\\(.+\\)$" "I learned something neat, and wanted to share!" "tags" "^\\#\\+filetags:[ ]*\\(.+\\)$" "" ;; String; Space separated sequence of tags ) by 'cdddr ;; See: https://stackoverflow.com/questions/19774603/convert-alist-to-from-regular-list-in-elisp do (goto-char (point-min)) collect (cons prop.name (if (search-forward-regexp prop.regex nil t) (match-string 1) prop.default)))) (cons "url" (concat "https://alhassy.com/" (f-base post-filename))) (cons "history" (format "https://github.com/alhassy/alhassy.github.io/commits/master/posts/%s.org" (f-base post-filename))) (cons "abstract" (progn (goto-char (point-min)) (when (re-search-forward "^\* Abstract" nil t) (beginning-of-line) (-let [start (point)] (org-narrow-to-subtree) (org-fold-show-entry) (re-search-forward "^ *:END:" nil t) ;; Ignore :PROPERTIES: drawer, if any. (forward-line) (buffer-substring-no-properties (point) (point-max))))))))))
The #+begin_abstract
is an Org-mode Special Block
Every article is intended to have a section named Abstract
, whose contents are
used as the preview of the article, in the index landing page.
See §3 for a template.
Below is an alteration from the examples of the docstring of org-defblock.
(org-defblock abstract (main) nil "Render a block in a slightly narrowed blueish box, titled \"Abstract\". Supported backends: HTML. " (format (concat "<div class=\"abstract\" style=\"border: 1px solid black;" "padding: 1%%; margin-top: 1%%; margin-bottom: 1%%;" "margin-right: 10%%; margin-left: 10%%; background-color: lightblue;\">" "<center> <strong class=\"tooltip\"" "title=\"What's the goal of this article?\"> Abstract </strong> </center>" "%s </div>") contents))
For example, the source:
#+begin_abstract In this article, we learn to have fun! #+end_abstract
Results in:
In this article, we learn to have fun!
Generating the Index Page
The actual look and feel of index.html
is due to the method
blog/make-index-page. It summarises all of my articles by their title, data
& image, ‘abstract’, and a read-more badge.
1: (cl-defun blog/make-index-page () 2: "Assemble the blog index page. 3: 4: The index page contains blurbs of all of my articles. 5: 6: Precondition: `blog/posts' refers to all posts, in reverse chronological order. 7: 8: You can view the generated ~/blog/index.html by invoking: 9: 10: (blog/make-index-page) 11: " 12: (interactive) 13: (blog/make-tags-page :export-file-name "~/blog/index.html"))
blog/make-tags-page & blog/make-all-tag-pages: Generating “tag-𝑻.html” pages
(defun blog/make-all-tag-pages () "Make tag pages for all of my tags" (interactive) (loop for total = (length blog/tags) for tag in blog/tags for n from 0 for progress = (* (/ (* n 1.0) total) 100) do (let ((inhibit-message t)) (blog/make-tags-page :tag tag)) (message "Progress ... %d%%" progress) ;; Slightly faster to generate all pages, /then/ to git add them all. ;; TODO: I'm doing a “git commit” here, where else? Maybe merge them all together? Likewise with “git add”s. finally (shell-command "cd ~/blog; git add \"tag-*.html\"; git commit -m \"Generated tags file\""))) ;; NOTE: Slightly faster if I get rid of the “Progress…” notifications.
where
(cl-defun blog/make-tags-page (&key (tag nil) (title (if tag (format "Posts tagged “%s”" tag) "")) (greeting (format "Here are some of my latest thoughts %s... badge:Made_with|Lisp|success|https://alhassy.github.io/ElispCheatSheet/CheatSheet.pdf|Gnu-Emacs such as doc:thread-first and doc:loop (•̀ᴗ•́)و tweet:https://alhassy.com @@html:<br><br>@@" (if tag (concat "on " tag) ""))) (export-file-name (concat-to-dir blog/publish-directory (if tag (concat "tag-" (downcase tag) ".html") "index.html")))) "Assemble a page of only articles tagged TAG blog index page. The page contains blurbs of all of my articles tagged TAG. Precondition: `blog/posts' refers to all posts, in reverse chronological order. Example uses: 1. (blog/make-tags-page :export-file-name \"~/blog/index.html\" :title \"Hello world\" :tag \"arabic\") 2. (blog/make-tags-page :tag \"arabic\") " (interactive) (view-echo-area-messages) (blog/preview/disable) (with-temp-buffer (insert (s-join "\n" (list ;; TODO: Actually look at this concat result and notice that osbe is adding ;; way too much content to the header! ;; (progn (org-special-block-extras-mode -1) "") (setq org-html-head-extra "") ;; Org-mode header (concat "#+EXPORT_FILE_NAME: " export-file-name) "#+options: toc:nil title:nil html-postamble:nil" "#+begin_export html" ;; MA: Not ideal, the sizes I've set in the actual article are best. ;; "<style>" ;; ".post-image { margin: auto; width: 30em !important; height: 30em !important; }" ;; "</style>" org-static-blog-page-preamble org-static-blog-page-header "#+end_export" (concat "#+html: <br><center style=\"font-size: 3em; font-weight: bolder;\">" title "</center>") ;; TODO: Delete the following comment when things work and are done. ;; Extra styling of abstracts. ;; Works; but not needed. ;; "\n#+HTML_HEAD_EXTRA: <style> div.abstract {background-color: pink !important;} </style>" ;; The greeting message that informs viewers what this page is about. "#+html: <br>" greeting "#+html: <br>" ;; TODO: Add this loop body to the info of each post, for future use via AngularJS view-by-tags. ;; Blurbs of posts (s-join "\n" (--map (if (and tag (not (seq-contains-p (s-split " " (map-elt it "tags")) tag))) "" (concat (progn (message "Processing %s..." it) "") ;; Progress indicator ;; TODO: Make this concat of ⟨0⟩-⟨4⟩ into a method `@preview' ;; ⟨0⟩ Title and link to article (format "#+HTML: <h2 class=\"title\"><a href=\"%s\"> %s</a></h2>" (@url it) (@title it)) ;; TODO: Look at all uses of @title and maybe move this @@html@@ into it. ;; NOTE: This is still a Phase Split since the JSON has the raw data, and the @ABC methods produce @@html⋯@@ code ^_^ ;; ⟨1⟩ Tags and reading time (format "\n#+begin_export html\n<center>%s\n</center>\n#+end_export" (@tags it)) ;; TODO: Look at all uses of @tags and maybe move this @@html@@ into it. ;; ⟨2⟩ Article image (format "\n@@html:%s@@\n" (@image it)) ;; TODO: Look at all uses of @image and maybe move this @@html@@ into it. ;; ⟨3⟩ Preview (@abstract it) ;; ⟨4⟩ “Read more” link ;; TODO: Make a @read-more method. (format (concat "\n@@html:<p style=\"text-align:right\">@@" " badge:Read|more|green|%s|read-the-docs @@html:</p>@@") (@url it)))) (seq--into-list blog/posts))) ;; “Show older posts” ;; This is the bottom-most matter in the index.html page "#+begin_export html" "<hr> <center> <em> Thanks for reading everything! 😁 Bye! 👋 </em> </center> <br/>" (blog/license) ;; TODO: Add a “proudly created with Emacs’ Org-mode” tagline? "\n#+end_export" ))) (org-mode) (org-html-export-to-html)))
4. Seamlessly Previewing Articles within Emacs 😲
Whenever I save, C-x C-s, I'd like to see the final product, the resulting web-page in a JavaScript-supported browser within Emacs.
- By “final product” I mean all styles and other features of my blog, as
discussed in this article; for example, this includes the article image,
abstract, and blog header and footer.
- I'd like to have all of my styles automatically loaded in the right places.
- This gives me a nonintrusive way to preview what I write; ≈WYSIWYG≈.
We accomplish these goals via the following methods.
org-link/blog: Using My Blog Styles Anywhere
In any Org-mode file —including random ones that are not even for my blog— I'll use the Org links blog:header
and
blog:footer
to obtain the nice styling of my blog.
This is a minor alteration from the examples within the docstring of org-deflink.
(org-deflink blog "Provide the styles for “www.alhassy.com”'s “header” and “footer”. The use of “blog:footer” aims to provide a clickable list of tags, produce an HTMLized version of the Org source, and provides a Disqus comments sections. For details, consult the `blog/footer' function. Finally, I want to avoid any `@@backend:...@@' from appearing in the browser frame's title. We accomplish this with the help of some handy-dandy JavaScript: Just use “blog:sanitise-title”. " (pcase o-label ("header" (concat org-static-blog-page-preamble org-static-blog-page-header "<link href=\"https://alhassy.github.io/org-notes-style.css\" rel=\"stylesheet\" type=\"text/css\" />" "<link href=\"https://alhassy.github.io/floating-toc.css\" rel=\"stylesheet\" type=\"text/css\" />" "<link href=\"https://alhassy.github.io/blog-banner.css\" rel=\"stylesheet\" type=\"text/css\" />" ;; The use of the “post-title” class is so that the org-static-blog-assemble-rss method can work as intended. (thread-last (org-static-blog-get-title (buffer-file-name)) (s-replace-regexp "@@html:" "") (s-replace-regexp "@@" "") (format "<br><center><h1 class=\"post-title\">%s</h1></center>")))) ("footer" (blog/footer (buffer-file-name))) ("sanitise-title" "<script> window.parent.document.title = window.parent.document.title.replace(/@@.*@@/, \"\") </script>") (_ "")))
See also: How do I make a new Org link type?
blog/style-setup: A function to insert org-link/blog into a buffer
(defun blog/style-setup (_backend) "Insert blog header (fancy title), tags, blog image (before “* Abstract”), and footer (links to tags). There are default options: TOC is at 2 levels, no classic Org HTML postamble nor drawers are shown. Notice that if you explicitly provide options to change the toc, date, or show drawers, etc; then your options will be honoured. (Since they will technically come /after/ the default options, which I place below at the top of the page.) " (goto-char (point-min)) (let ((post (blog/info (buffer-file-name)))) (insert "#+options: toc:2 html-postamble:nil d:nil" "\n#+date: " (format-time-string "%Y-%m-%d" (current-time)) (if (buffer-narrowed-p) "\n#+options: broken-links:t" "") "\n blog:header blog:sanitise-title \n" "\n* Tags, then Image :ignore:" "\n#+html: " "<center>" (@tags post) "</center>" "\n#+html: " (@image post ;; Need this conditional since AlBasmala lives in ~/blog whereas usually articles live in ~/blog/posts. ;; TODO: Consider just making AlBasmala live in ~/blog/posts, I don't think there's any real reason for breaking consistency. (if (equal (f-base (@file post)) "AlBasmala") "./images/" "../images/")) "\n") ;; Wrap contents of “* Abstract” section in the “abstract” Org-special-block ;; (In case we are narrowed, we only act when we can find the Abstract.) ;; TODO: Replace this with (@abstract (blog/info (buffer-file-name))), or: (@abstract post) (when (re-search-forward "^\* Abstract" nil t) (beginning-of-line) (-let [start (point)] (org-narrow-to-subtree) (org-show-entry) (re-search-forward "^ * :END:" nil t) ;; Ignore :PROPERTIES: drawer, if any. (forward-line) (insert "\n#+begin_abstract\n") (call-interactively #'org-forward-heading-same-level) ;; In case there is no next section, just go to end of file. (when (equal start (point)) (goto-char (point-max))) (insert "\n#+end_abstract\n") (widen))) (goto-char (point-max)) ;; The Org file's title is already shown via blog:header, above, so we disable it in the preview. (insert (format "\n* footer :ignore: \n blog:footer \n #+options: title:nil \n"))))
Inserting org-link/blog seamlessly via the export process; then preview with every save
(cl-defun blog/preview () "Enable preview-on-save, and add blog/style-setup from Org's export hook." (interactive) ;; Let's ensure we have no xwidget buffer lying around, otherwise Emacs might hang. (-let [kill-buffer-query-functions nil] (mapcar #'kill-buffer (--filter (equal 'xwidget-webkit-mode (buffer-local-value 'major-mode it)) (buffer-list)))) ;; Inserting org-link/blog /seamlessly/ via the export process (add-hook 'org-export-before-processing-hook #'blog/style-setup) ;; Preview with every save (setq org-preview-html-viewer 'xwidget) (org-preview-html-mode)) (cl-defun blog/preview/disable () "Disable preview-on-save, and remove blog/style-setup from Org's export hook." (interactive) (remove-hook 'org-export-before-processing-hook #'blog/style-setup) (org-preview-html-mode -1))
Upon a save, C-x C-s, a new HTML file is created —bearing the same name as the Org file. It seems an incremental export is performed and so this is rather fast —at least much faster than manually invoking C-c C-e h o.
Article Footer: HTMLized Source and Git History
1: (defun blog/footer (post-file-name) 2: "Returns the HTML rendering the htmlised source, version history, and comment box at the end of a post. 3: 4: This function is called for every post and the returned string is appended to the post body, as a postamble." 5: (let ((post (blog/info (buffer-file-name)))) 6: (concat 7: "<hr>" 8: "<center>" 9: (blog/htmlize-file post-file-name) 10: " " 11: (@history post) 12: ;; 13: ;; Consider only add this to posts tagged “arabic”? 14: (blog/css/arabic-font-setup) 15: ;; 16: "<br>" 17: "<a href=\"https://www.buymeacoffee.com/alhassy\"><img src=" 18: "\"https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee\">" 19: "</a>" 20: ;; 21: "<br><strong> Generated by Emacs and Org-mode (•̀ᴗ•́)و </strong>" 22: (blog/license) 23: ;; (blog/comments) ;; TODO. Not working as intended; low priority. 24: "</center>" 25: ;; The next line is required to make the org-static-blog-assemble-rss method work. 26: "<div hidden> <div id=\"postamble\" class=\"status\"> </div> </div>" 27: (blog/read-remaining-js))))
blog/htmlize-file: Generate an htmlized version of a given source file; return an HTML badge linking to the colourised file
(defun blog/htmlize-file (file-name) "Generate an htmlized version of a given source file; return an HTML badge linking to the colourised file. We do not take the extra time to produce a colourised file when we are previewing an article." (unless org-preview-html-mode (let ((org-hide-block-startup nil)) (with-temp-buffer (find-file file-name) ;; (insert "\n#+HTML_HEAD: <link href=\"../doom-solarized-light.css\" rel=\"stylesheet\">\n") (org-mode) (outline-show-all) (switch-to-buffer (htmlize-buffer)) (write-file (concat "~/blog/" (f-base file-name) ".org.html")) (kill-buffer)))) (concat "<a class=\"tooltip\" title=\"See the colourised Org source of this article; i.e., what I typed to get this nice webpage\" href=\"" (f-base file-name) ".org.html\"><img src=\"https://img.shields.io/badge/-Source-informational?logo=read-the-docs\"></a>"))
blog/license: HTML for Creative Commons Attribution-ShareAlike 3.0 Unported License
(defun blog/license () "Get HTML for Creative Commons Attribution-ShareAlike 3.0 Unported License." (s-collapse-whitespace (s-replace "\n" "" " <center style=\"font-size: 12px\"> <a rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/3.0/\"> <img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by-sa/3.0/88x31.png\"/> </a> <br/> <span xmlns:dct=\"https://purl.org/dc/terms/\" href=\"https://purl.org/dc/dcmitype/Text\" property=\"dct:title\" rel=\"dct:type\"> <em>Life & Computing Science</em> </span> by <a xmlns:cc=\"https://creativecommons.org/ns#\" href=\"https://alhassy.github.io/\" property=\"cc:attributionName\" rel=\"cc:attributionURL\"> Musa Al-hassy </a> is licensed under a <a rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/3.0/\"> Creative Commons Attribution-ShareAlike 3.0 Unported License </a> </center>")))
blog/comments: Embed Disqus Comments for my blog
1: (defun blog/comments () 2: "Embed Disqus Comments for my blog" 3: (s-collapse-whitespace (s-replace "\n" "" 4: " 5: <div id=\"disqus_thread\"></div> 6: <script type=\"text/javascript\"> 7: /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ 8: var disqus_shortname = 'life-and-computing-science'; 9: /* * * DON'T EDIT BELOW THIS LINE * * */ 10: (function() { 11: var dsq = document.createElement('script'); 12: dsq.type = 'text/javascript'; 13: dsq.async = true; 14: dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; 15: (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 16: })(); 17: </script> 18: <noscript>Please enable JavaScript to view the 19: <a href=\"http://disqus.com/?ref_noscript\">comments powered by Disqus.</a></noscript> 20: <a href=\"http://disqus.com\" class=\"dsq-brlink\">comments powered by <span class=\"logo-disqus\">Disqus</span></a>")))
blog/read-remaining-js: HTML to use ReadRemaining.js
(defun blog/read-remaining-js () "Get the HTML required to make use of ReadRemaining.js" ;; [Maybe Not True] ReadReamining.js does not work well with xWidget browser within Emacs (if (equal org-preview-html-viewer 'xwidget) "" ;; ReadRemaining.js ∷ How much time is left to finish reading this article? ;; ;; jQuery already loaded by org-special-block-extras. ;; "<script ;; src=\ ;; "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>" "<link rel=\"stylesheet\" href=\"readremaining.js-readremainingjs/css/rr_light.css\" type='text/css'/> <script src=\"readremaining.js-readremainingjs/src/readremaining.jquery.js\"></script> <script src='readremaining.js/src/readremaining.jquery.js' type='text/javascript'></script> <script type=\"text/javascript\"> $('body').readRemaining({showGaugeDelay : 10, showGaugeOnStart : true}); </script>"))
ReadRemaining.js gives us a little floating clock on the bottom left of the screen which says, e.g., 4m 9s left when reading an article.
- It tells us how much time is left before the article is done.
- The time adjusts dynamically as the user scrolls down —but not up.
- Apparently it has to be at the end of the HTML
<body>
, otherwise it wont work for me.- It may be best to avoid loading jQuery multiple times; see here for the necessary conditional.
5. Style! ✨ What do we want to be inserted into the head of every page?
For each article, I'll have a set of styles loaded as well as a set of <script>
code fragments. Here is an overview of
these fragments, and throughout the rest of this article, I'll tangle code to where appropriate.
5.1. Style Header Elements
Firstly, we want some styling rules to be loaded.
1: (concat 2: "<meta name=\"author\" content=\"Musa Al-hassy\"> 3: <meta name=\"referrer\" content=\"no-referrer\">" 4: "<link href=\"usual-org-front-matter.css\" rel=\"stylesheet\" type=\"text/css\" />" 5: "<link href=\"org-notes-style.css\" rel=\"stylesheet\" type=\"text/css\" />" 6: "<link href=\"floating-toc.css\" rel=\"stylesheet\" type=\"text/css\" />" 8: "<link rel=\"icon\" href=\"images/favicon.png\">")
- usual-org-front-matter.css
- Org-static-blog ignores any styling exported by Org, so let's bring that back in. I just exported this file with the usual C-c C-e h o, then saved the CSS it produced.
- org-notes-style.css
- I like the rose-style of this org-notes-style for HTML export. However, it seems loading the CSS directly from its homepage does not work, so I've copied the CSS file for my blog.
- floating-toc.css
- I want to have an unobtrusive floating table of contents, see §6.
- blog-banner.css
- Finally, we want a beautiful welcome mat, see §5.4.
5.2. Script Header Elements
In addition, we have two more pieces we would like to add to the header: Support
for dynamic code-line highlighting, §5.4, and support for using
LaTeX-style notation to write mathematics, §8. We will use a
noweb-ref named my-html-header
to refer to them, which are then catenated below.
Full, tangled, value of org-static-blog-page-header
(setq org-static-blog-page-header (concat ;; NOPE: org-html-head-extra ;; Altered by ‘org-special-block-extras’ (concat "<meta name=\"author\" content=\"Musa Al-hassy\"> <meta name=\"referrer\" content=\"no-referrer\">" "<link href=\"usual-org-front-matter.css\" rel=\"stylesheet\" type=\"text/css\" />" "<link href=\"org-notes-style.css\" rel=\"stylesheet\" type=\"text/css\" />" "<link href=\"floating-toc.css\" rel=\"stylesheet\" type=\"text/css\" />" "<link rel=\"icon\" href=\"images/favicon.png\">") "<script type=\"text/javascript\"> /* @licstart The following is the entire license notice for the JavaScript code in this tag. Copyright (C) 2012-2020 Free Software Foundation, Inc. The JavaScript code in this tag is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. As additional permission under GNU GPL version 3 section 7, you may distribute non-source (e.g., minimized or compacted) forms of that code without the copy of the GNU GPL normally required by section 4, provided you include this license notice and a URL through which recipients can access the Corresponding Source. @licend The above is the entire license notice for the JavaScript code in this tag. */ <!--/*--><![CDATA[/*><!--*/ function CodeHighlightOn(elem, id) { var target = document.getElementById(id); if(null != target) { elem.cacheClassElem = elem.className; elem.cacheClassTarget = target.className; target.className = \"code-highlighted\"; elem.className = \"code-highlighted\"; } } function CodeHighlightOff(elem, id) { var target = document.getElementById(id); if(elem.cacheClassElem) elem.className = elem.cacheClassElem; if(elem.cacheClassTarget) target.className = elem.cacheClassTarget; } /*]]>*///--> </script>" "<script type=\"text/x-mathjax-config\"> MathJax.Hub.Config({ displayAlign: \"center\", displayIndent: \"0em\", \"HTML-CSS\": { scale: 100, linebreaks: { automatic: \"false\" }, webFont: \"TeX\" }, SVG: {scale: 100, linebreaks: { automatic: \"false\" }, font: \"TeX\"}, NativeMML: {scale: 100}, TeX: { equationNumbers: {autoNumber: \"AMS\"}, MultLineWidth: \"85%\", TagSide: \"right\", TagIndent: \".8em\" } }); </script> <script type=\"text/javascript\" src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_HTML\"></script> " " <script type=\"text/x-mathjax-config\"> MathJax.Hub.Config({ \"HTML-CSS\": { styles: { \".MathJax nobr\": { padding: \"0.5em 0.5em\" }, } } }); </script> " ))
5.3. Lisp Header Elements
Some Lisp code is required to string everything together.
- Lisp fragments are tangled to AlBasmala.el.
5.4. Blog Banner and Dynamic Code Highlighting
I want to have a nice banner at the top of every page, which should link to useful parts of my blog.
1: (setq org-static-blog-page-preamble 2: "<div class=\"header\"> 3: <a href=\"https://alhassy.github.io/\" class=\"logo\">Life & Computing Science</a> 4: <br/> 5: <a href=\"https://alhassy.github.io/AlBasmala\">AlBasmala</a> 6: <a href=\"https://alhassy.github.io/rss.xml\">RSS</a> 7: <a href=\"https://alhassy.github.io/about\">About</a> 8: </div>")
I want to style it as follows:
- Line 1: The banner is in a box at the top with some shadowing and
centerd text using the
fantasy
font - Line 13: The blog's title is large and bold
- Line 18: All links in the banner are black
- Line 25: When you hover over a link, it becomes blue
CSS Details
1: .header { 2: /* Try to load ‘fantasy’ if possible, else try to load the others. */ 3: font-family: fantasy, monospace, Times; 4: text-align: center; 5: overflow: hidden; 6: /* background-color: #f1f1f1 !important; */ 7: /* background: #4183c4 !important; */ 8: padding-top: 10px; 9: padding-bottom: 10px; 10: box-shadow: 0 2px 10px 2px rgba(0, 0, 0, 0.2); 11: } 12: 13: .header a.logo { 14: font-size: 50px; 15: font-weight: bold; 16: } 17: 18: .header a { 19: color: black; 20: padding: 12px; 21: text-decoration: none; 22: font-size: 18px; 23: } 24: 25: .header a:hover { 26: background-color: #ddd; 27: background-color: #fff; 28: color: #4183c4; 29: }
Notice that as you hover over the references, such as this, the corresponding
line of code is highlighted! Within a src
block, one uses the switches -n -r
to enable references via line numbers, then declares (ref:name)
on line
and refers to it by [[(name)][description]]
. Org-mode by default styles
such highlighting.
Dynamic Code Highlighting
1: "<script type=\"text/javascript\"> 2: /* 3: @licstart The following is the entire license notice for the 4: JavaScript code in this tag. 5: 6: Copyright (C) 2012-2020 Free Software Foundation, Inc. 7: 8: The JavaScript code in this tag is free software: you can 9: redistribute it and/or modify it under the terms of the GNU 10: General Public License (GNU GPL) as published by the Free Software 11: Foundation, either version 3 of the License, or (at your option) 12: any later version. The code is distributed WITHOUT ANY WARRANTY; 13: without even the implied warranty of MERCHANTABILITY or FITNESS 14: FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. 15: 16: As additional permission under GNU GPL version 3 section 7, you 17: may distribute non-source (e.g., minimized or compacted) forms of 18: that code without the copy of the GNU GPL normally required by 19: section 4, provided you include this license notice and a URL 20: through which recipients can access the Corresponding Source. 21: 22: 23: @licend The above is the entire license notice 24: for the JavaScript code in this tag. 25: */ 26: <!--/*--><![CDATA[/*><!--*/ 27: function CodeHighlightOn(elem, id) 28: { 29: var target = document.getElementById(id); 30: if(null != target) { 31: elem.cacheClassElem = elem.className; 32: elem.cacheClassTarget = target.className; 33: target.className = \"code-highlighted\"; 34: elem.className = \"code-highlighted\"; 35: } 36: } 37: function CodeHighlightOff(elem, id) 38: { 39: var target = document.getElementById(id); 40: if(elem.cacheClassElem) 41: elem.className = elem.cacheClassElem; 42: if(elem.cacheClassTarget) 43: target.className = elem.cacheClassTarget; 44: } 45: /*]]>*///--> 46: </script>"
5.5. Miscellaneous Styles
Curvy Source Blocks & Pink Inline
The border-radius
property defines the radius of an
element's corners, we use it to make curvy looking source blocks.
Its behaviour changes depending on how many arguments it is given.
- We also style the code block's label to be curvy.
- Both
.src
andpre.src:before
are used by Org.
1: .src { 2: border: 0px !important; 3: /* 50px for top-left and bottom-right corners; 4: 20px for top-right and bottom-left cornerns. */ 5: border-radius: 50px 20px !important; 6: } 7: 8: pre.src:before { 9: /* border: 0px !important; */ 10: /* background-color: inherit !important; */ 11: padding: 3px !important; 12: border-radius: 20px 50px !important; 13: font-weight:700 14: } 15: 16: /* wrap lengthy lines for code blocks */ 17: pre{white-space:pre-wrap} 18: 19: /* Also curvy inline code with ~ ⋯ ~ and = ⋯ = */ 20: code { 21: /* background: Cyan !important; */ 22: background: pink !important; 23: border-radius: 7px; 24: /* border: 1px solid lightgrey; background: #FFFFE9; padding: 2px */ 25: }
Code such as (= 2 (+ 1 1))
now sticks out with a pink background ♥‿♥
Pink Tables
table { background: pink; border-radius: 10px; /* width:90% */ border-bottom: hidden; border-top: hidden; display: table !important; /* Put table in the center of the page, horizontally. */ margin-left:auto !important;margin-right:auto !important; font-family:"Courier New"; font-size:90%; } /* Styling for ‘t’able ‘d’ata and ‘h’eader elements */ th, td { border: 0px solid red; }
Prime | 2Prime |
---|---|
1 | 2 |
2 | 4 |
3 | 8 |
5 | 32 |
7 | 128 |
11 | 2048 |
;; Table captions should be below the tables (setq org-html-table-caption-above nil org-export-latex-table-caption-above nil)
Let's show folded, details, regions with a nice greenish colour
This is part of org-special-block-extras
, and it's something like this:
details { padding: 1em; background-color: #e5f5e5; /* background-color: pink; */ border-radius: 15px; color: hsl(157 75% 20%); font-size: 0.9em; box-shadow: 0.05em 0.1em 5px 0.01em #00000057; }
6. Ξ: Floating Table of Contents
I would like to have a table of contents that floats so that it is accessible to the reader in case they want to jump elsewhere in the document quickly —possibly going to the top of the document.
When we write #+toc: headlines 2
in our Org, HTML export produces the following.
1: <div id="table-of-contents"> 2: <h2>Table of Contents</h2> 3: <div id="text-table-of-contents"> 4: <ul> 5: <li> section 1 </li> 6: ⋮ 7: <li> section 𝓃 </li> 8: </ul> 9: </div> 10: </div>
Hence, we can style the table of contents by writing rules that target those
id
's. We use the following rules, adapted from the Worg community.
CSS for a floating TOC
1: /*TOC inspired by https://orgmode.org/worg/ */ 2: #table-of-contents { 3: /* Place the toc in the top right corner */ 4: position: fixed; right: 0em; top: 0em; 5: margin-top: 120px; /* offset from the top of the screen */ 6: 7: /* It shrinks and grows as necessary */ 8: padding: 0em !important; 9: width: auto !important; 10: min-width: auto !important; 11: 12: font-size: 10pt; 13: background: white; 14: line-height: 12pt; 15: text-align: right; 16: 17: box-shadow: 0 0 1em #777777; 18: -webkit-box-shadow: 0 0 1em #777777; 19: -moz-box-shadow: 0 0 1em #777777; 20: -webkit-border-bottom-left-radius: 5px; 21: -moz-border-radius-bottomleft: 5px; 22: 23: /* Ensure doesn't flow off the screen when expanded */ 24: max-height: 80%; 25: overflow: auto;} 26: 27: /* How big is the text “Table of Contents” and space around it */ 28: #table-of-contents h2 { 29: font-size: 13pt; 30: max-width: 9em; 31: border: 0; 32: font-weight: normal; 33: padding-left: 0.5em; 34: padding-right: 0.5em; 35: padding-top: 0.05em; 36: padding-bottom: 0.05em; } 37: 38: /* Intially have the TOC folded up; show it if the mouse hovers it */ 39: #table-of-contents #text-table-of-contents { 40: display: none; 41: text-align: left; } 42: 43: #table-of-contents:hover #text-table-of-contents { 44: display: block; 45: padding: 0.5em; 46: margin-top: -1.5em; }
Since the table of contents floats, the phrase Table of Contents is rather
‘in your face’, so let's use the more subtle Greek letter Ξ
.
1: (advice-add 'org-html--translate :before-until 'blog/display-toc-as-Ξ) 2: ;; (advice-remove 'org-html--translate 'display-toc-as-Ξ) 3: 4: (defun blog/display-toc-as-Ξ (phrase info) 5: (when (equal phrase "Table of Contents") 6: (s-collapse-whitespace 7: " <a href=\"javascript:window.scrollTo(0,0)\" 8: style=\"color: black !important; border-bottom: none !important;\" 9: class=\"tooltip\" 10: title=\"Go to the top of the page\"> 11: Ξ 12: </a> ")))
How did I get here?
- How does Org's HTML export TOCs? ⇒ org-html-toc
- Looking at its source, we see org-html--translate being the only place mentioning the string Table of Contents
- Let's advise it, with advice-add, to return “Ξ” only on that particular input string.
- Joy ♥‿♥
Finally,
;; I'd like to have tocs and numbered headings (setq org-export-with-toc t) (setq org-export-with-section-numbers t)
7. Clickable Sections with Sensible Anchors
7.1. Ensuring Useful HTML Anchors
Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks.
Default IDs are something like org1957a9d
, which does not endure the test of time:
Re-export will produce a different id. Here's a rough snippet to generate
IDs from headings, by replacing spaces with hyphens, for headings without IDs.
blog/ensure-useful-section-anchors: Advised to Org Export
(defun blog/ensure-useful-section-anchors (&rest _) "Org sections without an ID are given one based on its title. All non-alphanumeric characters are cleverly replaced with ‘-’. If multiple trees end-up with the same id property, issue a message and undo any property insertion thus far. E.g., ↯ We'll go on a ∀∃⇅ adventure ↦ We'll-go-on-a-adventure " (interactive) (let ((ids)) (org-map-entries (lambda () (org-with-point-at (point) (let ((id (org-entry-get nil "CUSTOM_ID"))) (unless id (thread-last (nth 4 (org-heading-components)) (s-replace-regexp "[^[:alnum:]']" "-") (s-replace-regexp "-+" "-") (s-chop-prefix "-") (s-chop-suffix "-") (setq id)) (if (not (member id ids)) (push id ids) (message-box "Oh no, a repeated id!\n\n\t%s" id) (undo) (setq quit-flag t)) (org-entry-put nil "CUSTOM_ID" id)))))))) ;; Whenever html & md export happens, ensure we have headline ids. (advice-add 'org-html-export-to-html :before 'blog/ensure-useful-section-anchors) (advice-add 'org-md-export-to-markdown :before 'blog/ensure-useful-section-anchors)
One may then use [[#my-custom-id]]
to link to the entry with CUSTOM_ID
property my-custom-id
.
Interestingly, org-set-property
, C-c C-x p
, lets us insert a property
from a selection of available ones, then we'll be prompted for a value
for it from a list of values you've used elsewhere. This is useful for
remaining consistent for when trees share similar properties.
7.2. Clickable Headlines
By default, HTML export generates ID's to headlines so they may be referenced to, but there is no convenient way to get at them to refer to a particular heading. The following spell fixes this issue: Headlines are now clickable, resulting in a link to the headline itself.
org-html-format-headline-function
;; Src: https://writepermission.com/org-blogging-clickable-headlines.html (setq org-html-format-headline-function (lambda (todo todo-type priority text tags info) "Format a headline with a link to itself." (let* ((headline (get-text-property 0 :parent text)) (id (or (org-element-property :CUSTOM_ID headline) (ignore-errors (org-export-get-reference headline info)) (org-element-property :ID headline))) (link (if id (format "<a href=\"#%s\">%s</a>" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info))))
Known Issues
Need to have a custom id declared.
:PROPERTIES: :CUSTOM_ID: my-header :END:
- Failing headers:
* [[link]]
nor* ~code~
nor* $math$
.- Any non-link text before it will work:
ok [[link]]
.- Using Unicode non-breaking space ‘ ’ is ok.
- Text only after the link is insufficient.
- Any non-link text before it will work:
Details on failing headers
Warning: The header cannot already be a link! Otherwise you get the cryptic and
unhelpful error (wrong-type-argument plistp :section-number)
; which then
pollutes the current Emacs session resulting in strange nil
errors after C-x
C-s
, thereby forcing a full Emacs restart. Instead, you need at least one
portion of each heading to be not a link.
8. MathJax Support — \(e^{i \cdot \pi} + 1 = 0\)
Org loads the MathJax display engine for mathematics whenever users write LaTeX-style math delimited by $...$
or by
\[...\]
. Here is an example.
Source
\[ p ⊓ q = p \quad ≡ \quad p ⊔ q = q \label{Golden-Rule}\tag{Golden-Rule}\] Look at \ref{Golden-Rule}, it says, when specialised to numbers, /the minimum of two items is the first precisely when the maximum of the two is the second/ ---d'uh!
Result
\[ p ⊓ q = p \quad ≡ \quad p ⊔ q = q \label{Golden-Rule}\tag{Golden-Rule}\]
Look at \ref{Golden-Rule}, it says, when specialised to numbers, the minimum of two items is the first precisely when the maximum of the two is the second —d'uh!
The script that Org loads by default
1: "<script type=\"text/x-mathjax-config\"> 2: MathJax.Hub.Config({ 3: displayAlign: \"center\", 4: displayIndent: \"0em\", 5: 6: \"HTML-CSS\": { scale: 100, 7: linebreaks: { automatic: \"false\" }, 8: webFont: \"TeX\" 9: }, 10: SVG: {scale: 100, 11: linebreaks: { automatic: \"false\" }, 12: font: \"TeX\"}, 13: NativeMML: {scale: 100}, 14: TeX: { equationNumbers: {autoNumber: \"AMS\"}, 15: MultLineWidth: \"85%\", 16: TagSide: \"right\", 17: TagIndent: \".8em\" 18: } 19: }); 20: </script> 21: <script type=\"text/javascript\" 22: src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_HTML\"></script> 23: "
8.1. Unicode Warning!
We can make an equation ℰ named 𝒩 and refer to it by ℒ by declaring \[ℰ \tag{𝒩}
\label{ℒ} \]
then refer to it with \ref{ℒ}
. However, if 𝒩 contains Unicode,
then the reference will not generally be ‘clickable’ —it wont take you to the
equation's declaration site. For example, \ref{⊑-Definition} (⊑-Definition
)
below has Unicode in both its tag and label, and so clicking that link wont go
anywhere, whereas \ref{Order-Definition} has Unicode only in its tag, with the
label being \label{Order-Definition}
, and clicking it takes you to the formula.
Source
\[ p ⊑ q \quad ≡ \quad p ⊓ q = p \tag{⊑-Definition}\label{⊑-Definition} \] \[ p ⊑ q \quad ≡ \quad p ⊔ q = q \tag{⊑-Definition}\label{Order-Definition} \]
Result
\[ p ⊑ q \quad ≡ \quad p ⊓ q = p \tag{⊑-Definition}\label{⊑-Definition} \]
\[ p ⊑ q \quad ≡ \quad p ⊔ q = q \tag{⊑-Definition}\label{Order-Definition} \]
8.2. Rule Resurrection
The following rule for anchors a {⋯}
resurrects \ref{}
calls via MathJax
—which org-notes-style
kills.
a { white-space: pre !important; }
8.3. Making Math Stick-out with Spacing!
Notice how the math expressions stick out in these following sentences:
- We use \(x\) as the name of the unknown.
- The phrase \(∀ x • ∃ y • x 〔R〕 y\) indicates that relation \(R\) is “total”.
Nice, the following adds extra whitespace around MathJax, so that math elements have extra whitespace about them so as to make them stand-out.
The script that Org loads by default
1: " 2: <script type=\"text/x-mathjax-config\"> 3: MathJax.Hub.Config({ 4: \"HTML-CSS\": { 5: styles: { 6: \".MathJax nobr\": { 7: padding: \"0.5em 0.5em\" 8: }, 9: } 10: } 11: }); 12: </script> 13: "
9. Arabic Font Setup
I'd like inline Arabic to be displayed using الخط الأمیری since that's how it looks within Emacs for me. But, Arabic within tables should be displayed in a more formal font, Scheherazade, that makes it really clear where letters start and end, and where the vowels above/below letters are positioned.
Details
(defun blog/css/arabic-font-setup () "Setup the CSS/HTML required to make my Arabic writing look nice. For a one-off use in an article, just place an “#+html:” in front of the result of this function." " <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Amiri'> <style> body {font-family: 'Amiri', sans-serif;} table {font-family: 'Scheherazade'; font-size: 105%; } </style>")
To understand why these styling rules work, see this website: Right-to-left Styling.
Source
For example, + Inline: اهلاً وسهلاً + Within a table: | اهلاً وسهلاً |
Result
For example,
- Inline: اهلاً وسهلاً
Within a table:
اهلاً وسهلاً
As the above left source demonstrates, unless some explicit action is taken, Arabic fonts are by default rendered hideously small.
10. Actually publishing an article
(cl-defun blog/git (cmd &rest args) "Execute git command CMD, which may have %s placeholders whose values are positional in ARGS." (shell-command (apply #'format (concat "cd ~/blog; git " cmd) args))) (cl-defun blog/publish-current-article () "Place HTML files in the right place, update index, rss, tags; git push!" (interactive) (blog/git "add %s" (buffer-file-name)) ;; Placed article html into the published blog directory (blog/preview) (save-buffer) (-let [article (concat (f-base (buffer-file-name)) ".html")] (shell-command (concat "mv " article " ~/blog/")) (blog/git "add %s %s" (buffer-file-name) article) ;; Make AlBasmala live with the other posts to avoid this conditional. (when (equal (f-base (buffer-file-name)) "AlBasmala") (blog/git "add AlBasmala.el"))) ;; Need to disable my export-preprocessing hooks. (blog/preview/disable) (view-echo-area-messages) (message "⇒ HTMLizing article...") (blog/htmlize-file (buffer-file-name)) (message "⇒ Assembling tags...") (blog/make-all-tag-pages) ;; TODO: I only need to update the tags pages relevant to the current article! ;; TODO: (message "⇒ Assembling RSS feed...") (org-static-blog-assemble-rss) (message "⇒ Assembling landing page...") (blog/make-index-page) (blog/git "add %s.org.html tag* rss.xml index.html" (f-base (buffer-file-name))) ;; TODO: If we're updating an existing article, prompt for a message. (blog/git "commit -m \"%s\"; git push" (if current-prefix-arg (read-string "Commit message: ") (format "Publish: Article %s.org" (f-base (buffer-file-name))))) (message "⇒ It may take up 20secs to 1minute for changes to be live at alhassy.com; congratulations!"))
11. The name: al-bas-mala
The prefix al is the Arabic definite particle which may correspond to English's the; whereas basmala refers to a beginning.
That is, this is a variation on the traditional "hello world" ;-)
Appendix: Using a Custom Domain: alhassy.com
Details
- Go to your repo: https://github.com/alhassy/alhassy.github.io/settings/pages
- Add a
Custom Domain
such as an “apex domain” likealhassy.com
(or a www domain likewww.alhassy.com
)- Apex domains require
A
records to be setup on your DNS provider. - www domains require
CNAME
records. - On my DNS provider, I setup both: That way, with or without
www.
, people will arrive at my blog.
- Apex domains require
Go to your DNS provider, and add two records
Type Name Priority Content TTL CNAME www 0 alhassy.github.io 14400 CNAME @ 0 alhassy.github.io 14400 - In a terminal, run:
dig www.alhassy.com +nostats +nocomments +nocmd
- The
www.
is intentional.
- The
You should see something like:
www.alhassy.com. 14400 IN CNAME alhassy.github.io. alhassy.github.io. 3600 IN A 185.199.111.153 alhassy.github.io. 3600 IN A 185.199.108.153 alhassy.github.io. 3600 IN A 185.199.109.153 alhassy.github.io. 3600 IN A 185.199.110.153
This says that it may take 3600 seconds, or 1hour, for the redirect of
alhassy.com
toalhassy.github.io
to be completed. It may take longer; keep reading.In your DNS provider, add 4 records, one for each IP Address you got from the
dig
command. (These addresses are also on the official Github docs).Type Name Priority Content TTL A @ 0 185.199.108.153 14400 A @ 0 185.199.109.153 14400 A @ 0 185.199.110.153 14400 A @ 0 185.199.111.153 14400 - Run
dig alhassy.com +noall +answer -t A
and ensure it points to these IP addresses.- Notice the lack of a
www.
- Notice the lack of a
- Open an incognito browser, private browsing session, and navigate to
alhassy.com
.- If this redirects to your
alhassy.github.io
blog, then joy!- If the redirect does not happen in your non-incognito browser, just clear your browsing history and try again.
- Otherwise, it may take some time (something like 1/2hour to 3 days) for the DNS propagation
to be completed.
- You can check the progress by using a service like this.
- It took me about 1/2hour for the URL to redirect to my github.io blog.
- If this redirects to your
Further resources:
Generated by Emacs and Org-mode (•̀ᴗ•́)و
Life & Computing Science by Musa Al-hassy is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License