GitHub や Zenn などでは当たり前のようにできるコードブロックのコピーですが、 Hugo 上ではデフォルトでできません。さすがに不便すぎるので作っていきましょう。 ついでに、ファイルパスを表示できるようにしましょう。
こんな感じです
これを markdown 上で表現するには以下のように書きます。
```text {data-filename="ファイル名をつけられます"}
こんな感じです
```
変更・追加するファイルはそれぞれ以下になります。
- コピーボタン追加
layout/partials/head.htmlstatic/css/code-copy.cssstatic/js/code-copy.js
- ファイル名追加
layout/partials/head.htmlstatic/css/code-copy.css
<link rel="stylesheet" href="/css/code-copy.css">
<link rel="stylesheet" href="/css/highlight.css">
.code-block {
position: relative;
}
.copy-btn {
position: absolute;
top: 2px;
right: 12px;
padding: 4px 8px;
font-size: 0.75rem;
border-radius: 6px;
background: #1e293b;
color: #fff;
border: none;
cursor: pointer;
opacity: 0.8;
z-index: 10;
}
.copy-btn:hover {
opacity: 1;
}
document.addEventListener("DOMContentLoaded", () => {
const blocks = document.querySelectorAll("pre > code");
blocks.forEach((code) => {
const pre = code.parentElement;
// すでに処理済みならスキップ
if (pre.parentElement.classList.contains("code-block")) return;
const wrapper = document.createElement("div");
wrapper.className = "code-block";
// ファイル名要素の作成
const highlight = pre.closest(".highlight");
if (highlight && highlight.dataset.filename) {
const filename = document.createElement("div");
filename.className = "code-filename";
filename.textContent = highlight.dataset.filename;
wrapper.appendChild(filename);
}
const button = document.createElement("button");
button.className = "copy-btn";
button.textContent = "Copy";
button.addEventListener("click", async () => {
await navigator.clipboard.writeText(code.textContent);
button.textContent = "Copied!";
setTimeout(() => (button.textContent = "Copy"), 1500);
});
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(button);
wrapper.appendChild(pre);
});
});
.highlight[data-filename] {
position: relative;
padding-top: 0;
}
.code-filename {
position: relative;
padding: 6px 12px;
font-size: 0.75rem;
font-family: monospace;
background: #0f172a;
color: #e5e7eb;
border-bottom-right-radius: 8px;
user-select: text;
cursor: text;
}