Zellij ターミナルワークスペースと WebAssembly プラグイン

自分は Linux をセットアップして最初に行う設定が tmux 関連だったりするのですが、新しい Windows 機の WSL2 にも秘伝の .tmux.conf を入れつつ、そうだ Zellij を試してみようと思い立ち、良い感じだったので紹介です。

Zellij

A terminal workspace with batteries included

Zellij (ぜりーじゅと読むそうです)は、いわゆるターミナルマルチプレクサーに種別されるソフトウェアで、セッション管理や、ターミナルエミュレータ上での画面分割やタブ制御を行うことができます。

Windows Terminal WSL2 上で動作している Zellij。

tmux よりも新しく作られていることもあり、デフォルトで自動起動スクリプトの出力やシステムクリップボード連携設定などが備わっており、また操作系も画面下部に表示することができるので非常に導入しやすいつくりとなっています。

導入

絶賛開発中 (記事執筆時点 0.36.0) の Rust でできたソフトウェアにて、導入は cargo install を使ってソースからビルドする方法が良いと思います。cargo なのでコマンド一発です。

Zellij User Guide – Installation

The easiest way to install Zellij is through a package for your OS.

If one is not available for your OS, you can download a prebuilt binary or even try Zellij without installing.

Otherwise, you can install it with Cargo.

(cargo を含む)Rust のツールチェインが未導入の場合は rustup で導入します。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

導入後 PATH などが設定されますので、いったんシェルを落とし上げします。

cargo install で zellij を導入。

cargo install --locked zellij
$ ls -laF ~/.cargo/bin/ | grep zel
-rwxr-xr-x  1 hiromasa hiromasa 17380208  4月 20 17:31 zellij*

なお、cargo install で導入したバイナリーのアップデートは cargo-install-update を使うことで行うことができます。

# cargo-update の導入 (初回のみ)
cargo install cargo-install-update
# アップデート確認
cargo install-update -l
    Polling registry 'https://index.crates.io/'.....

Package         Installed  Latest   Needs update
espup           v0.2.9     v0.4.0   Yes
cargo-generate  v0.18.2    v0.18.2  No
cargo-update    v13.0.1    v13.0.1  No
powerline-rs    v0.2.0     v0.2.0   No
zellij          v0.36.0    v0.36.0  No
# Needs update が Yes なものをアップデート
cargo install-update -a

フォント

Zellij は標準で powerline フォントに対応しています。お使いのターミナルエミュレータのフォントに設定しましょう。自分は HackGen を使わせていただいています。(とてもきれいです…!)

https://github.com/yuru7/HackGen

白源 (はくげん/HackGen) は、プログラミング向け英文フォント Hack と、源ノ角ゴシックの派生フォント源柔ゴシックを合成したプログラミングフォントです。

設定フォントは「源ノ角ゴシックの派生フォント源柔ゴシックに、Powerline フォントを含む Nerd Fonts を合成した」 HackGen_NF_*.zipHackGenConsoleNF-Regular.ttfHackGen Console NF)を設定すると良いと思います。

なお、Windows Terminal だと Ricty 系の powerline フォントの罫線がずれるようです。

自動起動設定

zellij 起動確認後は、シェル設定ファイルに Zellij の自動起動設定を入れておくと便利です。

Zellij User Guide – Autostart on shell creation

Autostart a new zellij shell, if not already inside one. Shell dependent, fish:

自分は .bashrc ですので次のようになります。

echo 'eval "$(zellij setup --generate-auto-start bash)"' >> ~/.bashrc

ちなみに zellij setup --generate-auto-start bash の中身は次のようになっています。tmux だと自分で書かなければいけないので小粋な感じでぐー。

if [[ -z "$ZELLIJ" ]]; then
    if [[ "$ZELLIJ_AUTO_ATTACH" == "true" ]]; then
        zellij attach -c
    else
        zellij
    fi

    if [[ "$ZELLIJ_AUTO_EXIT" == "true" ]]; then
        exit
    fi
fi

というわけで、お好みで ZELLIJ_AUTO_ATTACH を設定すると、Windows Teminal などがダウンしたときにや誤ってタブを落とした時に自動復帰できて良いかと思います。

`.bashrc`

ZELLIJ_AUTO_ATTACH=true
eval "$(zellij setup --generate-auto-start bash)"

関連して Zellij の default key binding に Ctrl + s, s がバッファのサーチに割り当てられていて、自分はうっかりロックを忘れて tty に START/STOP を送ってしまうことがあったので、以下も一緒に入れて tty の制御コード受付を disable にしています。

if [[ -t 0 && $- = *i* ]]
then
    stty -ixon
fi
ZELLIJ_AUTO_ATTACH=true
eval "$(zellij setup --generate-auto-start bash)"

設定

Zellij はシングルバイナリーで動作するようになっていますが、設定ファイルを使うこともできます。デフォルト設定を最初に出力すると良いかと思います。

mkdir -p ~/.config/zellij/themes
mkdir -p ~/.config/zellij/layouts
mkdir -p ~/.config/zellij/plugins

Zellij User Guide – Configuration

Zellij uses KDL as its configuration language.

# 設定ファイル
zellij setup --dump-config > ~/.config/zellij/config.kdl

自分のデフォルト設定から変えている定義は次の通りです。ほとんど変えてないですねw

// ペインのフレームをボーダレスに
pane_frames false
// 起動時の操作モードをインターフェース locked に
default_mode "locked"
// 配色テーマ
themes {
    tokyo-night-dark {
        fg 169 177 214
        bg 26 27 38
        black 56 62 90
        red 249 51 87
        green 158 206 106
        yellow 224 175 104
        blue 122 162 247
        magenta 187 154 247
        cyan 42 195 222
        white 192 202 245
        orange 255 158 100
    }
}
theme "tokyo-night-dark"

テーマは次のドキュメントからいただいてきたものです。

Zellij User Guide – Themes

Themes can be specified either in the configuration file under the themes section, or directly in a separate file.

レイアウト

Zellij の肝のひとつですが、レイアウトファイルを使うことによりペインに好きなパーツを配置することができます。

# デフォルト画面レイアウトファイル
zellij setup --dump-layout default > ~/.config/zellij/layouts/default.kdl

pain の一番目に後述する自分の WebAssembly プラグインを追加していますが、ファイルの内容は次のようになります。

layout {
    pane size=1 borderless=true {
        plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm"
    }
    pane size=1 borderless=true {
        plugin location="zellij:tab-bar"
    }
    pane
    pane size=2 borderless=true {
        plugin location="zellij:status-bar"
    }
}

この定義が起動時の画面レイアウトと対応する形のつくりになっています。タブバーや下部のステータスバーがデフォルトプラグインとして実装されているのが分かります。

レイアウトファイルの記述方法は以下のドキュメントが参考になります。

また Zellij 起動時の引数でレイアウトファイルを指定できるため、作業によってワークスペースを切り替えるようなことも可能です。

Zellij User Guide – Layouts

Layouts are text files that define an arrangement of Zellij panes and tabs.

You can read more about creating a layout

default_tab_template を使うと複数のタブで同じ設定を共有できます。

layout {
    tab name="t420s#1" focus=true
    tab name="t420s#2"
    default_tab_template {
        pane size=1 borderless=true {
            plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm"
        }
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}

例えばですが、シングルボードコンピュータへの ssh 接続を行い、各種リソースを表示するようなレイアウトを作成した例 です。(後述していますがこの例で使っている RISC-V Linux な SBC では直接 Zellij が動作しないため、ホスト側から ssh で接続しています。RPi 4A など Arm 機ではリモート側で Zellij を起動できるでしょう)

layout {
    tab name="LPi4A" focus=true {
        pane split_direction="vertical" {
            pane size="60%" close_on_exit=true {
                command "sshpass"
                args "-p" "licheepi" "ssh" "sipeed@172.16.15.201"
            }
            pane size="40%" {
                pane size="25%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "-n5" "df" "-k"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sudo" "cat" "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sensors"
                }
                pane size="43%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "htop"
                }
            }
        }
    }
    tab name="Host"
    default_tab_template {
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}

Ubuntu/Alactirry 上の動作:

Windows Terminal/WSL2 Ubuntu で動作させている様子(WSL2 Ubuntu 上の zellij-server が接続を維持してくれるのでターミナルを落としてしまっても復帰できる例):

キーバインドから特定のコマンドをペインで起動

例えば F12 押下で Python REPL を起動したいなんて時は config.kbl で次のようにします。

keybinds {
    normal {
        // uncomment this and adjust key if using copy_on_select=false
        // bind "Alt c" { Copy; }
        bind "F12" {
            Run "python" "-i" "/home/hiromasa/.local/share/micropython/imports.py" {
                cwd "/home/hiromasa"
                direction "Down"
            }
        }
    }

ワンボタンで計算機起動的。とても便利です。

ペイン内の操作シンク

クラスタ構成の同一構成複数サーバとかで同じコマンドを入力したい時につかうあれも OK です。tab 操作から sync を選択することで、ペイン全てに同じ操作ができます。

ペインとタブ間の Break 操作

Zellij 0.38 よりペインをタブに移動させたり、その逆ができるようになりました。便利です。

WebAssembly プラグイン拡張

Zellij プラグインは WebAssembly/WASI で作成することができます。勉強がてら 日時 date-time を表示するプラグインを Rust でかいてみました。

https://github.com/h1romas4/zellij-datetime

This plugin adds a date and time pane to [Zellij](https://zellij.dev/), a terminal multiplexer.

amd64 Ubuntu/Alacritty で動作している様子。

なお、システムインターフェースは WASI API の範囲となります。とりあえずまだ TIMEZONE がとれない気がした(たぶん)ので +9 JST 固定でビルドしました

(2023-06-21 追記)定義ファイルとマウスクリックでタイムゾーンを選択できるように改良しました。

WASI API 超えをしたい場合のアイディアメモ:(ホスト側で情報を取得するシェルスクリプトを動かして、結果をファイルかソケットでプロキシしていると思います)

https://github.com/yvt/zellij-cpulamp

Minimal CPU activity indicator plugin for Zellij

ファイルシステムへのセキュリティーポリシーなどがどうなっているかなど継続調査。WebAssembly ランタイムは Wasmer (執筆時点 2.3 系) が使われています。

https://zellij.dev/documentation/plugin-api-file-system.html

Plugin API – Reading from the Filesystem

(2023-06-19 追記) Zellij 0.37 からプラグインから Worker スレッドが作成できるようになりました…!

https://zellij.dev/documentation/plugin-api-workers.html

Plugin workers are a way to get around the fact that wasm/wasi threads are not stable yet. If a plugin has a potentially long operation to perform, it can declare a worker on startup and send and receive messages from it.

Wasm を使っているため .wasm ファイルは amd64 や Arm といった CPU の違いを気にせず同一バイナリーが使えますので便利です。 .wasm を配置してレイアウトファイルで読み込ませれば動作を開始します。(Wasmer は 3.2 から RISC-V 対応なので残念ながら現時点では RISC-V 機では動作しません)

今回は Rust を使いましたが、言語を選ばずプラグインがかけるのも良いです。

なお、まだ Rust と Zig 言語以外にはバインディングのリリースはなく、そのほかの言語では、引数をもらうのに WASI の stdin から JSON をパースする処理をかく必要がありそうです。Rust のバインディングも内部的にはこの構成になっています。この辺は、WebAssembly の Component Model (Interface Types) の仕様ができれば改善されることでしょう。

(2023-06-19 追記)Zellij は開発中のためプラグインの ABI (API) zellij-tile が現在のところまだ確定していません。このため Zellij の update が合った際は、Rust wasm32-wasi なプラグインは該当バージョンに合わせて dependencies を修正して .wasm ビルドしなおす必要があります。(これは近い将来に修正されると思われます)

(2023-08-28 追記)Zellij 0.38 で Wasm plugin API が protocol buffer 化され、下位互換が保たれるようになりました。API チェンジがない限り Zellij のバージョンが上がってもそれよりも下のバージョンでコンパイルされたプラグインが利用可能です。

https://zellij.dev/documentation/plugin-upgrading.html

Upgrading a Plugin

Since Zellij plugins using zellij-tile rely on shared data structures, currently one would need to compile a plugin against the corresponding zellij-tile package of the zellij version it is installed on.

The Zellij maintainers are aware this is an inconvenient and undesired scenario and are working on a more robust solution.

zellij-datetime リポジトリはその時点の最新版でリリースしていきます。もし筆者が Zellij のアップデートに気がついていなかったら、以下の部分のバージョンを Zellij コアのそれと合わせるように修正ビルドして .wasm を取得してみてください。

$ cat Cargo.toml | grep -A 2 dependencies
[dependencies]
zellij-tile = "^0.37"
zellij-tile-utils = "^0.37"

size=1 のペインを縦並びにするハック

デフォルトのペインの罫線表示を pane_frames false (非表示)とした場合、size=1 のペインを vertical で配置すると余計なボーダーが表示されてしまうのを、pane_frames trueとした上で、次のように個別に pane borderless=true として回避。

layout {
    tab name="thinkpad-t420s" focus=true {
        pane borderless=true
    }
    default_tab_template {
        pane size=1 split_direction="vertical" {
            pane size="75%" borderless=true {
                plugin location="zellij:tab-bar"
            }
            pane size="25%" borderless=true {
                plugin location="file:/home/hiromasa/.config/zellij/plugins/zellij-datetime.wasm" {
                    // Test multiple time zone definitions and default time zones.
                    timezone1 "PDT/-9"
                    timezone2 "UTC/0"
                    timezone3 "CEST/+2"
                    timezone4 "JST/+9"
                    default_timezone "JST"
                    // Test color settings.
                    background_color "#0080a0"
                    foreground_color "#ffffff"
                    pane_color "#383e5a"
                }
            }
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar" {
                supermode true
            }
        }
    }
}

次のスクショの 1行目のように、タブバーと時計を横並びに…などできる。(ただしウインドウサイズを変えるとボーダーがでてきたり、現在のところ少しバギー)

その他

もし Zellij が panic で起動しない場合はログを確認するのと、よく分からなければ次のテンポラリーファイルを削除して再起動してみます。(マルチユーザ使用の場合は気を付けて)

$ rm -Rf ~/.cache/zellij/*
$ rm -Rf /tmp/zellij*

Zellij 起動中に新しい Zellij を cargo install-update するとセッションがアップグレードできずに起動しない場合があります。 rm -Rf ~/.cache/zellij/* で修正できるはずです。

https://github.com/zellij-org/zellij/issues/2553

zellij does not start anymore #2553

ERROR  |zellij_utils::errors::not| 2023-06-17 18:54:54.876 [async-std/runti] [/home/amtoine/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/zellij-utils-0.36.0/src/errors.rs:585]: Panic occured:
             thread: async-std/runtime
             location: At /home/amtoine/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/rkyv-0.7.42/src/impls/core/mod.rs:267:17
             message: assertion failed: !result.is_null() 

おわりに

今回 Zellij のプラグインをかいていたら、昔よく VZ Editor のマクロをかいていたことを思い出しました。。Zellij 拡張も テトリスやスネークゲームなどもつくれそうな雰囲気です。

これだけ拡張性が高いと心強いですね。良ければお試しください。

関連

コメントを残す