GitHub や Zenn などでは当たり前のようにできるコードブロックのコピーですが、 Hugo 上ではデフォルトでできません。さすがに不便すぎるので作っていきましょう。 ついでに、ファイルパスを表示できるようにしましょう。

こんな感じです

これを markdown 上で表現するには以下のように書きます。

```text {data-filename="ファイル名をつけられます"}
こんな感じです
```

変更・追加するファイルはそれぞれ以下になります。

  • コピーボタン追加
    • layout/partials/head.html
    • static/css/code-copy.css
    • static/js/code-copy.js
  • ファイル名追加
    • layout/partials/head.html
    • static/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;
}