From 27a039a955bf94254c04b7662de0283b0aee0888 Mon Sep 17 00:00:00 2001 From: fido-node Date: Mon, 24 Jun 2024 19:34:54 +0000 Subject: [PATCH] deploy: fido-node/org@6a9bddb2036bda8e25459b55b118a6f5ecdd06c1 --- index.html | 26 +- posts.html | 8 +- posts/about_blog.html | 515 ++++++++++++++--------------- posts/add_rss_to_blog.html | 182 +++++----- posts/keeb.html | 253 +++++++------- resources/css/prism-dark-theme.css | 0 resources/css/quirks.css | 4 + rss.html | 26 +- rss.xml | 4 +- 9 files changed, 485 insertions(+), 533 deletions(-) create mode 100644 resources/css/prism-dark-theme.css create mode 100644 resources/css/quirks.css diff --git a/index.html b/index.html index 94b194d..44e98a2 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,11 @@ -Alex Mikhailov
+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 c5ffdf6..7945083 100644 --- a/posts.html +++ b/posts.html @@ -1,7 +1,7 @@ -Alex Mikhailov - Blog
-
-

Posts

-
+Alex Mikhailov - Blog
+
+

Posts

+
  • Keyboard journey
  • Org to HTML and back
  • diff --git a/posts/about_blog.html b/posts/about_blog.html index 994e5c2..5442d67 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
    -
    -

    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,316 +69,305 @@ 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.

    -
    -
    (defun my/header (info)
    -  `(header (@ (class "header"))
    +
    (defun my/header (info)
    +  `(header (@ (class "header"))
         (nav
          (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"))
    -      (li (a (@ (href "/rss.xml")) "RSS"))
    +      (li (a (@ (href "/index.html")) "About"))
    +      (li (a (@ (href "/blog.html")) "Blog"))
    +      (li (a (@ (href "/rss.xml")) "RSS"))
           )
          ))
       )
     
    -(defun my/footer (info)
    -  `(footer (@ (class "footer"))
    +(defun my/footer (info)
    +  `(footer (@ (class "footer"))
         (hr)
    -    (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")
            )
         ))
     
    -
    -
    +

    Looks neat for me. At least I don't need to mess with string concatenation. Whole template wiring looks like that. Not much, but it works and easy to maintain.

    -
    -
    (defun my/template (contents info)
    +
    (defun my/template (contents info)
       (concat
    -   "<!DOCTYPE html>"
    +   "&lt;!DOCTYPE html&gt;"
        (sxml-to-xml
    -    `(html (@ (lang "en"))
    +    `(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")))
    -       (meta (@ (name "description") (content "Personal page with a blog about my technical adventures")))
    -       (link (@ (rel "icon") (type "image/x-icon") (href "/resources/favicon.ico")))
    -       (link (@ (rel "stylesheet") (href "/resources/css/pico.sand.min.css")))
    +       (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")))
    +       (meta (@ (name "description") (content "Personal page with a blog about my technical adventures")))
    +       (link (@ (rel "icon") (type "image/x-icon") (href "/resources/favicon.ico")))
    +       (link (@ (rel "stylesheet") (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")) ())
    -       (title ,(org-export-data (plist-get info :title) info)))
    +       (script (@ (defer "true") (src "https://umami.dokutsu.xyz/script.js") (data-website-id "d52d9af1-0c7d-4531-84c6-0b9c2850011f")) ())
    +       (title ,(org-export-data (plist-get info :title) info)))
     
           (body
    -       (main (@ (class "container"))
    -             ,(my/header info)
    -             (*RAW-STRING* ,contents)
    -             ,(my/footer info)
    -             )
    +       (main (@ (class "container"))
    +	     ,(my/header info)
    +	     (*RAW-STRING* ,contents)
    +	     ,(my/footer info)
    +	     )
            ))
         ))
       )
    -
    -
    +

    Ok, now we need some additional steps to wire these templating function.

    -
    -
    ;; Derive new backend with our custom tepmplating function
    -;; We derive it from regular HTML backend
    +
    ;; Derive new backend with our custom tepmplating function
    +;; We derive it from regular HTML backend
     
    -(org-export-define-derived-backend 'my-html 'html
    -  :translate-alist '((template . my/template)
    -                     ))
    +(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)
    -  "Publish an Org file to HTML using the custom backend."
    -  (org-publish-org-to 'my-html filename ".html" plist pub-dir))
    -
    -
    +;; Define publish function which uses our freshly derived backend +(defun my/publish-to-html (plist filename pub-dir) + "Publish an Org file to HTML using the custom backend." + (org-publish-org-to 'my-html filename ".html" plist pub-dir)) +

    So everything is almost done. Time to use our custom publishing function in projects list.

    -
    -
    (setq org-publish-project-alist
    +
    (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
    -             )
    +       (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
    +	     )
            ))
    -
    +
    -
    -
    -

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

    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.

    -
    -
    ;; Load the publishing system
    -;; Configure environment
    -;;
    -(setq debug-on-error t)
    +
    ;; Load the publishing system
    +;; Configure environment
    +;;
    +(setq debug-on-error t)
     
    -(let ((default-directory  (concat "~/.config/emacs/.local/straight/build-" emacs-version "/")))
    +(let ((default-directory  (concat "~/.config/emacs/.local/straight/build-" emacs-version "/")))
       (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"))
    -(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"))
    +(add-to-list 'custom-theme-load-path
    +	     (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"))
     
     
    -(require 'xml)
    -(require 'dom)
    -(require 'ox-publish)
    -(require 'ox-rss)
    -(require 'org)
    -(require 'esxml)
    +(require 'xml)
    +(require 'dom)
    +(require 'ox-publish)
    +(require 'ox-rss)
    +(require 'org)
    +(require 'esxml)
     
    -;;
    -;;Variables
    -;;
    -(setq
    - my/url "https://fidonode.me"
    - my/web-export-path "./public"
    - my/blog-src-path "./home/05 Blog"
    - org-html-validation-link nil            ;; Don't show validation link
    - org-html-htmlize-output-type 'inline-css
    +;;
    +;;Variables
    +;;
    +(setq
    + my/url "https://fidonode.me"
    + my/web-export-path "./public"
    + my/blog-src-path "./home/05 Blog"
    + org-html-validation-link nil            ;; Don't show validation link
    + org-html-htmlize-output-type 'inline-css
      org-src-fontify-natively t)
     
    -;;
    -;;Templates
    -;;
    -(defun my/footer (info)
    -  `(footer (@ (class "footer"))
    +;;
    +;;Templates
    +;;
    +(defun my/footer (info)
    +  `(footer (@ (class "footer"))
         (hr)
    -    (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)
    -  `(header (@ (class "header"))
    +(defun my/header (info)
    +  `(header (@ (class "header"))
         (nav
          (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"))
    -      (li (a (@ (href "/rss.xml")) "RSS"))
    +      (li (a (@ (href "/index.html")) "About"))
    +      (li (a (@ (href "/blog.html")) "Blog"))
    +      (li (a (@ (href "/rss.xml")) "RSS"))
           )
          ))
       )
     
    -(defun my/template (contents info)
    +(defun my/template (contents info)
       (concat
    -   "<!DOCTYPE html>"
    +   "&lt;!DOCTYPE html&gt;"
        (sxml-to-xml
    -    `(html (@ (lang "en"))
    +    `(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")))
    -       (meta (@ (name "description") (content "Personal page with a blog about my technical adventures")))
    -       (link (@ (rel "icon") (type "image/x-icon") (href "/resources/favicon.ico")))
    -       (link (@ (rel "stylesheet") (href "/resources/css/pico.sand.min.css")))
    +       (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")))
    +       (meta (@ (name "description") (content "Personal page with a blog about my technical adventures")))
    +       (link (@ (rel "icon") (type "image/x-icon") (href "/resources/favicon.ico")))
    +       (link (@ (rel "stylesheet") (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")) ())
    -       (title ,(org-export-data (plist-get info :title) info)))
    +       (script (@ (defer "true") (src "https://umami.dokutsu.xyz/script.js") (data-website-id "d52d9af1-0c7d-4531-84c6-0b9c2850011f")) ())
    +       (title ,(org-export-data (plist-get info :title) info)))
     
           (body
    -       (main (@ (class "container"))
    -             ,(my/header info)
    -             (*RAW-STRING* ,contents)
    -             ,(my/footer info)
    -             )
    +       (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)
    -                     ))
    +(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."
    -  (org-publish-org-to 'my-html filename ".html" plist pub-dir))
    +(defun my/publish-to-html (plist filename pub-dir)
    +  "Publish an Org file to HTML using the custom backend."
    +  (org-publish-org-to 'my-html filename ".html" plist pub-dir))
     
    -;;
    -;;Helpers
    -;;
    -(defun my/format-date-subtitle (file project)
    -  "Format the date found in FILE of PROJECT."
    -  (format-time-string "posted on %Y-%m-%d" (org-publish-find-date file project)))
    +;;
    +;;Helpers
    +;;
    +(defun my/format-date-subtitle (file project)
    +  "Format the date found in FILE of PROJECT."
    +  (format-time-string "posted on %Y-%m-%d" (org-publish-find-date file project)))
     
     
     
    -;;
    -;;Clear folder with results
    -;;
    -(when (file-directory-p my/web-export-path)
    +;;
    +;;Clear folder with results
    +;;
    +(when (file-directory-p my/web-export-path)
       (delete-directory my/web-export-path t))
     (mkdir my/web-export-path)
     
     
    -;;
    -;;Main blog configuration
    -;;
    -(setq org-publish-project-alist
    +;;
    +;;Main blog configuration
    +;;
    +(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
    -             )
    -       (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
    -             )
    +       (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
    +	     )
    +       (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
    +	     )
            ))
     
     
    -;; Generate the site output
    +;; Generate the site output
     (org-publish-all t)
     
    -(message "Build complete!")
    +(message "Build complete!")
     
    -
    -
    +
    -
    -

    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. @@ -386,24 +375,22 @@ 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:

    -
    -
    sudo apt install emacs-nox --yes
    -
    -
    +
    sudo apt install emacs-nox --yes
    +

    This way has a downside - you will install Emacs on each action run since the system state is disposable.

    -
    -

    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.

    @@ -411,60 +398,51 @@ I need to take extra steps since I use Doom Emacs and have my configs in Org. Yo

    Fetch doom guts

    -
    -
    git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.config/emacs
    -
    -
    +
    git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.config/emacs
    +

    Prepare minimal config for rendering Org file to config.

    -
    -
    echo '(doom! :config literate)' > ~/.config/doom/init.el
    -echo '(setq +literate-config-file "'$(pwd)'/config/config.org")' > ~/.config/doom/cli.el
    -
    -
    +
    echo '(doom! :config literate)' &gt; ~/.config/doom/init.el
    +echo '(setq +literate-config-file "'$(pwd)'/config/config.org")' &gt; ~/.config/doom/cli.el
    +

    Finally, install all dependencies according to my config. Yes, it is overhead, but I can be sure that I have the same dependencies as on my dev machine.

    -
    -
    ~/.config/emacs/bin/doom sync -B
    -
    -
    +
    ~/.config/emacs/bin/doom sync -B
    +

    Of course, I use a caching step to make the whole process faster:

    -
    -
    - name: Cache doom-emacs
    +
    - name: Cache doom-emacs
       uses: actions/cache@v4
       id: cache-doom-save
       with:
         path: ~/.config/emacs
         key: ${{ runner.os }}-doom
    -
    -
    +
    -
    -

    BTW I use GNU Emacs

    -
    +
    +

    BTW I use GNU Emacs

    +

    Here's the whole publishing workflow.

    -
    -
    name: pages
    +
    name: pages
     on:
       push:
         branches:
    -      - "main"
    +      - "main"
         # Do not trigger build on changes in other folders
         paths-ignore:
    -      - "./home/02 Action"
    -      - "./home/03 PKM"
    -      - "./home/04 Log"
    -      - "./home/06 Projects"
    +      - "./home/02 Action"
    +      - "./home/03 PKM"
    +      - "./home/04 Log"
    +      - "./home/06 Projects"
       workflow_dispatch:
     
     jobs:
    @@ -495,11 +473,11 @@ jobs:
     
     	# 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)' &gt; ~/.config/doom/init.el
     
     	# 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")' &gt; ~/.config/doom/cli.el
     
     	# Build doomemacs deps. Should be relativelly fast, cause almost everything cached.
           - name: Sync doom
    @@ -525,32 +503,31 @@ jobs:
     	  publish_branch: gh-pages
     	  publish_dir: ./public
     
    -
    +
    -
    -
    -

    What is next

    -
    +
    +

    What is next

    +

    I have a plans to make posts about next features:

    -
    -

    RSS Feed

    + -
    -

    Open Graph

    -
    +
    +

    Open Graph

    +

    Already implemented. Need to document process.

    -
    -

    Code highlighting

    -
    +
    +

    Code highlighting

    +

    Only rudimentary highlight. Want something fancier.

    diff --git a/posts/add_rss_to_blog.html b/posts/add_rss_to_blog.html index 33cfdc2..4fc2221 100644 --- a/posts/add_rss_to_blog.html +++ b/posts/add_rss_to_blog.html @@ -1,138 +1,128 @@ -Org blog with RSS
    +Org blog with RSS
    -
    -

    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

    -
    -
    -
    (setq org-publish-project-alist
    +
    +

    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)
    +       (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)
            ))
    -
    -
    +

    How does it work? As you can see, we use the default sitemap generator from Org Export with some additional steps. By default, the sitemap generator collects all org files from :base-directory. It then places links to these files as separate entries into an intermediate org file and publishes this org file. We use custom functions for collecting entries, formatting entries, and publishing the org file.

    -
    -

    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.

    -
    -
    (require 'ox-rss)
    -
    -
    +
    (require 'ox-rss)
    +

    Here's the core of the process. We need to prepare entries before feeding them to the org-rss-publish-to-rss function. Since we're doing it our own way, we need to start by creating a bullet with a link to the post, and then add RSS_PERMALINK, RSS_TITLE, and PUBDATE. I also add the first two lines of the blog post instead of a description. This is the native way to do a preview in the RSS world.

    -
    -
    (defun my/format-rss-feed-entry (entry style project)
    -  "Format ENTRY for the RSS feed.
    -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)))
    +
    (defun my/format-rss-feed-entry (entry style project)
    +  "Format ENTRY for the RSS feed.
    +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)))
     
    -
    -
    +

    This function creates the content of the intermediate org file. Mostly a rudimentary title, and then we ask Org to unwind the list of collected files with the my/format-rss-feed-entry function.

    -
    -
    (defun my/format-rss-feed (title list)
    -  "Generate RSS feed, as a string.
    -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 ""))))
    -
    -
    +
    (defun my/format-rss-feed (title list)
    +  "Generate RSS feed, as a string.
    +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 ""))))
    +

    This function replaces the default publishing function to filter everything except the intermediate file before publishing.

    -
    -
    (defun my/publish-to-rss (plist filename pub-dir)
    -  "Publish RSS with PLIST, only when FILENAME is 'rss.org'.
    -PUB-DIR is when the output will be placed."
    -  (if (equal "rss.org" (file-name-nondirectory filename))
    +
    (defun my/publish-to-rss (plist filename pub-dir)
    +  "Publish RSS with PLIST, only when FILENAME is 'rss.org'.
    +PUB-DIR is when the output will be placed."
    +  (if (equal "rss.org" (file-name-nondirectory filename))
           (org-rss-publish-to-rss plist filename pub-dir)))
    -
    -
    +

    Voila! Now you have an rss.xml in your export path.

    diff --git a/posts/keeb.html b/posts/keeb.html index 27d7704..d8655f0 100644 --- a/posts/keeb.html +++ b/posts/keeb.html @@ -1,52 +1,52 @@ -My keyboard journey
    +My keyboard journey
    -
    -

    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. @@ -92,9 +92,9 @@ 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.

    @@ -107,9 +107,9 @@ Nothing special. Ordered, paid, got it, tried it. Everything worked. Looked good
    -
    -

    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. @@ -158,9 +158,9 @@ 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.

    @@ -174,13 +174,13 @@ 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. @@ -216,9 +216,9 @@ Overall, I'm happy with results. I also printed bottom plates and -

    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.

    @@ -234,9 +234,9 @@ 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.

    @@ -249,9 +249,9 @@ I used a bootleg Pro Micro called Tenstar Robot, based on the ATmega32u4. It's p
    -
    -

    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.

    @@ -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 @@ -296,135 +296,116 @@ Prerequiremets:

    Clone QMK, setup QMK CLI.

    -
    -
    git clone git@github.com:qmk/qmk_firmware.git
    -cd qmk_firmware
    +
    git clone git@github.com:qmk/qmk_firmware.git
    +cd qmk_firmware
     qmk setup -h ./
    -
    -
    +

    You may want to create a separate keyboard entry in QMK.

    -
    -
    qmk new-keyboard
    -
    -
    +
    qmk new-keyboard
    +
    -
    -

    Make own layout

    -
    +
    +

    Make own layout

    +

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

    -
    -
    "features": {
    -    "rgb_matrix": false,
    -    "bootmagic": true,
    -    "command": false,
    -    "console": false,
    -    "extrakey": true,
    -    "mousekey": true,
    -    "nkro": true,
    -    "rgblight": true
    +
    "features": {
    +    "rgb_matrix": false,
    +    "bootmagic": true,
    +    "command": false,
    +    "console": false,
    +    "extrakey": true,
    +    "mousekey": true,
    +    "nkro": true,
    +    "rgblight": true
     },
    -
    -
    +

    Bootloader flashed to Pro Micro and target MC.

    -
    -
    "bootloader": "caterina",
    -"processor": "atmega32u4",
    -
    -
    +
    "bootloader": "caterina",
    +"processor": "atmega32u4",
    +

    Pins used to attach keyboard matrix.

    -
    -
    "matrix_pins": {
    -    "cols": ["B5", "B4", "E6", "D7", "C6", "D4", "D0"],
    -    "rows": ["B1", "B3", "B2", "B6"]
    +
    "matrix_pins": {
    +    "cols": ["B5", "B4", "E6", "D7", "C6", "D4", "D0"],
    +    "rows": ["B1", "B3", "B2", "B6"]
     },
    -
    -
    +

    RGB underglow configuration.

    -
    -
    "ws2812": {
    -    "pin": "D3"
    +
    "ws2812": {
    +    "pin": "D3"
     },
    -"rgblight": {
    -    "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
    +"rgblight": {
    +    "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
         },
    -    "layers": {
    -	"enabled": true,
    -	"blink": true
    +    "layers": {
    +	"enabled": true,
    +	"blink": true
         },
    -    "default": {
    -	"animation": "rainbow_mood"
    +    "default": {
    +	"animation": "rainbow_mood"
         },
    -    "split": true,
    -    "split_count": [8, 8]
    +    "split": true,
    +    "split_count": [8, 8]
     }
    -
    -
    +

    Need to use another direction, since I use Amoeba pcbs turned 90 degrees.

    -
    -
    "diode_direction": "ROW2COL",
    -
    -
    +
    "diode_direction": "ROW2COL",
    +

    Turn on split feature, assign pin for halves communication, choose what to sync.

    -
    -
    "split": {
    -    "enabled": true,
    -    "soft_serial_pin": "D2",
    -    "transport": {
    -	"protocol": "serial",
    -	"sync": {
    -	    "layer_state": true,
    -	    "indicators": true,
    -	    "modifiers": true
    +
    "split": {
    +    "enabled": true,
    +    "soft_serial_pin": "D2",
    +    "transport": {
    +	"protocol": "serial",
    +	"sync": {
    +	    "layer_state": true,
    +	    "indicators": true,
    +	    "modifiers": true
     	}
         }
     },
    -
    -
    +

    This is the config.h file. Enables keeping handness information in EEPROM of MC. You need to flash each half once with a special commands to write EEPROM data. qmk flash -bl avrdude-split-right and qmk flash -bl avrdude-split-left

    -
    -
    #pragma once
    -#define EE_HANDS
    -
    -
    +
    #pragma once
    +#define EE_HANDS
    +

    This is the keymaps/default/keymap.c file.

    -
    -#include QMK_KEYBOARD_H
    +
    #include QMK_KEYBOARD_H
     
     #define _B 0
     #define _L 1
    @@ -483,12 +464,12 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
         // clang-format on
     };
     
    -
    +
    -
    -

    Whats next?

    +
    +

    Whats next?

    diff --git a/resources/css/prism-dark-theme.css b/resources/css/prism-dark-theme.css new file mode 100644 index 0000000..e69de29 diff --git a/resources/css/quirks.css b/resources/css/quirks.css new file mode 100644 index 0000000..946cb53 --- /dev/null +++ b/resources/css/quirks.css @@ -0,0 +1,4 @@ +code[class*="language-"], +pre[class*="language-"] { + font-size: 0.95em !important; +} diff --git a/rss.html b/rss.html index dcec5f0..e77a66e 100644 --- a/rss.html +++ b/rss.html @@ -1,27 +1,27 @@ -rss
    +rss
    -
    diff --git a/rss.xml b/rss.xml index 14de843..2ee8baa 100644 --- a/rss.xml +++ b/rss.xml @@ -14,8 +14,8 @@ https://fidonode.me en - Sun, 23 Jun 2024 18:44:00 +0000 - Sun, 23 Jun 2024 18:44:00 +0000 + Mon, 24 Jun 2024 19:34:52 +0000 + Mon, 24 Jun 2024 19:34:52 +0000 Emacs 27.1 Org-mode 9.3 iam@fidonode.me (Alex M)