From b79bf211aceef10e85a87d5e86937b4e18c07405 Mon Sep 17 00:00:00 2001 From: fido-node Date: Sat, 19 Oct 2024 16:09:53 +0000 Subject: [PATCH] deploy: fido-node/org@c35c4a016fa6778ce5d08e5f485bee186049c3af --- index.html | 26 +- posts.html | 80 +-- posts/about_blog.html | 284 +++++----- posts/add_rss_to_blog.html | 128 ++--- posts/blog_index_and_tags_automation.html | 636 +++++++++++----------- posts/improve_code_blocks.html | 358 ++++++------ posts/keeb.html | 242 ++++---- posts/posts_preview.html | 126 ++--- rss.html | 142 ++--- rss.xml | 137 +++-- tags/@diy.html | 12 +- tags/@elisp.html | 30 +- tags/@highlightjs.html | 12 +- tags/@imagemagick.html | 12 +- tags/@keeb.html | 12 +- tags/@org-mode.html | 36 +- tags/@rss.html | 12 +- tags/@tags.html | 12 +- 18 files changed, 1144 insertions(+), 1153 deletions(-) diff --git a/index.html b/index.html index 8839cb5..2a0f038 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,11 @@ Alex Mikhailov
-
+

avatar.jpg

-
-

 

-
+
+

 

+

Full stack engineer FP-curious | λ-affected @@ -13,9 +13,9 @@ Wanna be rustacean 🦀 and/or secops guy 🔒

-
-

Experience

-
+
+

Experience

+
  • Current position @SamsungFood. Internal tools engineer for data platform ➡ Internal tools engineer for developers.
  • @@ -24,9 +24,9 @@ Fullstack engineer.
-
-

Technologies

-
+
+

Technologies

+

Work with:

@@ -43,9 +43,9 @@ Work with:
-
-

Contacts

-
+
+

Contacts

+

Contact me via:

diff --git a/posts.html b/posts.html index b25a74c..0163941 100644 --- a/posts.html +++ b/posts.html @@ -1,11 +1,11 @@ Alex Mikhailov - Blog
-
-

Posts

-
+
+

Posts

+
-
-

Blog index and tags automation

-
+
+

Blog index and tags automation

+

Let's add tags to blog posts

@@ -20,9 +20,9 @@ drafted on 2024-07-05

-
-

Posts preview

-
+
+

Posts preview

+

Add post preview for OpenGraph cards

@@ -37,9 +37,9 @@ drafted on 2024-06-28

-
-

Improve code blocks

-
+
+

Improve code blocks

+

Use highlight.js for code syntax highlighting

@@ -54,9 +54,9 @@ drafted on 2024-06-25

-
-

Org blog with RSS

-
+
+

Org blog with RSS

+

Let's add RSS feed to blog

@@ -71,9 +71,9 @@ drafted on 2024-06-23

-
-

Org to HTML and back

-
+
+

Org to HTML and back

+

Blog post about publishing my blog with Org Mode

@@ -88,9 +88,9 @@ drafted on 2024-06-22

-
-

My keyboard journey

-
+
+

My keyboard journey

+

Blog post about my keyboards

@@ -106,33 +106,33 @@ drafted on 2024-06-05
-
-

Tags

-
+
+

Tags

+
-
-

@org-mode (5)

+
+

@org-mode (5)

-
-

@elisp (4)

+
+

@elisp (4)

-
-

@rss (1)

+
+

@rss (1)

-
-

@tags (1)

+
+

@tags (1)

-
-

@highlightjs (1)

+ -
-

@keeb (1)

+
+

@keeb (1)

-
-

@diy (1)

+
+

@diy (1)

-
diff --git a/posts/about_blog.html b/posts/about_blog.html index 652f6c7..54e0e50 100644 --- a/posts/about_blog.html +++ b/posts/about_blog.html @@ -1,45 +1,45 @@ -Org to HTML and back

Org to HTML and back

Blog post about publishing my blog with Org Mode

+Org to HTML and back

Org to HTML and back

Blog post about publishing my blog with Org Mode

Table of Contents

- -
-

Disclaimer

-
+
+

Disclaimer

+

I'm neither proficient in Org Mode (further on "Org"), nor a good front-end engineer. I think that a simple solution is better than no solution. If you see a mistake, you can contact me via iam@fidonode.me.

-
-

What is Org?

-
+
+

What is Org?

+

Your life in plain text @@ -55,9 +55,9 @@ Everything you can do in Org is to write a text. With a special markup, of cours

-
-

Why Org Mode?

-
+
+

Why Org Mode?

+
  1. Plain text. Plain text as a data source offers significant versatility. You can read and understand what happens in org files without needing Emacs.
  2. @@ -69,16 +69,16 @@ I do not have a habit of collecting and keeping information. I believe that disc
-
-

Render Org to blog or whatever

-
+
+

Render Org to blog or whatever

+

Org already has a way to render files into HTML, allowing you to create simple HTML files with minimal styling. I'm not interesting in styling from org, so I decide to use picocss framework.

-
-

Render HTML

-
+
+

Render HTML

+

I want to change some templates here and there. I've found esxml package. It is a decent DSL for writing XML/HTML. Here is how page header and footer look in this DSL. @@ -89,7 +89,7 @@ Here is how page header and footer look in this DSL. (ul (li (strong - ,(org-export-data (plist-get info :title) info)))) + ,(org-export-data (plist-get info :title) info)))) (ul (li (a (@ (href "/index.html")) "About")) (li (a (@ (href "/blog.html")) "Blog")) @@ -123,7 +123,7 @@ Whole template wiring looks like that. Not much, but it works and easy to mainta (meta (@ (charset "utf-8"))) (meta (@ (author "Alex Mikhailov"))) (meta (@ (name "viewport") - (content "width=device-width, initial-scale=1, shrink-to-fit=no"))) + (content "width=device-width, initial-scale=1, shrink-to-fit=no"))) (meta (@ (name "color-scheme") (content "light dark"))) (meta (@ (http-equiv "content-language") (content "en-us"))) (meta (@ (name "description") (content "Personal page with a blog about my technical adventures"))) @@ -135,10 +135,10 @@ Whole template wiring looks like that. Not much, but it works and easy to mainta (body (main (@ (class "container")) - ,(my/header info) - (*RAW-STRING* ,contents) - ,(my/footer info) - ) + ,(my/header info) + (*RAW-STRING* ,contents) + ,(my/footer info) + ) )) )) ) @@ -152,7 +152,7 @@ Ok, now we need some additional steps to wire these templating function. (org-export-define-derived-backend 'my-html 'html :translate-alist '((template . my/template) - )) + )) ;; Define publish function which uses our freshly derived backend (defun my/publish-to-html (plist filename pub-dir) @@ -167,37 +167,37 @@ So everything is almost done. Time to use our custom publishing function in proj

(setq org-publish-project-alist
       (list
        (list "blog"
-	     :recursive t
-	     :base-directory my/blog-src-path
-	     :publishing-directory my/web-export-path
-	     :publishing-function 'my/publish-to-html
-	     :html-html5-fancy t
-	     :htmlized-source t
-	     :with-author nil
-	     :with-creator t
-	     :with-toc t
-	     :section-numbers nil
-	     :time-stamp-file nil
-	     )
+             :recursive t
+             :base-directory my/blog-src-path
+             :publishing-directory my/web-export-path
+             :publishing-function 'my/publish-to-html
+             :html-html5-fancy t
+             :htmlized-source t
+             :with-author nil
+             :with-creator t
+             :with-toc t
+             :section-numbers nil
+             :time-stamp-file nil
+             )
        ))
 
-
-

Static files

-
+
+

Static files

+

Yep, you may want to publish some photos with your blog or any other static files.

(setq org-publish-project-alist
       (list
        (list "static"
-	     :base-directory my/blog-src-path
-	     :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt"
-	     :publishing-directory my/web-export-path
-	     :recursive t
-	     :publishing-function 'org-publish-attachment
-	     )
+             :base-directory my/blog-src-path
+             :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt"
+             :publishing-directory my/web-export-path
+             :recursive t
+             :publishing-function 'org-publish-attachment
+             )
       ))
 

@@ -205,9 +205,9 @@ Looks self explanatory.

-
-

Whole build script

-
+
+

Whole build script

+

Here is the whole elisp script which I use to publish my blog. It have some additional quirks to work with doomscript ./build-site.el.

@@ -220,7 +220,7 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (normal-top-level-add-subdirs-to-load-path)) (add-to-list 'custom-theme-load-path - (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) + (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/base16-theme")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/moe-theme")) @@ -263,7 +263,7 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (ul (li (strong - ,(org-export-data (plist-get info :title) info)))) + ,(org-export-data (plist-get info :title) info)))) (ul (li (a (@ (href "/index.html")) "About")) (li (a (@ (href "/blog.html")) "Blog")) @@ -281,7 +281,7 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (meta (@ (charset "utf-8"))) (meta (@ (author "Alex Mikhailov"))) (meta (@ (name "viewport") - (content "width=device-width, initial-scale=1, shrink-to-fit=no"))) + (content "width=device-width, initial-scale=1, shrink-to-fit=no"))) (meta (@ (name "color-scheme") (content "light dark"))) (meta (@ (http-equiv "content-language") (content "en-us"))) (meta (@ (name "description") (content "Personal page with a blog about my technical adventures"))) @@ -293,10 +293,10 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (body (main (@ (class "container")) - ,(my/header info) - (*RAW-STRING* ,contents) - ,(my/footer info) - ) + ,(my/header info) + (*RAW-STRING* ,contents) + ,(my/footer info) + ) )) )) ) @@ -304,7 +304,7 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (org-export-define-derived-backend 'my-html 'html :translate-alist '((template . my/template) - )) + )) (defun my/publish-to-html (plist filename pub-dir) "Publish an Org file to HTML using the custom backend." @@ -333,25 +333,25 @@ Here is the whole elisp script which I use to publish my blog. It have some addi (setq org-publish-project-alist (list (list "static" - :base-directory my/blog-src-path - :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" - :publishing-directory my/web-export-path - :recursive t - :publishing-function 'org-publish-attachment - ) + :base-directory my/blog-src-path + :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" + :publishing-directory my/web-export-path + :recursive t + :publishing-function 'org-publish-attachment + ) (list "blog" - :recursive t - :base-directory my/blog-src-path - :publishing-directory my/web-export-path - :publishing-function 'my/publish-to-html - :html-html5-fancy t - :htmlized-source t - :with-author nil - :with-creator t - :with-toc t - :section-numbers nil - :time-stamp-file nil - ) + :recursive t + :base-directory my/blog-src-path + :publishing-directory my/web-export-path + :publishing-function 'my/publish-to-html + :html-html5-fancy t + :htmlized-source t + :with-author nil + :with-creator t + :with-toc t + :section-numbers nil + :time-stamp-file nil + ) )) @@ -365,9 +365,9 @@ Here is the whole elisp script which I use to publish my blog. It have some addi
-
-

Publish through GitHub Action

-
+
+

Publish through GitHub Action

+

With all previous preparations, this step sounds simple like: emacs -Q --script ./build-site.el I've chosen a pretty standard way to publish static sites through GitHub Pages. Since I keep my Org files in a private repo, I need some additional steps to address it. I use the peaceiris/actions-gh-pages@v3 action to publish from my Org repo to the Pages repo. @@ -375,9 +375,9 @@ However, since I use Doom Emacs as my configuration framework, we n

-
-

Install Emacs

-
+
+

Install Emacs

+

If you want to run Emacs Lisp, you need the whole Emacs, at least without GUI. In a GitHub Action, you can simply run:

@@ -388,9 +388,9 @@ This way has a downside - you will install Emacs on each action run since the sy

-
-

Just bring everything

-
+
+

Just bring everything

+

I need to take extra steps since I use Doom Emacs and have my configs in Org. You may also need to install dependencies for your configuration.

@@ -426,9 +426,9 @@ Of course, I use a caching step to make the whole process faster:
-
-

BTW I use GNU Emacs

-
+
+

BTW I use GNU Emacs

+

Here's the whole publishing workflow.

@@ -450,78 +450,78 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out - uses: actions/checkout@v1 + uses: actions/checkout@v1 - #Install emacs without GUI components + #Install emacs without GUI components - name: Install Emacs - run: sudo apt install emacs-nox --yes + run: sudo apt install emacs-nox --yes - #Clone doomemacs. Yep, always the most fresh master. Let it fire. + #Clone doomemacs. Yep, always the most fresh master. Let it fire. - name: Install doom - run: git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.config/emacs + run: git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.config/emacs - # Use cached files to shave some time + # Use cached files to shave some time - name: Restore cached doom-emacs - id: cache-doom-restore - uses: actions/cache/restore@v4 - with: - path: ~/.config/emacs - key: ${{ runner.os }}-doom + id: cache-doom-restore + uses: actions/cache/restore@v4 + with: + path: ~/.config/emacs + key: ${{ runner.os }}-doom - name: Create folder - run: mkdir -p ~/.config/doom/ + run: mkdir -p ~/.config/doom/ - # I use literate config, so we need some extra steps to botstrap my config + # I use literate config, so we need some extra steps to botstrap my config - name: Put template for literate config - run: echo '(doom! :config literate)' > ~/.config/doom/init.el + run: echo '(doom! :config literate)' > ~/.config/doom/init.el - # Yep. I also keep my emacs config in org in my org repo + # Yep. I also keep my emacs config in org in my org repo - name: Propagate org conf - run: echo '(setq +literate-config-file "'$(pwd)'/config/config.org")' > ~/.config/doom/cli.el + run: echo '(setq +literate-config-file "'$(pwd)'/config/config.org")' > ~/.config/doom/cli.el - # Build doomemacs deps. Should be relativelly fast, cause almost everything cached. + # Build doomemacs deps. Should be relativelly fast, cause almost everything cached. - name: Sync doom - run: ~/.config/emacs/bin/doom sync -B + run: ~/.config/emacs/bin/doom sync -B - #Put files into cache + #Put files into cache - name: Cache doom-emacs - uses: actions/cache@v4 - id: cache-doom-save - with: - path: ~/.config/emacs - key: ${{ runner.os }}-doom + uses: actions/cache@v4 + id: cache-doom-save + with: + path: ~/.config/emacs + key: ${{ runner.os }}-doom - name: Build the site - run: ~/.config/emacs/bin/doomscript ./build-site.el + run: ~/.config/emacs/bin/doomscript ./build-site.el - # Deploy from this repo to that ~external_repository~ + # Deploy from this repo to that ~external_repository~ - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - deploy_key: ${{ secrets.PRIVATE_KEY }} - external_repository: fido-node/fido-node.github.io - publish_branch: gh-pages - publish_dir: ./public + uses: peaceiris/actions-gh-pages@v3 + with: + deploy_key: ${{ secrets.PRIVATE_KEY }} + external_repository: fido-node/fido-node.github.io + publish_branch: gh-pages + publish_dir: ./public
-
-

What is next

-
+
+

What is next

+

I have a plans to make posts about next features:

-
diff --git a/posts/add_rss_to_blog.html b/posts/add_rss_to_blog.html index 5e034b8..1cd5b4b 100644 --- a/posts/add_rss_to_blog.html +++ b/posts/add_rss_to_blog.html @@ -1,57 +1,57 @@ -Org blog with RSS

Org blog with RSS

Let's add RSS feed to blog

+Org blog with RSS

Org blog with RSS

Let's add RSS feed to blog

Table of Contents

- -
-

Why do you even need RSS?

-
+
+

Why do you even need RSS?

+

RSS might seem like an outdated, marginal thing. But it still has at least one benefit—you can use an RSS feed as a sitemap for search engines. Plus, it's pretty geeky.

-
-

Add RSS feed

-
+
+

Add RSS feed

+

So, what's happening here? Let's start by integrating our templating functions into the build.

-
-

Use sitemap backend in the build

-
+
+

Use sitemap backend in the build

+
(setq org-publish-project-alist
       (list
        (list "blog-rss"
-	     :author "Alex M"
-	     :email "iam@fidonode.me"
-	     :base-directory my/blog-src-path
-	     :base-extension "org"
-	     :recursive t
-	     :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org"))
-	     :publishing-function 'my/publish-to-rss
-	     :publishing-directory my/web-export-path
-	     :rss-extension "xml"
-	     :html-link-home my/url
-	     :html-link-use-abs-url t
-	     :html-link-org-files-as-html t
-	     :auto-sitemap t
-	     :sitemap-filename "rss.org"
-	     :sitemap-title "rss"
-	     :sitemap-style 'list
-	     :sitemap-sort-files 'anti-chronologically
-	     :sitemap-function 'my/format-rss-feed
-	     :sitemap-format-entry 'my/format-rss-feed-entry)
+             :author "Alex M"
+             :email "iam@fidonode.me"
+             :base-directory my/blog-src-path
+             :base-extension "org"
+             :recursive t
+             :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org"))
+             :publishing-function 'my/publish-to-rss
+             :publishing-directory my/web-export-path
+             :rss-extension "xml"
+             :html-link-home my/url
+             :html-link-use-abs-url t
+             :html-link-org-files-as-html t
+             :auto-sitemap t
+             :sitemap-filename "rss.org"
+             :sitemap-title "rss"
+             :sitemap-style 'list
+             :sitemap-sort-files 'anti-chronologically
+             :sitemap-function 'my/format-rss-feed
+             :sitemap-format-entry 'my/format-rss-feed-entry)
        ))
 

@@ -59,9 +59,9 @@ How does it work? As you can see, we use the default sitemap generator from Org

-
-

Publishing and formatting functions

-
+
+

Publishing and formatting functions

+

We need a mandatory dependency because we don't want to mess with forming correct XML by ourselves.

@@ -75,31 +75,31 @@ Here's the core of the process. We need to prepare entries before feeding them t ENTRY is a file name. STYLE is either 'list' or 'tree'. PROJECT is the current project." (cond ((not (directory-name-p entry)) - (let* ((file (org-publish--expand-file-name entry project)) - (title (org-publish-find-title entry project)) - (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) - (link (concat (file-name-sans-extension entry) ".html"))) - (with-temp-buffer - (org-mode) - (insert (format "* [[file:%s][%s]]\n" file title)) - (org-set-property "RSS_PERMALINK" link) - (org-set-property "RSS_TITLE" title) - (org-set-property "PUBDATE" date) - (let ((first-two-lines (with-temp-buffer - (insert-file-contents file) - (buffer-substring-no-properties - (point-min) - (progn (forward-line 2) (point)))))) - (if (string-suffix-p "\n" first-two-lines) - (setq first-two-lines (substring first-two-lines 0 -1))) - (insert first-two-lines)) - (goto-char (point-max)) - (insert "...") - (buffer-string)))) - ((eq style 'tree) - ;; Return only last subdir. - (file-name-nondirectory (directory-file-name entry))) - (t entry))) + (let* ((file (org-publish--expand-file-name entry project)) + (title (org-publish-find-title entry project)) + (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) + (link (concat (file-name-sans-extension entry) ".html"))) + (with-temp-buffer + (org-mode) + (insert (format "* [[file:%s][%s]]\n" file title)) + (org-set-property "RSS_PERMALINK" link) + (org-set-property "RSS_TITLE" title) + (org-set-property "PUBDATE" date) + (let ((first-two-lines (with-temp-buffer + (insert-file-contents file) + (buffer-substring-no-properties + (point-min) + (progn (forward-line 2) (point)))))) + (if (string-suffix-p "\n" first-two-lines) + (setq first-two-lines (substring first-two-lines 0 -1))) + (insert first-two-lines)) + (goto-char (point-max)) + (insert "...") + (buffer-string)))) + ((eq style 'tree) + ;; Return only last subdir. + (file-name-nondirectory (directory-file-name entry))) + (t entry)))

@@ -111,8 +111,8 @@ TITLE is the title of the RSS feed. LIST is an internal representation for the files to include, as returned by `org-list-to-lisp'. PROJECT is the current project." (concat "#+TITLE: " title "\n" - "#+STARTUP: showall \n\n" - (org-list-to-subtree list 1 '(:icount "" :istart "")))) + "#+STARTUP: showall \n\n" + (org-list-to-subtree list 1 '(:icount "" :istart ""))))

This function replaces the default publishing function to filter everything except the intermediate file before publishing. diff --git a/posts/blog_index_and_tags_automation.html b/posts/blog_index_and_tags_automation.html index 2cf2f91..37aaf62 100644 --- a/posts/blog_index_and_tags_automation.html +++ b/posts/blog_index_and_tags_automation.html @@ -1,32 +1,32 @@ -Blog index and tags automation

Blog index and tags automation

Let's add tags to blog posts

+Blog index and tags automation

Blog index and tags automation

Let's add tags to blog posts

Table of Contents

- -
-

Tags.

-
+
+

Tags.

+

Tags are a nice and easy way to organize posts without explicit search. In the simplest way, you have a list of tags in posts, and each tag links to a page with all posts having the corresponding tag. It is also helpful to have a page with all tags available in the blog. And, of course, I don't want to maintain the list of tags manually.

-
-

Automate tags.

-
+
+

Automate tags.

+

Simple idea - go through all files, collect titles, dates and descriptions, render into org files. Easy peazy lemon squezy. Let me show you parts of function body. @@ -51,34 +51,34 @@ Create folder for tags files. Than we go through each file and collect plists wi (with-temp-buffer (insert-file-contents file) (let* ((parsed-info (list :parse-tree (org-element-parse-buffer))) - (tags (split-string (my/org-get-property "TAGS" parsed-info))) - (title (my/org-get-property "TITLE" parsed-info)) - (date (my/org-get-property "DATE" parsed-info)) - (description (my/org-get-property "DESCRIPTION" parsed-info)) - (published (my/org-get-property "PUBLISHED" parsed-info)) - (preview (with-temp-buffer - (insert-file-contents file) - (my/get-first-two-meaningful-lines))) - ) + (tags (split-string (my/org-get-property "TAGS" parsed-info))) + (title (my/org-get-property "TITLE" parsed-info)) + (date (my/org-get-property "DATE" parsed-info)) + (description (my/org-get-property "DESCRIPTION" parsed-info)) + (published (my/org-get-property "PUBLISHED" parsed-info)) + (preview (with-temp-buffer + (insert-file-contents file) + (my/get-first-two-meaningful-lines))) + ) (if (and published (string= published "true")) - (progn - (setq link-plist - (list - :title title - :description description - :preview preview - :file file - :date date)) + (progn + (setq link-plist + (list + :title title + :description description + :preview preview + :file file + :date date)) - (push link-plist posts-list) + (push link-plist posts-list) - (dolist (tag tags) - (puthash tag (cons link-plist (gethash tag tag-map)) tag-map)) + (dolist (tag tags) + (puthash tag (cons link-plist (gethash tag tag-map)) tag-map)) - (setq posts-list - (sort posts-list - (lambda (a b) - (date-less-p (plist-get b :date) (plist-get a :date)))))))))) + (setq posts-list + (sort posts-list + (lambda (a b) + (date-less-p (plist-get b :date) (plist-get a :date))))))))))

Go through resulting map and render org files for each tag. @@ -94,17 +94,17 @@ Go through resulting map and render org files for each tag. (insert (format "* %s\n" tag)) (dolist (link-plist link-plists) - (let* - ((title (plist-get link-plist :title)) - (description (plist-get link-plist :description)) - (file (plist-get link-plist :file)) - (date (plist-get link-plist :date)) - (preview (plist-get link-plist :preview)) - (relative-file (file-relative-name file (file-name-directory tag-file)))) - (insert (format "** [[file:%s][%s]]\n" relative-file title)) - (insert (format "%s\n" description)) - (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) - (insert (format "%s\n" (my/date-format date)))))))) + (let* + ((title (plist-get link-plist :title)) + (description (plist-get link-plist :description)) + (file (plist-get link-plist :file)) + (date (plist-get link-plist :date)) + (preview (plist-get link-plist :preview)) + (relative-file (file-relative-name file (file-name-directory tag-file)))) + (insert (format "** [[file:%s][%s]]\n" relative-file title)) + (insert (format "%s\n" description)) + (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) + (insert (format "%s\n" (my/date-format date)))))))) tag-map)

@@ -113,54 +113,54 @@ Resulting page example: @org-mode

-
-

Posts index.

-
+
+

Posts index.

+

Previously, I maintained a list of posts manually. Now that I have more than 5 posts, I no longer want to do it by hand. So, after introducing tags, I've decided to automate the page with the list of posts. The same idea, almost the same data, will be integrated into the same file. Pagination has not yet been implemented and is not planned.

-
-

Posts index automation.

-
+
+

Posts index automation.

+

Here is the part of function which renders list of posts and list of tags with number of posts in each tag category.

(let
-	((index-file (concat base-dir "/posts.org")))
+        ((index-file (concat base-dir "/posts.org")))
       (with-temp-file index-file
-	(org-mode)
+        (org-mode)
 
-	(insert "#+TITLE: Alex Mikhailov - Blog\n")
-	(insert "#+AUTHOR: Aleksandr Mikhailov\n")
-	(insert "#+DESCRIPTION: Index page for my blog\n")
-	(insert "#+OPTIONS: toc:nil\n\n")
+        (insert "#+TITLE: Alex Mikhailov - Blog\n")
+        (insert "#+AUTHOR: Aleksandr Mikhailov\n")
+        (insert "#+DESCRIPTION: Index page for my blog\n")
+        (insert "#+OPTIONS: toc:nil\n\n")
 
-	(insert "* Posts\n")
+        (insert "* Posts\n")
 
-	(dolist (post posts-list)
-	  (let*
-	      ((title (plist-get post :title))
-	       (description (plist-get post :description))
-	       (file (plist-get post :file))
-	       (date (plist-get post :date))
-	       (preview (plist-get post :preview))
-	       (relative-file (file-relative-name file (file-name-directory file))))
+        (dolist (post posts-list)
+          (let*
+              ((title (plist-get post :title))
+               (description (plist-get post :description))
+               (file (plist-get post :file))
+               (date (plist-get post :date))
+               (preview (plist-get post :preview))
+               (relative-file (file-relative-name file (file-name-directory file))))
 
-	    (insert (format "** [[file:./posts/%s][%s]]\n" relative-file title))
-	    (insert (format "%s\n" description))
-	    (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview))
-	    (insert (format "%s\n" (my/date-format date)))
-	    )
-	  )
+            (insert (format "** [[file:./posts/%s][%s]]\n" relative-file title))
+            (insert (format "%s\n" description))
+            (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview))
+            (insert (format "%s\n" (my/date-format date)))
+            )
+          )
 
-	(insert "* Tags\n")
-	(maphash
-	 (lambda (tag posts)
-	   (insert (format "** [[file:./tags/%s.org][%s]] (%d) \n" tag tag (length posts)))          )
-	 tag-map
-	 )
-	)
+        (insert "* Tags\n")
+        (maphash
+         (lambda (tag posts)
+           (insert (format "** [[file:./tags/%s.org][%s]] (%d) \n" tag tag (length posts)))          )
+         tag-map
+         )
+        )
 
       )
 
@@ -170,17 +170,17 @@ Here is an example of resulting page:
Posts<
-
-

Cons.

-
+
+

Cons.

+

Now, with the way the rendering function is integrated into the process, it is called when going through each Org file found by Org-Export. This introduces O(n2) complexity. It's not ideal, but never mind, I will redo it before reaching the 100th post.

-
-

Whole config.

-
+
+

Whole config.

+

As usual, between posts, I decided to tackle a whole bunch of small tasks, so here is the entire config. I'm thinking about splitting the exporter into modules and maybe open-sourcing it properly. A neat thing I've implemented is a way to filter out unpublished posts using a PUBLISHED property. This allows me to work on drafts without affecting the blog's current state. @@ -201,7 +201,7 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (normal-top-level-add-subdirs-to-load-path)) (add-to-list 'custom-theme-load-path - (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) + (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/base16-theme")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/moe-theme")) @@ -232,71 +232,71 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (defun my/footer (info) `(footer ((class . "footer")) (div ((class . "container")) - (hr () ) - (small () - (p () "Alex Mikhailov") - (p () "Built with: " - (a ((href . "https://www.gnu.org/software/emacs/")) "GNU Emacs") " " - (a ((href . "https://orgmode.org/")) "Org Mode") " " - (a ((href . "https://picocss.com/")) "picocss") - ))))) + (hr () ) + (small () + (p () "Alex Mikhailov") + (p () "Built with: " + (a ((href . "https://www.gnu.org/software/emacs/")) "GNU Emacs") " " + (a ((href . "https://orgmode.org/")) "Org Mode") " " + (a ((href . "https://picocss.com/")) "picocss") + ))))) (defun my/header (info) (let ((title-str (org-export-data (plist-get info :title) info))) `(header ((class . "header")) (div ((class . "container")) - (nav () - (ul () - (li () - (strong () ,"Alex Mikhailov"))) - (ul () - (li () (a ((href . "/index.html")) "About")) - (li () (a ((href . "/posts.html")) "Blog")) - (li () (a ((href . "/rss.xml")) "RSS")) - )))))) + (nav () + (ul () + (li () + (strong () ,"Alex Mikhailov"))) + (ul () + (li () (a ((href . "/index.html")) "About")) + (li () (a ((href . "/posts.html")) "Blog")) + (li () (a ((href . "/rss.xml")) "RSS")) + )))))) (defun my/src-block (src-block contents info) "Translate SRC-BLOCK element into HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let* ( - (org-language (format "language-%s" (org-element-property :language src-block))) - (language (my/replace-substrings org-language)) - (code (org-element-property :value src-block))) + (org-language (format "language-%s" (org-element-property :language src-block))) + (language (my/replace-substrings org-language)) + (code (org-element-property :value src-block))) (esxml-to-xml `(pre () (code ((class . ,language)) - ,(org-html-encode-plain-text code) - ))))) + ,(org-html-encode-plain-text code) + ))))) (defun my/render-preview (file-name title description) (let* ((has-imagemagick (executable-find "magick")) - (full-file-path (file-truename(format "%s%s/resources/images/preview/%s.png" script-directory my/blog-src-path file-name ))) - (file-dir (file-name-directory full-file-path)) - (has-dir (file-directory-p file-dir)) - (has-file (file-exists-p full-file-path)) - (path-to-script-root (format "%shelpers" script-directory)) - (path-to-script (format "%s/og_image_gen.sh" path-to-script-root))) + (full-file-path (file-truename(format "%s%s/resources/images/preview/%s.png" script-directory my/blog-src-path file-name ))) + (file-dir (file-name-directory full-file-path)) + (has-dir (file-directory-p file-dir)) + (has-file (file-exists-p full-file-path)) + (path-to-script-root (format "%shelpers" script-directory)) + (path-to-script (format "%s/og_image_gen.sh" path-to-script-root))) (if (and has-imagemagick - (and description (not (string= description "")))) - (progn - (when (not has-file) - (progn - (when (not has-dir) - (make-directory file-dir t)) - (shell-command (format "bash '%s' '%s' '%s' '%s' '%s'" path-to-script title description full-file-path path-to-script-root)) - ) - )) + (and description (not (string= description "")))) + (progn + (when (not has-file) + (progn + (when (not has-dir) + (make-directory file-dir t)) + (shell-command (format "bash '%s' '%s' '%s' '%s' '%s'" path-to-script title description full-file-path path-to-script-root)) + ) + )) (message "Imagemagick is not installed. Preview generation skipped.") ))) (defun my/html-header (info) (let* ((title-str (org-export-data (plist-get info :title) info)) - (description-str (org-export-data (plist-get info :description) info)) - (file-path-str (org-export-data (plist-get info :input-file) info)) - (base-directory-str (org-export-data (plist-get info :base-directory) info)) - (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str))) - (img-link-str (format "%s/resources/images/preview/%s.png" my/url file-name-str)) - (has-src-blocks (my/org-has-src-blocks-p info))) + (description-str (org-export-data (plist-get info :description) info)) + (file-path-str (org-export-data (plist-get info :input-file) info)) + (base-directory-str (org-export-data (plist-get info :base-directory) info)) + (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str))) + (img-link-str (format "%s/resources/images/preview/%s.png" my/url file-name-str)) + (has-src-blocks (my/org-has-src-blocks-p info))) (my/render-preview file-name-str title-str description-str) @@ -309,7 +309,7 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (meta ((charset . "utf-8"))) (meta ((author . "Alex Mikhailov"))) (meta ((name . "viewport") - (content . "width=device-width, initial-scale=1, shrink-to-fit=no"))) + (content . "width=device-width, initial-scale=1, shrink-to-fit=no"))) (meta ((name . "color-scheme") (content . "light dark"))) (meta ((http-equiv . "content-language") (content . "en-us"))) ;; OG block @@ -329,13 +329,13 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (title () ,title-str) ,@(when has-src-blocks - (list - `(link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) - `(script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) - `(script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) - `(script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) - `(script ((src . "/resources/js/theme-selector.js")) ()) - ))))) + (list + `(link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) + `(script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) + `(script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) + `(script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) + `(script ((src . "/resources/js/theme-selector.js")) ()) + ))))) (defun my/regular-template (contents info) `(main ((class . "container")) @@ -343,18 +343,18 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (defun my/blog-post-template (contents info) (let* ((title-str (org-export-data (plist-get info :title) info)) - (description-str (org-export-data (plist-get info :description) info)) - (tags (split-string (my/org-get-property "TAGS" info))) - (tags-html (cl-map 'list (lambda (tag) - `(li () (mark () (a ((href . ,(format "/tags/%s.html" tag)) (class . "secondary")) ,(format "%s" tag))))) tags)) - ) + (description-str (org-export-data (plist-get info :description) info)) + (tags (split-string (my/org-get-property "TAGS" info))) + (tags-html (cl-map 'list (lambda (tag) + `(li () (mark () (a ((href . ,(format "/tags/%s.html" tag)) (class . "secondary")) ,(format "%s" tag))))) tags)) + ) `(main ((class . "container blog-post")) (hgroup () - (h1 () ,title-str) - (p () ,description-str) - (nav () (ul () (li () "Tags:") ,@tags-html)) + (h1 () ,title-str) + (p () ,description-str) + (nav () (ul () (li () "Tags:") ,@tags-html)) - ) + ) (raw-string ,contents) ))) @@ -366,21 +366,21 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < "&lt;!DOCTYPE html&gt;" (esxml-to-xml `(html ((lang . "en")) - ,(my/html-header info) - (body () - ,(my/header info) - ,(if (string-match-p "\/posts\/" file-path-str) - (my/blog-post-template contents info) - (my/regular-template contents info)) - ,(my/footer info) - )))))) + ,(my/html-header info) + (body () + ,(my/header info) + ,(if (string-match-p "\/posts\/" file-path-str) + (my/blog-post-template contents info) + (my/regular-template contents info)) + ,(my/footer info) + )))))) (org-export-define-derived-backend 'my-html 'html :translate-alist '( - (template . my/template) - (src-block . my/src-block) - )) + (template . my/template) + (src-block . my/src-block) + )) (defun my/publish-to-html (plist filename pub-dir) "Publish an Org file to HTML using the custom backend." @@ -394,12 +394,12 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < "Process all Org files in 'posts' and 'tags' directories, create index file, and insert links." (let* ((base-dir (plist-get plist :base-directory) ) - (tag-dir (concat base-dir "/tags/")) - (posts-dir (concat base-dir "/posts")) - (post-org-files (directory-files-recursively posts-dir "\\.org$")) - (tag-map (make-hash-table :test 'equal)) - (posts-list (list)) - ) + (tag-dir (concat base-dir "/tags/")) + (posts-dir (concat base-dir "/posts")) + (post-org-files (directory-files-recursively posts-dir "\\.org$")) + (tag-map (make-hash-table :test 'equal)) + (posts-list (list)) + ) ;; Ensure tag directory exists (unless (file-directory-p tag-dir) @@ -408,72 +408,72 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < ;; Scan all org files and collect tags (dolist (file post-org-files) (with-temp-buffer - (insert-file-contents file) - (let* ((parsed-info (list :parse-tree (org-element-parse-buffer))) - (tags (split-string (my/org-get-property "TAGS" parsed-info))) - (title (my/org-get-property "TITLE" parsed-info)) - (date (my/org-get-property "DATE" parsed-info)) - (description (my/org-get-property "DESCRIPTION" parsed-info)) - (published (my/org-get-property "PUBLISHED" parsed-info)) - (preview (with-temp-buffer - (insert-file-contents file) - (my/get-first-two-meaningful-lines))) - ) - (if (and published (string= published "true")) - (progn - (setq link-plist - (list - :title title - :description description - :preview preview - :file file - :date date)) + (insert-file-contents file) + (let* ((parsed-info (list :parse-tree (org-element-parse-buffer))) + (tags (split-string (my/org-get-property "TAGS" parsed-info))) + (title (my/org-get-property "TITLE" parsed-info)) + (date (my/org-get-property "DATE" parsed-info)) + (description (my/org-get-property "DESCRIPTION" parsed-info)) + (published (my/org-get-property "PUBLISHED" parsed-info)) + (preview (with-temp-buffer + (insert-file-contents file) + (my/get-first-two-meaningful-lines))) + ) + (if (and published (string= published "true")) + (progn + (setq link-plist + (list + :title title + :description description + :preview preview + :file file + :date date)) - (push link-plist posts-list) + (push link-plist posts-list) - (dolist (tag tags) - (puthash tag (cons link-plist (gethash tag tag-map)) tag-map)) + (dolist (tag tags) + (puthash tag (cons link-plist (gethash tag tag-map)) tag-map)) - (setq posts-list - (sort posts-list - (lambda (a b) - (date-less-p (plist-get b :date) (plist-get a :date)))))))))) + (setq posts-list + (sort posts-list + (lambda (a b) + (date-less-p (plist-get b :date) (plist-get a :date)))))))))) (let - ((index-file (concat base-dir "/posts.org"))) + ((index-file (concat base-dir "/posts.org"))) (with-temp-file index-file - (org-mode) + (org-mode) - (insert "#+TITLE: Alex Mikhailov - Blog\n") - (insert "#+AUTHOR: Aleksandr Mikhailov\n") - (insert "#+DESCRIPTION: Index page for my blog\n") - (insert "#+OPTIONS: toc:nil\n\n") + (insert "#+TITLE: Alex Mikhailov - Blog\n") + (insert "#+AUTHOR: Aleksandr Mikhailov\n") + (insert "#+DESCRIPTION: Index page for my blog\n") + (insert "#+OPTIONS: toc:nil\n\n") - (insert "* Posts\n") + (insert "* Posts\n") - (dolist (post posts-list) - (let* - ((title (plist-get post :title)) - (description (plist-get post :description)) - (file (plist-get post :file)) - (date (plist-get post :date)) - (preview (plist-get post :preview)) - (relative-file (file-relative-name file (file-name-directory file)))) + (dolist (post posts-list) + (let* + ((title (plist-get post :title)) + (description (plist-get post :description)) + (file (plist-get post :file)) + (date (plist-get post :date)) + (preview (plist-get post :preview)) + (relative-file (file-relative-name file (file-name-directory file)))) - (insert (format "** [[file:./posts/%s][%s]]\n" relative-file title)) - (insert (format "%s\n" description)) - (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) - (insert (format "%s\n" (my/date-format date))) - ) - ) + (insert (format "** [[file:./posts/%s][%s]]\n" relative-file title)) + (insert (format "%s\n" description)) + (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) + (insert (format "%s\n" (my/date-format date))) + ) + ) - (insert "* Tags\n") - (maphash - (lambda (tag posts) - (insert (format "** [[file:./tags/%s.org][%s]] (%d) \n" tag tag (length posts))) ) - tag-map - ) - ) + (insert "* Tags\n") + (maphash + (lambda (tag posts) + (insert (format "** [[file:./tags/%s.org][%s]] (%d) \n" tag tag (length posts))) ) + tag-map + ) + ) ) @@ -481,24 +481,24 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (maphash (lambda (tag link-plists) (let - ((tag-file (concat tag-dir tag ".org"))) - (with-temp-file tag-file - (insert (format "#+TITLE: Tag: %s\n" tag)) - (insert "#+OPTIONS: toc:nil\n\n") - (insert (format "* %s\n" tag)) + ((tag-file (concat tag-dir tag ".org"))) + (with-temp-file tag-file + (insert (format "#+TITLE: Tag: %s\n" tag)) + (insert "#+OPTIONS: toc:nil\n\n") + (insert (format "* %s\n" tag)) - (dolist (link-plist link-plists) - (let* - ((title (plist-get link-plist :title)) - (description (plist-get link-plist :description)) - (file (plist-get link-plist :file)) - (date (plist-get link-plist :date)) - (preview (plist-get link-plist :preview)) - (relative-file (file-relative-name file (file-name-directory tag-file)))) - (insert (format "** [[file:%s][%s]]\n" relative-file title)) - (insert (format "%s\n" description)) - (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) - (insert (format "%s\n" (my/date-format date)))))))) + (dolist (link-plist link-plists) + (let* + ((title (plist-get link-plist :title)) + (description (plist-get link-plist :description)) + (file (plist-get link-plist :file)) + (date (plist-get link-plist :date)) + (preview (plist-get link-plist :preview)) + (relative-file (file-relative-name file (file-name-directory tag-file)))) + (insert (format "** [[file:%s][%s]]\n" relative-file title)) + (insert (format "%s\n" description)) + (insert (format "#+BEGIN_QUOTE\n%s ...\n#+END_QUOTE\n" preview)) + (insert (format "%s\n" (my/date-format date)))))))) tag-map) ) ) @@ -511,31 +511,31 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < ENTRY is a file name. STYLE is either 'list' or 'tree'. PROJECT is the current project." (cond ((not (directory-name-p entry)) - (let* ((file (org-publish--expand-file-name entry project)) - (title (org-publish-find-title entry project)) - (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) - (link (concat (file-name-sans-extension entry) ".html"))) - (with-temp-buffer - (org-mode) - (insert (format "* [[file:%s][%s]]\n" file title)) - (org-set-property "RSS_PERMALINK" link) - (org-set-property "RSS_TITLE" title) - (org-set-property "PUBDATE" date) - (let ((first-two-lines (with-temp-buffer - (insert-file-contents file) - (buffer-substring-no-properties - (point-min) - (progn (forward-line 2) (point)))))) - (if (string-suffix-p "\n" first-two-lines) - (setq first-two-lines (substring first-two-lines 0 -1))) - (insert first-two-lines)) - (goto-char (point-max)) - (insert "...") - (buffer-string)))) - ((eq style 'tree) - ;; Return only last subdir. - (file-name-nondirectory (directory-file-name entry))) - (t entry))) + (let* ((file (org-publish--expand-file-name entry project)) + (title (org-publish-find-title entry project)) + (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) + (link (concat (file-name-sans-extension entry) ".html"))) + (with-temp-buffer + (org-mode) + (insert (format "* [[file:%s][%s]]\n" file title)) + (org-set-property "RSS_PERMALINK" link) + (org-set-property "RSS_TITLE" title) + (org-set-property "PUBDATE" date) + (let ((first-two-lines (with-temp-buffer + (insert-file-contents file) + (buffer-substring-no-properties + (point-min) + (progn (forward-line 2) (point)))))) + (if (string-suffix-p "\n" first-two-lines) + (setq first-two-lines (substring first-two-lines 0 -1))) + (insert first-two-lines)) + (goto-char (point-max)) + (insert "...") + (buffer-string)))) + ((eq style 'tree) + ;; Return only last subdir. + (file-name-nondirectory (directory-file-name entry))) + (t entry))) (defun my/format-rss-feed (title list) "Generate RSS feed, as a string. @@ -543,8 +543,8 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < representation for the files to include, as returned by `org-list-to-lisp'. PROJECT is the current project." (concat "#+TITLE: " title "\n" - "#+STARTUP: showall \n\n" - (org-list-to-subtree list 1 '(:icount "" :istart "")))) + "#+STARTUP: showall \n\n" + (org-list-to-subtree list 1 '(:icount "" :istart "")))) (defun my/publish-to-rss (plist filename pub-dir) "Publish RSS with PLIST, only when FILENAME is 'rss.org'. @@ -573,24 +573,24 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < ;; Now read and return the first two meaningful lines (cleanup-org-line (buffer-substring-no-properties - (point) - (progn (forward-line 2) (point))) - )) + (point) + (progn (forward-line 2) (point))) + )) ) (defun date-less-p (date1 date2) "Return t if DATE1 is less than DATE2. DATE1 and DATE2 should be strings in the format &lt;YYYY-MM-DD Day&gt;." (let* ((date1 (substring date1 1 11)) ; Extract the date part - (date2 (substring date2 1 11)) - (time1 (apply 'encode-time (parse-time-string (concat date1 " 00:00:00")))) - (time2 (apply 'encode-time (parse-time-string (concat date2 " 00:00:00"))))) + (date2 (substring date2 1 11)) + (time1 (apply 'encode-time (parse-time-string (concat date1 " 00:00:00")))) + (time2 (apply 'encode-time (parse-time-string (concat date2 " 00:00:00"))))) (time-less-p time1 time2))) (defun my/date-format (org-date) "Convert an Org-mode date string ORG-DATE to a formatted date string." (let* ((parsed-time (org-parse-time-string org-date)) - (time (apply 'encode-time parsed-time))) + (time (apply 'encode-time parsed-time))) (format-time-string "posted on %Y-%m-%d" time))) (defun my/format-date-subtitle (file project) @@ -618,7 +618,7 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (org-element-map (plist-get info :parse-tree) 'keyword (lambda (keyword) (when (string= (org-element-property :key keyword) property) - (org-element-property :value keyword))) + (org-element-property :value keyword))) nil t)) (defun my/org-has-src-blocks-p (info) @@ -632,8 +632,8 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (let ((output-string input-string)) (dolist (pair my/lang-substitution-map) (let ((old (regexp-quote (car pair))) - (new (cdr pair))) - (setq output-string (replace-regexp-in-string old new output-string)))) + (new (cdr pair))) + (setq output-string (replace-regexp-in-string old new output-string)))) output-string)) ;; @@ -650,51 +650,51 @@ A neat thing I've implemented is a way to filter out unpublished posts using a < (setq org-publish-project-alist (list (list "blog-index" - :base-directory my/blog-src-path - :base-extension "org" - :recursive t - :publishing-directory my/web-export-path - :publishing-function 'my/publish-blog-index) + :base-directory my/blog-src-path + :base-extension "org" + :recursive t + :publishing-directory my/web-export-path + :publishing-function 'my/publish-blog-index) (list "static" - :base-directory my/blog-src-path - :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" - :publishing-directory my/web-export-path - :recursive t - :publishing-function 'org-publish-attachment - ) + :base-directory my/blog-src-path + :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" + :publishing-directory my/web-export-path + :recursive t + :publishing-function 'org-publish-attachment + ) (list "blog" - :recursive t - :base-directory my/blog-src-path - :publishing-directory my/web-export-path - :publishing-function 'my/publish-to-html - :html-html5-fancy t - :htmlized-source t - :with-author nil - :with-creator t - :with-toc t - :section-numbers nil - :time-stamp-file nil - ) + :recursive t + :base-directory my/blog-src-path + :publishing-directory my/web-export-path + :publishing-function 'my/publish-to-html + :html-html5-fancy t + :htmlized-source t + :with-author nil + :with-creator t + :with-toc t + :section-numbers nil + :time-stamp-file nil + ) (list "blog-rss" - :author "Alex M" - :email "iam@fidonode.me" - :base-directory my/blog-src-path - :base-extension "org" - :recursive t - :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org")) - :publishing-function 'my/publish-to-rss - :publishing-directory my/web-export-path - :rss-extension "xml" - :html-link-home my/url - :html-link-use-abs-url t - :html-link-org-files-as-html t - :auto-sitemap t - :sitemap-filename "rss.org" - :sitemap-title "rss" - :sitemap-style 'list - :sitemap-sort-files 'anti-chronologically - :sitemap-function 'my/format-rss-feed - :sitemap-format-entry 'my/format-rss-feed-entry) + :author "Alex M" + :email "iam@fidonode.me" + :base-directory my/blog-src-path + :base-extension "org" + :recursive t + :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org")) + :publishing-function 'my/publish-to-rss + :publishing-directory my/web-export-path + :rss-extension "xml" + :html-link-home my/url + :html-link-use-abs-url t + :html-link-org-files-as-html t + :auto-sitemap t + :sitemap-filename "rss.org" + :sitemap-title "rss" + :sitemap-style 'list + :sitemap-sort-files 'anti-chronologically + :sitemap-function 'my/format-rss-feed + :sitemap-format-entry 'my/format-rss-feed-entry) )) diff --git a/posts/improve_code_blocks.html b/posts/improve_code_blocks.html index bc9009f..34bb34c 100644 --- a/posts/improve_code_blocks.html +++ b/posts/improve_code_blocks.html @@ -1,34 +1,34 @@ -Improve code blocks

Improve code blocks

Use highlight.js for code syntax highlighting

+Improve code blocks

Improve code blocks

Use highlight.js for code syntax highlighting

Table of Contents

- -
-

What is the problem with default highlighting?

-
+
+

What is the problem with default highlighting?

+

Htmlize works poorly with headless publishing. It lacks extensibility, including features like line numbers, a copy button, and the ability to highlight predefined parts of the code.

-
-

Highlight.js

-
+
+

Highlight.js

+
-
-

Change code block template

-
+
+

Change code block template

+

We need to make small changes in how code blocks are rendered. By default, Org Export exports code blocks as <pre></pre>. For Highlight.js, we need <pre><code></code></pre>. Additionally, we need to set the correct language name in the class attribute. Since Highlight.js does not support elisp, I rewrite it to regular lisp using the my/replace-substring function.

@@ -40,38 +40,38 @@ We need to make small changes in how code blocks are rendered. By default, Org E (let ((output-string input-string)) (dolist (pair my/lang-substitution-map) (let ((old (regexp-quote (car pair))) - (new (cdr pair))) - (setq output-string (replace-regexp-in-string old new output-string)))) + (new (cdr pair))) + (setq output-string (replace-regexp-in-string old new output-string)))) output-string)) (defun my/src-block (src-block contents info) "Translate SRC-BLOCK element into HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let* ( - (org-language (format "language-%s" (org-element-property :language src-block))) - (language (my/replace-substrings org-language)) - (code (org-element-property :value src-block))) + (org-language (format "language-%s" (org-element-property :language src-block))) + (language (my/replace-substrings org-language)) + (code (org-element-property :value src-block))) (esxml-to-xml `(pre () (code ((class . ,language)) - ,(org-html-encode-plain-text code) - )) + ,(org-html-encode-plain-text code) + )) ) ) ) (org-export-define-derived-backend 'my-html 'html :translate-alist '( - (template . my/template) - (src-block . my/src-block) - )) + (template . my/template) + (src-block . my/src-block) + ))
-
-

Plug Highlight.js

-
+
+

Plug Highlight.js

+

I do not want to load Highlight.js on every page, so I need to check if the initial Org file contains code blocks. Depending on this, we will render the part of the tree with JavaScript or not.

@@ -84,20 +84,20 @@ I do not want to load Highlight.js on every page, so I need to check if the init (defun my/template (contents info) (let* ((title-str (org-export-data (plist-get info :title) info)) - ... - (has-src-blocks (my/org-has-src-blocks-p info))) + ... + (has-src-blocks (my/org-has-src-blocks-p info))) ... (script ((defer . "true") (src . "https://umami.dokutsu.xyz/script.js") (data-website-id . "d52d9af1-0c7d-4531-84c6-0b9c2850011f")) ()) ,(when has-src-blocks `(nil () - (link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) - (script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) - (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) - (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) - (script ((src . "/resources/js/theme-selector.js")) ()) - ) + (link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) + (script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) + (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) + (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) + (script ((src . "/resources/js/theme-selector.js")) ()) + ) ) (title () ,title-str) @@ -105,9 +105,9 @@ I do not want to load Highlight.js on every page, so I need to check if the init
-
-

Respect prefers-color-scheme

-
+
+

Respect prefers-color-scheme

+

Additionally, I think it's a good idea to respect the prefers-color-scheme property of the user's browser. We can switch CSS files based on this property. We should also subscribe to changes in this property to handle the edge case when it switches while reading the page.

@@ -142,9 +142,9 @@ window
-
-

Whole config

-
+
+

Whole config

+

In between posts I've switched from sxml to esxml so here is the current config.

@@ -157,7 +157,7 @@ In between posts I've switched from sxml to esxml so h (normal-top-level-add-subdirs-to-load-path)) (add-to-list 'custom-theme-load-path - (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) + (concat "~/.config/emacs/.local/straight/build-" emacs-version "/doom-themes")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/base16-theme")) (add-to-list 'custom-theme-load-path (concat "~/.config/emacs/.local/straight/build-" emacs-version "/moe-theme")) @@ -189,28 +189,28 @@ In between posts I've switched from sxml to esxml so h `(footer ((class . "footer")) (hr () ) (small () - (p () "Alex Mikhailov") - (p () "Built with: " - (a ((href . "https://www.gnu.org/software/emacs/")) "GNU Emacs") " " - (a ((href . "https://orgmode.org/")) "Org Mode") " " - (a ((href . "https://picocss.com/")) "picocss") - ) - ) + (p () "Alex Mikhailov") + (p () "Built with: " + (a ((href . "https://www.gnu.org/software/emacs/")) "GNU Emacs") " " + (a ((href . "https://orgmode.org/")) "Org Mode") " " + (a ((href . "https://picocss.com/")) "picocss") + ) + ) )) (defun my/header (info) (let ((title-str (org-export-data (plist-get info :title) info))) `(header ((class . "header")) (nav () - (ul () - (li () - (strong () ,title-str))) - (ul () - (li () (a ((href . "/index.html")) "About")) - (li () (a ((href . "/posts.html")) "Blog")) - (li () (a ((href . "/rss.xml")) "RSS")) - ) - )) + (ul () + (li () + (strong () ,title-str))) + (ul () + (li () (a ((href . "/index.html")) "About")) + (li () (a ((href . "/posts.html")) "Blog")) + (li () (a ((href . "/rss.xml")) "RSS")) + ) + )) ) ) @@ -218,14 +218,14 @@ In between posts I've switched from sxml to esxml so h "Translate SRC-BLOCK element into HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let* ( - (org-language (format "language-%s" (org-element-property :language src-block))) - (language (my/replace-substrings org-language)) - (code (org-element-property :value src-block))) + (org-language (format "language-%s" (org-element-property :language src-block))) + (language (my/replace-substrings org-language)) + (code (org-element-property :value src-block))) (esxml-to-xml `(pre () (code ((class . ,language)) - ,(org-html-encode-plain-text code) - )) + ,(org-html-encode-plain-text code) + )) ) ) ) @@ -234,12 +234,12 @@ CONTENTS is nil. INFO is a plist holding contextual information." (defun my/template (contents info) (let* ((title-str (org-export-data (plist-get info :title) info)) - (description-str (org-export-data (plist-get info :description) info)) - (file-path-str (org-export-data (plist-get info :input-file) info)) - (base-directory-str (org-export-data (plist-get info :base-directory) info)) - (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str))) - (img-link-str (format "%s/resources/images/%s.png" my/url file-name-str)) - (has-src-blocks (my/org-has-src-blocks-p info))) + (description-str (org-export-data (plist-get info :description) info)) + (file-path-str (org-export-data (plist-get info :input-file) info)) + (base-directory-str (org-export-data (plist-get info :base-directory) info)) + (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str))) + (img-link-str (format "%s/resources/images/%s.png" my/url file-name-str)) + (has-src-blocks (my/org-has-src-blocks-p info))) (set-text-properties 0 (length title-str) nil title-str) (set-text-properties 0 (length description-str) nil description-str) @@ -249,57 +249,57 @@ CONTENTS is nil. INFO is a plist holding contextual information." "&lt;!DOCTYPE html&gt;" (esxml-to-xml `(html ((lang . "en")) - (head () - (meta ((charset . "utf-8"))) - (meta ((author . "Alex Mikhailov"))) - (meta ((name . "viewport") - (content . "width=device-width, initial-scale=1, shrink-to-fit=no"))) - (meta ((name . "color-scheme") (content . "light dark"))) - (meta ((http-equiv . "content-language") (content . "en-us"))) - ;; OG block - ;; "Personal page with a blog about my technical adventures" - (meta ((name . "description") (content . ,description-str))) - (meta ((name . "og:description") (content . ,description-str))) - (meta ((name . "twitter:description") (content . ,description-str))) + (head () + (meta ((charset . "utf-8"))) + (meta ((author . "Alex Mikhailov"))) + (meta ((name . "viewport") + (content . "width=device-width, initial-scale=1, shrink-to-fit=no"))) + (meta ((name . "color-scheme") (content . "light dark"))) + (meta ((http-equiv . "content-language") (content . "en-us"))) + ;; OG block + ;; "Personal page with a blog about my technical adventures" + (meta ((name . "description") (content . ,description-str))) + (meta ((name . "og:description") (content . ,description-str))) + (meta ((name . "twitter:description") (content . ,description-str))) - (meta ((name . "og:image") (content . ,img-link-str))) - (meta ((name . "twitter:image") (content . ,img-link-str))) + (meta ((name . "og:image") (content . ,img-link-str))) + (meta ((name . "twitter:image") (content . ,img-link-str))) - (meta ((name . "og:title") (content . ,title-str))) - (meta ((name . "twitter:title") (content . ,title-str))) + (meta ((name . "og:title") (content . ,title-str))) + (meta ((name . "twitter:title") (content . ,title-str))) - (meta ((name . "twitter:card") (content . "summary_large_image"))) + (meta ((name . "twitter:card") (content . "summary_large_image"))) - (link ((rel . "icon") (type . "image/x-icon") (href . "/resources/favicon.ico"))) - (link ((rel . "stylesheet") (type . "text/css") (href . "/resources/css/pico.sand.min.css"))) - (script ((defer . "true") (src . "https://umami.dokutsu.xyz/script.js") (data-website-id . "d52d9af1-0c7d-4531-84c6-0b9c2850011f")) ()) - ,(when has-src-blocks - `(nil () - (link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) - (script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) - (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) - (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) - (script ((src . "/resources/js/theme-selector.js")) ()) - ) - ) - (title () ,title-str) - ) - (body () - (main ((class . "container")) - ,(my/header info) - (raw-string ,contents) - ,(my/footer info) - ) - )) + (link ((rel . "icon") (type . "image/x-icon") (href . "/resources/favicon.ico"))) + (link ((rel . "stylesheet") (type . "text/css") (href . "/resources/css/pico.sand.min.css"))) + (script ((defer . "true") (src . "https://umami.dokutsu.xyz/script.js") (data-website-id . "d52d9af1-0c7d-4531-84c6-0b9c2850011f")) ()) + ,(when has-src-blocks + `(nil () + (link ((id . "highlight-theme") (rel . "stylesheet") (type . "text/css"))) + (script ((src . "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js")) ()) + (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js")) ()) + (script ((src . "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js")) ()) + (script ((src . "/resources/js/theme-selector.js")) ()) + ) + ) + (title () ,title-str) + ) + (body () + (main ((class . "container")) + ,(my/header info) + (raw-string ,contents) + ,(my/footer info) + ) + )) )) )) (org-export-define-derived-backend 'my-html 'html :translate-alist '( - (template . my/template) - (src-block . my/src-block) - )) + (template . my/template) + (src-block . my/src-block) + )) (defun my/publish-to-html (plist filename pub-dir) "Publish an Org file to HTML using the custom backend." @@ -312,31 +312,31 @@ CONTENTS is nil. INFO is a plist holding contextual information." ENTRY is a file name. STYLE is either 'list' or 'tree'. PROJECT is the current project." (cond ((not (directory-name-p entry)) - (let* ((file (org-publish--expand-file-name entry project)) - (title (org-publish-find-title entry project)) - (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) - (link (concat (file-name-sans-extension entry) ".html"))) - (with-temp-buffer - (org-mode) - (insert (format "* [[file:%s][%s]]\n" file title)) - (org-set-property "RSS_PERMALINK" link) - (org-set-property "RSS_TITLE" title) - (org-set-property "PUBDATE" date) - (let ((first-two-lines (with-temp-buffer - (insert-file-contents file) - (buffer-substring-no-properties - (point-min) - (progn (forward-line 2) (point)))))) - (if (string-suffix-p "\n" first-two-lines) - (setq first-two-lines (substring first-two-lines 0 -1))) - (insert first-two-lines)) - (goto-char (point-max)) - (insert "...") - (buffer-string)))) - ((eq style 'tree) - ;; Return only last subdir. - (file-name-nondirectory (directory-file-name entry))) - (t entry))) + (let* ((file (org-publish--expand-file-name entry project)) + (title (org-publish-find-title entry project)) + (date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))) + (link (concat (file-name-sans-extension entry) ".html"))) + (with-temp-buffer + (org-mode) + (insert (format "* [[file:%s][%s]]\n" file title)) + (org-set-property "RSS_PERMALINK" link) + (org-set-property "RSS_TITLE" title) + (org-set-property "PUBDATE" date) + (let ((first-two-lines (with-temp-buffer + (insert-file-contents file) + (buffer-substring-no-properties + (point-min) + (progn (forward-line 2) (point)))))) + (if (string-suffix-p "\n" first-two-lines) + (setq first-two-lines (substring first-two-lines 0 -1))) + (insert first-two-lines)) + (goto-char (point-max)) + (insert "...") + (buffer-string)))) + ((eq style 'tree) + ;; Return only last subdir. + (file-name-nondirectory (directory-file-name entry))) + (t entry))) (defun my/format-rss-feed (title list) "Generate RSS feed, as a string. @@ -344,8 +344,8 @@ TITLE is the title of the RSS feed. LIST is an internal representation for the files to include, as returned by `org-list-to-lisp'. PROJECT is the current project." (concat "#+TITLE: " title "\n" - "#+STARTUP: showall \n\n" - (org-list-to-subtree list 1 '(:icount "" :istart "")))) + "#+STARTUP: showall \n\n" + (org-list-to-subtree list 1 '(:icount "" :istart "")))) (defun my/publish-to-rss (plist filename pub-dir) "Publish RSS with PLIST, only when FILENAME is 'rss.org'. @@ -387,8 +387,8 @@ PUB-DIR is when the output will be placed." (let ((output-string input-string)) (dolist (pair my/lang-substitution-map) (let ((old (regexp-quote (car pair))) - (new (cdr pair))) - (setq output-string (replace-regexp-in-string old new output-string)))) + (new (cdr pair))) + (setq output-string (replace-regexp-in-string old new output-string)))) output-string)) ;; @@ -405,45 +405,45 @@ PUB-DIR is when the output will be placed." (setq org-publish-project-alist (list (list "static" - :base-directory my/blog-src-path - :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" - :publishing-directory my/web-export-path - :recursive t - :publishing-function 'org-publish-attachment - ) + :base-directory my/blog-src-path + :base-extension "css\\|js\\|png\\|jpg\\|jpeg\\|gif\\|pdf\\|ico\\|txt" + :publishing-directory my/web-export-path + :recursive t + :publishing-function 'org-publish-attachment + ) (list "blog" - :recursive t - :base-directory my/blog-src-path - :publishing-directory my/web-export-path - :publishing-function 'my/publish-to-html - :html-html5-fancy t - :htmlized-source t - :with-author nil - :with-creator t - :with-toc t - :section-numbers nil - :time-stamp-file nil - ) + :recursive t + :base-directory my/blog-src-path + :publishing-directory my/web-export-path + :publishing-function 'my/publish-to-html + :html-html5-fancy t + :htmlized-source t + :with-author nil + :with-creator t + :with-toc t + :section-numbers nil + :time-stamp-file nil + ) (list "blog-rss" - :author "Alex M" - :email "iam@fidonode.me" - :base-directory my/blog-src-path - :base-extension "org" - :recursive t - :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org")) - :publishing-function 'my/publish-to-rss - :publishing-directory my/web-export-path - :rss-extension "xml" - :html-link-home my/url - :html-link-use-abs-url t - :html-link-org-files-as-html t - :auto-sitemap t - :sitemap-filename "rss.org" - :sitemap-title "rss" - :sitemap-style 'list - :sitemap-sort-files 'anti-chronologically - :sitemap-function 'my/format-rss-feed - :sitemap-format-entry 'my/format-rss-feed-entry) + :author "Alex M" + :email "iam@fidonode.me" + :base-directory my/blog-src-path + :base-extension "org" + :recursive t + :exclude (regexp-opt '("rss.org" "index.org" "404.org" "posts.org")) + :publishing-function 'my/publish-to-rss + :publishing-directory my/web-export-path + :rss-extension "xml" + :html-link-home my/url + :html-link-use-abs-url t + :html-link-org-files-as-html t + :auto-sitemap t + :sitemap-filename "rss.org" + :sitemap-title "rss" + :sitemap-style 'list + :sitemap-sort-files 'anti-chronologically + :sitemap-function 'my/format-rss-feed + :sitemap-format-entry 'my/format-rss-feed-entry) )) diff --git a/posts/keeb.html b/posts/keeb.html index 5757201..c5a8c9a 100644 --- a/posts/keeb.html +++ b/posts/keeb.html @@ -1,59 +1,59 @@ -My keyboard journey

My keyboard journey

Blog post about my keyboards

+My keyboard journey

My keyboard journey

Blog post about my keyboards

Table of Contents

- -
-

My end-game (at least I hope) keyboard

-
+
+

My end-game (at least I hope) keyboard

+
-
-

Keebs path

-
+
+

Keebs path

+

Sometimes I think about the long journey I've made with keebs. In childhood, I had decent membrane keyboards, most of which had an ergonomic profile like the MS. Not sure if it somehow affected my taste because I started my career with the simplest, cheapest board and typed countless lines of code on such keebs. Then I heard about clickity-clack mechanical keyboards and decided to try one. It was a simple Chinese keeb with a thick metal body, double-shot caps, and Cherry Brown switches. A decent thing to annoy everyone around you. I think this purchase marked my dive into mech keebs I'm not a geeky aficionado who thinks you can fix everything with a new keyboard, but I built a couple of them. I hope I've finally built the last one for quite some time.

-
-

Dactyl manuform

-
+
+

Dactyl manuform

+

Almost all of the time, I struggle with my maximalism. So I decided to build the ultimate mechanical ergonomic split keyboard and chose the Dactyl Manuform. Sounds like a crazy idea. Zero experience with QMK, zero experience with hand-wired keyboards, and zero experience in 3D printing. The last problem was the easiest one; I just asked my friend to print the bodies from PETG polymer, and Bob's your uncle. I got two pieces of rough-layered plastic with all the support structures. God, it was a nightmare to clean these prints from supports and small artifacts, but I was happy.

-
+

dactyl-manuform-6.jpg

@@ -63,25 +63,25 @@ I ordered a set of Kailh Brown switches, cheap no-name DSA caps, two controllers

-
+

dactyl-manuform-5.jpg

-
+

dactyl-manuform-4.jpg

-
+

dactyl-manuform-3.jpg

-
+

dactyl-manuform-2.jpg

@@ -92,37 +92,37 @@ To be honest, this keeb was ugly, and I decided that I wanted a beautiful factor

-
-

Moonlander

-
+
+

Moonlander

+

Nothing special. Ordered, paid, got it, tried it. Everything worked. Looked good. Happy year of typing. Bored. Annoyed. Too big and chunky. No concave. Quality not the best. Started planning the next one.

-
+

moonlander.jpg

-
-

Custom Corne

-
+
+

Custom Corne

+

This journey started with discovering the Jian keyboard. It is a niche keeb from the Ru community focused on full support of the whole Russian layout. It was originally created by KGOH. I missed the group buy and decided that I could easily patch a Corne board with two additional keys to mimic the Jian. Interesting journey. I learned how to use KiCad, and how to export gerbers.

-
+

jirne-5.png

-
+

jirne-6.png

@@ -132,13 +132,13 @@ I've ordered PCBs at JLCPCB.

-
+

jirne-8.jpeg

-
+

jirne-9.jpeg

@@ -148,7 +148,7 @@ The build came out pretty decent. I was happy. RGB underglow. Low-profile switch

-
+

jirne-7.jpeg

@@ -158,15 +158,15 @@ Daily driver for ~6 months. Then the world changed, and I decided to leave my ho

-
-

Dactyl manuform again

-
+
+

Dactyl manuform again

+

Two years late I've settled down in new country and decide that I want to bring back my dactyl manuform experience.

-
+

dactyl-pitch.jpeg

@@ -174,26 +174,26 @@ Two years late I've settled down in new country and decide that I want to bring
-
-

Hardware

-
+
+

Hardware

+
-
-

Body

-
+
+

Body

+

I've choose to use a Ryan's generator and generate body on top of Corne preset with all keys in last row and disabled stagger for the last two columns. Generator preset The body was printed by JLC3DP (JLCPCB printing department). I've choose SLS from nylon. Print has minor artifacts; I expected better quality.

-
+

dactyl-body-2.jpeg

-
+

dactyl-body-1.jpeg

@@ -203,28 +203,28 @@ Overall, I'm happy with results. I also printed bottom plates and +

dactyl-body-3.jpeg

-
+

dactyl-body-4.jpeg

-
-

Switches and caps

-
+
+

Switches and caps

+

I've chosen Kailh BOX Navy switches. I really like the clickity-clack sound. They have a dedicated clickbar to produce this sound, and the box profile helps with moving down perpendicularly.

-
+

kailh-box.jpg

@@ -234,30 +234,30 @@ The caps are inherited from the Moonlander. They are thick, double-shot caps wit

-
-

Controllers

-
+
+

Controllers

+

I used a bootleg Pro Micro called Tenstar Robot, based on the ATmega32u4. It's perfectly supported by QMK, pin-to-pin and size-compatible with the Pro Micro.

-
+

dactyl-all-3.jpg

-
-

Amoeba things

-
+
+

Amoeba things

+

During this build, I decided that I did not want to make a big mess of wires and chose Amoeba single-switch PCBs.

-
+

amoeba.jpg

@@ -267,13 +267,13 @@ They are nice, have diodes on board, and simplify wiring. However, they have the

-
+

dactyl-all-1.jpg

-
+

dactyl-all-5.jpg

@@ -281,13 +281,13 @@ They are nice, have diodes on board, and simplify wiring. However, they have the
-
-

Software

-
+
+

Software

+
-
-

Plain default - QMK

-
+
+

Plain default - QMK

+

Prerequiremets: QMK CLI @@ -309,9 +309,9 @@ You may want to create a separate keyboard entry in QMK.

-
-

Make own layout

-
+
+

Make own layout

+

I'll try to go through setting of my personal layout. It is based on Jian layout.

@@ -354,17 +354,17 @@ RGB underglow configuration. "led_count": 16, "led_map": [7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15], "animations": { - "static_light": true, - "breathing": true, - "rainbow_mood": true, - "snake": false + "static_light": true, + "breathing": true, + "rainbow_mood": true, + "snake": false }, "layers": { - "enabled": true, - "blink": true + "enabled": true, + "blink": true }, "default": { - "animation": "rainbow_mood" + "animation": "rainbow_mood" }, "split": true, "split_count": [8, 8] @@ -384,12 +384,12 @@ Turn on split feature, assign pin for halves communication, choose what to sync. "enabled": true, "soft_serial_pin": "D2", "transport": { - "protocol": "serial", - "sync": { - "layer_state": true, - "indicators": true, - "modifiers": true - } + "protocol": "serial", + "sync": { + "layer_state": true, + "indicators": true, + "modifiers": true + } } }, @@ -413,13 +413,13 @@ This is the keymaps/default/keymap.c file. #define _S 3 const rgblight_segment_t PROGMEM system_layer[] = RGBLIGHT_LAYER_SEGMENTS({0, 4, HSV_RED}, - {12, 4, HSV_RED}); + {12, 4, HSV_RED}); const rgblight_segment_t PROGMEM lower_layer[] = RGBLIGHT_LAYER_SEGMENTS({5, 6, HSV_CYAN}); const rgblight_segment_t PROGMEM raise_layer[] = RGBLIGHT_LAYER_SEGMENTS({5, 6, HSV_PURPLE}); const rgblight_segment_t* const PROGMEM my_rgb_layers[] = RGBLIGHT_LAYERS_LIST(system_layer, - lower_layer, - raise_layer + lower_layer, + raise_layer ); void keyboard_post_init_user(void) { @@ -436,30 +436,30 @@ layer_state_t layer_state_set_user(layer_state_t state) { const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { // clang-format off - [_B] = LAYOUT_split_4x7_3( - KC_GRAVE, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LEFT_BRACKET, - KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, RCTL_T(KC_QUOTE), - KC_LALT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLASH, RALT_T(KC_BACKSLASH), - KC_CAPS, KC_NO, KC_NO, KC_LEFT_GUI, LT(_R, KC_TAB), LSFT_T(KC_SPC), LT(_L, KC_ENT), LSFT_T(KC_BSPC), LT(_R, KC_DEL), LGUI_T(KC_RCBR), KC_D, KC_E, KC_F, LT(_L, KC_ESC) - ), - [_L] = LAYOUT_split_4x7_3( - KC_UNDS, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, - LCTL_T(KC_PLUS), KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, RCTL_T(KC_KP_MINUS), - LALT_T(KC_PEQL), KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_RALT, - KC_NO, KC_NO, KC_NO, KC_LEFT_GUI, KC_TRNS, LSFT_T(KC_SPC), KC_TRNS, LSFT_T(KC_BSPC), KC_TRNS, LGUI_T(KC_F12), KC_NO, KC_NO, KC_NO, KC_TRNS - ), - [_R] = LAYOUT_split_4x7_3( - KC_NUM, KC_PSLS, KC_7, KC_8, KC_9, KC_PMNS, KC_VOLU, KC_HOME, KC_PSCR, KC_PGUP, KC_SCRL, KC_CAPS, - LCTL_T(KC_PEQL), KC_PAST, KC_4, KC_5, KC_6, KC_PPLS, KC_MUTE, KC_LEFT, KC_UP, KC_RIGHT, KC_INS, RCTL_T(KC_APP), - KC_LALT, KC_0, KC_1, KC_2, KC_3, KC_PDOT, KC_VOLD, KC_END, KC_DOWN, KC_PGDN, KC_PAUS, KC_RALT, - KC_NO, KC_NO, KC_NO, KC_LEFT_GUI, KC_TRNS, LSFT_T(KC_SPC), KC_TRNS, LSFT_T(KC_BSPC), KC_TRNS, KC_LGUI, KC_NO, KC_NO, KC_NO, KC_TRNS - ), + [_B] = LAYOUT_split_4x7_3( + KC_GRAVE, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LEFT_BRACKET, + KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, RCTL_T(KC_QUOTE), + KC_LALT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLASH, RALT_T(KC_BACKSLASH), + KC_CAPS, KC_NO, KC_NO, KC_LEFT_GUI, LT(_R, KC_TAB), LSFT_T(KC_SPC), LT(_L, KC_ENT), LSFT_T(KC_BSPC), LT(_R, KC_DEL), LGUI_T(KC_RCBR), KC_D, KC_E, KC_F, LT(_L, KC_ESC) + ), + [_L] = LAYOUT_split_4x7_3( + KC_UNDS, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, + LCTL_T(KC_PLUS), KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, RCTL_T(KC_KP_MINUS), + LALT_T(KC_PEQL), KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_RALT, + KC_NO, KC_NO, KC_NO, KC_LEFT_GUI, KC_TRNS, LSFT_T(KC_SPC), KC_TRNS, LSFT_T(KC_BSPC), KC_TRNS, LGUI_T(KC_F12), KC_NO, KC_NO, KC_NO, KC_TRNS + ), + [_R] = LAYOUT_split_4x7_3( + KC_NUM, KC_PSLS, KC_7, KC_8, KC_9, KC_PMNS, KC_VOLU, KC_HOME, KC_PSCR, KC_PGUP, KC_SCRL, KC_CAPS, + LCTL_T(KC_PEQL), KC_PAST, KC_4, KC_5, KC_6, KC_PPLS, KC_MUTE, KC_LEFT, KC_UP, KC_RIGHT, KC_INS, RCTL_T(KC_APP), + KC_LALT, KC_0, KC_1, KC_2, KC_3, KC_PDOT, KC_VOLD, KC_END, KC_DOWN, KC_PGDN, KC_PAUS, KC_RALT, + KC_NO, KC_NO, KC_NO, KC_LEFT_GUI, KC_TRNS, LSFT_T(KC_SPC), KC_TRNS, LSFT_T(KC_BSPC), KC_TRNS, KC_LGUI, KC_NO, KC_NO, KC_NO, KC_TRNS + ), [_S] = LAYOUT_split_4x7_3( - DB_TOGG, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, DB_TOGG, - QK_BOOT, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, QK_BOOT, - RGB_MOD, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, - RGB_RMOD, KC_NO, KC_NO, QK_RBT, KC_TRNS, KC_NO, KC_TRNS, KC_NO, KC_TRNS, QK_RBT, KC_NO, KC_NO, KC_NO, KC_TRNS - ), + DB_TOGG, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, DB_TOGG, + QK_BOOT, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, QK_BOOT, + RGB_MOD, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, + RGB_RMOD, KC_NO, KC_NO, QK_RBT, KC_TRNS, KC_NO, KC_TRNS, KC_NO, KC_TRNS, QK_RBT, KC_NO, KC_NO, KC_NO, KC_TRNS + ), // clang-format on }; @@ -468,8 +468,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
-
-

Whats next?

+
+

Whats next?

diff --git a/posts/posts_preview.html b/posts/posts_preview.html index d159d30..b9e2ad2 100644 --- a/posts/posts_preview.html +++ b/posts/posts_preview.html @@ -1,32 +1,32 @@ -Posts preview

Posts preview

Add post preview for OpenGraph cards

+Posts preview

Posts preview

Add post preview for OpenGraph cards

Table of Contents

- -
-

What is imagemagick

-
+
+

What is imagemagick

+

Imagemagick is a ffmpeg of the image world. You can do a lot of fun things with it. For example you can take a picture, cut corners on it, place it on top of another image, add some text and get final result. So it is looks like a good tool for making previews from code.

-
-

Basic idea of this process

-
+
+

Basic idea of this process

+

I want to hook a process of rendering post. Since this function called on each run of publishing and for each post it is a good idea to cache resulting images. I gonna simply check presence of preview image and use it as guard for running image generation. After that I gonna extract #+TITLE and #+DESCRIPTION properties from Org file. Each Org file I have, has next header:

@@ -48,30 +48,30 @@ So I with all these data I can generate my previews with simple script:
-
-

Integrate into build

-
+
+

Integrate into build

+

As I already mention I gonna skip file generation when file already here. Here is the whole function. Pretty simple. Just prepare pathes, check some dependencies, create pathes and execute script which calls imagemagick.

(defun my/render-preview (file-name title description)
   (let* ((has-imagemagick (executable-find "magick"))
-	 (full-file-path (file-truename(format "%s%s/resources/images/preview/%s.png" script-directory my/blog-src-path file-name )))
-	 (file-dir (file-name-directory full-file-path))
-	 (has-dir (file-directory-p file-dir))
-	 (has-file (file-exists-p full-file-path))
-	 (path-to-script-root (format "%shelpers" script-directory))
-	 (path-to-script (format "%s/og_image_gen.sh" path-to-script-root)))
+         (full-file-path (file-truename(format "%s%s/resources/images/preview/%s.png" script-directory my/blog-src-path file-name )))
+         (file-dir (file-name-directory full-file-path))
+         (has-dir (file-directory-p file-dir))
+         (has-file (file-exists-p full-file-path))
+         (path-to-script-root (format "%shelpers" script-directory))
+         (path-to-script (format "%s/og_image_gen.sh" path-to-script-root)))
     (if (and has-imagemagick
-	     (and description (not (string= description ""))))
-	(progn
-	  (when (not has-file)
-	    (progn
-	      (when (not has-dir)
-		(make-directory file-dir t))
-	      (shell-command (format "bash '%s' '%s' '%s' '%s' '%s'" path-to-script title description full-file-path path-to-script-root))
-	      )
-	    ))
+             (and description (not (string= description ""))))
+        (progn
+          (when (not has-file)
+            (progn
+              (when (not has-dir)
+                (make-directory file-dir t))
+              (shell-command (format "bash '%s' '%s' '%s' '%s' '%s'" path-to-script title description full-file-path path-to-script-root))
+              )
+            ))
       (message "Imagemagick is not installed. Preview generation skipped.")
       )))
 
@@ -82,11 +82,11 @@ And here is the part of my/template function related to OpenGraph m
(defun my/template (contents info)
 
   (let* ((title-str (org-export-data (plist-get info :title) info))
-	 (description-str (org-export-data (plist-get info :description) info))
-	 (file-path-str (org-export-data (plist-get info :input-file) info))
-	 (base-directory-str (org-export-data (plist-get info :base-directory) info))
-	 (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str)))
-	 (img-link-str (format "%s/resources/images/preview/%s.png" my/url file-name-str))
+         (description-str (org-export-data (plist-get info :description) info))
+         (file-path-str (org-export-data (plist-get info :input-file) info))
+         (base-directory-str (org-export-data (plist-get info :base-directory) info))
+         (file-name-str (file-relative-name file-path-str (format "%s/%s" script-directory base-directory-str)))
+         (img-link-str (format "%s/resources/images/preview/%s.png" my/url file-name-str))
 
     (my/render-preview file-name-str title-str description-str)
 
@@ -94,18 +94,18 @@ And here is the part of my/template function related to OpenGraph m
     (set-text-properties 0 (length description-str) nil description-str)
     (set-text-properties 0 (length img-link-str) nil img-link-str)
 ...
-	      ;; OG block
-	      (meta ((name . "description") (content .  ,description-str)))
-	      (meta ((name . "og:description") (content . ,description-str)))
-	      (meta ((name . "twitter:description") (content . ,description-str)))
+              ;; OG block
+              (meta ((name . "description") (content .  ,description-str)))
+              (meta ((name . "og:description") (content . ,description-str)))
+              (meta ((name . "twitter:description") (content . ,description-str)))
 
-	      (meta ((name . "og:image") (content . ,img-link-str)))
-	      (meta ((name . "twitter:image") (content . ,img-link-str)))
+              (meta ((name . "og:image") (content . ,img-link-str)))
+              (meta ((name . "twitter:image") (content . ,img-link-str)))
 
-	      (meta ((name . "og:title") (content . ,title-str)))
-	      (meta ((name . "twitter:title") (content . ,title-str)))
+              (meta ((name . "og:title") (content . ,title-str)))
+              (meta ((name . "twitter:title") (content . ,title-str)))
 
-	      (meta ((name . "twitter:card") (content . "summary_large_image")))
+              (meta ((name . "twitter:card") (content . "summary_large_image")))
 
 

@@ -113,38 +113,38 @@ You can check whole function in previous post -

Whats next?

-
+
+

Whats next?

+
-
-

Tags

-
+ -
-

Post series

-
+
+

Post series

+

Dunno how, but I'll figure out something.

-
-

Adopt/fix htmlize.el

-
+
+

Adopt/fix htmlize.el

+

I want to highlight code during publishing step.

-
-

Show more meta on posts index page.

-
+
+

Show more meta on posts index page.

+

Creation date, preview, tags, whatever. Blog index and tags automation diff --git a/rss.html b/rss.html index da015f1..66cbdfe 100644 --- a/rss.html +++ b/rss.html @@ -1,87 +1,93 @@ -rss

+Alex's M Personal Blog

Table of Contents

- -
-

Tag: @tags

-
+
+

Blog index and tags automation

+
+

+Let's add tags to blog posts +

+ +

+Tags are a nice and easy way to organize posts without explicit search. In the simplest way, you have a list of tags in posts, and each tag links to a page with all posts having the corresponding tag. It is also helpful to have a page with all tags available in the blog. And, of course, I don't want to maintain the list of tags manually. +Automate tags…. +

-
-

Tag: @imagemagick

-
+
+

Posts preview

+
+

+Add post preview for OpenGraph cards +

+ +

+Imagemagick is a ffmpeg of the image world. You can do a lot of fun things with it. For example you can take a picture, cut corners on it, place it on top of another image, add some text and get final result. So it is looks like a good tool for making previews from code. +Basic idea of this process… +

-
-

Tag: @diy

-
+
+

Improve code blocks

+
+

+Use highlight.js for code syntax highlighting +

+ +

+Htmlize works poorly with headless publishing. It lacks extensibility, including features like line numbers, a copy button, and the ability to highlight predefined parts of the code. +Highlight.js… +

-
-

Tag: @keeb

-
+
+

Org blog with RSS

+
+

+Let's add RSS feed to blog +

+ +

+RSS might seem like an outdated, marginal thing. But it still has at least one benefit—you can use an RSS feed as a sitemap for search engines. Plus, it's pretty geeky. +Add RSS feed… +

-
-

Tag: @highlightjs

-
+
+

Org to HTML and back

+
+

+Blog post about publishing my blog with Org Mode +

+ +

+I'm neither proficient in Org Mode (further on "Org"), nor a good front-end engineer. I think that a simple solution is better than no solution. If you see a mistake, you can contact me via iam@fidonode.me. +What is Org?… +

-
-

Tag: @elisp

-
-
-
- - - - - - -
-

My keyboard journey

-
+
+

My keyboard journey

+
+

+Blog post about my keyboards +

+ +

+Sometimes I think about the long journey I've made with keebs. In childhood, I had decent membrane keyboards, most of which had an ergonomic profile like the MS. Not sure if it somehow affected my taste because I started my career with the simplest, cheapest board and typed countless lines of code on such keebs. Then I heard about clickity-clack mechanical keyboards and decided to try one. It was a simple Chinese keeb with a thick metal body, double-shot caps, and Cherry Brown switches. A decent thing to annoy everyone around you. I think this purchase marked my dive into mech keebs +I'm not a geeky aficionado who thinks you can fix everything with a new keyboard, but I built a couple of them. I hope I've finally built the last one for quite some time…. +

diff --git a/rss.xml b/rss.xml index 0413fef..b7caf4f 100644 --- a/rss.xml +++ b/rss.xml @@ -9,85 +9,22 @@ xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"> - rss + Alex's M Personal Blog https://fidonode.me - + en - Sun, 07 Jul 2024 09:10:01 +0000 - Sun, 07 Jul 2024 09:10:01 +0000 - Emacs 27.1 Org-mode 9.3 + Sat, 19 Oct 2024 16:09:51 +0000 + Sat, 19 Oct 2024 16:09:51 +0000 + Emacs 29.3 Org-mode 9.6.15 iam@fidonode.me (Alex M) - https://orgmode.org/img/org-mode-unicorn-logo.png - rss + https://fidonode.me/resources/images/index/avatar.jpg + Alex's M Personal Blog https://fidonode.me - - Tag: @imagemagick - https://fidonode.me/tags/@imagemagick.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@imagemagick.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @diy - https://fidonode.me/tags/@diy.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@diy.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @org-mode - https://fidonode.me/tags/@org-mode.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@org-mode.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @keeb - https://fidonode.me/tags/@keeb.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@keeb.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @highlightjs - https://fidonode.me/tags/@highlightjs.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@highlightjs.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @tags - https://fidonode.me/tags/@tags.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@tags.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - - - Tag: @elisp - https://fidonode.me/tags/@elisp.html - iam@fidonode.me (Alex M) - https://fidonode.me/tags/@elisp.html - Sun, 07 Jul 2024 00:00:00 +0000 - - - Blog index and tags automation https://fidonode.me/posts/blog_index_and_tags_automation.html @@ -95,7 +32,15 @@ https://fidonode.me/posts/blog_index_and_tags_automation.html Fri, 05 Jul 2024 00:00:00 +0000 - + + Let's add tags to blog posts +

+ +

+ Tags are a nice and easy way to organize posts without explicit search. In the simplest way, you have a list of tags in posts, and each tag links to a page with all posts having the corresponding tag. It is also helpful to have a page with all tags available in the blog. And, of course, I don't want to maintain the list of tags manually. + Automate tags.... +

+ ]]>
Posts preview @@ -104,7 +49,15 @@ https://fidonode.me/posts/posts_preview.html Fri, 28 Jun 2024 00:00:00 +0000 - + + Add post preview for OpenGraph cards +

+ +

+ Imagemagick is a ffmpeg of the image world. You can do a lot of fun things with it. For example you can take a picture, cut corners on it, place it on top of another image, add some text and get final result. So it is looks like a good tool for making previews from code. + Basic idea of this process... +

+ ]]>
Improve code blocks @@ -113,7 +66,15 @@ https://fidonode.me/posts/improve_code_blocks.html Tue, 25 Jun 2024 00:00:00 +0000 - + + Use highlight.js for code syntax highlighting +

+ +

+ Htmlize works poorly with headless publishing. It lacks extensibility, including features like line numbers, a copy button, and the ability to highlight predefined parts of the code. + Highlight.js... +

+ ]]>
Org blog with RSS @@ -122,7 +83,15 @@ https://fidonode.me/posts/add_rss_to_blog.html Sun, 23 Jun 2024 00:00:00 +0000 - + + Let's add RSS feed to blog +

+ +

+ RSS might seem like an outdated, marginal thing. But it still has at least one benefit—you can use an RSS feed as a sitemap for search engines. Plus, it's pretty geeky. + Add RSS feed... +

+ ]]>
Org to HTML and back @@ -131,7 +100,15 @@ https://fidonode.me/posts/about_blog.html Sat, 22 Jun 2024 00:00:00 +0000 - + + Blog post about publishing my blog with Org Mode +

+ +

+ I'm neither proficient in Org Mode (further on "Org"), nor a good front-end engineer. I think that a simple solution is better than no solution. If you see a mistake, you can contact me via iam@fidonode.me. + What is Org?... +

+ ]]>
My keyboard journey @@ -140,7 +117,15 @@ https://fidonode.me/posts/keeb.html Wed, 05 Jun 2024 00:00:00 +0000 - + + Blog post about my keyboards +

+ +

+ Sometimes I think about the long journey I've made with keebs. In childhood, I had decent membrane keyboards, most of which had an ergonomic profile like the MS. Not sure if it somehow affected my taste because I started my career with the simplest, cheapest board and typed countless lines of code on such keebs. Then I heard about clickity-clack mechanical keyboards and decided to try one. It was a simple Chinese keeb with a thick metal body, double-shot caps, and Cherry Brown switches. A decent thing to annoy everyone around you. I think this purchase marked my dive into mech keebs + I'm not a geeky aficionado who thinks you can fix everything with a new keyboard, but I built a couple of them. I hope I've finally built the last one for quite some time.... +

+ ]]>
diff --git a/tags/@diy.html b/tags/@diy.html index 601f0ad..4321d8e 100644 --- a/tags/@diy.html +++ b/tags/@diy.html @@ -1,11 +1,11 @@ Tag: @diy
-
-

@diy

-
+
+

@diy

+
-
-

My keyboard journey

-
+
+

My keyboard journey

+

Blog post about my keyboards

diff --git a/tags/@elisp.html b/tags/@elisp.html index 8a579bc..2e37651 100644 --- a/tags/@elisp.html +++ b/tags/@elisp.html @@ -1,11 +1,11 @@ Tag: @elisp
-
-

@elisp

-
+
+

@elisp

+
-
-

Posts preview

-
+
+

Posts preview

+

Add post preview for OpenGraph cards

@@ -20,9 +20,9 @@ drafted on 2024-06-28

-
-

Improve code blocks

-
+
+

Improve code blocks

+

Use highlight.js for code syntax highlighting

@@ -37,9 +37,9 @@ drafted on 2024-06-25

-
-

Blog index and tags automation

-
+
+

Blog index and tags automation

+

Let's add tags to blog posts

@@ -54,9 +54,9 @@ drafted on 2024-07-05

-
-

Org blog with RSS

-
+
+

Org blog with RSS

+

Let's add RSS feed to blog

diff --git a/tags/@highlightjs.html b/tags/@highlightjs.html index b8aaf54..b2f62bf 100644 --- a/tags/@highlightjs.html +++ b/tags/@highlightjs.html @@ -1,11 +1,11 @@ Tag: @highlightjs
-
-

@highlightjs

-
+
+

@highlightjs

+
-
-

Improve code blocks

-
+
+

Improve code blocks

+

Use highlight.js for code syntax highlighting

diff --git a/tags/@imagemagick.html b/tags/@imagemagick.html index 533aa3e..7461e51 100644 --- a/tags/@imagemagick.html +++ b/tags/@imagemagick.html @@ -1,11 +1,11 @@ Tag: @imagemagick
-
-

@imagemagick

-
+
+

@imagemagick

+
-
-

Posts preview

-
+
+

Posts preview

+

Add post preview for OpenGraph cards

diff --git a/tags/@keeb.html b/tags/@keeb.html index e95e637..f94e341 100644 --- a/tags/@keeb.html +++ b/tags/@keeb.html @@ -1,11 +1,11 @@ Tag: @keeb
-
-

@keeb

-
+
+

@keeb

+
-
-

My keyboard journey

-
+
+

My keyboard journey

+

Blog post about my keyboards

diff --git a/tags/@org-mode.html b/tags/@org-mode.html index 7c13aba..77a85d0 100644 --- a/tags/@org-mode.html +++ b/tags/@org-mode.html @@ -1,11 +1,11 @@ Tag: @org-mode
-
-

@org-mode

-
+
+

@org-mode

+
-
-

Posts preview

-
+
+

Posts preview

+

Add post preview for OpenGraph cards

@@ -20,9 +20,9 @@ drafted on 2024-06-28

-
-

Improve code blocks

-
+
+

Improve code blocks

+

Use highlight.js for code syntax highlighting

@@ -37,9 +37,9 @@ drafted on 2024-06-25

-
-

Blog index and tags automation

-
+
+

Blog index and tags automation

+

Let's add tags to blog posts

@@ -54,9 +54,9 @@ drafted on 2024-07-05

-
-

Org blog with RSS

-
+
+

Org blog with RSS

+

Let's add RSS feed to blog

@@ -71,9 +71,9 @@ drafted on 2024-06-23

-
-

Org to HTML and back

-
+
+

Org to HTML and back

+

Blog post about publishing my blog with Org Mode

diff --git a/tags/@rss.html b/tags/@rss.html index 39dd291..59e4523 100644 --- a/tags/@rss.html +++ b/tags/@rss.html @@ -1,11 +1,11 @@ Tag: @rss
-
-

@rss

-
+
+

@rss

+
-
-

Org blog with RSS

-
+
+

Org blog with RSS

+

Let's add RSS feed to blog

diff --git a/tags/@tags.html b/tags/@tags.html index 5b929f4..86b5725 100644 --- a/tags/@tags.html +++ b/tags/@tags.html @@ -1,11 +1,11 @@ Tag: @tags
-
-

@tags

-
+
+

@tags

+
-
-

Blog index and tags automation

-
+
+

Blog index and tags automation

+

Let's add tags to blog posts