二画面ファイラーを作った。

15 年以上前、まだ Windows ユーザーだったころ、二画面ファイラーを使っていました。

ファイラーとは、コピーや移動、リネームなど一連のファイル操作を行うソフトウェアのことで、いわゆるファイルマネージャー、Mac で言うところの Finder、Windows での Explorer にあたります。検索しても出てこないので、この文脈でのファイラーという単語の使い方はもしかしたら日本独特なのかもしれません。

二画面ファイラーは左右二画面を同時表示してファイル操作するという強い特徴があり、使い方によっては業務効率をかなり上げることができます。私が使っていたのは [あふ](http://akt.d.dooo.jp/akt_afxw.html) というソフトウェアで、その世界では知らぬ者はいない定番のファイラーでした。久しぶりに調べたところまだ健在のようですごく嬉しいです。

以下はコンソールで動かす二画面ファイラー [Midnight Commander](https://midnight-commander.org/) のスクリーンショットです。見た目は異なれど二画面ファイラーはどれも機能的には似ているのでだいたい感じがつかめるかと思います。
![二画面ファイラー](https://upload.wikimedia.org/wikipedia/commons/b/b8/GNU_Midnight_Commander_4.1.36_Windows_Vista.png)

Mac に移ってからは普通に Finder を使っていて、正直あまり使いやすいとは思えず悶々としていましたが、やがて慣れてしまって今に至ります。ただ、ずっと自分で作ってみたいなーと何となく思っていたのですが、このたび長年勤めた会社を退職し~~暇になった~~時間ができたので、思い切って作ってみました。

[https://github.com/takanopontaro/footloose](https://github.com/takanopontaro/footloose)

![0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/7b816e17-2198-2e46-13ed-fa142bb9a86b.gif)

Footloose は React で作られたブラウザーベースの二画面ファイラーです。クライアント＆サーバーのアーキテクチャーで作られており、起動すると Web + Socket サーバーが立ち上がり、クライアントソフトウェアとしてブラウザーを使用します。キーボードによる操作を前提としており、マウスでは主要な機能はほとんど使えません。キーボード専用と言っても差し支えありません。

基本的な使い方は、左右に表示されたディレクトリーに対してファイル操作を行うものです。相手窓に対して行う操作（コピーや移動）、自窓に対して行う操作（リネーム、削除など）があり、都度使い分けていくことになります。基本的にはこれだけです。これだけですが、左右に固定された窓があり、お互いを操作し合えるという状況が人によっては意外なほど業務効率を上げるのです。

## レイアウト

Footloose は以下のように、ディレクトリー、ログ、タスクマネージャーの 3 つのフレームで構成されており、基本的にはディレクトリーフレームを使用します。ファイル操作やコマンドの実行結果がログフレームに出力され、タスクマネージャーには現在実行中のコマンドが表示されます。コマンド実行結果の確認や中止もここから可能です。デフォルトでは全コマンドが表示されますが、フィルタをかけることもできます。

![layout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/6ef15edf-6229-0185-1407-d7f70a51d26c.png)

## 基本機能

たくさんありますが、ざっと挙げると…

### カーソル移動、アイテム選択、フレーム間のフォーカス移動など

コピーなど何かの操作をするときは、選択行があればそれが、無ければカレント行が対象になります。

![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/7c502271-ecbb-9a93-a55e-526cc9e81403.gif)

### コピー、移動など

基本的に、自窓のアイテムがソース、相手窓が行き先になります。

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/afb20103-3678-ce4b-4e36-efed4bafad41.gif)

### 削除、リネーム、ディレクトリー＆ファイル作成など

自窓だけで完結する操作です。

![2 5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/18d84f6a-7a0d-77b6-5139-98a7b5d1c9ac.gif)

### zip 圧縮、解凍など

zip ファイル上で enter を押すと自動的に一時ディレクトリに解凍され、そこに cd します。zip ファイルの中に入ったような形になります（仮想ディレクトリー機能）。

![2 4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/6896c685-5c70-f930-a5df-0a3c80a58966.gif)

### 絞り込み検索

正規表現によるインクリメンタルサーチが可能です。

![3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/a02c8ffb-d755-fa53-1d1e-cee4065a4bc0.gif)

### 履歴、ブックマークなど

ダイアログ上で、絞り込みや候補の削除も可能です。
デフォルトでは、データは localStorage に保存されますが、編集機能は提供していません。並び替えや変更などは localStorage を直接さわる必要があります。

![3 2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/524af871-a4cb-60bd-e26e-89cec5586ef8.gif)

### ギャラリーモード

サムネイル表示に切り替えることが可能です。デフォルトでは画像ファイルを img タグで表示するようになっていますが、このふるまいは自由に変更できます。

![4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26225/2e544f2b-3cc8-8915-8007-0a10da615d7a.gif)

### その他

- 数字、アルファベットキー押下によるダイレクト選択
- ファイルをアプリケーションで開く、Finder で表示する
- プロンプトによるディレクトリー変更
- ソート
- パスのクリップボードへのコピー

などなど…

基本機能はコマンドとして提供されており、キーボードショートカットからそのコマンドを呼び出しています。ショートカットは自由に変更可能なので、ここで自分好みにカスタマイズしていくことになります。

## 特徴

### 使用している技術

使い慣れた React を選択しました。そのためクライアント＆サーバーアーキテクチャになりました。ずっとフロント屋なので、クライアントとしてブラウザーを使うというのはやりやすかったです。ちなみにサーバー側は Socket.IO を使用しています。

状態管理として初めて Recoil を使ったんですが、すごくいいですね。Redux があまり好みではないため、今まで状態管理で色々試行錯誤してましたが、いい選択肢ができたと感じています。useState などを用いた通常の state よりもコンポーネントと疎結合にしやすいと感じていて、今後 React を使うときはまず Recoil を検討したいと思っています。

全体的に、特筆するような技術は用いておらず、いたって普通かと思います。

### CSS を使った i18n

国際化対応、というほどの規模のアプリケーションではないですが、静的テキストが使われている箇所はちょいちょいあるので、最初は言語ファイルを用意しようと思っていました。が、ちょっと違う手法を試してみようと思って、今回 CSS を使ってみました。私はずっと大規模サイトの設計＆構築を手掛けてきたため、CSS と相性が良いので、寄せられるところは寄せちゃおうという発想です。

具体的には React 側ではクラス名だけを付与して、実際のテキストは ::before, ::after などの疑似要素を使って CSS 側で挿入するというやり方です。このやり方だと、デザインと言語が同じファイルとして一元管理できることになり、Footloose のような小規模なアプリケーションに合っていると思います。もちろん、例えばテキスト選択ができないなど、疑似要素特有のデメリットはあるんですが、それでも魅力的だと思っています。

### CSS による見た目の調整

Footloose は言ってみれば単なる Web ページなので、見た目は CSS で自由にいじることができます。そのため、見た目調整機能の実装は最低限に抑え、必要なら直接 CSS をいじってもらう、というポリシーで作っています。例えば、列の幅を変えたり、非表示になっている列（デフォルトでたくさんあります）を見えるようにしたり、といったことは手作業で行う必要があります。これは不便といえば不便ですが、それほど頻繁に使わない機能なら割り切ってしまった方が効率が良いという判断をしました。

### 必要最低限の機能

前述したようにブックマークや履歴はブラウザーの localStorage に保存されますが、Footloose はその編集機能自体は提供していません。そのため、並び替えや変更などは localStorage を直接さわる必要があります。可能な限り機能をシンプルにしたかったのが主な理由ですが、ブラウザーベースのアプリケーションであれば、例えば専用の拡張機能を自分で作ってしまうなど技術的に解決できる手段が多いので、そこを期待しての仕様、という意味もあります。

## 使い方

```shell
$ npx footloose -p 3500
```

ブラウザで `http://localhost:3500` にアクセスすると Footloose が表示されます。

|オプション|説明|
|-|-|
|p|ポート番号|
|m|バイナリファイルのMIMEタイプを取得するか否か。ディレクトリー内の各ファイルのサイズによっては表示が重くなります。MIMEタイプが不要なら指定しないでOKです。|
|c|ユーザーコンフィグ（後述）|

## 動作環境

Mac でのみ確認しています。Windows, Linux は環境が無いため未確認です。デフォルトの設定が Mac を想定したものになっているため、上手く動かないのでは…と思っています。Windows には [あふ](http://akt.d.dooo.jp/akt_afxw.html) という素晴らしい二画面ファイラーがあり、Footloose も多大な影響を受けています。Windows の方は [あふ](http://akt.d.dooo.jp/akt_afxw.html) の使用をオススメします。

**現在ベータ版のため、細かい不具合がある可能性があります。ご使用の際はその旨ご了承ください。**特にデザインは素人のためかなりイケてません &#x1f4a9; が、後述するように見た目は自分で変更可能です。

必要な Node.js のバージョンは 14.14.0 以上です。

## 関連パッケージ

- [footloose](https://github.com/takanopontaro/footloose)（アプリケーション）
- [footloose-client](https://github.com/takanopontaro/footloose-client)（ブラウザー用のクライアント）
- [footloose-config](https://github.com/takanopontaro/footloose-config)（デフォルトのコンフィグ）

## キーボードショートカット

Footloose はキーボードショートカットの管理に [Mousetrap](https://craig.is/killing/mice) を使用しています。Mousetrap とほぼ同じルールでショートカットの設定ができ、キーボードから Footloose の多彩なコマンドを呼び出すことでアプリケーションの操作を行っています。カンマ区切りで複数のキーを一度に登録できるため、カンマ自体を表現したいときは文字列としての `comma` を使用します。

ショートカットはコンテキストごとに分けられており、デフォルトで以下が設定されています。

### アプリケーション

|キー|説明|
|-|-|
|tab, shift+tab|無効|
|~|フレーム間のフォーカス移動|

### ダイアログ（alert, prompt, confirm など）

|キー|説明|
|-|-|
|tab, shift+tab|フォーム要素間のフォーカス移動|
|enter|決定|
|escape|キャンセル|

### ダイアログ（item selector。履歴やブックマークなど）

|キー|説明|
|-|-|
|tab, shift+tab|エリア間のフォーカス移動|
|enter|決定|
|escape|キャンセル|
|up, down|候補間のフォーカス移動|
|backspace, delete|候補の削除|

### ディレクトーリフレーム

|キー|説明|
|-|-|
|数字, 英小文字, _ - .|1 文字目によるダイレクト選択|
|tab, shift+tab|絞り込み入力欄へのフォーカス移動|
|enter|カレント行の実行|
|backspace, delete|親ディレクトリーへの移動|
|pageup, pagedown|ページング|
|home, end|先頭末尾へのカレント行の移動|
|up, down|カレント行の移動|
|left, right|窓間のフォーカス移動。※ギャラリー時はカーソル移動|
|shift+left, shift+right|窓間のフォーカス移動|
|meta+left, meta+right|履歴の行き来|
|space|カレント行の選択|
|shift+a|全行選択|
|shift+ctrl+a|全行非選択|
|shift+ctrl+meta+a|選択状態の反転|
|shift+c|相手窓へコピー|
|shift+m|相手窓へ移動|
|shift+z|zip して相手窓へ保存|
|shift+ctrl+z|zip ファイルを相手窓へ解凍|
|shift+d|削除|
|shift+k|ディレクトリーの作成|
|shift+t|ファイルの作成|
|shift+r|リネーム|
|f5|再読み込み|
|plus|アクティブアイテムのパスをクリップボードにコピー|
|_|カレントディレクトリーをクリップボードにコピー|
|shift+i|カレントディレクトリーやアクティブアイテムをログフレームに出力|
|escape|絞り込み入力欄のクリア|
|shift+h|履歴の表示|
|shift+b|ブックマークの表示|
|shift+ctrl+b|カレントディレクトリーをブックマーク|
|shift+j|ディレクトリーの変更|
|shift+p|自窓を相手窓と同じディレクトリーにする|
|shift+ctrl+p|相手窓を自窓と同じディレクトリーにする|
|shift+ctrl+meta+p|両窓のディレクトリーを入れ替える|
|shift+o|アクティブアイテムを開く|
|shift+ctrl+o|アクティブアイテムを Finder で表示する|
|!|長いアイテム名の省略位置を変える|
|shift+g|ギャラリーモードへの切り替え|

### 絞り込み入力欄

|キー|説明|
|-|-|
|tab, shift+tab|フォーカスの移動|
|escape, enter|入力欄のクリア|
|up, down|カレント行の移動|

### ログフレーム

なし

### タスクマネージャーフレーム

|キー|説明|
|-|-|
|tab, shift+tab|フォーカスの移動|

## イジェクト

デフォルトでは、見た目はおろかショートカットすら変更できませんが、イジェクトという機能を使うとそれが可能になります。…と言ってもそういうコマンドがあるわけではなく &#x1f4a9; 、単に Footloose を立ち上げる時に設定ファイルのあるディレクトリを指定するだけです。

```shell
$ npx footloose -p 3500 -c /path/to/your-config-dir
```

### デフォルトのコンフィグ

デフォルトのコンフィグをクローンすると、TypeScript なので型が付いていてとても便利です。編集＆コンパイルすると dist ディレクトリができますので、ここを `-c` オプションで指定するのが一番手っ取り早いでしょう。このディレクトリーは Footloose サーバーの公開ディレクトリーになるので、ブラウザーからアクセス可能になります。例えば、ブックマークや履歴を編集する自分用の Web ページを作って活用するといったことも可能です。

[https://github.com/takanopontaro/footloose-config](https://github.com/takanopontaro/footloose-config)

主なファイルは以下の 3 つです。

### index.html

Footloose の HTML。基本的に変更する必要はありませんが、いじることは可能です。

### theme.scss

Footloose のデザインを定義しているスタイルファイル。見た目を変える場合はこのファイルを編集します。SCSS で記述することが可能です。

### bootstrap.js

Footloose の立ち上げ時に実行されるファイル。アプリケーションの設定とキーマップを定義する関数を default export します。キーマップ関数は何度も実行されるため、冪等性を保つ必要があります。

|関数|説明|
|-|-|
|getSettings|各フレームの設定関数|
|getKeyMaps|キーマップ定義関数。何度も実行される。|

設定関数 `getSettings` は以下のコンテキストに分かれています。

### アプリケーション

|プロパティ|説明|
|-|-|
|persistentSettings|カレントディレクトリーやソート状態などを localStorage に保存するか否か|

### ディレクトリーフレーム

|プロパティ|説明|
|-|-|
|selectedRowsOnly|コピーや移動をするときは、選択行があればそれを、無ければカレント行を対象にします。この設定を `true` にすると選択行のみを対象とするようになります|
|openPathApps|ファイルを開く時のアプリケーションを設定します。詳しくは[こちら](https://github.com/sindresorhus/open#app)。|
|sorts|デフォルトのソート|
|filter|デフォルトの絞り込み正規表現文字列|
|path|デフォルトのディレクトリー|
|previewMaker|ギャラリーモード時のレンダー関数|

### ログフレーム

|プロパティ|説明|
|-|-|
|filter|ログデータを引数に取り、`true` なら表示、`false` なら非表示にするフィルタ関数|

### タスクマネージャーフレーム

|プロパティ|説明|
|-|-|
|filter|タスクデータを引数に取るフィルタ関数|

## 鋭意制作中

現在ドッグフーディングしながら随時更新中です。ドキュメントがまったく間に合っていませんが、ユーザーコンフィグの書式は TypeScript のおかげでだいぶ分かるようになっています。もともとエンジニアを対象にしているうえ、ブラウザーベースで DevTools を使って色々調べることもできるので、まずはリリースしてみようという心意気で出してみました。

## 気になっている点

### DOM を直接さわっている

カレント行や選択行は data-active 属性や aria-selected 属性をセットすることにより表現しています。React のセオリーだとこれらはコード側で持つ state になりますが、Footloose では直接 DOM をいじっています。つまり querySelector や setAttribute などを使って実現しています。これは明らかに悪手ですが、100 や 200 くらいならよくても、数千、万の位のファイル数になるとさすがにパフォーマンスに影響が出てしまい、カーソルを移動するだけで少しもっさりするようになってしまったため、妥協しました。メモ化など色々工夫しても改善しなかったので、ここは今後の課題だと思っています。

### 全体的に、重い

全体的に動作がちょっと重いです…。見直さないといけないところたくさんあるなあ〜…

### まだまだ足りない機能が多い

あふを使っていた頃はほとんどあふだけでなんでも完結していたように記憶しています。でも Footloose は、使ってるとちょいちょい他のアプリケーションに切り替えていることに気づくので、やっぱりまだまだだなあ〜と感じています。今後に期待。

### デザインがひどい

… &#x1f4a9;

## 二画面ファイラーの思い出

あふ、正確には あふw ですが、昔 Windows を使っていたころはこれがないと仕事にならないというくらい使い倒してました。見た目はちょっといかにもという感じなんですが、とても使いやすくて細かいところまで考えられていて、感動したのを思えています。Mac に乗り換えた当初は、Parallels の Coherence 機能を使って無理矢理あふを使っていたくらいです。さすがにパフォーマンス的にちょっと…という感じだったので諦めましたが、あふが使えるならそれが一番かなと思いますね。

[あふ](http://akt.d.dooo.jp/akt_afxw.html)

あくまで 15 年前の知識になりますが、Windows は二画面ファイラーが豊富で、あふの他にも [だいなファイラー](https://hp.vector.co.jp/authors/VA004117/dyna.html) とか [内骨格](https://sites.google.com/site/craftware/cfiler) とか、いくつも魅力的なものがありました。今は離れて久しいので最新情報に疎いですが、今も愛用者は多いんじゃないかなと思います。

Mac にも二画面ファイラーはいくつもあり、結構試したんですが私はなぜか馴染めず、ずっと Finder を使い続けてきてしまいました。今はどうなんですかね。使いやすいのがあるのかもしれません。ちょっと調べてみようかな。

他にも [FD](https://w.wiki/3Pg3) とか [Midnight Commander](https://midnight-commander.org/) とか、コンソールベースの楽しいファイラもあります。Emacs の dired も面白いですね。これをきっかけに二画面ファイラーに注目が集まってくれると嬉しいな。

## ライセンス

MIT © [takanopontaro](https://kin-can.web.app/)