# -*- eval: (org-babel-load-file "AlBasmala.org") -*-
#+title: AlBasmala: @@html:
@@ Blogging with Emacs & Org-mode (•̀ᴗ•́)و
#+description: How my blog is setup (•̀ᴗ•́)و
#+date: <2020-05-03 Sun>
#+filetags: emacs org html css javascript git lisp
#+fileimage: org_logo.png 150 150 no-border
#+property: header-args :tangle "~/blog/AlBasmala.el" :results silent :exports code :noeval
# Use checkmarks instead of boring bullet points
#+html_head:
* COMMENT Speculative TODO-s
:my-html-header__variable:
See [[the-html-header]] above
In addition, we have two more pieces we would like to add to the header: Support
for /dynamic/ code-line highlighting, §[[blog-banner]], and support for using
LaTeX-style notation to write mathematics, §[[MathJax-Support]]. We will use a
[[https://github.com/alhassy/emacs.d#what-does-literate-programming-look-like][noweb-ref]] named =my-html-header= to refer to them, which are then catenated below.
:end:
#+begin_remark "ლ(ಠ益ಠ)ლ"
I want to consistently use the same theme to htmlize Org source, rather than the /current session's/ theme.
#+end_remark
;; TODO: Consider a tag “draft”, such that articles with this tag are not considered
;; for publication? Or better, they are published and have a badge “Warning: Incomplete Draft”, after all I think most articles are drafts since I want to be able to continously update them, that's why I have the “history” badge.
;;
;; Use “#+filetags: τ₁ τ₂ … τₙ”
;; See (@tags ⋯) method.
;; TODO. (defun @tags (json) (s-split " " (map-elt json "tags"))) ;; TODO Have this be a list, in the actual json for future AngularJS search by tags
TODO: The sections that have been tangled from my init: Just move that code
here. That code was written with my blog in mind, and so it deserves to be here,
and not in my init.
TODO: Look at stuff in my init and see what there is specifically serving my
blog; and move that here. Can always link to AlBasmala.html.
# ;; (length blog/posts) ⇒ 16
# TODO: When I'm done, look around and ensure there are no 'TODO's; or if there are any, move them to my todo.org::Blog::Ideas section!
# ;; 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.
#
# In-particular, besides some extra code in blog/style-setup, by breaking consistency, AlBasmala does not produce tag
# pages /not/ contribute to them! This is probably a good enough reason to have it live alongside the other posts!
# TODO: Following method crashes unexpectedly when there is no #image; this
# should not be the case.
# (blog/preview)
# wrt org-link/blog:
# TODO: For the footer, we want ~blog:footer|src|history~ where ~src~ is Boolean to
# indicate generating the *.org.html file and ~history~ is the relevant repo.
#
# Low priority.
** font for links
#+html:
#+begin_remark
Before we move on, I'd like to have heavy red font for links.
# +begin_src css :tangle ~/blog/blog-banner.css :noeval -n :tangle no
HERE PLS
# +end_src
But this causes the table of contents to be red, which I dislike ლ(ಠ益ಠ)ლ
#+end_remark
** wrt floating toc:
#+begin_remark Strange
If I zoom in over 100% in my browser, the toc disappears until I zoom out.
#+end_remark
(org-export-string-as
"*blog/new-article*"
'html
:body-only)
** COMMENT TODO: Make the floating toc “Ξ” be aware of links...
... so that “doc:blog/new-article” renders nicely in the floating doc!
** TODO COMMENT Add dates, and sort by them.
** TODO COMMENT avoid being asked for unicode encodings
(cl-defun select-safe-coding-system-interactively (&rest args) (setq last-coding-system-specified 'utf-8))
Related:
- https://github.com/abingham/emacs-ycmd/issues/496
- https://stackoverflow.com/questions/63644928/emacs-failed-quit-with-error-of-utf-8-cannot-encode
** TODO Misc :noexport:
:PROPERTIES:
:CUSTOM_ID: Misc
:END:
TODO: Add #+date when publishing, otherwise order's are all wonky.
TODO:
Find-replace all px with percentages, then ensure things look good!
*** Magit and large HTML files
:PROPERTIES:
:CUSTOM_ID: Magit-and-large-HTML-files
:END:
Since I'm producing /massive/ HTML files for each post, trying to review changes with the default Magit buffer /which shows
all changes in the repository/ can be painfully slow. As such, when I'm blogging, let's change my ~C-x g~ binding from
doc:magit-status to doc:magit-file-dispatch, which just narrow's Magit's view to the file I'm currently working with:
~C-x g D u~ to see the “u”nstaged “d”ifferences for the file, then stage what I like with ~s~, then ~C-x g c~ to “c”ommit whatever differences I have staged.
#+begin_src emacs-lisp
(bind-key "C-x g" #'magit-file-dispatch)
#+end_src
*** Column Width
:PROPERTIES:
:CUSTOM_ID: Column-Width
:END:
When blogging, I see live HTML previews whenever I save thanks to
doc:blog/preview. As such, I cannot have my lines being too wide. I'll settle
for a modest 80 columns. Then kbd:M-q will format my paragraphs to wrap nicely
into this 80-column width.
#+begin_src emacs-lisp
(setq-default fill-column 80)
#+end_src
(When I'm coding with a single open window, I usually use a comfortable 120 columns.)
*** Required loads
:PROPERTIES:
:CUSTOM_ID: Clean-this-section-up
:END:
#+begin_src emacs-lisp :exports none
;; cl-lib was published as a better (namespaced!) alternative to cl, which has a deprecation warning in Emacs27.
;; Yet some old pacakges require cl, and so the below setq silences the deprecation warning.
(setq byte-compile-warnings '(cl-functions))
(require 'cl-lib) ;; to get loop instead of cl-loop, etc.
(require 'shortdoc) ;; Essentially "tldr" but for Emacs Lisp!
;; (cl-defun define-short-documentation-group (&rest _))
;; (cl-defun org-duration-to-minutes (&rest _) )
;; (cl-defun org-id-find-id-file (&rest _))
(ignore-errors
(use-package org-preview-html)
(setq org-preview-html-viewer 'xwidget)
;; (xwidget-webkit-browse-url "https://github.com/adithyaov/helm-org-static-blog")
(advice-add #'xwidget-webkit-browse-url :before (lambda (&rest _) (doom-modeline-mode 0)))
(advice-add #'xwidget-webkit-browse-url :after (lambda (&rest _) (--map (with-current-buffer it (setq mode-line-format "%b %p L%l C%c")) (buffer-list)) ))
(advice-add #'doom-modeline-mode :before (lambda (&rest _) (-let [kill-buffer-query-functions nil]
(mapcar #'kill-buffer (--filter (s-starts-with? "*xwidget" (buffer-name it)) (buffer-list)))
)))
(use-package org-special-block-extras)
;;(load-file "~/blog/AlBasmala.el")
) ;; M-x blog/preview
;; Required for Github Actions; i.e., testing.
;; TODO Clean me!
(defun quelpa-read-cache ()) ;; Used somewhere, but not defined.
;; See: quelpa-persistent-cache-file
(setq quelpa-cache nil)
;; Eager macro-expansion failure: (void-function all-the-icons-faicon)
;; Symbol’s function definition is void: all-the-icons-faicon
#+end_src
#+begin_src emacs-lisp :exports none
;; Error in kill-emacs-hook (org-clock-save): (void-function org-clocking-buffer)
(cl-defun org-clocking-buffer (&rest _))
#+end_src
*** COMMENT Arabic Setup :Possibly_already_in_AlBasmala_so_just_load_that:
:PROPERTIES:
:CUSTOM_ID: COMMENT-Arabic-Setup
:END:
#+begin_src emacs-lisp
(bind-key "M-x" #'execute-extended-command)
(set-fontset-font "fontset-default" '(#x600 . #x6ff) "Amiri Quran Colored") ;; Makes all dots, hamza, diadiract marks colored!
;; Note: "arabic" input method just changes my English query keyboard into an Arabic keyboard ---useful if one's mastered touch typing in Arabic!
;; In contrast, the Perso-Arabic input method uses a system of transliteration: Ascii keys are phonetically mapped to Arabic letters.
(bind-key* "M-SPC" (lambda () (interactive)
(message (if (not current-input-method)
(progn (set-input-method "farsi-transliterate-banan" t) "Perso-Arabic! Hint: M-x describe-input-method")
(progn (set-input-method nil) "English!")))))
;; Press C-q on a word to quote it with nice unicode quotes.
(bind-key* "C-q" (lambda () (interactive) (insert (format "“%s”" (thing-at-point 'word))) (kill-word 1)))
#+end_src
**** COMMENT [HERE TODAY] Rndm
:PROPERTIES:
:CUSTOM_ID: COMMENT-HERE-TODAY-Rndm
:END:
;; After startup, if Emacs is idle for 10 seconds, then open my work file;
;; which is a GPG file and so requires passphrase before other things can load.
;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg")))
;; NOTE: Will not work when doom-modeline is enabled!
;; (xwidget-webkit-browse-url "https://www.reddit.com/r/emacs/")
;; Press C-q on a word to quote it with nice unicode quotes.
(bind-key* "C-q" (lambda () (interactive) (insert (format "“%s”" (thing-at-point 'word))) (kill-word 1)))
(load-file "~/blog/AlBasmala.el")
#+begin_src emacs-lisp :exports code
(org-deflink card
"Show one of 6 hardcoded phrases as a small inline image."
(-let [url
(pcase o-label
("Let's take a break" "https://i0.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-19.png")
("Yes" "https://i1.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-20.png")
("No" "https://i0.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-21.png")
("Agree" "https://i0.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-22.png")
("Disagree" "https://i1.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-23.png")
("I have a question" "https://i1.wp.com/oerabic.llc.ed.ac.uk/wp-content/uploads/2020/09/Visual-Communication-Signs-IRAQI-35.png"))]
(format "" url o-label url)))
#+end_src
** COMMENT Ideas
:PROPERTIES:
:CUSTOM_ID: COMMENT-todo
:END:
# TODO: Automate the insertation of HTML Premable/postable for statics like about/AlBasmala
# (maybe-clone "https://github.com/alhassy/alhassy.github.io.git" "~/blog")
# teal:TODO: Make C-u C-c C-b switch to a particular theme so all exported html stuffs
# look the same ^_^
# Idea: Make the tags at the bottom be badges, alter/advice the corresponding
# function
# Idea: Add “last updated” date to footer?
+ in the index, under each article's name:
- twitter link ;-)
- per article via advice
#+begin_export html
#+end_export
+ Footer should include
- See Org Source; see HTML source
- buy-me-a-coffee
#+html:
The [[file:template.org][example]] source, #+HTML: # TODO. ? +INCLUDE: "template.org" src org #+HTML: | #+HTML: Results in, #+BEGIN_EXPORT html #+END_EXPORT #+HTML: |
#+BEGIN_EXPORT html
block; COLOR is an optional argument indicating the text colour of the resulting block." (insert "\n#+html:" (format "" color)) (org-next-visible-heading 1) (insert "#+html:")) #+end_src See http://alhassy.com/AlBasmala#deftag for the definition of =org-deftag=. *** Bye! :ignore: /Have a great evening!/ @@html: @@ ** Known bug and proposed fix. :noexport: Warning! #+begin_src org :tangle no ,* Useful notes :noexport: Be a good person. ,* Advice to readers :details: Since this section is rewritten as ablock, it will, by structure, reside as an element in the previous tree which is not exported! This this tree wont be visible! #+end_src TODO: Fix the above bug by ensuring all altered headings are preserved as follows. #+begin_src org :tangle no ,* original heading :FOO: becomes ,* original heading :ignore: ⦃transformed heading⦄ #+end_src This way we ensure that the new tree is completely independent of the previous tree. *** COMMENT Posterity :Delete_when_things_settle: :PROPERTIES: :CUSTOM_ID: COMMENT-Posterity :END: #+BEGIN_SRC emacs-lisp :export none (defun my-headline-alteration (backend) "BACKEND is the export back-end being used, as a symbol." (setq first-heading t) (outline-show-all) (org-map-entries (lambda () (kill-line) ;; (thing-at-point 'line) (let ((heading (car kill-ring))) (if (not (s-contains? ":NOPE:" heading 'ignoring-case)) (insert heading) (insert heading) (insert "NIIIIICE") ;; (if first-heading (setq first-heading nil) ;; (insert "+latex:\\end\{alertblock\}")) ;; (insert "\n\n+latex: \\begin\{alertblock\}\{") (org-next-visible-heading 1) (insert "WOW :: " (s-chop-prefix "\** " heading)) (insert "\n") ;; Otherwise we impede on the auto-inserted “* footer :ignore:” ;; (insert "\}") ) ) ))) (add-hook 'org-export-before-parsing-hook 'my-headline-alteration) (remove-hook 'org-export-before-parsing-hook 'my-headline-alteration) #+END_SRC (org-next-visible-heading 1) * TODO COMMENT Glossary Library of Arabic Linguistic Jargon :PROPERTIES: :CUSTOM_ID: COMMENT-Glossary-Library-of-Arabic-Linguistic-Jargon :END: #+begin_src emacs-lisp ;; See: http://alhassy.com/org-special-block-extras/#Tooltips-for-Glossaries-Dictionaries-and-Documentation ;; My personal documentation library can be seen [[https://alhassy.github.io/org-special-block-extras/documentation.html][here]] (push "~/blog/posts/arabic-glossary.org" org-docs-libraries) #+end_src * COMMENT Presentation Order :PROPERTIES: :CUSTOM_ID: COMMENT-Presentation-Order :END: We start off with 1. the styling I'd like to have, 2. then move on to grouping those styles togther, 3. then making that result practical to use anywhere via a new Org-mode link, namely doc:blog, 4. then doing this blog-link injection seemlessly/automatically. 5. Wrapping all of this up into a nice "article preview" background function. 6. Finally, hooking this stuff up into the org-static-blog setup. - Which I use since it provides me with a nice index.html to showcase my posts, a tagging mechanism, an RSS mechanism, etc. * COMMENT Preview :move_to_init:Mention_in_AlBasmala: :PROPERTIES: :CUSTOM_ID: COMMENT-Preview :END: #+begin_src emacs-lisp ;; Nearly instanteous preview: Just Save and it'll rebuild, and pop-up the preview to the side! (setq org-preview-html-viewer 'xwidget) (org-preview-html-mode) ;; TODO. Need to advise this to turn off doom-modeline first! ;; Why? Since xwidget does not work well with doom-modeline. ;; TODO. Something in my init.el breaks this package!?! ;; ;; Mention this in my AlBasmala.org as a way to preview my articles ^_^ ;; Turn off doom-modeline when previewing. ;; (advice-add #'org-preview-html-mode :before (lambda (&rest _) (doom-modeline-mode 0))) ;; ;; The following suffices. ;; (xwidget-webkit-browse-url "https://github.com/adithyaov/helm-org-static-blog") (advice-add #'xwidget-webkit-browse-url :before (lambda (&rest _) (doom-modeline-mode 0))) ;; Make the modeline minimal, otherwise it's super ugly. (advice-add #'xwidget-webkit-browse-url :after (lambda (&rest _) (--map (with-current-buffer it (setq mode-line-format "%b %p L%l C%c")) (buffer-list)) )) ;; ;; Make an Issue on both doom-modeline github and on org-preview-html Github. Link these issues to each other. ;; Maybe instead make a README PRs that have the above advice-add clause; I think that might be best ---along with MWE init.el in the PR description to justify these additions. ;; ;; Along with a MWE init.el to substanitate my claim. ;; Conversely, when we start doom-modeline let's ensure we have no xwidget buffer lying around, otherwise Emacs will hang. (advice-add #'doom-modeline-mode :before (lambda (&rest _) (-let [kill-buffer-query-functions nil] (mapcar #'kill-buffer (--filter (s-starts-with? "*xwidget" (buffer-name it)) (buffer-list))) ))) #+end_src * Typical workflow: How do I publish an article? :PROPERTIES: :CUSTOM_ID: COMMENT-Typical-workflow-How-do-I-publish-an-article :END: # TODO. ? +include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode 1. Open an Org-mode buffer ---or invoke doc:blog/new-article. 2. src_emacs-lisp[:exports code]{(org-babel-load-file "~/blog/AlBasmala.org")} 3. Invoke doc:blog/preview to get live WYSIWYG in an adjacent buffer after every save kbd:C-x_C-s. 4. Until content: 1. Write, write, and write! 2. kbd:C-x_C-s 3. Preview 🤔 Consider using kbd:C-x_n_s, or kbd:C-x_n_n, to focus your attention on a particular section, /thereby/ dramatically increasing the speed at which the preview renders. 5. Execute doc: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. * Why not use an existing blogging platform? :PROPERTIES: :CUSTOM_ID: Why-not-use-an-existing-blogging-platform :END: 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 kbd: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 [[https://alhassy.com/arabic-roots.html][root-meaning program to learn Arabic]] and to have a [[https://alhassy.com/cartoon][tiny web-app for Arabic shows for my kids]]. * “Goal-driven development” ---or, Getting Started: doc:blog/new-article :PROPERTIES: :CUSTOM_ID: blog-new-article :END: <> < > Here's what an example article source looks like: #+begin_example org #+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. #+end_example 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. #+html: #+begin_src emacs-lisp :tangle no :results replace value :wrap example js :exports results (f-read-text "~/blog/posts.json") #+end_src #+RESULTS: #+begin_example js [ { "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:<
@@\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\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:
@@\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:
@@\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" } ] #+end_example #+html:> 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 the =Abstract= 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 :details: #+begin_src emacs-lisp -n :exports code (use-package org-static-blog) (use-package lf) ;; So we can use `lf-string' for multi-line strings supporting interpolation: ;; (lf-string "100/2 is ${ (/ 100 2) }; neato!") ;; ⇒ "100/2 is 50; neato!" #+end_src ** Basic facts (global variables) of my blog :details: #+begin_src emacs-lisp :exports code (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'.") #+end_src ** blog/new-article: Helper function to make a new article :details: #+BEGIN_SRC emacs-lisp -n (defun blog/new-article () "Make a new article for my blog; prompting for the necessary ingredients. If the filename entered already exists, we simply write to it. The user notices this and picks a new name. This sets up a new article based on existing tags and posts. + Use C-SPC to select multiple tag items Moreover it also enables `org-preview-html-mode' so that on every alteration, followed by a save, C-x C-s, will result in a live preview of the blog article, nearly instantaneously." (interactive) (let (file desc) (thread-last blog/posts-directory f-entries (mapcar #'f-filename) (completing-read "Filename (Above are existing): ") (concat blog/posts-directory) (setq file)) ;; For some reason, ‘find-file’ in the thread above ;; wont let the completing-read display the possible completions. (find-file file) (insert "#+title: " (read-string "Title: ") "\n#+author: " user-full-name "\n#+email: " user-mail-address ;; "\n#+date: " (format-time-string "<%Y-%m-%d %H:%M>") "\n#+filetags: " (s-join " " (helm-comp-read "Tags: " blog/tags :marked-candidates t)) "\n#+fileimage: emacs-birthday-present.png" ;; "\n#+fileimage: " (completing-read ;; "Image: " ;; (mapcar #'f-filename (f-entries "~/blog/images/"))) ;; "\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!) "\n#+description: " (setq desc (read-string "Article Purpose: ")) "\n\n* Abstract :ignore: \n" desc "\n\n* ???") (save-buffer) (blog/preview))) #+END_SRC ** blog/create-posts-json-file :details: #+begin_src emacs-lisp -n (defun blog/create-posts-json-file () "Create cache info about posts." (interactive) (require 'json) (cl-loop for file in (f-files "~/blog/posts") when (s-ends-with? ".org" file) collect (blog/info file) into posts finally ;; Sorted in descending time; i.e., the latest article should be first (setq posts (sort posts (lambda (newer older) (time-less-p (date-to-time (@date older)) (date-to-time (@date newer)))))) (f-write-text (json-encode posts) 'utf-8 (f-expand "~/blog/posts.json")) (find-file "~/blog/posts.json") (json-pretty-print-buffer) (write-file "~/blog/posts.json"))) (defvar blog/posts (with-temp-buffer (insert-file-contents "~/blog/posts.json") (json-parse-buffer)) "Load cached info about posts") (defvar blog/tags (sort (seq-uniq (-flatten (seq-map (lambda (it) (s-split " " (map-elt it "tags"))) blog/posts))) #'string<) "Tags for my blog articles.") #+end_src ** Convenient accessor methods: Given a JSON hashmap, get the specified key values :details: 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. #+begin_src emacs-lisp ;; 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")) #+end_src ** @history: Get an HTML badge that points to the Github history of a given file name, in my blog :details: #+begin_src emacs-lisp (defun @history (json) "Get an HTML badge that points to the Github history of a given file name, in my blog." (concat "")) #+end_src ** @tags: Get an HTML listing of tags, as shields.io bages, associated with the given file :details: #+begin_src emacs-lisp (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. ;; " " ;; (org-static-blog-post-taglist file-name) ;; "" ;; Badges implementation (concat (format " %s " (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"))))))) #+end_src ** @image :details: <> 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= :-) #+html: #+begin_src emacs-lisp (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 " " href title src no-border? width height))))) #+end_src ** blog/info: Core helper to get the plist/JSON metadata about each post :details: #+begin_src emacs-lisp (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)))))))))) #+end_src ** The #+begin_abstract
is an Org-mode Special Block :details_orange: 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 §[[new-article]] for a template. Below is an alteration from the examples of the docstring of doc:org-defblock. #+begin_src emacs-lisp (org-defblock abstract (main) nil "Render a block in a slightly narrowed blueish box, titled \"Abstract\". Supported backends: HTML. " (format (concat "" "") contents)) #+end_src #+html:Abstract " "%s★ ★ ★ For example, the source: #+begin_example org ,#+begin_abstract In this article, we learn to have fun! ,#+end_abstract #+end_example Results in: #+begin_abstract In this article, we learn to have fun! #+end_abstract ** Generating the Index Page :details: <> The actual look and feel of ~index.html~ is due to the method doc:blog/make-index-page. It summarises all of my articles by their title, data & image, ‘abstract’, and a read-more badge. #+begin_src emacs-lisp -n (cl-defun blog/make-index-page () "Assemble the blog index page. The index page contains blurbs of all of my articles. Precondition: `blog/posts' refers to all posts, in reverse chronological order. You can view the generated ~/blog/index.html by invoking: (blog/make-index-page) " (interactive) (blog/make-tags-page :export-file-name "~/blog/index.html")) #+end_src ** blog/make-tags-page & blog/make-all-tag-pages: Generating “tag-𝑻.html” pages :details: #+begin_src emacs-lisp (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. #+end_src where #+begin_src emacs-lisp (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:
@@" (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. ;; "" org-static-blog-page-preamble org-static-blog-page-header "#+end_export" (concat "#+html:" title " ") ;; TODO: Delete the following comment when things work and are done. ;; Extra styling of abstracts. ;; Works; but not needed. ;; "\n#+HTML_HEAD_EXTRA: " ;; The greeting message that informs viewers what this page is about. "#+html:
" greeting "#+html:
" ;; 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:%s
" (@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%s\n \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:@@" " badge:Read|more|green|%s|read-the-docs @@html:
@@") (@url it)))) (seq--into-list blog/posts))) ;; “Show older posts” ;; This is the bottom-most matter in the index.html page "#+begin_export html" "
Thanks for reading everything! 😁 Bye! 👋
" (blog/license) ;; TODO: Add a “proudly created with Emacs’ Org-mode” tagline? "\n#+end_export" ))) (org-mode) (org-html-export-to-html))) #+end_src * /Seamlessly/ Previewing Articles /within/ Emacs 😲 :PROPERTIES: :CUSTOM_ID: Seamlessly-Previewing-Articles-within-Emacs :END: Whenever I /save/, kbd: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 :details: 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 doc:org-deflink. #+begin_src emacs-lisp :exports code (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 "" "" "" ;; 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 "")))) ("footer" (blog/footer (buffer-file-name))) ("sanitise-title" "") (_ ""))) #+end_src See also: [[http://alhassy.com/org-special-block-extras/#Links][How do I make a new Org link type?]] ** COMMENT blog/posts FUNCTION #+begin_src emacs-lisp (defun blog/posts (file-name) "Retrieve the JSON cache from `blog/posts' regarding FILE-NAME. Example usage: (blog/posts \"java-cheat-sheet\") " (seq-filter (lambda (it) (equal (map-elt it "file") file-name)) blog/posts)) #+end_src ** blog/style-setup: A function to insert org-link/blog into a buffer :details: #+begin_src emacs-lisp (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: " " %s
" (@tags post) " " "\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")))) #+end_src ** Inserting org-link/blog seamlessly via the export process; then preview with every save :details: #+begin_src emacs-lisp (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)) #+end_src Upon a save, kbd: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 kbd:C-c_C-e_h_o. ** Article Footer: HTMLized Source and Git History :details: :PROPERTIES: :CUSTOM_ID: Article-Footers :END: <> #+begin_src emacs-lisp -n (defun blog/footer (post-file-name) "Returns the HTML rendering the htmlised source, version history, and comment box at the end of a post. This function is called for every post and the returned string is appended to the post body, as a postamble." (let ((post (blog/info (buffer-file-name)))) (concat "
" "" (blog/htmlize-file post-file-name) " " (@history post) ;; ;; Consider only add this to posts tagged “arabic”? (blog/css/arabic-font-setup) ;; " " ;; The next line is required to make the org-static-blog-assemble-rss method work. "
" "" "" ;; "
Generated by Emacs and Org-mode (•̀ᴗ•́)و " (blog/license) ;; (blog/comments) ;; TODO. Not working as intended; low priority. "" (blog/read-remaining-js)))) #+end_src ** blog/htmlize-file: Generate an htmlized version of a given source file; return an HTML badge linking to the colourised file :details: #+begin_src emacs-lisp (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: \n") (org-mode) (outline-show-all) (switch-to-buffer (htmlize-buffer)) (write-file (concat "~/blog/" (f-base file-name) ".org.html")) (kill-buffer)))) (concat "")) #+end_src ** blog/license: HTML for Creative Commons Attribution-ShareAlike 3.0 Unported License :details: <> #+begin_src emacs-lisp (defun blog/license () "Get HTML for Creative Commons Attribution-ShareAlike 3.0 Unported License." (s-collapse-whitespace (s-replace "\n" "" " "))) #+end_src ** blog/comments: Embed Disqus Comments for my blog :details: #+begin_src emacs-lisp -n (defun blog/comments () "Embed Disqus Comments for my blog" (s-collapse-whitespace (s-replace "\n" "" " comments powered by Disqus"))) #+end_src ** blog/read-remaining-js: HTML to use ReadRemaining.js :details: #+begin_src emacs-lisp (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. ;; "" " ")) #+end_src [[https://aerolab.github.io/readremaining.js/][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 ==, otherwise it wont work for me. - It may be best to avoid loading jQuery multiple times; see [[https://alhassy.github.io/org-special-block-extras/#tooltipster][here]] for the necessary conditional. * /Style/! ✨ What do we want to be inserted into the head of every page? :PROPERTIES: :CUSTOM_ID: HTML-Header :END: <
Life & Computing Science by Musa Al-hassy is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License> For each article, I'll have a set of styles loaded /as well as/ a set of ~" #+end_src #+end_details ** Miscellaneous Styles ** Curvy Source Blocks & Pink Inline :details: < > The =border-radius= property defines the radius of an element's corners, we use it to make curvy looking source blocks. Its behaviour [[https://www.w3schools.com/cssref/css3_pr_border-radius.asp][changes]] depending on how many arguments it is given. - We also style the code block's label to be curvy. - Both =.src= and =pre.src:before= are used by Org. #+begin_src css -r -n :tangle ~/blog/blog-banner.css :noeval -n .src { border: 0px !important; /* 50px for top-left and bottom-right corners; 20px for top-right and bottom-left cornerns. */ border-radius: 50px 20px !important; } pre.src:before { /* border: 0px !important; */ /* background-color: inherit !important; */ padding: 3px !important; border-radius: 20px 50px !important; font-weight:700 } /* wrap lengthy lines for code blocks */ pre{white-space:pre-wrap} /* Also curvy inline code with ~ ⋯ ~ and = ⋯ = */ code { /* background: Cyan !important; */ background: pink !important; border-radius: 7px; /* border: 1px solid lightgrey; background: #FFFFE9; padding: 2px */ } #+end_src Code such as ~(= 2 (+ 1 1))~ now sticks out with a pink background ♥‿♥ ** Pink Tables :details: #+begin_src css :tangle ~/blog/blog-banner.css :noeval -n 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; } #+end_src #+caption: Example table | Prime | 2^{Prime} | |-------+-----------| | | | | 1 | 2 | | 2 | 4 | | 3 | 8 | | 5 | 32 | | 7 | 128 | | 11 | 2048 | #+TBLFM: $2='(expt 2 $1);N # For the line wrapping, it may be useful to have # =#+PROPERTY: header-args -n= at the top of the file # to have all blocks displayed with line numbers. #+begin_src emacs-lisp ;; Table captions should be below the tables (setq org-html-table-caption-above nil org-export-latex-table-caption-above nil) #+end_src ** Let's show folded, details, regions with a nice greenish colour :details: This is part of =org-special-block-extras=, and it's something like this: #+begin_src css :tangle ~/blog/blog-banner.css :noeval -n 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; } #+end_src * Ξ: Floating /Table of Contents/ :PROPERTIES: :CUSTOM_ID: Floating-TOC :END: < > 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. #+html: ★ ★ ★
When we write =#+toc: headlines 2= in our Org, HTML export produces the following. #+begin_src html -n :exports code :tangle no :noeval#+end_src Hence, we can style the table of contents by writing rules that target those =id='s. We use the following rules, adapted from [[https://orgmode.org/worg/][the Worg community]]. #+begin_details "CSS for a floating TOC" #+begin_src css -n :tangle ~/blog/floating-toc.css :noeval /*TOC inspired by https://orgmode.org/worg/ */ #table-of-contents { /* Place the toc in the top right corner */ position: fixed; right: 0em; top: 0em; margin-top: 120px; /* offset from the top of the screen */ /* It shrinks and grows as necessary */ padding: 0em !important; width: auto !important; min-width: auto !important; font-size: 10pt; background: white; line-height: 12pt; text-align: right; box-shadow: 0 0 1em #777777; -webkit-box-shadow: 0 0 1em #777777; -moz-box-shadow: 0 0 1em #777777; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; /* Ensure doesn't flow off the screen when expanded */ max-height: 80%; overflow: auto;} /* How big is the text “Table of Contents” and space around it */ #table-of-contents h2 { font-size: 13pt; max-width: 9em; border: 0; font-weight: normal; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; } /* Intially have the TOC folded up; show it if the mouse hovers it */ #table-of-contents #text-table-of-contents { display: none; text-align: left; } #table-of-contents:hover #text-table-of-contents { display: block; padding: 0.5em; margin-top: -1.5em; } #+end_src #+end_details # /* TOC entries, unnumbered lists, should not be indented too much */ # #text-table-of-contents ul { padding-left: 20px } Since the table of contents floats, the phrase /Table of Contents/ is rather ‘in your face’, so let's use the more subtle Greek [[https://en.wikipedia.org/wiki/Xi_(letter)][letter]] =Ξ=. #+begin_src emacs-lisp -n (advice-add 'org-html--translate :before-until 'blog/display-toc-as-Ξ) ;; (advice-remove 'org-html--translate 'display-toc-as-Ξ) (defun blog/display-toc-as-Ξ (phrase info) (when (equal phrase "Table of Contents") (s-collapse-whitespace " Ξ "))) #+end_src How did I get here? 1. How does Org's HTML export TOCs? ⇒ doc:org-html-toc 2. Looking at its source, we see doc:org-html--translate being the only place mentioning the string /Table of Contents/ 3. Let's advise it, with doc:advice-add, to return “Ξ” /only/ on that particular input string. 4. Joy ♥‿♥ # ( The Unicode whitespace ‘ ’ before and after =Ξ= is to appease the [[clickable-headlines][clickable headlines utility]], below. ) Finally, #+begin_src emacs-lisp :exports code ;; I'd like to have tocs and numbered headings (setq org-export-with-toc t) (setq org-export-with-section-numbers t) #+end_src * Clickable Sections with Sensible Anchors ** Ensuring Useful HTML Anchors :PROPERTIES: :CUSTOM_ID: Ensuring-Useful-HTML-Anchors :END: 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. #+begin_details "blog/ensure-useful-section-anchors: Advised to Org Export" #+BEGIN_SRC emacs-lisp (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) #+END_SRC #+end_details 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. ** Clickable Headlines :PROPERTIES: :CUSTOM_ID: Clickable-Headlines :END: 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. #+begin_details org-html-format-headline-function #+begin_src emacs-lisp ;; 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 "%s" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info)))) #+end_src #+end_details #+begin_box "Known Issues" :background-color cyan 1. Need to have a custom id declared. #+BEGIN_SRC org :tangle no :PROPERTIES: :CUSTOM_ID: my-header :END: #+END_SRC 2. 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. #+begin_details 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. #+end_details #+end_box * MathJax Support --- $e^{i \cdot \pi} + 1 = 0$ :PROPERTIES: :CUSTOM_ID: MathJax-Support :END: <Table of Contents
- section 1
⋮- section 𝓃
> Org loads the [[https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference][MathJax]] display engine for mathematics whenever users write LaTeX-style math delimited by ~$...$~ or by =\[...\]=. Here is an example. #+begin_org-demo \[ 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! #+end_org-demo #+html:
#+begin_details "The script that Org loads by default" #+begin_src emacs-lisp -n :noweb-ref my-html-header :tangle no " " #+end_src #+end_details ** Unicode Warning! :PROPERTIES: :CUSTOM_ID: Unicode-Warning :END: *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. #+begin_org-demo \[ p ⊑ q \quad ≡ \quad p ⊓ q = p \tag{⊑-Definition}\label{⊑-Definition} \] \[ p ⊑ q \quad ≡ \quad p ⊔ q = q \tag{⊑-Definition}\label{Order-Definition} \] #+end_org-demo ** Rule Resurrection :PROPERTIES: :CUSTOM_ID: Rule-Resurrection :END: The following rule for anchors =a {⋯}= resurrects =\ref{}= calls via MathJax ---which =org-notes-style= kills. #+begin_src css :tangle ~/blog/blog-banner.css :noeval a { white-space: pre !important; } #+end_src ** /Making Math Stick-out with Spacing!/ :PROPERTIES: :CUSTOM_ID: COMMENT-nice-math-spacing :END: Notice how the /math expressions/ stick out in these following sentences: 1. We use $x$ as the name of the unknown. 2. 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. #+HTML_MATHJAX: padding: 25px 25px #+begin_details "The script that Org loads by default" #+begin_src emacs-lisp -n :noweb-ref my-html-header :tangle no " " #+end_src #+end_details ** COMMENT Example calculation from that MathJax Setup? :PROPERTIES: :CUSTOM_ID: COMMENT-Example-calculation-from-that-MathJax-Setup :END: Maybe move the MathJax setup into AlBasmala directly, then include it? Or, maybe incorporate the MathJax setup via Emacs directly ♥‿♥ Such as org-html-head-extra \begin{calc} x \;⊓\; ¬ x \quad=\quad ⊥ \step{ \ref{⊑-antisymmetric} } (x \;⊓\; ¬ x) \sqleqs ⊥ \landS ⊥ \sqleqs (x \;⊓\; ¬ x) \step{ \ref{Bottom Element} } x \;⊓\; ¬ x \sqleqS ⊥ \step{ \ref{Modus Ponens} } \mathsf{true} \end{calc} Then, \[\eqn{Constructive De Morgan}{¬(x \;⊔\; y) \quad=\quad ¬ x \;⊓\; ¬ y}\] * Arabic Font Setup :PROPERTIES: :CUSTOM_ID: Arabic-Font-Setup :END: I'd like /inline/ Arabic to be displayed using [[https://www.amirifont.org/][الخط الأمیری]] since that's [[http://alhassy.com/arabic-roots#Arabic-Input-Setup][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. #+begin_details #+begin_src emacs-lisp (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." " ") #+end_src To understand /why/ these styling rules work, see this website: [[https://rtlstyling.com/posts/rtl-styling#handling-fonts][Right-to-left Styling]]. #+end_details #+html:
#+begin_org-demo :source-color white :result-color white For example, + Inline: اهلاً وسهلاً + Within a table: | اهلاً وسهلاً | #+end_org-demo # Yuck! + Inline code: =اهلاً وسهلاً= As the above left source demonstrates, unless some explicit action is taken, Arabic fonts are by default rendered hideously small. * Actually publishing an article :PROPERTIES: :CUSTOM_ID: Actually-publishing-an-article :END: # My preview setup does a great job at "publishing" my articles, as such, it seems I don't really need use org-static-blog-publish-file. # (I would need org-static-blog-publish-file if I were to republish the entire blog, say via org-static-blog-publish) #+begin_src emacs-lisp (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!")) #+end_src * COMMENT Publishing with =[C-u C-u] C-c C-b= :PROPERTIES: :CUSTOM_ID: Publishing-with-C-u-C-u-C-c-C-b :END: :Hide_fornow: #+begin_src emacs-lisp -n :tangle no ;; No lock files, for now ;; The “.#file” files ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Interlocking.html#Interlocking (setq create-lockfiles nil) #+end_src :End: #+begin_src emacs-lisp -r -n ;; Override all minor modes that use this binding. (bind-key* (kbd "C-c C-b") (lambda (&optional prefix) "C-c C-b ⇒ Publish current buffer C-u C-c C-b ⇒ Publish entire blog C-u C-u C-c C-b ⇒ Publish entire blog; re-rendering all blog posts (This will take time!) " (interactive "P") (pcase (or (car prefix) 0) (0 (org-static-blog-publish-file (f-full (buffer-file-name))) (browse-url-of-file (format "file://%s%s.html" (file-truename blog/publish-directory) (f-base (buffer-file-name))))) ;; Apparently I have to publish the current buffer before trying ;; to publish the blog; otherwise I got some errors. (4 (org-static-blog-publish-file (f-full (buffer-name))) (org-static-blog-publish)) (16 ;; (org-static-blog-publish t) ⇒ Crashes. ;; Delete all .html files, except “about” (thread-last (f-entries "~/blog/") (--filter (and (equal (f-ext it) "html") (not (member (f-base it) '("about"))))) (--map (f-delete it))) (ref:delAll) ;; Publish as usual (org-static-blog-publish-file (f-full (buffer-name))) (org-static-blog-publish))))) #+end_src Line [[(delAll)]]: To re-render an article, just remove its corresponding .html file ;-) * COMMENT [Part of publishing a file] org-static-blog-post-preamble: Making this work with =org-notes-style= and =org-static-blog= :PROPERTIES: :CUSTOM_ID: Making-this-work-with-org-notes-style-and-org-static-blog :END: To make use of =org-notes-style=, I need the title to use the =title= class but =org-static-blog= uses the =post-title= blog, so I'll override the =org-static-blog= preamble method to simply use an auxiliary div. - Along the way, I'll position the article image under the article's title. - Line [[(fixTitle)]]: =org-notes-style= has too much vertical space after the title, let's reduce it so that the article's data can follow it smoothly. # - Line [[(BR)]]: =org-static-blog= does not support Org-markup in the title, since it # just dumps the title in-place; here I provide a support for just =
=. #+begin_src emacs-lisp -r -n -n (defun org-static-blog-post-preamble (post-filename) "Returns the formatted date and headline of the post. This function is called for every post and prepended to the post body. Modify this function if you want to change a posts headline." (let ((post (blog/info (buffer-file-name)))) (concat ;; The title "" "
" (ref:fixTitle) "" (@title post) "" "" ;; The date "" (@date post) "" ;; The article's image (@image post) "Abstract "))) #+end_src # Before we move on, Org-notes adds extra whitespace after the title, let's avoid # that. # #+begin_src css :tangle ~/blog/blog-banner.css :noeval # .title { margin: 0 0 0 0 !important; } # #+end_src * The name: [[https://en.wikipedia.org/wiki/Basmala][al-bas-mala]] :PROPERTIES: :CUSTOM_ID: the-name :END: 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 [[https://en.wikipedia.org/wiki/%22Hello,_World!%22_program]["hello world"]] ;-) * Appendix: Using a Custom Domain: ~alhassy.com~ :PROPERTIES: :CUSTOM_ID: COMMENT-Using-a-Custom-Domain-alhassy-com :UNNUMBERED: t :END: #+begin_details 1. Go to your repo: https://github.com/alhassy/alhassy.github.io/settings/pages 2. Add a =Custom Domain= such as an “apex domain” like =alhassy.com= (or a www domain like =www.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. 3. 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 | 4. In a terminal, run: =dig www.alhassy.com +nostats +nocomments +nocmd= - The =www.= is intentional. 5. You should see something like: #+begin_example bash 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 #+end_example This says that it may take 3600 seconds, or 1hour, for the redirect of ~alhassy.com~ to ~alhassy.github.io~ to be completed. It may take longer; keep reading. 6. 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 [[https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site][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 | 7. Run =dig alhassy.com +noall +answer -t A= and ensure it points to these IP addresses. - Notice the lack of a =www.= 8. Open an incognito browser, private browsing session, and navigate to =alhassy.com=. a. 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. b. 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 [[https://www.whatsmydns.net/#A/alhassy.com][service like this]]. - It took me about 1/2hour for the URL to redirect to my github.io blog. Further resources: - [[https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site][Managing a custom domain for your GitHub Pages site - GitHub Docs]] - [[https://richpauloo.github.io/2019-11-17-Linking-a-Custom-Domain-to-Github-Pages/][Linking A Custom Domain To Github Pages]] #+end_details * COMMENT Archives ** Org Marco: “This section has been #+include'd from my init.org” :ignore: :PROPERTIES: :CUSTOM_ID: Org-Marco-This-section-has-been-include'd-from-my-init-org :END: # Some sections in this article come from my init.org. # # As such, the {{{from-my-init}}} macro is used to provide a nice, small, linkable, message. # Implementation below. ############################################################ # This CSS rule targets all images within anchors within athat has a class named “tiny”. In particular, it's used # to target badges so that they are about the same size as text. #+html: #+macro: init badge:A_Life_Configuring|Emacs|green|https://alhassy.github.io/emacs.d|gnu-emacs #+macro: from-my-init-text /This section has been =#+include='d from my init.org/, {{{init}}} #+macro: from-my-init @@html: @@ {{{from-my-init-text}}} @@html: @@ *** COMMENT Example usage {{{from-my-init}}} <> #+include: "~/.emacs.d/init.org::*Ensuring Useful HTML Anchors" :only-contents t ** COMMENT unsplash link setup :Leave_as_cute_remark: :PROPERTIES: :CUSTOM_ID: COMMENT-unsplash-link-setup :END: #+begin_src emacs-lisp :exports code ;; If you download images, from unspash, you'll have to host them somewhere! ;; An alternative is just to provide direct links to the unsplash images themselves! ;; ;; Example usage: ;; unsplash:gySMaocSdqs ;; ;; This shows an image along with a useful tooltip; image size is 200x200 by default. ;; The image is also a link, redirecting to the source, including whomever took the original photo. #+end_src (-let [unsplash (cl-second (s-match ".*unsplash.com/photos/\\(.*\\)" "https://unsplash.com/photos/Vc2dD4l57og"))] (if unsplash (format " " unsplash unsplash))) unsplash:XXX0GQfgMy8