HUGO:コードブロックのコピーボタンを表示する&コンソールのときはプロンプト($)を除外する
コマンドの実行を示すとき、コマンドであることを伝えるためにも、次のようにプロンプト($
)を書くことが多い。
```bash {iscopy=true}
$ hugo new posts/ramen.md
$ hugo server
```
反対に、コードブロックをコピーしたときのテキストにプロンプトが含まれていると、コマンドとして実行できない。
プロンプト($
)を除外してコピーできると便利そうなので、その実現方法を調べる。
コードブロックのコピーボタンを表示する
実装方法
HUGOにはCode Block Render Hooksという、コードブロックがHTMLにレンダリングされる際、処理を差し込める機能がある。
Code Block Render Hooksの使い方は次の記事で説明したため、詳細の詳細は割愛する。
今回は、次のように言語の識別子の後に{iscopy=true}
と書くと、コピーボタンを表示することにする。
```js {iscopy=true}
(() => {
'use strict';
console.log("Hello World!");
})();
```
実装
layouts/_default/_markup/render-codeblock.html
は、次のように書く。iscopy
にtrue
が渡されたときだけコピーボタンのDOMを生成する。
<div class="codeblock-container">
{{ if .Attributes.iscopy }}
<button class="codeblock-button" title="copy">
Copy
</button>{{ end }}
<div class="codeblock-content">
{{- highlight ( .Inner | safeHTML) .Type .Options }}
</div>
</div>
コピーボタンを押した際の処理を行うJavaScriptのコードは次のとおり。
(function () {
const copyToClipboad = async (text) => {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
}
};
window.addEventListener("load", function () {
const button = document.querySelector(".codeblock-button");
if (button) {
button.addEventListener("click", function () {
const codeblock = document.querySelector(".codeblock-content");
if (codeblock) {
// 最後にスペース 2 つが入るので除外する
const content = codeblock.textContent.replace(/ $/g, "");
copyToClipboad(content);
}
});
}
});
})();
このJavaScriptのコードをHUGOテンプレートで読み込む。
<body>
<!-- 省略 -->
{{- $js := resources.Get "js/copy.js" | resources.Minify | resources.Fingerprint -}}
{{- with $js -}}
<script src="{{ .RelPermalink }}"></script>
{{- end -}}
</body>
動作確認
[Copy]を押すと、次の内容をコピーできた。
(() => {
'use strict';
console.log("Hello World!");
})();
コピー時にプロンプト($)を除外する
実装
プロンプト($
)を書くのは、言語の識別子にbash
を指定したときだけにする。render-codeblock.html
内で分岐しても良いが、render-codeblock-言語の識別子.html
というファイルで言語ごとに処理を分けることもできる。
今回の言語の識別子はbash
なのでlayouts/_default/_markup/render-codeblock-bash.html
というファイルを作成する。
<!-- 内容は「コードブロックのコピーボタンを表示する」と同じなので省略 -->
プロンプトを取り除く処理は、JavaScript側で行う。
コードブロックのコピーボタンを表示するで示したコードに対し、冒頭の$
を正規表現で取り除く処理を追加する。
(function () {
const copyToClipboad = async (text) => {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
}
};
window.addEventListener("load", function () {
const button = document.querySelector(".codeblock-button");
if (button) {
button.addEventListener("click", function () {
const codeblock = document.querySelector(".codeblock-content");
if (codeblock) {
// 最後にスペース 2 つが入るので除外する
const content = codeblock.textContent.replace(/ $/g, "");
- copyToClipboad(content);
+ const contentWithoutPrompt = content.replace(/^\$\s+/gm, "");
+ copyToClipboad(contentWithoutPrompt);
}
});
}
});
})();
JavaScriptのコードをテンプレートで読み込む処理は、先ほどと同じなので省略する。
動作確認
[Copy]を押すと、次の内容をコピーできた。
hugo new posts/ramen.md
hugo server
補足:CSS
今回は、コピーボタンに次のCSSを適用した。
.codeblock-button {
width: fit-content;
margin: 1rem 0 0;
padding: 0.4rem;
font-size: 0.9rem;
color: rgb(255, 255, 255);
background-color: #05acc1;
border: 0;
}
.codeblock-button:hover {
filter: brightness(1.25);
}
.codeblock-button + .codeblock-content {
margin-top: -1.25rem;
}