実に久しぶりの投稿になります。
前回の投稿が2021年なのでもう5年前。そのあいだ何をしていたのか、振り返ると特に何も思い浮かばず軽く引いている今日このごろです。
さて、自分は5年前に以下の記事を投稿しました。
Footloose という Web アプリケーションを作りましたという投稿なんですが、かいつまんで言うと今回の記事はこの Footloose をバージョンアップしたよという内容になります。
上記の記事はおそらく誰も読んでないと思うので、今回も同じようにまず二画面ファイラーとはなんぞやというところから説明したいと思います。
二画面ファイラーとは
ファイラーと言うのは、Mac でいう Finder、Windows でいう Explorer のことを指します。ファイルやディレクトリを扱うアプリケーションのことですね。使い方的には和製英語で、英語では file manager とか file explorer と言うのではないかと思います。
探してみると分かりますが世の中にはたくさんのファイラーがあって、それぞれ OS 標準のファイラーにはない特徴を備えています。使いこなすと業務が激しく効率化される魔法のようなアプリです。
そして二画面ファイラーというのは、その名の通り左右二画面になっているファイラーのことで、それぞれに任意のディレクトリを表示し、お互いにファイルを移動したりコピーしたりするなどして、ファイル操作を効率化するような仕組みになっています。
古くはターミナルで動く「FD/FDclone」や「Midnight Commander」、アプリでは Windows の「あふw」や「だいなファイラー」などが老舗かと思います。大昔はあまり選択肢がなかった記憶がありますが、今はたくさんの二画面ファイラーがあるので検索してみるときっと楽しいですよ。ちなみに何となく想像できるかもしれませんが、基本的にはマウスではなくキーボードで操作します。
Windows の二画面ファイラー「あふw」
自分は昔からこの二画面ファイラーというやつが大好きでした。前述の「あふw」をメイン使いしていたんですね。その後 Windows から Mac にスイッチした時になかなか気に入る二画面ファイラーが見つからず、なんとなく Finder を使い続けていました。そんなこんなで時は過ぎ、だいぶ経ったあと退職を機に「いっちょ自分で作ったるか」と意気込んで作ったのが Footloose になります。
なぜふたたび作ろうと思ったのか
一生懸命作って愛着も強かったこのアプリですが、実はリリース後自分ではそれほど使わないまま塩漬け状態になっていました。
当時 Web 制作会社に勤めていてかなり良いスペックの Mac を支給されていて、個人開発もその Mac で行っていました。その後まもなくフリーランス (実際は穀潰し) になったのですが、当時の自分用 Mac が9年落ち (!) のインテル Mac で、それで初めて Footloose を動かしたらまあびっくり、もっさりしてるではないですか。
重くなっていたのが Node.js のサーバー側の処理なんですが、特に無茶な事はしておらずわりとシンプルな処理だったので、ここが重いとなると結構厄介かもなあとちょっと凹んでしまい…。まぁ古い Mac は切り捨てればよいだけの話なんですが、やはり自分の Mac で思ったように動かなかったというのは結構なショックで、結果として放置することになってしまいました。
そんな中ここ1年ぐらいで Footloose を作り直したいなあという想いがふつふつと湧き上がり、Footloose2 のプロジェクトがスタートしました。ちょうど Rust を勉強し始めたころで、よっしゃサーバーを Rust で書き直したろ思ったのがきっかけです。
Footloose2 はクライアントは前と同じく React ですが、バックエンドは Rust で書いてます。旧版ではファイル操作系の処理はすべて Node.js のコードとして実装していたのを、今回はシステムコマンドを直接呼び出す手法に変更しました。これは開発コストとパフォーマンスを考慮しての選択です。
まだまだ足りないところはたくさんあると思ってますが、作ったからには外に出さないと!と思いまして、この記事を書くことにした次第です。二画面ファイラーに興味がある方、Finder とは違うファイラーを探している方、もしよろしければ触ってみてくださいね。前置きが長いですね、すみません。
別記事で技術スタックについても書いています。よろしければこちらどうぞ。
ちなみに今のところ Mac 専用です。当初は UNIX/Linux 系全般を想定していて、Windows でも WSL2 なら動くようにする予定だったんですが、自分が Mac しか持っていないこともあり後手に回っており未だ対応できていません。とはいえ Windows の方は素直に「あふw」等を使うのが一番良いかなと思います。
インストール
インストールは不要です。以下のように npx コマンドで起動します。
npx @footloose2/app start -p 3000 -b ./bookmark.json
| オプション | 説明 | デフォルト |
|---|---|---|
| -p | ポート番号 | 3000 |
| -b | ブックマークファイルのパス。存在しない場合は自動作成される。無指定の場合はブックマーク機能を利用できない。 | |
| -c | 設定ファイルのパス (後述)。 | ビルトイン |
| -s | CSS ファイルのパス (後述)。 | ビルトイン |
| -t | エントリの日時表記。 | %y/%m/%d %H:%M:%S |
ブラウザで http://localhost:3000/ にアクセスしてみてください。以下のような画面が表示されれば成功です 😀
使い方
Footloose2 は以下のような画面構成になっています。
黄色い枠がついているのが現在フォーカスのあるフレームで「操作元」、反対側のフレームが「出力先」になります。なにか良い呼び名がないものかと考えたのですが思いつきませんでした 😞 コード的には src / dest という名称なんですが、そーす/ですてぃねーしょん、というのもちょっとなと…。とりあえず「操作元」「出力先」としておきます。
フレームには現在のディレクトリに含まれるすべてのエントリが一覧表示されています。左右それぞれに任意のディレクトリを表示でき、左から右へ右から左へといった感じで両方を見ながらファイル操作していきます。
下部にはエントリの絞り込みフィールドがありリアルタイムで絞り込めます。その際のマッチ方法も、入力値そのままや正規表現などから選ぶことができます。ステータスバーには総エントリ数や現在のソートなど各種情報が表示されます。
下のログフレームにはコマンドの実行ログやメッセージが表示されます。
ファイル操作
この左右に分割された画面でコピーや移動等のファイル操作を行っていくのが二画面ファイラーの基本的な使い方になります。なんらかの出力を伴うコマンドはすべて、操作元から出力先へ送られます。例えばコピーを行うと操作元の対象リソースが出力先に複製されるといった具合です。一方リネーム等の自己完結型コマンドは操作元だけで完結します。
ファイル操作はすべてシェルコマンドで実行されます。圧縮は zip コマンド、コピーは cp コマンド、といった具合ですね。後述しますがこれらのコマンドはすべてカスタマイズ可能なので、例えば zip よりも 7zip がいいとか、rm は嫌だから trash にしたいとか、そういった好みに合わせた変更が可能になっています。
絞り込み
エントリフレームの下部には絞り込みフィールドがあります。ここに文字を入力することでエントリを名前でフィルタできます。通常 / 正規表現 / migemo の3パターンによる絞り込みが可能です。
migemo というのはローマ字で日本語をインクリメンタル検索するもので、昔からある非常に強力なツールです。自分は大昔にこれを使って感動したことがあり、Footloose2 にもぜひ取り入れたいと思い実装しました。ぜひ使ってみてくださいね。
jsmigemoを使わせていただいております ![]()
ブックマーク
よく使うディレクトリをブックマークしておけば次回から素早く移動できます。データは起動時にオプション指定したブックマークファイルに保存されるため、アプリを終了しても残るので安心です。ブックマークを絞り込んだり削除したりすることも可能ですし、なんだったらファイルを直接手で編集しても大丈夫ですよ。ただし自己責任で 😉
履歴
表示したディレクトリは履歴に残り、自由に行き来することが可能です。データはブラウザの localStorage に保存されますので、同じブラウザであればアプリを終了しても保持されます。ブックマークと同じく静的ファイルに保存することも考えたのですが、ディレクトリ移動はファイラーの基本中の基本なため、頻繁にファイルアクセスすることになるので避けました。でももしかしたら意外にそれほど気にする必要はないのかも? 🤔 将来的には対応するかもしれません。
履歴もブックマークと同じように絞り込みが可能ですが、削除機能はありません。今のところ localStorage を直接いじることでのみ編集が可能です。
仮想ディレクトリ
zip などのアーカイブファイルを仮想的なディレクトリとして扱う機能です。通常のディレクトリと同じ感覚でアーカイブ内に直接入ることができ、いちいち解凍せずに中身を確認できるため非常に便利です。エントリのコピーにも対応しており、アーカイブ内の特定のエントリだけ抽出することも可能になっています。対応アーカイブは今のところ zip / tar / tgz のみっつです。
ギャラリーモード
画像の確認に便利な表示モードです。グリッド表示になり、画像エントリのサムネイルが表示されるようになります。もちろんこの状態でもすべてのファイル操作が可能です。
プレビューモード
操作元エントリのプレビューを出力先にオーバーレイ表示するモードです。画像はもちろん、テキストファイルや動画、PDF もプレビュー可能です。ちょっとした確認にどうぞ。
ギャラリーモードとプレビューモードを組み合わせることももちろん可能です。
キーバインド
Footloose2 はマウスには完全非対応で、キーボードでしか操作できません。すべてショートカットキーで操作します。これは後述するように変更可能ですが、デフォルトでは以下のようになっています。ちなみに meta というのは ⌘ キーのことです。
エントリフレームにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| ctrl+f | 絞り込みフィールドにフォーカス |
| ctrl+; | マッチモードを切り替え |
| tab | 反対側のエントリフレームにフォーカス |
| shift+tab | ログフレームにフォーカス |
| up / down | カーソル移動 |
| shift+up / shift+down | カーソルを高速移動 |
| left / right | ギャラリーモード時にカーソル移動 |
| shift+left / shift+right | ギャラリーモード時にカーソルを高速移動 |
| pageup / pagedown | ページ単位でカーソル移動 |
| home / end | 先頭・末尾へ移動 |
| 0-9, a-z, 記号キー | 入力文字で始まるエントリへ移動 |
| space | 行選択をトグルしてカーソルを下に移動 |
| shift+space | 行選択をトグルしてカーソルを上へ移動 |
| ctrl+w | 現在行より上にある選択行から現在行までを選択 |
| shift+w | 現在行より下にある選択行から現在行までを選択 |
| ctrl+a | 全選択 |
| shift+a | 全選択解除 |
| shift+ctrl+a | 選択状態を反転 |
| enter | エントリを開く / ディレクトリへ移動 |
| backspace | 親ディレクトリへ移動 |
| ctrl+s | 出力先のパスを操作元のパスにする |
| shift+s | 操作元のパスを出力先のパスにする |
| shift+ctrl+s | 左右ペインのパスを入れ替え |
| ctrl+j | 指定ディレクトリへ移動 |
| ctrl+h | 履歴一覧を表示 |
| meta+left | 履歴を戻る |
| meta+right | 履歴を進む |
| ctrl+b | ブックマーク一覧を表示 |
| shift+b | 現在のディレクトリをブックマーク |
| ctrl+= | 現在行のパスをクリップボードへコピー |
| shift+= | 現在のディレクトリパスをコピー |
| ctrl+c | エントリをコピー |
| ctrl+m | エントリを移動 |
| ctrl+d | エントリを削除 |
| ctrl+r | エントリ名を変更 |
| ctrl+k | ディレクトリを作成 |
| ctrl+t | ファイルを作成 |
| ctrl+o | エントリを外部アプリで開く |
| shift+o | 現在のディレクトリを開く |
| ctrl+z | ZIP圧縮 |
| ctrl+u | アーカイブを展開 |
| ctrl+0 | ソートをリセット (名前でソートにする) |
| ctrl+1 | パーミッションでソート |
| ctrl+2 | 名前でソート |
| ctrl+3 | サイズでソート |
| ctrl+4 | 更新日時でソート |
| ctrl+\ | ディレクトリの表示位置を切り替え |
| ctrl+g | ギャラリーモードの切り替え |
| ctrl+p | プレビュー表示の切り替え |
| meta+up / meta+down | プレビューをスクロール |
| shift+meta+up / shift+meta+down | プレビューを高速スクロール |
| meta+pageup / meta+pagedown | プレビューをページ単位でスクロール |
| meta+home / meta+end | プレビューの先頭・末尾へ移動 |
| esc | 絞り込みフィールドをクリア |
| ctrl+/ | 長いファイル名の省略位置の切り替え |
絞り込みフィールドにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| up / down / enter | エントリフレームにフォーカス |
| esc | フィルタをクリアしてエントリフレームにフォーカス |
| ctrl+; | マッチモードを切り替え |
ログフレームにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| shift+tab | エントリフレームにフォーカス |
| esc | ログをすべてクリア |
| up / down | ログをスクロール |
| shift+up / shift+down | ログを高速スクロール |
| pageup / pagedown | ページ単位でスクロール |
| home / end | 先頭・末尾へ移動 |
リスト表示モーダル (ブックマークなど) にフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| up / down | カーソル移動 |
| shift+up / shift+down | カーソルを高速移動 |
| enter | 選択中の項目を実行 |
| esc | モーダルを閉じる |
| backspace | セカンダリアクションを実行 (ブックマーク削除など) |
| ctrl+f | フィルタ入力欄にフォーカス |
| ctrl+; | マッチモードを切り替え |
モーダルの絞り込みフィールドにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| up / down / enter | リストモーダルにフォーカス |
| esc | フィルタをクリアしてリストモーダルにフォーカス |
| ctrl+; | マッチモードを切り替え |
入力欄付きモーダルにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| tab | 次の要素にフォーカス |
| shift+tab | 前の要素にフォーカス |
| enter | 選択中のアクションを実行 |
| esc | キャンセル |
確認モーダルにフォーカスがあるとき
| ショートカット | 説明 |
|---|---|
| tab | 次の要素にフォーカス |
| shift+tab | 前の要素にフォーカス |
| enter | 選択中のアクションを実行 |
| esc | キャンセル |
カスタマイズ
Footloose2 はショートカットキーや対応するコマンド、表示テーマなど豊富なカスタマイズ要素を備えています。これらを変更するにはまず eject を行い、設定ファイルをローカルに出力します。
mkdir my-footloose2 && cd $_
npx @footloose2/app eject
これで設定ファイルやスタイルシートが静的に出力されます。
自分の思うようにいじったら、以下のようにオプションで各ファイルを指定して起動すれば OK です。
npx @footloose2/app start -p 3000 -b ./bookmark.json -c ./config.ts -s ./app.css -t "%y/%m/%d %H:%M:%S"
ひとつ注意点としては、Footloose2 のバージョンが更新された時に設定ファイルやスタイルシートに変更があった場合、それらを取り込む必要があることです。もう一度 eject したうえで、自分でカスタマイズした部分を移植するのが簡単かと思います。
これは正直面倒なので、なにかいい仕組みがないか思案中です ![]()
設定 / config.ts
Footloose2 の各種設定を行うファイルです。TypeScript で書かれており型定義ファイルも用意されています。以下のむっつの設定が可能になっています。
| ファイル | 説明 |
|---|---|
| config/associations.ts | エントリを開くアプリの紐づけを行う。 |
| config/commands.ts | コマンドの設定。API 経由で Footloose2 の様々な操作が可能。 |
| config/messages.ts | エラーメッセージなどアプリで表示されるメッセージの設定。デフォルトは英語。 |
| config/mimeTypes.ts | MIME タイプと拡張子のマッピング。プレビューモードで使用される。 |
| config/settings.ts | 履歴の保持数など、アプリ全体の設定。 |
| config/shortcuts.ts | コマンドを呼び出すショートカットキーの設定。 |
設定項目の詳しい仕様は型定義ファイルの JSDoc をご参照いただくとして、以下に各設定の概要を説明します。
associations.ts
エントリをアプリで開く時、特に設定しなければデフォルトのアプリで開きますが、変更することもできます。下記の例では MIME タイプを元にして開くアプリを決定しています。
// toml ファイルを VSCode で開く。
const associations: AssociationsConfig = [
(mime, entry) => {
if (mime === 'application/toml') {
return 'Visual Studio Code';
}
return null;
},
];
commands.ts
Footloose2 のアクションはすべてコマンドとして定義されています。API 経由で様々な操作が可能になっており、後述するショートカットキーと組み合わせて、アプリの操作を好きなようにカスタマイズできます。
// カーソルを動かすコマンド (moveCursor API をコールしている)。
const commands: CommandsConfig = [
{
name: 'MoveCursor',
action(api, combo, { direction, loop, step }) {
api.moveCursor(step, direction, loop);
},
},
];
messages.ts
エラーメッセージや確認ダイアログのメッセージなど、Footloose2 で表示される各種テキストの設定ファイルです。今のところメッセージ数は少ないので i18n のライブラリ等は使わずに単純な配列になっています。デフォルトは英語ですが日本語に書き換えることでアプリの日本語化が可能です。
mimeTypes.ts
MIME タイプと拡張子のカスタムマッピングです。プレビューモードではエントリの拡張子から MIME タイプが判定され、どのようにプレビューされるかが決定されます。この判定を上書きすることができます。
// `ts` はそのままだと video/mp2t と判定される。
// これをテキストファイルとしてプレビューさせる。
const mimeTypes: MimeTypesConfig = [
{ mime: 'text/plain', pattern: '\\.tsx?$' },
];
settings.ts
履歴の数やログの最大行数など、アプリ全体の設定を行います。以下の設定が可能です。
| 設定項目 | 説明 |
|---|---|
| clearEntryFilterOnDirChange | ディレクトリ変更時に絞り込みフィールドをクリアするか否か |
| logScrollAmount | ログフレームのスクロール量 (px) |
| maxHistoryCount | 履歴の最大保持数 |
| maxLogCount | ログの最大保持数 |
| previewScrollAmount | プレビューエリアのスクロール量 (px) |
| progressTaskLogInterval | ProgressTask のログ更新間隔 (ms) |
| virtualDirExcludePatterns | 仮想ディレクトリの表示から除外したいエントリの正規表現パターン一覧 |
shortcuts.ts
commands.ts で定義したコマンドをショートカットキーに割り当てます。ひとつのショートカットに複数のコマンドを割り当てたり、逆にひとつのコマンドに複数のショートカットを割り当てることもできます。またコマンドに引数を渡すことができます。
ショートカットの管理は Mousetrap で行っており、柔軟な設定が可能です。ctrl+z のような一般的なキーに加え、g i のような Gmail 風のキーシーケンスにも対応しています。
※単一キーにはデフォルトで MoveCursorByStartingLetter がアサインされています。キーシーケンスを設定したい場合はまずこれを外してください。
// ↑ と g ↑ に MoveCursor コマンドを割り当てる。
const shortcuts: ShortcutsConfig = {
DataFrame: {
'up, g up': [
{
cmd: 'MoveCursor',
args: { step: 1, direction: 'up', loop: true },
},
],
},
};
表示テーマ / app.css
Footloose2 の CSS ファイルです。変更することで見た目のカスタマイズが可能です。主要なスタイルは CSS 変数として先頭に定義されており、基本的にはこれらを変更します。
カスタマイズしやすいよう Footloose2 はプレーンなクラスベースのスタイリングを採用しています。やろうと思えば簡単に壊すこともできますので、CSS 変数以外をいじる際はご注意ください。
おすすめ設定
ここでは Footloose2 のおすすめ設定をいくつかご紹介します。
associations.ts
エンジニアならテキストファイルは VSCode 等の好きなエディタで開きたいはずです。これは以下のように AssociationsConfig を設定することで実現できます。開きたい拡張子を列挙するだけです。他にも動画ファイルをお気に入りのプレーヤーと紐づけたり、いろいろ試してみてください。
const associations: AssociationsConfig = [
(mime, entry) => {
if (entry.isFile() && /\.(txt|json|[jt]sx?|html?|css)$/.test(entry.name)) {
return 'Visual Studio Code';
}
return null;
},
];
拡張子だけでなく MIME タイプを元にすることもできます。またオブジェクト形式でアプリ名を指定する書き方も可能です。詳しくは AssociationsConfig の JSDoc をご参照ください。
commands.ts
Footloose2 のコマンドは、シェルコマンドとして実行したい文字列をサーバーに送信する形式をとっています。例えばエントリを削除する RemoveEntries は以下のようになっています。ここでは rm コマンドを送信していますが、例えばこれを trash コマンドに差し替えればより使い勝手が良くなりそうです。
const commands: CommandsConfig = [
{
name: 'RemoveEntries',
async action(api, combo) {
(中略)
await api.runProgressTask((entries, srcDir, destDir) => {
return {
label,
cmd: 'rm -vr %s',
total: 'find %s | wc -l',
src: entries.map((e) => e.name),
};
});
},
},
];
他にも zip コマンドではなく 7z にする、cp の代わりに ditto にする、等々お好みのコマンドに書き換えることでより使いやすい Footloose2 になっていくはずです。
ひとつ注意点としては、実行途中で y/n といったユーザーの入力を促す interactive なコマンドはうまく動きません。Footloose2 で実行されるコマンドは非対話的である必要があります。この点ご注意ください。
mimeTypes.ts
エンジニアにとって ts という拡張子は TypeScript ファイルですが、MIME タイプ的には video/mp2t です。Footloose2 のプレビューモードでは拡張子から推測された MIME タイプが使われるためそのままだと動画としてプレビューされてしまいます。mimeTypes.ts に以下の設定を加えると ts ファイルがテキストファイルとしてプレビューされるようになります。
const mimeTypes: MimeTypesConfig = [
{ mime: 'text/plain', pattern: '\\.tsx?$' },
];
settings.ts
Footloose2 には圧縮ファイルを仮想的なディレクトリとして扱う機能があります。Mac で作られた圧縮ファイルには __MACOSX や ._ファイル名 といった不可視エントリが含まれていることがあり、デフォルトではこれらも表示されてしまいます。これが気になるときは virtualDirExcludePatterns を設定してみてください。仮想ディレクトリ内のエントリをフィルタすることができ、視覚的なノイズを減らすことができます。
const settings: SettingsConfig = {
(中略)
virtualDirExcludePatterns: ['^(?:__MACOSX/|\\._.+)'],
};
ヘルプ
以下のコマンドでヘルプを表示できます。
npx @footloose2/app -h
npx @footloose2/app start -h
npx @footloose2/app eject -h
おわりに
これまで読んでいただきどうもありがとうございました。簡単ではありますが、以上で Footloose2 のご紹介は終わりです。
前述の通り二画面ファイラーは探すと結構ありまして、当然のことながら高機能しかもフリーだったりします。それを考えるとこのような個人の習作的なアプリを無理して使うメリットはあまりないというのが現実だと思います。
でもそれでも全然かまわないです。もともと「自分が作りたい!」というモチベーションドリブンで始まったプロジェクトなので、自己満足の極地と言いますか、自分自身が作ってて楽しい、使ってて楽しい、というのが一番だと思っています。他のアプリを見てもブラウザベースというのは見たことないですし、css を使った自由度の高いカスタマイズ性やシェルコマンドを用いた拡張の容易さはなかなか面白いのではないかと思っています。
もし誰かがこのアプリに興味を持ってちょっと触ってみて、「おっ」と思ってくれたら、こんなに嬉しいことはありません。
長文失礼いたしました。それではまた。👋







