HexoとIcarusをCustomizeする

HexoとIcarusをCustomizeする

Intro

Hexoを、Icarusを、初期投資としてガンガン便利にしていく。

TL; DR

VSCode側で画像のpathを手動で修正せずとも、scirpts配下のjsファイルで自動で対応した。
さらに、見た目のよいAlertboxも作成できるようになった。

Difference of Image Path between VSCode and Hexo

HexoでMarkdownをHTMLにCompileすると、画像が表示されなくなる問題が発生した。画像が全部消えるのは困るよねぇ。

Image Path on VSCode

VSCodeでは1-2年前からMarkdownを編集する際、拡張機能なしのPureな状態で画像をEditor上に直接Pasteやドラッグできるようになった。
その際、設定の内容に従って所定のpathで画像ファイルが作成されつつ、Markdown側にはMarkdown記法で画像が挿入される。

Editorに画像を直接Pasteできるようになった on VSCode

画像ファイル名を気にせずドキュメント作成できるのはあまりに便利だ。
筆者も常用している。

VSCodeへのPasteで自動作成された画像たち

Image Path on Hexo

では何が問題になるのかを説明するために、ChatGPTへの実際の指示を修正したものを示そう。
画像でも作ればわかりやすいのかもしれないが、面倒

筆者 - 修正されたChatGPTへの指示

VSCodeに画像をペーストすると、指定した形式に従ってフォルダやファイルが生成されつつ、結果が表示される。
ただ、そのままではhexoのcompile後に画像の指定が失敗する。
すなわち、

  • hexo compile前: source/_post/2024/07/06/sample.mdからの相対pathとして解釈される。
    • Markdownファイルにpasteした時点でsource/_post/2024/07/06/sample/sample1.pngに保存される。
    • 同時に./sample/sample1.pngとしてMarkdownファイルに自動記載されて、うまく解釈される。
  • hexo compile後: 記事の位置がdomain.com/2024/07/06/sampleになる。そして、domain.com/つまりrootからのパスとして解釈される。
    • このとき./sample/sample1.pngdomain.com/sample/sample1.pngと変換される。
    • ここではdomain.com/2024/07/06/sample/sample1.pngと変換されてほしい

Solution

上記で生成された回答を多少修正することで解決した。

ChatGPT - 修正された回答

HexoでMarkdownファイルに埋め込まれた画像パスが、コンパイル前後で異なる問題を解決するためには、いくつかのアプローチがあります。以下は、画像パスを正しく解釈させるための方法です。

(前略)

方法 3: カスタムフィルターを作成

Hexoのカスタムフィルターを使用して、Markdownファイルの画像パスをビルド時に動的に変更する方法もあります。

スクリプトの作成

  1. scripts フォルダをHexoプロジェクトのルートに作成します。
  2. その中に image_path_filter.js という名前のファイルを作成します。

image_path_filter.js の内容

image_path_filter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ここの内容を実際の回答から修正した
hexo.extend.filter.register('before_post_render', function (data) {
const path = require('path');
const postAssetFolder = hexo.config.post_asset_folder;

if (!postAssetFolder) return data;

const regex = /!\[([^\]]*)\]\(([^)]+)\)/g;

data.content = data.content.replace(regex, function (match, altText, imgPath) {
const postDir = path.dirname(data.source);
const fullPath = path.join('/', postDir, imgPath).replace(/\\/g, '/').replace("_posts/", "");
return `![${altText}](/2024/07/07/${fullPath})`;
});

// YAML Front Matterの画像パス変換も追加
data.thumbnail = data.thumbnail ? path.join('/', path.dirname(data.source), data.thumbnail).replace(/\\/g, '/').replace("_posts/", "") : null;
data.cover = data.cover ? path.join('/', path.dirname(data.source), data.cover).replace(/\\/g, '/').replace("_posts/", "") : null;

return data;
});

このスクリプトはMarkdownファイルの画像パスを自動的に調整します。

(後略)

Warning

なお、この節の内容のようにjsやcssをhexoに適用する場合、hexo clean && hexo generateだけでは反映されず、
remove public db.json -rf && touch db.json; hexo c && hexo g && hexo dが必要になる。

YAML内の画像path変換

記事本体内の画像pathは修正できたが、その時点ではYAML内で指定している画像(coverやthumbanil)のpathは修正できていなかった。

正常に動作しているCover
正常に動作しているCard内のThumbnail
当初はこれらの画像が表示されていなかった。

YAML内の画像をhtmlに入れ込む部分を特定して、themeのjsxファイルをいじることである程度対応できた。
ただ、なぜか一向にCard内のThumbnailのpathを修正できない。Thumbnailで検索をかけても、未着手のファイルは見つからない。お前はどこで変換されてるんだ。

article.jsx
1
2
3
4
5
6
// 修正の例
// before
const cover = page.cover ? url_for(page.cover) : null;
// after
const cover = page.cover ? url_for(page.path + path.basename(page.cover)) : null;
// 記事のpathとディレクトリを一致させている

結果として

先のjs内でYAML内の画像pathも修正することに。

image_path_filter.js
1
2
3
// YAML Front Matterの画像パス変換も追加
data.thumbnail = data.thumbnail ? path.join('/', path.dirname(data.source), data.thumbnail).replace(/\\/g, '/').replace("_posts/", "") : null;
data.cover = data.cover ? path.join('/', path.dirname(data.source), data.cover).replace(/\\/g, '/').replace("_posts/", "") : null;

実際、この方がシンプルで安全ではある。
不採用になったとはいえ、jsxファイルを修正していたからこそたどり着けた発想だろう。

Plugins for Hexo

  1. 参照: https://photo-tea.com/p/usecase-of-hexo-plugin/

Hexoには多くのプラグインが存在し、機能を拡張することができる。以下にいくつか代表的なプラグインを紹介する。

  • hexo-toc: 記事内の見出しを自動で目次に変換するプラグイン。
  • hexo-uglify: JavaScriptを圧縮するプラグイン。
  • hexo-tag-instagram: Instagramの投稿を埋め込むプラグイン。
  • hexo-include-markdown: Markdownファイルを挿入するプラグイン。

たとえば、この記事の場合<\!-- toc -->(実際は\なし)が自動的に冒頭の目次に変換されている。

generated toc

Alertbox for Hexo

  1. 参照: https://github.com/kristofzerbe/hexo-tag-plugins?tab=readme-ov-file
  2. 参照: https://omathin.com/icarus-bulma/

HackMDやQiitaにあるようなAlertboxが欲しくて調べてみた。ないわけがないよなぁと思いつつ。

Alertbox on HackMD

すると、下記のようなRepositoryにたどり着いた。
シンプルでよさそうだ。

Alertbox in tag plugins for kiko.io

こちらは作者の考えのもと、あえてmodule化されてない。
npm installする代わりに、直接Scriptを設置する。実際、単体で機能するのでその方が安全ではある。が、正直面倒。
さらに、目的のalertboxに関して、htmlタグへの変換はできたが、cssが適用されない。困った困った。

そして再度調べていて、参照サイト2を見つけた。こちらはIcarusで利用されているBulmaを適用しているらしい。見た目もいいじゃないか。

Alertbox with Bulma

サイト内ではMarkdown内にhtmlタグを直接書き込む方法で示されていたが、それは面倒だ。
それに何より、参照1の結果としてhtmlタグへの変換まではできている。そこを少しいじるだけでよいはずだ。

Tweet

というわけで、その結果が下記の通り。ついでにLabelも設定できるようにした。

このTweetは次のように入力されている。

1
2
3
{% alertbox info Tweet %}
というわけで、その結果が下記の通り。ついでにLabelも設定できるようにした。
{% endalertbox %}

得られたscriptは下記の通り。<hexo_root>/scripts/ytomo_tag-alertbox.jsに配置する。

scripts/ytomo_tag-alertbox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
Alertbox Tag
modified from https://github.com/kristofzerbe/hexo-tag-plugins?tab=readme-ov-file
MIT License

Syntax:
{% alertbox alertType:(exclamation,question,warning,info,success,note) ?alertLabel %}
content
{% endalertbox %}
*/

hexo.extend.tag.register("alertbox", function (args, content) {

const alertType = args[0].toLowerCase();
const alertLabel = args[1] || alertType.charAt(0).toUpperCase() + alertType.slice(1);
const content_renderred = hexo.render.renderSync({ text: content, engine: 'markdown' });

const element = `
<article class="message is-${alertType}">
<div class="message-header">
<p>${alertLabel}</p>
</div>
<div class="message-body">
${content_renderred}
</div>
</article>
`;

return element;

}, { ends: true });

Tweet

endtag入力が面倒すぎてそれを省略したalertmsgも作成してみた

上記tweetは下記のように入力されている。

1
{% alertmsg info Tweet endtag入力が面倒すぎてそれを省略したalertmsgも作成してみた %}

で、それを実現しているのが下記のスクリプトだ。

scripts/ytomo_tag-alertmsg.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
Alertmsg Tag
modified from https://github.com/kristofzerbe/hexo-tag-plugins?tab=readme-ov-file
MIT License

Syntax:
{% alertmsg alertType:(exclamation,question,warning,info,success,note) alertLabel content %}
*/

hexo.extend.tag.register("alertmsg", function (args) {

const alertType = args[0].toLowerCase();
const alertLabel = args[1];
const content_renderred = hexo.render.renderSync({ text: args.slice(2).join(" "), engine: 'markdown' });

const element = `
<article class="message is-${alertType}">
<div class="message-header">
<p>${alertLabel}</p>
</div>
<div class="message-body">
${content_renderred}
</div>
</article>
`;

return element;

}, { ends: false });

Snippets on VSCode

直前のAlertboxを含めて、hexoでは (厳密にはhexo以外でも多くのMarkdownの拡張では){% TAG %}のような表記が多い。何なら{% alertbox info Tweet %}\nTweetの内容\n{% endalertbox %}のようにendを伴う場合はさらに面倒だ。

1
2
3
4

{% alertbox info Tweet %}
はじめ、YAML内の画像path変換のためにthemeのjsxファイルをいじっていたが、一向にCard内のThumbnailを修正できなかった。とはいえ、上記のjs内でYAMLの値にも直接触れることを思いつくのは、jsxファイルを修正していたからだろう。
{% endalertbox %}

これを毎回手で打つのは煩雑なので、何らかの入力支援が必要だなぁと思って調べたところ、Snippetsを利用すればよいらしい。

今までに何度もなんとなく目にしていたSnippetsは、なんとなく入力補完くらいのものだと思っていたが、どちらかというと自由度の高い変換機能だった。
こんにちは、Snippets。

markdown.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Hexo End Tag": {
"prefix": [
"tagend"
],
"body": [
"{% ${1|alertbox,blockquote|} %}",
"$0",
"{% end${1|alertbox,blockquote|} %}"
],
"description": "Insert tag plugin with end for hexo"
},
"Hexo Single Tag": {
"prefix": [
"tagplg"
],
"body": [
"{% $0 %}"
],
"description": "Insert single tag plugin for hexo"
}
}

しかし、何度試してもVSCodeのSnippetsが効かない。
どうもGitHub Copilotと共存できなさそうな気がしてきた。Tabを食い合ってるものなぁ。

Tweet

さようなら、Snippets on VSCode。多分、他人だった。

Icarus Style

脱中華圏Font

  1. 参照: https://omathin.com/icarus-theme-change/

Alt text

Icarus Themeの最優先フォントはMicrosoft YaHeiだ。これは中華フォントで、日本語の表示には適しているとは言えない。
themes/icarus/layout/base.stylを編集して、Microsoft YaHeiを削除した。ついでに好きなfontを上位に設定した。

Tweet

Hexoの開発や界隈における中華圏の割合をうかがい知ることができる。HackMDもそうだったなぁ。

Width

  1. 参照: https://hyabc.github.io/hexo-icarus-customization/

参照先に合わせて対応したが、どうも変化してないように見える。
保留だなぁ。

ついでに

GitHub Copilot for Markdown

  1. 参照: https://techblog.ap-com.co.jp/entry/2023/12/28/175847

GitHub Copilotは、VSCodeの拡張機能として提供されているAIアシスタントだ。Markdownの記述をサポートしてくれるため、記事作成時に大変便利だ。
しかし、Markdownを開いている際は動作しない。てっきり、プログラミングじゃないからそういう仕様なのかと思っていたが、そうではなかった。

わざわざ設定でMarkdownについては無効にされていただけだった。

MarkdownでもGCopilotを叩き起こす in 設定画面

Tweet

ちなみに、この小節の大半はGitHub Copilotによって生成された。

LinkedInなど

blogの設定をするついでに、SNSアカウントを作成するなり整理するなりした。Twitterの(実質)本名垢も作るか……それか再始動するか……。

しかし、LinkedInて(そういうサービスだとは言え)スキル登録する際、個人開発の部分は経験歴の登録はできないんだね。冷たいやつだ……。

これはGitHubをもう少しまともに作れということなのかもしれない。
しかし、PJ演習で作成したTypeScriptは社外公開はできないよなぁ。

まとめ

  • HexoのIcarusテーマをカスタマイズするために、フォントや幅を変更した。
  • Alertboxを作成するためのスクリプトを作成した。
  • YAML内の画像path変換もできるようにした。
  • その他、HexoのプラグインやIcarusの機能についても調査した。

Closing

正直、かなり期待通りの修正ができた。

コメント