1 ポイント 投稿者 GN⁺ 1 시간 전 | 1件のコメント | WhatsAppで共有
  • ターミナルアプリはテキストベースなので本質的にアクセシブルだという前提は、現代の TUI では崩れており、Ink、Bubble Tea、tcell のようなフレームワークがスクリーンリーダー利用者にとって、より敵対的な環境を作りうる
  • CLIstdin/stdout の線形ストリームとして出力が時系列に積み上がるが、TUI はターミナルを文字セルベースの 2D グリッド として扱うため、スクリーンリーダーが流れを追いにくくなる
  • gemini-cli では、Ink が React コンポーネントツリーをターミナルのグリッドに合わせて再描画しながら、スピナー・タイマー・会話履歴の間でカーソルを移動させることで、Speakup や NVDA に反復読み上げ、クラッシュ、入力遅延を引き起こしうる
  • nanovimmenuconfig、Irssi のような古いツールは、カーソル非表示、単一列フォーカス、VT100 の スクロール領域 活用によって座標更新ノイズを減らし、入力行との干渉を最小化している
  • アクセシブルなターミナルツールを作るには、ターミナルをキャンバスのように扱う宣言的 UI フレームワークや攻撃的な再描画を避け、単純で線形的な CLI ストリームに近い動作を保証する必要がある

「テキストだからアクセシブルだ」という誤解

  • ターミナルで動作するアプリケーションが本質的にアクセシブルだという前提は、実際の利用環境とは一致しない
  • グラフィック、複雑な DOM、WebGL キャンバスがないため、スクリーンリーダーが生の ASCII テキストを簡単に解釈できるという期待は、現代の TUI では崩れている
  • Ink(JS/React)、Bubble Tea(Go)、tcell のようなターミナル UI フレームワークは、開発者体験(DX)の改善を目指しているが、視覚障害者にとってはより敵対的な環境を作ることがある
  • 実装の悪いグラフィカルインターフェースよりも、現代の TUI のほうがアクセシビリティの面で悪い場合が多い

CLI と TUI の構造的な違い

  • CLI: 線形ストリーム

    • CLIstdin/stdout ベースで動作し、コマンドを入力すると結果が下に追加され、カーソルが下がる
    • 出力が線形で時系列に積み上がるため、Speakup のようなカーネルレベルのスクリーンリーダーに適している
  • TUI: 2D グリッド

    • TUI はターミナルウィンドウをテキストストリームではなく、各文字セルがピクセルのように使われる 2D グリッドとして扱う
    • 時間的な流れを捨てて空間的レイアウトを優先することで、スクリーンリーダーが追いにくい構造になる

gemini-cli で明らかになる問題

  • gemini-cli は Node.js と Ink フレームワークで書かれたツールであり、表面的には単純なチャットインターフェースに見える
  • 内部では Ink が React コンポーネントツリーをターミナルのグリッドに合わせて調整しようとしている
  • Speakup(Linux) や NVDA(Windows) で使うと、アプリケーションが単に失敗するレベルにとどまらず、スクリーンリーダーに読み上げる内容を絶えず流し込み続ける
  • レスポンシブなキャンバスのように振る舞う画面

    • フレームワークが画面をレスポンシブなキャンバスとして扱うため、あらゆる更新が再描画を引き起こす
    • AI が「考え中」のとき、タイマーやスピナーを更新するためにハードウェアカーソルをタイマー位置へ移動し、新しい時刻を書き込み、再び元の位置へ戻す
    • 視覚ユーザーには一瞬で過ぎる動作だが、スクリーンリーダー利用者には「Responding... Time elapsed 1s... Responding... Time elapsed 2s...」のように繰り返し聞こえる
    • カーソルがステータス表示、スピナー、会話履歴の間を瞬間的に移動するたび、Speakup はその瞬間にカーソル下にある内容を読み上げようとする
    • その結果、タイマー更新と会話の断片が混ざって聞こえ、実際に入力している内容に集中しにくくなる
  • NVDA と貼り付け時に発生する不安定さ

    • Windows で NVDA を使ってターミナルを開き、Linux マシンに SSH 接続したうえで screen セッションに入り、テキストを貼り付けると、NVDA が即座にクラッシュしたり、システム全体が大きく不安定になったりすることがある
    • 文字を入力したりテキストを貼り付けたりするたびにアプリケーション状態が変わり、フレームワークはインターフェースを再レンダリングすべきだと判断する
    • 会話履歴が状態に含まれていると、数千行のテキストレイアウトを即座に再描画または再計算しようとする
    • 会話メッセージが多いほど、この問題はより頻繁に発生する
    • 動的コンテンツ通知を避けるための Insert+5 の組み合わせでも、この問題は回避できない
  • 入力遅延ループ

    • Ink のようなフレームワークが Node.js のようなシングルスレッド環境で動作すると、履歴が大きくなるほど性能低下が大きくなる
    • 大きなテキストブロックを貼り付けると、数千行分の差分を計算する必要がある
    • システムは画面をどう再描画するかの計算に追われ、入力処理が遅れる
    • キーを 1 回押しても、文字が再表示されるまで最大 10 秒 待たされることがある

古いツールが動作する理由

  • nanovimmenuconfig のようなツールが、常にアクセシビリティに完璧だから使われているわけではない
  • 重要なのは、これらのツールがカーソルを完全に隠したり、カーソル位置追跡によって生じるノイズを減らしたりできる点にある
  • nanovim: カーソルを隠す

    • nano--constantshow のようなカーソル位置表示オプション付きで実行したり、vim を特定の設定なしで使ったりすると、使い勝手が壊れることがある
    • カーソルが見えており追跡が有効になると、Speakup は文字エコーよりもカーソル位置更新を優先する
    • ユーザーが「a」を入力すると「a」ではなく「Column 2」が聞こえ、「b」を入力すると「Column 3」が聞こえる
    • これらの古いツールは、視覚カーソルやステータスバー更新を抑制するよう設定できるため、スクリーンリーダーが座標更新ではなく文字入力ストリームに依存できるようにする
    • 現代のフレームワークは通常、「no-cursor」や「headless」モードを提供せず、視覚カーソルが必須だと想定している
  • menuconfig: 単一列フォーカス

    • Linux カーネルの menuconfig は、厳格な単一列フォーカスを維持しているため機能する
    • 枠線やタイトルがあっても、アクティブな領域は縦方向のリストであり、カーソルはそのリストに固定される
    • 時計更新のために右下へ移動し、次にタイトル更新のために左上へ移動する、といった動きはしない
    • 空間的複雑さが低く保たれるため、スクリーンリーダーが迷子になりにくい
  • Irssi: スクロール領域の活用

    • Irssi はたまたまアクセシブルなのではなく、20年以上にわたって独自レンダリングエンジン経由で VT100 の スクロール領域 を活用してきたチャットツールである
    • 新しいメッセージが届くと、ターミナルドライバに「1 行目から 23 行目までをスクロール領域として定義せよ」と指示する
    • 続いて「上へスクロールせよ」という命令を送り、ターミナルが内容を上へ移動させたあと、その領域の下端に新しいテキストを描画する
    • この方式は入力行との干渉を最小限に抑える
    • 画面上のすべての文字を手動で書き直すのではなく、ターミナルのハードウェア機能に依存している
    • 現代のフレームワークはこのようなハードウェア機能を無視し、画面状態の差分を計算して文字を書き直す方式を取るが、これは計算コストが高く、アクセシビリティにも敵対的である

gemini-cli のイシュー対応の問題

  • Google と gemini-cli メンテナーはアクセシビリティを気にしているように見えるが、リポジトリでは重要なアクセシビリティ回帰が放置されている
  • Issue #3435 や Issue #11305 のようなアクセシビリティ回帰には、議論もロードマップも修正もない
  • Issue #1553 はこうしたアクセシビリティ失敗を追跡するためのイシューだったが解決されず、ボットによって自動的にクローズされた
  • ボットは、長期間活動がなくバックログ管理のために閉じるという一般的な文言でイシューを終了している
  • メンテナーが数か月手を付けていないという理由でアクセシビリティ報告を閉じるのは、整理ではなく証拠隠しに近い
  • バグを十分長く無視すれば存在しなかったことになる、というシグナルを与え、実際のソフトウェアは視覚障害者にとって依然として使えないまま残る
  • プロジェクトの「Closed Issues」指標は改善して見えるかもしれないが、アクセシビリティ問題は解決されていない

アクセシブルなターミナルツールを作るための結論

  • ターミナル向けアプリケーションでアクセシビリティを重視するなら、ターミナルをキャンバスのように扱う宣言的 UI フレームワークの使用をやめるべきである
  • 「モダン」な TUI スタックは、開発者が React のようにコードを書きやすくする方向に最適化されており、機械がテキストを効率よくレンダリングする能力を犠牲にしている
  • アプリケーションがユーザーによるカーソル非表示を保証できなかったり、スピナーやタイマー表示のために攻撃的な再描画に依存したりするなら、それはアクセシブルでないツールになる
  • 視覚障害者にとっては、遅く、読み上げる内容を絶えず流し込み、カーソルを画面全体にばらまく「賢い」TUI よりも、単純で線形的な CLI ストリームのほうがはるかに優れている

1件のコメント

 
GN⁺ 1 시간 전
Lobste.rsの意見
  • この記事は、他のブログ記事と同じく AI支援で書かれたような臭い がかなり強い
    LLMはこういうタイトルを好む: “The Architectural Flaw”, “The Lag Loop”, “Why The ‘Old Guard’ Works”, “The Lost Art of Scrolling Regions”, “The ‘Stale Bot’ excuse: A Case Study in Neglect”

    • タイトルからしてAI量産記事のように読める。テーマ自体は新しいのでスパム報告は少しやりすぎだったかもしれないが、1) どこでも繰り返される同じ文体にはもううんざりだし、2) 内容の正確さまで疑いたくなる
      優れたブログ記事になれたはずなのに、著者がアウトラインをChatGPTに放り込んで終わりにしたように見えるなら、読者にも著者にも損だ
    • LLM文章でいちばん嫌いな特徴の一つが、“The <まったく定着していない概念>” という書き方で、あたかも正式な概念のように見せることだ
      ごく特殊な一回限りの問題を「古典的な」問題と呼ぶのも同じだ
  • 本当に憂うつだ。要するに、Irssiのような アクセシブルなTUI もあるのに、現代のTUIフレームワークはそうした先例を無視し、グリッド差分計算とカーソル移動に依存している
    スクリーンリーダーはカーソルが動くたびにその位置の内容を読むので、結果としてめちゃくちゃになったり、膨大な読み上げスパムが発生したりする

  • ここの技術的説明が完全に正確かは疑わしい
    特にInkは長い間 増分レンダリング をまったくサポートしておらず、Inkを使うアプリの大半もまだ有効化していない。その増分レンダリングも 行ベース なので、実際のタイマー位置へカーソルを移動したりはしない
    Gemini CLIは増分レンダリングを有効にするには 代替バッファの使用が必要 で、これは内蔵の スクリーンリーダー向けモード が有効だと 無効化される。関連オプションの文書は ここ にある
    付け加えると、Pythonのrich/textualは、より遅く主にシングルスレッドな言語の上にありながらも、Inkよりずっと高速なことが多い。数千行の差分計算が必ずしもそこまで遅いわけではなく、10秒もかかるようなものではない
    ユーザー体験が苛立たしく壊れている点は疑わないが、示されている正確な原因はLLMの幻覚か、不完全な情報に基づいている可能性がある。Inkの増分レンダリングは有効でも、説明されているようには動かない
    実際には、全画面の再描画がスクリーンリーダーを混乱させ、行ベースの再描画が変更と無関係な任意の途切れたテキスト断片を再び読ませてしまう、という形で悪い可能性が高い

  • TUIだけを責めるのは公平ではない
    本当の問題は、ほぼスタック全体の アクセシビリティ対応 がひどいことにある
    第一に、GPUレンダリングのターミナルエミュレーターの大半は、システム提供のアクセシビリティAPIをまったく使っていない。テキストがGPUでレンダリングされると、アクセシビリティツールには「読める」ものではなく、ただの画像のように見えてしまう。Kitty、Alacritty、WezTermがこれに当たる。私のターミナルGhosttyはmacOSではアクセシビリティAPIで読めるし、iTerm2とTerminal.appも可能だ
    第二に、TUIがアクセシビリティ情報をターミナルエミュレーターへ渡すためのターミナルシーケンスや標準的な仕組みがまったくない。ターミナルセル、実行区間、領域に対するARIA風の注釈に相当するものが必要だが、そうした試みは存在しない。TUIがカーソルをうまく扱っても、多くのユースケースでは問題が起きるだろう
    例としてGhosttyでは、OSC133 とアクセシビリティAPIを統合し、各シェルプロンプト、入力、コマンドを単なるテキストボックスではなく、構造的に意味のある要素として公開する作業を進めてきた。これは、ターミナル仕様、TUI、ターミナルエミュレーターが一緒に噛み合う必要があることを示している
    スタック全体が腐っていて、本気で直そうとしている人もほとんどいない。私も時間が限られる中で最善を尽くしているだけだが、これはエコシステム政治まで必要な巨大なテーマで、背負いきれない
    おまけに、かっこよくも恐ろしい現実として、AIがここでアクセシビリティ改善を後押ししている。多くのAIツールがアクセシビリティAPIを使ったり悪用したりして、ウィンドウ一覧を読み、入力を実行している。そのため、より多くのアプリがAIのユースケースを理由に、アクセシビリティ統合をはるかに真剣に扱い始めている

    • ターミナルは本当に 小さなブラウザ になりつつある
  • Claude Codeとgemini-cliが readlineベース ではないので毎日腹が立つ
    似たようなキー入力はいくつか入っているが、慣れ親しんだreadlineショートカットの長い裾野が欠けている
    Anthropicは、「Web開発のように作るべきだ」という判断が誤りだったと認めて、readlineでやり直してもいい
    こういうツールを作る開発者にとって馴染みのある開発体験が、ツールを使うユーザーにとって馴染みのあるユーザー体験より重要だ、という考えは間違っている

    • この問題の大きな部分は、Inkが事実上 レンダリングバックエンド であることに満足していて、入力ウィジェットを提供していない点にあると理解している
      実際、きちんと保守されている有名なサードパーティ解決策もほとんどない。柔軟な入力ボックスが必要なら、最初から自分で作るしかない
      Textualの優れたInputウィジェット や、JSエコシステムの別ライブラリである OpenTUI と対照的だ
    • readlineはGNUライセンスではなかったか? 誰かがついにGPLではない版を作ったのか?
      LLMは好きではないので、UIが悪いのは個人的には利点だが、readlineを使わないのには理由があるのかもしれない
  • kakouneやhelixのようなターミナルエディタは、「カーソルを隠す」手口を使わない限り アクセシビリティ基準 を通すのは難しそうだ
    それでもVS Codeほどアクセシブルではない可能性が高い
    VS Code以外で、アクセシブルなクロスプラットフォームのIDE-liteやIDEには何があるだろう? VS Codeのますます敵対的な姿勢は気に入らない。JetBrains IDEかもしれない

    • emacspeak があり、Emacsに非常にアクセシビリティの高いインターフェースを提供する
      欠点は、Emacs自体はクロスプラットフォームでも、emacspeakはTTSの都合でLinuxに弱い依存があるかもしれない点だ。あるいはそうでもないかもしれない。Windowsでは試したことがない
    • まず誰のためのアクセシビリティなのかが先だ。聴覚障害者向けなら、現地言語の文章と現地の手話を提供する必要がある。米国なら通常はASLだ
      視覚障害者向けのアクセシビリティなら、emacspeakやプラットフォームの視覚障害者向け支援ツールが必要だ
      アクセシビリティはスペクトラム であって、チェックボックスではない
  • Linksには別個の 点字ターミナルモード があり、偽のGUI要素をより単純な全画面メニューに置き換え、方向キーでの移動も行単位に変えている
    もう一つ興味深い例は edbrowse だ。視覚障害者のKarl Dahlkeが作ったテキストモードブラウザで、より人気のあるテキストモードWebブラウザと違ってTUIを使わず、edスタイルのコマンドラインインターフェースを使っている

  • Inkフレームワーク なら、CLIがCPU 100%を使い、長いチャット履歴を延々と再描画したまま固まり続ける理由としてありそうだ。残念だ