ディレクトリ構造をサクッとツリー図にする「Directory tree generator」を作りました
READMEやドキュメントにディレクトリ構造を載せたいとき、毎回 ├── や └── を手打ちしていませんか?
地味に面倒で、しかも深い階層になるとズレが気になって直したり、深さが変わるたびに縦線の調整が必要になったり。
そこで、インデントを入れたテキストをそのままツリー図に変換するツールを作りました。
まずは完成品
https://mifa.tokyo/tools/directory-tree-generator
主な機能:
- インデントされたテキストをリアルタイムでツリー図に変換
- スペース・タブ混在も自動判定
行をコメントとしてツリーに挿入- テキストのコピー /
.txtダウンロード / PNG画像として保存 - 全角スペースの自動補正
使い方
左の入力欄に、インデントでディレクトリ構造を書くだけです。
src
pages
index.astro
components
Header.astro
styles
global.css
public
favicon.ico右にすぐ変換されます。インデントがスペースでもタブでも自動で判定します。
技術的な実装
1. インデントの自動検出
ユーザーにインデント種別を選ばせるUIは省いて、入力テキストから自動で判定する方式にしました。
やっていることはシンプルで、コメント行と空行を除いた最初の有効行を見て、インデントに使われている文字を確認するだけです。
JavaScript
function detectIndent(lines: string[]): IndentInfo {
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const m = line.match(/^(\s+)/);
if (!m) continue;
const ws = m[1];
if (ws.includes('\t')) {
const tabs = (ws.match(/\t/g) ?? []).length;
return { type: 'tab', size: tabs, label: `タブ×${tabs}` };
}
return { type: 'space', size: ws.length, label: `スペース×${ws.length}` };
}
return { type: 'space', size: 2, label: 'デフォルト (スペース×2)' };
}タブが含まれていればタブ、そうでなければスペースとして扱い、最初のインデント量をそのまま1単位として使います。スペース2つで書いても4つで書いても、それぞれ正しく深さに変換されます。
2. コメント行の枝判定除外
で始まる行をコメントとしてツリーに挿入できます。枝記号(├─ └─)を付けずに親の縦線だけ維持して表示するイメージです。
.
├─ src
│ # コンポーネント類
│ ├─ components
│ └─ styles
└─ publicここで気をつけないといけないのが、コメント行が 隣接する実ノードの枝判定に影響しないようにすることです。
通常のノードは「同じ深さに後続ノードがあれば ├─、なければ └─」で枝を決めます。コメント行をそのまま混ぜると、コメントを見て ├─ にすべき実ノードが └─ になってしまうことがあります。
JavaScript
// isLast: コメント行を除外して判定
let isLast = true;
for (let j = i + 1; j < nodes.length; j++) {
if (nodes[j].depth < d) break;
if (nodes[j].depth === d && !nodes[j].isComment) {
isLast = false;
break;
}
}同様に、コメントが最後の行のとき(後続に実ノードがない)は縦線も不要なので、isLast をそのままコメント行の表示にも流用しています。
JavaScript
function commentStr(isLast: boolean, depth: number): string {
const pipe = depth > 1 ? ' │ ' : '│ ';
const blank = depth > 1 ? ' ' : ' ';
return isLast ? blank : pipe;
}3. 全角スペースの自動補正
日本語環境だと、IMEの影響でインデントに全角スペースが混入することがあります。ツリー変換時にインデントとして認識されないため、入力のたびに検出して半角に置換するようにしました。
単純に textarea.value を書き換えるだけだと、カーソル位置が先頭にリセットされてしまいます。selectionStart / selectionEnd で書き換え前の位置を保存して、書き換え後に復元することで自然な入力感を保てます。
JavaScript
function normalizeInput(el: HTMLTextAreaElement): void {
const normalized = el.value.replace(/ /g, ' ');
if (normalized === el.value) return; // 全角がなければ何もしない
const start = el.selectionStart;
const end = el.selectionEnd;
el.value = normalized;
el.setSelectionRange(start, end); // カーソル位置を復元
}全角スペースがない場合は早期リターンするので、通常の入力時は value の比較だけで終わります。
4. PNG生成をCanvas APIで自前実装
PNG出力はよく html2canvas が使われますが、今回はテキストを描画するだけなのでCanvas APIを直接使って実装しました。外部ライブラリへの依存をなくしつつ、Retina対応や文字色の切り替えも細かく制御できます。
JavaScript
const dpr = window.devicePixelRatio || 2;
cvs.width = w * dpr;
cvs.height = h * dpr;
ctx.scale(dpr, dpr);devicePixelRatio を掛けてキャンバスの実解像度を上げ、scale() で描画座標を元に戻すことで高DPIディスプレイでも滲まない画像になります。
ライトモード / ダークモードの判定も matchMedia で拾えるので、保存した画像がユーザーの環境に合った配色になります。
JavaScript
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const bg = isDark ? '#1a1a1a' : '#ffffff';
const fg = isDark ? '#e8e8e8' : '#1a1a1a';コメント行はグレーで描画して、テキスト出力との見た目の一貫性を持たせています。
まとめ
ツール自体の実装はシンプルですが、「インデント自動判定」「コメント行の枝への影響を排除」あたりは地味に考える箇所がありました。
ドキュメントを書く機会があれば使ってみてください。