Hachi Blog
2026-02-08

Docusaurusのようなサイドバーの追加

#python #gemini #mupy

これまでシングルカラムのレイアウトでしたが、Docusaurusのように左ペインにツリー構造、右ペインに目次となるようなレイアウトを作成します。

  • レイアウトをDocusaurusのような3カラム構成にする
  • 強調表示(Callouts)機能を追加する
NOTE

Docusaurus はMeta社が開発しているReactベースのSSGで、特にドキュメントに特化したフレームワーク。

Docusaurus 風レイアウトへ

このブログサイトは、Markdownのドキュメントベースに記事を書いています。Docusaurusのような、左ペインにドキュメント構造、右ペインに目次という構成は非常に相性が良いので、同じ構成を目指します。

右ペインに目次を追加

まず、右ペインに目次を追加していきましょう。 いつもと違って、GeminiにGeminiCLIへの指示文を考えてもらった。

あなたはPythonとモダンなWebフロントエンド開発のエキスパートです。
私が自作している静的サイトジェネレーター(SSG)に、「スクロール追従する目次(Table of Contents)」を実装するためのコードを作成してください。

Docusaurusのような右ペインの目次を想定しています。

## 要件

### 1. Python側の処理 (BeautifulSoup4)
Markdownから変換されたHTML文字列を受け取り、以下の処理を行う関数 `extract_toc(html_content)` を作成してください。
- `h2`, `h3` タグを抽出する。
- 各タグに `id` 属性がなければ、テキスト内容からユニークなIDを生成して付与する(例: `<h2 id="section-1">...</h2>`)。
- テンプレートに渡すための目次データ(リスト形式)を返す。
  - 形式例: `[{'id': '...', 'text': '...', 'level': 2}, ...]`
- IDが付与された状態の修正済みHTML文字列も返す。

### 2. HTML/Jinja2テンプレート
- 右サイドバー用の `<nav>` タグと、上記リストデータをループしてリンクを表示するJinja2テンプレートを作成してください。
- 現在表示中のセクションを示すクラス(例: `.active`)が当たることを想定したクラス設計にしてください。

### 3. CSS (Sticky配置)
- 目次が画面上部に固定されて追従するよう、`position: sticky` を使ったCSSを書いてください。
- 現在のアクティブなリンク(`.active`)をハイライトするスタイルも含めてください。

### 4. JavaScript (IntersectionObserver)
- 記事内の `h2`, `h3` のスクロール位置を監視し、現在読んでいるセクションに対応する目次のリンクに `.active` クラスを付与/削除するスクリプトを書いてください。
- パフォーマンスを考慮し、`IntersectionObserver` APIを使用してください。


## 参考情報

### 参考URL
- Docusaurusで作成されたページ: <https://docusaurus.io/blog/releases/3.9>
- DocusaurusのGitHub: <https://github.com/facebook/docusaurus>

微妙にデザインが気に食わない部分があったので、多少やり取りしてデザインを最終化しました。

左ペインにドキュメントの構造を追加

次に左ペインにドキュメント構造を追加していきます。 そもそも、管理方法(別ファイルで構造を管理するか、フォルダ構造をそのまま使うか)も含めてGeminiと相談しながら決めました。

あなたはPythonとモダンなWeb開発のエキスパートです。
現在開発中の自作静的サイトジェネレーター(SSG)に、Docusaurusライクな「左サイドバー(ドキュメントナビゲーション)」を実装するためのコードを作成してください。

## 前提条件
- 言語: Python 3.11
- テンプレートエンジン: Jinja2
- ディレクトリ構成: `content/` 配下にMarkdownファイルとサブディレクトリがネストされている。

## 実装要件

### 1. Pythonロジック: `build_sidebar_tree(root_dir)`
再帰的にディレクトリを走査し、サイドバー描画用のネストされた辞書リストを作成する関数を実装してください。

**重要なロジック(Directory-First Approach):**
1. **ディレクトリの表示名(Label)**:
   - ディレクトリ内に `index.md` (または `_index.md`) が存在する場合、そのFrontmatterの `title` をディレクトリの表示ラベルとして採用する。
   - `index.md` がない場合、ディレクトリ名をCapitalizeしてラベルとする。
2. **アイテムの除外**:
   - ディレクトリの代表として使われた `index.md` は、そのディレクトリの `children` リストには**ファイルとして含めない**(重複排除)。
3. **並び順(Sorting)**:
   - ファイルおよびディレクトリのFrontmatterにある `sidebar_position` (int) の昇順でソートする。
   - `sidebar_position` がない場合は、ファイル名/ディレクトリ名の辞書順とする。

**生成するデータ構造の例:**
```python
[
  {
    "type": "category",
    "label": "技術記事",       # content/tech/index.md の title
    "url": "/tech/",          # ディレクトリへのリンク
    "collapsed": True,
    "children": [
      {
        "type": "link",
        "label": "Python入門", # content/tech/python.md の title
        "url": "/tech/python/",
        "active": False
      }
    ]
  }
]
```

### 2. Jinja2テンプレート: `sidebar.html`
- **再帰マクロ (`macro`)** を使用して、階層の深さに制限のないツリーを描画してください。
- **カテゴリ(フォルダ)**:
  - `<details>``<summary>` タグを使用し、開閉可能にする。
  - 現在表示中のページが含まれる親カテゴリは、初期状態で `<details open>` になるようロジックを組む。
- **リンク(ファイル)**:
  - 現在表示中のページとURLが一致する場合、`class="active"` を付与する。

### 3. CSS (Styling)
- 階層が深くなるごとに `padding-left` でインデントを付ける。
- `.active` クラスが付いたリンクをハイライト(太字や背景色変更)する。
- `<summary>` のマーカー(▶)のスタイルを整える。

まあまあいい感じ。今後、何か不具合があれば修正していく。

強調表示機能に対応する

GitHubやObsidianではデフォルトで、Calloutsと呼ばれているMarkdownの拡張記法に対応しています。 MuPyでもちおうしていきます。

GitHubやObsidianで導入されている強調表示機能を追加したいです。

下記のようなソースコードを記載したときに、特別に表示されるようにしたい。

参考URL:<https://github.com/orgs/community/discussions/16925>

```md
> [!NOTE]  
> Highlights information that users should take into account, even when skimming.

> [!TIP]
> Optional information to help a user be more successful.

> [!IMPORTANT]  
> Crucial information necessary for users to succeed.

> [!WARNING]  
> Critical content demanding immediate user attention due to potential risks.

> [!CAUTION]
> Negative potential consequences of an action.
```

それぞれのサンプル

NOTE


Highlights information that users should take into account, even when skimming.


TIP

Optional information to help a user be more successful.


IMPORTANT


Crucial information necessary for users to succeed.


WARNING


Critical content demanding immediate user attention due to potential risks.


CAUTION

Negative potential consequences of an action.