NeovimのGUI「Zonvie」を使う 設定と外部化UIの中身
Zonvieは、Zigで書かれたNeovim用のGUIです。macOSではMetal、WindowsではDirect3D 11で描画し、設定はTOMLファイル1枚に書きます。設定しだいでコマンドラインやメッセージ、補完メニュー、タブの表示までNeovimから引き取ります。v0.4.1を入れて、どの部品を引き取っているかをNeovim側から読み出す手順を中心に確かめました。同じ手順はNeovideなど他のGUIにも使えます。
検証環境:Zonvie v0.4.1(macOS arm64のdmg)、NVIM v0.12.4、macOS 26.4.1。比較にNeovide 0.16.2も使いました。nvim --headless --listenで立てたNeovimにZonvieをつなぎ、別のターミナルからnvim_list_uis()やウィンドウの一覧を読む方法で確かめています。掲載したJSONと出力は実行結果です。画面を見ないと判断できない日本語入力の変換中の表示や、タブに出るAIエージェントの状態表示は確かめていません。
目次(9項目)
Zonvieはgoneovimの作者が作る次のGUI
作者はakiyosi氏で、Qtで作られたNeovim GUIのgoneovimを長く開発してきた人です。goneovimのREADME(v0.6.17)は、Qt 5.15のサポート終了を理由に機能開発を止めたと書いたうえで、次のように続けています。
The author has started developing Zonvie, a new Neovim GUI built with Zig and native frontends, empowered by generative AI. Future efforts will be focused there.
goneovimの利用者にはそのまま乗り換え先の候補になります。ライセンスはMITで、2026年10月22日時点の最新リリースはv0.4.1(8月15日)です。配布物はmacOS用のdmgとWindows用のzipで、Linux対応はロードマップ上まだ途中です。NeovimそのものについてはNeovimとは何か、Vimとの違いにまとめてあります。
macOSに入れてconfig.tomlを用意する
READMEにはXcodeでソースからビルドする手順しかありませんが、GitHubのReleasesにdmgがあります。zonvie.appをアプリケーションフォルダに入れれば終わりです。ただし署名はad-hocで、spctl -a -vv zonvie.appの判定はrejectedでした。curlで取得した今回はターミナルから起動できましたが、ブラウザで落としてFinderから開く場合は試していません。
コマンドラインからは、アプリの中の実行ファイルを呼びます。--noforkを付けるとターミナルにつながったまま起動し、カレントディレクトリも引き継ぎます。
$ /Applications/zonvie.app/Contents/MacOS/zonvie --version
zonvie v0.4.1
$ /Applications/zonvie.app/Contents/MacOS/zonvie --install
Default config.toml created: ~/.config/zonvie/config.toml
--installはひな型を書いて終わり、2回目はConfig file already exists, skippedと出て上書きしません(出力のホームのパスは~に置き換えています)。できたファイルは19行で、[font]・[neovim]・[window]・[server]の項目がすべてコメントのまま並んでいました。
XDG_CONFIG_HOMEを設定すると$XDG_CONFIG_HOME/zonvie/config.tomlだけを読み、ファイルが無くても~/.configは見ませんでした(READMEどおり)。一方HOMEを一時ディレクトリに向けても効かず、--installは本来のホームに書き込みます。設定を切り替えて試すならXDG_CONFIG_HOMEを使います。
見た目の基本は次の3節です。opacityはREADMEに「when blur=true」とあり、透過はぼかしと組みで効きます。
[font]
family = "Menlo"
size = 16
linespace = 4
[window]
blur = false
opacity = 1.0
[scrollbar]
enabled = true
show_mode = "always"
フォントでは2つ引っかかりました。ひな型のコメントにあるSF MonoはこのMacに入っておらず、--logで書き出したログにskipped unavailable 'SF Mono'と出てMenloに落ちていました。存在しない名前も同じく飛ばして起動します。ところがfamily = "Osaka"(このMacのフォント一覧には無い)では、ウィンドウは出るのにNeovimへの接続が始まらず、2回とも止まりました。止まったときは--logのログで原因を追えます。
外部化UI(ext_*)でNeovimの何がGUIに移るか
NeovimのGUIはnvim_ui_attachでNeovimにつなぐとき、「この部品は自分で描く」と申告できます。これが外部化UIで、部品ごとにext_で始まる名前が付いています。
| Neovimの名前 | GUIに移るもの | Zonvieの設定 | 起動オプション |
|---|---|---|---|
ext_cmdline | :や/の入力欄 | [cmdline] external | --extcmdline |
ext_popupmenu | 補完メニューとwildmenu | [popup] external | --extpopup |
ext_messages | メッセージ全般 | [messages] external | --extmessages |
ext_tabline | タブページの一覧 | [tabline] external | --exttabline |
ひな型にはどのexternalも無いので、入れた直後のZonvieは4つとも自分では描かず、ターミナルのNeovimと同じく最下行に文字で出します。設定ファイルと起動オプションは足し算で、ログにもhasExtMessages = true (cli=false, config=true)のように両方が記録されます。起動オプションで無効にする手段はありません。
複数のUIが同じNeovimにつながったときの決まりは、Neovimのヘルプにあります。
By default Nvim requires all connected UIs to support the same capabilities, thus the active capabilities are the intersection of those requested.
Zonvieで外部化を有効にしていても、ターミナルのUIが1つつながれば、ターミナルが対応していない外部化は効かなくなるということです。
nvim_list_uis()でGUIが引き受けている部品を確かめる
各UIの申告はnvim_list_uis()で読めます。外から読めるよう、ソケット付きのNeovimにZonvieをつなぎます。
# 端末1: 画面を持たないNeovimを立てる
nvim --headless --clean --listen /tmp/x.sock
# 端末2: Zonvieをつなぐ
/Applications/zonvie.app/Contents/MacOS/zonvie --nofork --connect-nvim=/tmp/x.sock
# 端末3: つながっているUIの申告を読む
nvim --server /tmp/x.sock --remote-expr 'json_encode(nvim_list_uis())'
ひな型の設定のままつないだ結果です(改行だけ入れています)。
[{"ext_multigrid": true, "term_name": "", "height": 18, "stdout_tty": false,
"chan": 4, "ext_wildmenu": false, "ext_popupmenu": false, "width": 47,
"rgb": true, "override": false, "ext_hlstate": false, "ext_messages": false,
"ext_termcolors": false, "ext_tabline": false, "ext_linegrid": true,
"term_background": "", "ext_cmdline": false, "stdin_tty": false, "term_colors": 0}]
ext_multigridがtrueなので、ウィンドウごとに別の描画面を受け取る方式で動いています。4つの外部化はfalseです。見たい値だけ抜き出して、設定と起動オプションを変えながら読んだ結果が次の表です。
nvim --server /tmp/x.sock --remote-expr 'json_encode(map(nvim_list_uis(),
{_,u->{"cmdline":u.ext_cmdline,"messages":u.ext_messages,
"popupmenu":u.ext_popupmenu,"tabline":u.ext_tabline}}))'
| 起動のしかた | cmdline | messages | popupmenu | tabline |
|---|---|---|---|---|
| ひな型のまま | false | false | false | false |
--extcmdline --extmessages | true | true | false | false |
--extmessagesだけ | true | true | false | false |
4つのexternal = true | true | true | true | true |
--extmessagesだけでもcmdlineがtrueになるのは、ヘルプにあるとおりext_messagesがext_cmdlineを暗黙に有効にするためです。メッセージだけ外に出してコマンドラインを画面に残す組み合わせは作れません。
どのGUIかはnvim_get_chan_info()のclientで分かり、Zonvieは"name": "zonvie"を名乗っていました。g:zonvie_channelも設定されるので、設定ファイルで分岐する目印になります。
共通部分の決まりも試しました。Zonvieをつないだままnvim --remote-ui --server /tmp/x.sockでターミナルのUIを足すと、Zonvie側のext_messagesはtrueと表示されたままなのに、次の節の分割表示の規則は発動しなくなりました。nvim_list_uis()が返すのは各UIの申告なので、効いているかはつながっているUIを全部並べて判断します。
メッセージの行き先を決めるviewとroutes
[messages] external = trueにすると、メッセージの表示はZonvieが受け持ちます。出し先は種類ごとにviewで決め、mini・ext-float・confirm・split・none・notificationから選びます。
[messages]
external = true
view_history = "split"
[[messages.routes]]
event = "msg_show"
min_height = 20
view = "split"
view_historyは:messagesの出し先です。:echomsgを3回打ってから:messagesを実行すると、Neovimのウィンドウが横に分割され、高さ3行の[Scratch](buftype=nofile)に3行が入りました。カーソルはそちらへ移り、qで閉じます。GUIの飾りではなくNeovimの本物のウィンドウなので、nvim_list_wins()にも現れます。
[[messages.routes]]は標準の振り分けより先に見る規則です。:echo join(range(1,N),"\n")で行数を変えて試しました。
| 出力の行数 | 規則あり | 規則なし |
|---|---|---|
| 10行 | 分割しない | 分割しない |
| 20行 | 分割する | 分割しない |
| 30行 | 分割する(14行) | 分割しない |
20行ちょうどで分割したので、min_heightは「以上」です。規則による分割では:messagesと違ってカーソルは元のウィンドウに残りました。規則が無くても、30行の出力でPress ENTERの待ちには入らず、mode()はnのままでした。
気になるのがNeovim 0.12のui2との関係です。ui2も、Press ENTERを消すui2のメッセージ欄で書いたとおり、メッセージの描き方を置き換える仕組みです。Zonvieをつないだままrequire('vim._core.ui2').enable({})を実行すると、cmdheightが0になり、cmd・msg・pager・dialogの浮動ウィンドウが作られました。そこで30行を出すと中身はui2のpagerに入り、Zonvieの規則による分割は作られませんでした。Zonvie側のext_messagesはtrueのままです。
Zonvieのext-floatにも同時に出ていたかは画面を見ていないので分かりませんが、規則が効かなくなる以上、メッセージはどちらか一方に任せるほうが挙動を追えます。vim.ui_attachで描くnoice.nvimも似た関係になると考えられますが、試してはいません。
起動中のNeovimにつなぐ --connect-nvim と :detach
Zonvieは普段、自分でnvim --embedを起動します。--connect-nvim(別名--remote-ui)を付けると、動いているNeovimのソケットにつなぎます。
ターミナルのNeovimをZonvieに移す流れを試しました。tmuxの中でnvim --listen /tmp/b.sock foo.txtを起動し、保存せずに1行書いて:detachを実行すると、ターミナルのUIは閉じ、Neovimは&modifiedが1のまま裏で残りました。zonvie --connect-nvim=/tmp/b.sockを実行すると、未保存のfoo.txtを開いた状態でつながります。手元の0.12.4でも:detachは使えました。SSHが切れたときの使い方はNeovimの:detachで作業を残す方法に書いています。
Zonvieを終了させてもつないだ先のNeovimは残り、つなぎ直せます。一方、別のターミナルから--remote-sendで:detachを送ってもZonvieは外れませんでした。:detachは操作しているUIを外すコマンドなので、Zonvieの中で打つ必要があります。その操作自体は今回確かめていません。
Neovideと比べてどちらを選ぶか
Neovide 0.16.2も--serverでソケットにつなげるので、同じ手順で読みました。
| 申告 | Zonvie v0.4.1(4つ有効) | Neovide 0.16.2 |
|---|---|---|
ext_cmdline | true | false |
ext_messages | true | false |
ext_popupmenu | true | false |
ext_tabline | true | false |
ext_multigrid | true | true |
ext_hlstate | false | true |
Neovideは部品を外部化せず、ウィンドウごとの描画面とハイライトの詳細を受け取って自分で描く作りです。コマンドラインまわりはターミナルのNeovimと同じなので、ui2やnoice.nvimとぶつかりません。Zonvieは部品そのものをGUIに移して描きます。ui2やnoice.nvimでメッセージの見た目を作り込んでいるなら、外部化を切ったZonvieかNeovideを選ぶと競合を避けられます。goneovimからの乗り換えや、タブやメッセージをGUIの部品として扱いたいなら、Zonvieの外部化を有効にする価値があります。Linuxで使うなら、配布物があるのはいまはNeovideです。VSCodeから移ってきてGUIを探しているなら、VSCodeユーザーがVimに移行する方法も参考になります。
いちばん時間を取られたのは:detachでした。別のターミナルから--remote-sendで送り、nvim_list_uis()にZonvieが残っているのを見て、最初はZonvieが切断に対応していないのだと思い込みました。ヘルプを読み直して、外れるのはコマンドを打ったUIだけだと気づき、ターミナルのNeovimで打つ手順に組み直しています。検証用にHOMEを変えたつもりで--installを流し、自分のホームに設定ファイルができて慌てたこともありました。
まとめ
Zonvieはgoneovimの後継として開発が続くNeovim GUIで、macOSならReleasesのdmgで入ります。入れた直後は外部化が全部切れていて、config.tomlのexternalを有効にするとコマンドライン・メッセージ・補完メニュー・タブがGUIに移ります。どれが移ったかはnvim_list_uis()で読めますが、実際に効くのは全UIの申告の共通部分です。ui2と併用するとメッセージの振り分けが効かなくなるので、メッセージを誰が描くかを先に決めておくと迷いません。