days since this article was written, please be aware of its timeliness
Preface
Previously, I set up a blog automation workflow and later a 2022 blog automation workflow. However, there were still several pain points:
-
Uploading images pulled from Craft to Tencent Cloud via Github CI often encountered network errors, making the process unreliable.
-
When images were uploaded to Tencent Cloud for the blog, they were forcibly converted to
png, but more formats likegifshould be supported. -
The Markdown output converted from Craft had many inconsistencies with standard Markdown, such as:
-
Nested lists were not supported.
-
When lists were nested inside blockquotes, the rendered result placed the lists outside the blockquote.
-
In Craft, “focus” blocks and “block” (focus) were treated differently—only the latter was recognized as a blockquote, while both should have been.
-
Craft’s bookmarks contained rich metadata like page descriptions and titles, but after conversion to Markdown, only a link wrapped in a paragraph remained, resulting in information loss.
-
Craft itself did not support image captions.
-
Code blocks generated by Craft did not automatically include the language identifier after backticks, causing some languages to be misidentified as
plaintextwithout syntax highlighting.
-
To address these issues, I’ve made yet another round of optimizations.
Optimization 1: Using a Custom CraftBlockToMarkdown Function
After reporting to Craft that their craft.markdown.craftBlockToMarkdown conversion was incorrect:
However, the official team showed no intention of fixing it, so I wrote a simple function to handle the conversion manually:
Note that this function is tailored for my Jekyll workflow (see Optimization 3 below), so it specially processes imageBlock and urlBlock block types:

I’ve shared this function on the official plugin developer forum for discussion:
Optimization 2: Moving the Github CI Workflow Locally
In the previous workflow, clicking the plugin’s publish button (after filling in the GitHub Token, COS details, etc.) would trigger the process, and then you’d just wait.
For this optimization, to maintain consistency with the previous workflow, I created another Craft plugin. When clicked, it fetches the document content, performs simple processing (such as extracting image copyright information from headerImg into the meta), and then calls a specific URL with the document information passed as parameters:
1 | |
The purpose of this specific URL is to invoke an Application written in Apple Script. For details on how to write an Application and respond to URL calls, refer to this document I wrote. The Application then calls Node.js to execute the same code previously run in Github CI. Here’s a simple screenshot:

Result:

In the previous workflow, for simplicity, I converted all images to the png format because Craft-uploaded images might not always have extensions (e.g., images uploaded via drag-and-drop or file upload have extensions, while those pasted or web-uploaded do not). This time, I removed that logic: if an image has an extension, it retains it; if not, it’s forcibly converted to png. I highly recommend using the Sharp library—it’s extremely handy:
So… here’s a GIF!

Note: Previously, for convenience (no authentication, no need for a fixed-IP server), I used WeChat Cloud Hosting and even implemented logic to send summaries to a WeChat public account. However, the catch is that this server was only set up for publishing to the public account, which I typically use once every three months. Each time, it’s a cold start (their server shuts down instances by default after 30 minutes of inactivity since billing is based on instance runtime), leading to frequent failures. Debugging this took more time than manually copying and pasting content into WeChat’s editor. So, I gave up on the public account and opted for manual pasting… which works just fine!
Optimization 3: Implementing Bookmark and Image Caption in Craft
Achieving this effect in a Jekyll-based blog system is possible because I don’t directly use Github’s Jekyll service. Instead, I build the HTML with Jekyll locally and then push it to the repository. For the rationale and process, see here.
Bookmark and image caption rendering leverage special Jekyll tags generated in the previous step, combined with custom Jekyll plugins. Bookmarks are rendered using render_bookmark, and image captions are implemented with render_caption (written in Ruby, which I learned in just two hours):
1 | |
Using plugins in Jekyll is straightforward: create a _plugins directory in the root, place the Jekyll plugin written in ruby inside, and it will load during Jekyll builds.
Other Details
The previous content was standard Markdown-generated HTML, so RSS readers fetched it with proper formatting. However, after adding Bookmarks, the RSS format broke (screenshot from Reeder 5):

To fix this, I wrote another Ruby plugin to filter the tags and convert them into regular HTML links:
1 | |
Then, simply use it in feed.xml: post.content | bookmark_filter.
Feel free to discuss!
-
Previous
"Translation" Official Guide Series: JavaScript Debugging Guide (Part 1) -
Next
Common Chrome Debugging Methods and More



I often wish that when facing some key decisions in life, someone could tell me the best course of action so that I would not waste my precious time. Putting myself in others' shoes, I therefore write blogs often, hoping to record in this tiny corner of the vast Internet the once-in-a-lifetime experiences that matter to me, and to help those who seek help.