Welcome to Alex’s Blog

This blog is powered by Jekyll, themed by jekyll-theme-yat and hosted on Github Pages.

To avoid info fragmentation, this page is being continuously updated as this blog evolves.

Server-side site search (Elasticsearch) is added by the guidance in this blog post, but I have to rebuild the Searchkit client parts since the tech world has been moving forward.

As a temporary solution, the code is integrated into my fork of jekyll-theme-yat: https://github.com/alexforks/jekyll-theme-yat. The demo is available on the current site and the config can be found here.

Indexing

Searchyll is used to index the site.

Searchyll seems to be out of maintenance, the master branch is not merged and released for years.

You will be happy to refer to the below repo to get the recent fixes, at least until they are merged and released to official Gem.

This repo is based on omc/Searchyll master plus my fix to overcome the annoying updating alias error when running into multiple indices.

# _config.yml
group :jekyll_plugins do
  gem "searchyll", git: "https://github.com/alexforks/searchyll", ref: "fix-update-alias-404"
end

Search UI

Searchkit is used to search the Elasticsearch server to build the search result.

I built a new search UI component with Searchkit v2. The layout and style are sticky with Searchkit as much as possible, but being overridden to suit well with jekyll-them-yat.

I’m not a React guy, just knowing enough to get it to work. Hope this universal solution is coming soon.

Credential

There are two places where the Elasticsearch URL and credential will be needed.

  1. Searchyll

    An URL with a credential with write permission is necessary. Don’t put the credential within _config.yml, use environment variable to test locally, like below:

     $ bundle update && ELASTICSEARCH_URL="https://yourusername:yourpassword@yourserver.com" bundle exec jekyll serve
    

    And configure the same URL and credential as environment variables in the yourname.github.io -> Github Environments -> github-pages for the server-side.

  2. Searchkit

    An URL and a credential with read-only permission are enough.

     # _config.yml
     elasticsearch:
       readonly_url: https://yourusername:yourpassword@yourserver.com
    

    ⚠️ WARNING: The readonly_url:

    • will be exposed to the Internet in site source.
    • should only have the read-only permission to the Elasticsearch server.

Elasticsearch Service

Where to get an Elasticsearch service? To minimize the maintenance needs, mine is powered by Bonsai for free.

Use my referral link to sign up, the free plan is enough. But when you start using a paid plan, you and I will both get $50 credit.

Development

If you would like to make changes to the SearchUI, you will need to deal with https://github.com/alexforks/jekyll-theme-yat. All you need is npm, get it ready. And run in your site root, to get your own search.js.

$ npm install
$ webpack

Related environment:

$ ruby --version
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin20]

$ npm --version
7.10.0

Plugins with Github Pages

There are a lot of Jekyll plugins out there giving you the features that Jekyll and your theme don’t have. But the truth is most of them won’t work with Github Pages.

Github Pages has this dependency list. The unlisted Jekyll plugins will work only if you run jekyll build locally. Github Pages is ignoring them on remote as they are don’t exist.

But this is not all the picture.

If you are using the yourusername.github.io repo, the only thing you want to be pushed to the configured branch is the generated _site, nothing else. If the Jekyll source is pushed to this branch, the remote Jekyll build is overriding your pushed local build. To what end? you end up with a solution without the remote build capability!

Solution

To get the unlisted Jekyll plugins functional on Github Pages, you need a CI, such as Travis and Github Workflow, to gain control of the remote build.

Here it is, jekyll-deploy-action, a Github Action, will help to simplify the process with Github Workflow.

Just follow the usage document, after that, your plugins will work both LOCALLY AND REMOTELY, like a charm!

Responsive Images

Responsive Images is good, to the readers. But it’s hard, to the publishers. It costs me days to come out with a satisfied solution. All difficulties are related to Github Pages.

Solution

⚠️ WARNING:The Zero-Width Space (ZWSP) is used in below code to workaround the liquid tags rendering issue. So, don’t simply copy & paste the code, the ZWSP must be removed.

I tried two well used responsive images plugins:

  1. jekyll_picture_tag
  2. jekyll-responsive-image

I take jekyll_picture_tag for now. I like its presets idea and the way of handling output directory structure. Both plugins don’t support markdown image tags. This is quietly surprising me. I don’t like the responsive image liquid tags within my content. That is just not the way to write.

To overcome this, I need a transformer to translate markdown image tags, like:

!​[Thinkpad](/assets/images/banners/thinkpad.png)

to the responsive image liquid tags, like:

{​% picture banners/thinkpad.png --alt Thinkpad %}

I wrote this universal hook plugin jekyll-hooks, which does the job.

Then here is jekyll_picture_tag. It’s rendering the liquid tag to HTML code as below:

<picture><source srcset="/assets/images/resized/banners/thinkpad-640-237e1d.webp 640w, /assets/images/resized/banners/thinkpad-800-237e1d.webp 800w, /assets/images/resized/banners/thinkpad-1024-237e1d.webp 1024w" type="image/webp"><source srcset="/assets/images/resized/banners/thinkpad-640-237e1d.png 640w, /assets/images/resized/banners/thinkpad-800-237e1d.png 800w, /assets/images/resized/banners/thinkpad-1024-237e1d.png 1024w" type="image/png"><img src="/assets/images/resized/banners/thinkpad-800-237e1d.png" alt="Thinkpad"></picture>

And your browser is rendering the HTML as below. Change your browser window size and reload the page, to see the different images are being downloaded.

Thinkpad

The jekyll-hooks can do more, such as letting the images be previewable while writing. I write the markdown image like this:

!​[Thinkpad](../assets/images/banners/thinkpad.png)

This way the image is referring to a valid relative local path and is previewable while writing. Then one action will remove the part .. from the path in a pre_render hook. Which makes it a valid absolute web path.

Image Compress Lib

Choose vips or ImageMagick? With Github Pages, we don’t have a choice.

By now (Oct 2021), Github Pages’s pages-gem v221 is locking down Jekyll 3.9.0. The latest jekyll_picture_tag has dropped the support of Jekyll v3 because of a cache_dir issue. We stuck at jekyll_picture_tag 1.10.2. And this version doesn’t support vips, it supports ImageMagick only.

Sample Config

jekyll-hooks is not in the official gem yet, for now, use the git URL.

# Gemfile

group :jekyll_plugins do
  ...
  gem "jekyll-hooks", git: "https://github.com/alexzhangs/jekyll-hooks"
  gem "jekyll_picture_tag", "~> 1.10.2"
end
# _config.yml

plugins:
  - jekyll-hooks
  - jekyll_picture_tag

# jekyll_picture_tag
picture:
  source: assets/images
  output: assets/images/resized

# jekyll-hooks
hooks:
  actions:
    - type: posts
      exts: [markdown,mkdown,mkdn,mkd,md]
      find: >
        (!\[[^\]]*\]\()/assets/images/(.+)
      replace: >
        \1\2
      disabled: false
    - type: posts
      exts: [markdown,mkdown,mkdn,mkd,md]
      # !​[alt](/path/to/image "title"){:.class}
      find: >
        !\[([^\]]*)\]\(((?!http[s]?://)[^"'\n]+)(?:\s['"]([^'"]*)['"])?\)(?:\{:\.([^{]+)\})?
      # both present and non-present quotes matter
      replace: >
        {% picture \2 --alt \1 --img class="\4" title="\3" %}
      case-insensitive: true
      disabled: false
  disabled: false

Of course, we need jekyll-deploy-action. The last three lines are important, to get ImageMagick and its dependency installed on the docker. The jekyll-deploy-action is running in a separate docker, so the dependencies must be installed with pre_build_commands. I’m using webp format for my images. It has an amazing compression ratio in most situations. So it’s included in the installation list.

# .github/workflows/build-jekyll.yml
      ...
      - uses: jeffreytse/jekyll-deploy-action@v0.3.1
        with:
          ...
          pre_build_commands: |      # Installing additional dependencies (Arch Linux)
            pacman -S --noconfirm imagemagick libwebp
            identify --version