AetherEchoesEngineering
Engineering#019810 min2,3425 view

superfile に学ぶ TUI ファイルマネージャの設計 — 描画・キーバインド・状態管理を読む

GitHub で 2 万スター超の Go 製ターミナルファイルマネージャ superfile を、機能紹介ではなく設計のサンプルとして読む。Bubbletea の状態管理、マルチパネルの描画、TOML で外出しするキーバインド設計を、自作 TUI の判断材料として運用者目線で棚卸しする。

SoSoraEndo2026年7月27日 09:0510 min2,342

動画で読む

superfile を「機能」ではなく「設計」として読む

superfile は、TUI(Terminal User Interface、端末上で動くGUI風の画面)を自作するときの設計サンプルとして読む価値がある。使い方の紹介ではなく、状態管理・描画・キーバインドの分け方を盗む対象として眺めると、得られるものが多い。

superfile は Go 製のターミナルファイルマネージャで、この記事を書いている時点で GitHub のスターが 2 万を超えている。マルチパネル、ファジー検索、画像プレビュー、プラグイン、テーマと、機能表だけ見れば「よくある高機能ファイラー」だ。だが私が惹かれたのは機能ではなく、それらを支える内部の分け方のほうだった。

私は少し前に、社内のログ調査を楽にするための小さな TUI を Go で書こうとして、状態管理の設計で一度詰まった。パネルを 2 つに増やした途端、どのキー入力がどのパネルに効くのかが分からなくなり、if 文が雪だるまになった。その後 superfile の構成を読んで、「ああ、ここを分けておけばよかった」と何度も思った。以下はその棚卸しだ。

Bubbletea の Model / Update / View が効く理由

最初に効いてくるのは、superfile が Bubbletea という TUI フレームワークの上に建っていることだ。Bubbletea は The Elm Architecture(状態を 1 か所に集め、入力ごとに新しい状態を作り直す設計)を Go に持ち込んだもので、これが状態管理の泥沼を避ける鍵になる。

仕組みは 3 つの部品に尽きる。アプリ全体の状態を持つ Model、入力イベントを受けて次の Model を返す UpdateModel から画面文字列を組み立てる View だ。画面のどこかを直接書き換える、という命令的な操作をしない。入力が来たら状態を作り直し、その状態から画面を丸ごと描き直す。

// Bubbletea の骨格。superfile の main モデルもこの形に乗っている
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        return m.handleKey(msg) // 新しい model を返すだけ
    }
    return m, nil
}

func (m model) View() string {
    return m.renderPanels() // model から画面を毎回組み立て直す
}

この「毎回作り直す」がなぜ楽なのか。私が自作 TUI で詰まった原因は、パネル A の変数とパネル B の変数を別々に持ち、片方を更新し忘れて表示がずれることだった。状態を 1 つの Model に集約すると、この種のずれが原理的に起きにくい。描き直しのコストは端末の狭い画面なら誤差だ。

マルチパネルとフォーカスは「状態」で持つ

マルチパネルの本質は、パネルを並べることではなく「いまフォーカスがどこにあるか」を状態として一元管理することにある。superfile は複数のファイルパネルとサイドバー、プレビュー領域を持つが、アクティブなパネルの番号を Model 側の 1 つの値として保持している。

これが分かると、キー入力の分岐がすっきりする。矢印キーが来たとき、どのパネルのカーソルを動かすかは「アクティブなパネル番号」を見れば決まる。パネルごとに別々のハンドラを書いて取り合いをさせる必要がない。フォーカスという概念をデータにした瞬間、描画もロジックも一本化される。

私の失敗作は、フォーカスを boolean のフラグでパネルごとに持っていた。panelA.activepanelB.active が両方 true になる、あってはならない状態が普通に作れてしまう。superfile 式に「アクティブは整数ひとつ」にすれば、その矛盾はそもそも表現できない。表現できない状態は、バグにならない。これは Rust 製のローカルツールを読んだ破壊的コマンドガードの記事でも感じたことで、「まずい状態を型やデータ構造で作れなくする」設計は言語を問わず効く。

キーバインドは TOML で外に出す

キーバインドはコードに直書きせず、設定ファイルに外出しするのが superfile の判断だ。superfile は TOML(読みやすい設定ファイル形式)でホットキーを定義し、しかも default 版と vim 版の 2 系統を用意している。

この分離が効くのは、キー割り当てが「人によって正解が違う」領域だからだ。Vim 使いは hjkl で動きたいし、そうでない人は矢印キーで十分だと思う。ここをコードに埋めると、好みの数だけ if 文が増える。設定ファイルにキー名とアクション名の対応表として持てば、コードは「アクション名」だけを知っていればよくなり、キーの好みには一切関知しなくて済む。

自作するときの実務的な線引きはこうだ。アクション(「下に移動」「削除」「タブを開く」)はコードの語彙、キー(j / / Ctrl-n)は設定の語彙。この 2 つを混ぜないだけで、後からキーを足すときにコードを触らずに済む。私は最初これを混ぜて書き、ユーザーから「Emacs バインドが欲しい」と言われた瞬間に全部書き直す羽目になった。TUI のキーバインドは、増える。必ず増える。

プレビューとプラグインは UI から切り離す

重い処理は描画ループから追い出す、というのが最後の学びだ。ファイルプレビューや画像表示は、内容の読み込みに時間がかかる。これを View の中で同期的にやると、カーソルを動かすたびに画面がもたつく。

Bubbletea はここに Cmd(非同期の副作用を表す仕組み)という逃げ道を用意している。「このファイルを読め」という命令を Cmd として発行し、読み終わったら結果がメッセージとして Update に返ってくる。描画スレッドは待たない。superfile の画像プレビューやプラグインも、この「重い仕事は外、結果だけ受け取る」の形に乗っている。

プラグイン機構を別レイヤーに切っているのも同じ思想だ。コア機能を汚さずに拡張点を用意しておくと、本体の描画ロジックは小さく保てる。この「速さのために処理を別の場所へ逃がす」判断は、Linear の同期エンジンを読んだ記事で見た設計とも通じる。UI を軽く保つために、重さをどこか別の層へ押し込む。層の名前が違うだけで、やっていることは同じだ。

自作するなら、何を真似て何を捨てるか

結論として、superfile から真似るべきは「状態の一元化・キーの外出し・重い処理の分離」の 3 点で、逆に最初から全機能を追う必要はない。20 種類のプレビューやプラグイン機構は、あなたの道具に必要になってから足せばいい。

私がログ調査 TUI を書き直したときは、この 3 点だけを守った。状態を 1 つの Model に集め、キーは小さな TOML に出し、ログの grep は Cmd で非同期に回す。パネルは 2 つで固定、プレビューは付けなかった。それでも「片方を更新し忘れて表示がずれる」あの地獄からは抜け出せた。設計の骨さえ真似れば、機能の数は後からで間に合う。

CLI やターミナル系の道具は、ここ数年で作り手の裾野が一気に広がった。ランタイムが静かに入れ替わるような動きもあって、端末の中は今わりと面白い。superfile はその流れの中で、機能の見本市であると同時に、設計の教科書としても読める一本だと思う。

よくある質問

superfile はどんなツールですか?
Go 製のターミナルファイルマネージャで、マルチパネル・ファジー検索・画像プレビュー・プラグイン・テーマを備えています。GitHub のスターは 2 万を超え、Linux / macOS / Windows で動きます。TUI フレームワークの Bubbletea 上に構築されています。
Bubbletea の The Elm Architecture とは何ですか?
状態を 1 か所(Model)に集め、入力ごとに Update で新しい状態を作り直し、View でその状態から画面全体を組み立て直す設計です。画面を直接書き換える命令的な操作をしないため、複数パネルの表示ずれのようなバグが起きにくくなります。
自作 TUI で superfile から真似るべき点はどこですか?
状態を 1 つの Model に集約すること、フォーカスを整数ひとつの状態として持つこと、キーバインドを TOML など設定ファイルに外出しすること、プレビューなど重い処理を Cmd で描画ループから切り離すことの 4 点です。全機能を追う必要はありません。

参考文献

  1. superfile — GitHub リポジトリ
  2. Bubble Tea — A Go framework based on The Elm Architecture
  3. superfile 公式ドキュメント

Reaction

Share

X (Twitter)