3 Punkte von GN⁺ 2 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Die Annahme, dass Terminal-Apps textbasiert und damit von Natur aus barrierefrei seien, zerbricht bei modernen TUIs; Frameworks wie Ink, Bubble Tea und tcell können für Screenreader-Nutzer sogar ein feindlicheres Umfeld schaffen
  • CLIs arbeiten mit linearen stdin/stdout-Streams, in denen sich Ausgaben chronologisch ansammeln, während TUIs das Terminal als zeichenzellenbasiertes 2D-Raster behandeln, dem Screenreader nur schwer folgen können
  • Bei gemini-cli zeichnet Ink den React-Komponentenbaum passend zum Terminalraster neu und bewegt dabei den Cursor zwischen Spinner, Timer und Gesprächsverlauf, was bei Speakup und NVDA wiederholtes Vorlesen, Abstürze und Eingabeverzögerungen auslösen kann
  • Ältere Werkzeuge wie nano, vim, menuconfig und Irssi reduzieren den Lärm durch Koordinatenaktualisierungen und minimieren Störungen der Eingabezeile durch versteckten Cursor, Fokus auf einer einzelnen Spalte und die Nutzung von VT100-Scrollbereichen
  • Wer barrierefreie Terminal-Werkzeuge bauen will, sollte deklarative UI-Frameworks vermeiden, die das Terminal wie eine Leinwand behandeln und aggressiv neu zeichnen, und stattdessen ein Verhalten nahe an einfachen, linearen CLI-Streams sicherstellen

Das Missverständnis „Text ist also barrierefrei“

  • Die Annahme, dass Anwendungen, die im Terminal laufen, von Natur aus barrierefrei sind, passt nicht zur realen Nutzungspraxis
  • Die Erwartung, dass Screenreader rohen ASCII-Text leicht interpretieren können, weil es keine Grafik, kein komplexes DOM und keine WebGL-Canvas gibt, bricht bei modernen TUIs zusammen
  • Terminal-UI-Frameworks wie Ink (JS/React), Bubble Tea (Go) und tcell sollen die Developer Experience (DX) verbessern, können für blinde Nutzer aber ein feindlicheres Umfeld schaffen
  • In Sachen Barrierefreiheit sind moderne TUIs oft sogar schlechter als schlecht umgesetzte grafische Oberflächen

Strukturelle Unterschiede zwischen CLI und TUI

  • CLI: linearer Stream

    • CLIs arbeiten auf Basis von stdin/stdout; man gibt einen Befehl ein, das Ergebnis wird darunter angefügt und der Cursor wandert nach unten
    • Weil die Ausgabe linear ist und sich chronologisch aufbaut, passt sie gut zu kernelnahen Screenreadern wie Speakup
  • TUI: 2D-Raster

    • TUIs behandeln das Terminalfenster nicht als Textstream, sondern als 2D-Raster, in dem einzelne Zeichenfelder wie Pixel beschrieben werden
    • Damit wird der zeitliche Fluss aufgegeben und ein räumliches Layout priorisiert, was für Screenreader schwer nachzuverfolgen ist

Die Probleme, die sich bei gemini-cli zeigen

  • gemini-cli ist ein mit Node.js und dem Ink-Framework geschriebenes Werkzeug und wirkt oberflächlich wie ein simples Chat-Interface
  • Intern versucht Ink, den React-Komponentenbaum an das Terminalraster anzupassen
  • Bei der Nutzung mit Speakup (Linux) oder NVDA (Windows) scheitert die Anwendung nicht einfach nur, sondern überschüttet den Screenreader fortlaufend mit neu zu lesenden Inhalten
  • Ein Bildschirm, der sich wie eine reaktive Leinwand verhält

    • Das Framework behandelt den Bildschirm wie eine reaktive Leinwand, weshalb jede Aktualisierung ein Neuzeichnen auslöst
    • Während die KI „nachdenkt“, bewegt es den Hardware-Cursor zur Position des Timers, schreibt die neue Zeit und setzt ihn dann wieder zurück
    • Für sehende Nutzer ist das ein sofort vorübergehender Vorgang, für Screenreader-Nutzer klingt es aber wie „Responding... Time elapsed 1s... Responding... Time elapsed 2s...“ in Dauerschleife
    • Während der Cursor kurz zwischen Statusanzeige, Spinner und Gesprächsverlauf springt, versucht Speakup jeweils den Inhalt unter dem Cursor vorzulesen
    • Dadurch vermischen sich Timer-Aktualisierungen und Gesprächsfragmente, sodass es schwer wird, sich auf die eigentliche Eingabe zu konzentrieren
  • Instabilität mit NVDA und beim Einfügen

    • Wenn man unter Windows mit NVDA ein Terminal öffnet, sich per SSH auf ein Linux-System verbindet, an eine screen-Sitzung anhängt und dann Text einfügt, kann NVDA sofort abstürzen oder das System stark instabil werden
    • Jedes eingegebene Zeichen und jedes eingefügte Textstück verändert den Zustand der Anwendung, woraufhin das Framework entscheidet, dass die Oberfläche neu gerendert werden muss
    • Wenn der Gesprächsverlauf Teil dieses Zustands ist, versucht es sofort, das Layout von tausenden Textzeilen neu zu berechnen oder neu zu zeichnen
    • Je mehr Gesprächsnachrichten vorhanden sind, desto häufiger tritt dieses Problem auf
    • Selbst die Tastenkombination Insert+5, mit der dynamische Inhaltsmeldungen vermieden werden sollen, hilft hier nicht
  • Schleife aus Eingabeverzögerungen

    • Wenn Frameworks wie Ink in einer Single-Thread-Umgebung wie Node.js laufen, nimmt der Leistungsabfall mit wachsendem Verlauf stark zu
    • Beim Einfügen großer Textblöcke müssen Differenzen über tausende Zeilen berechnet werden
    • Das System ist dann damit beschäftigt zu ermitteln, wie der Bildschirm neu gezeichnet werden soll, und verarbeitet Eingaben verspätet
    • Schon nach einem einzelnen Tastendruck kann man bis zu 10 Sekunden warten, bis das Zeichen wieder angezeigt wird

Warum alte Werkzeuge funktionieren

  • Werkzeuge wie nano, vim und menuconfig werden nicht genutzt, weil sie in Sachen Barrierefreiheit immer perfekt wären
  • Entscheidend ist vielmehr, dass diese Werkzeuge den Cursor vollständig verbergen oder den Lärm aus der Cursorpositionsverfolgung reduzieren können
  • nano und vim: Cursor verbergen

    • Wenn man nano mit Optionen wie --constantshow startet, die die Cursorposition anzeigen, oder vim ohne bestimmte Einstellungen nutzt, kann die Benutzbarkeit zusammenbrechen
    • Wenn der Cursor sichtbar ist und die Verfolgung aktiv bleibt, priorisiert Speakup Cursorpositionsaktualisierungen gegenüber dem Zeichen-Echo
    • Gibt der Nutzer „a“ ein, hört er statt „a“ dann „Column 2“, bei „b“ entsprechend „Column 3“
    • Diese alten Werkzeuge lassen sich so konfigurieren, dass visuelle Cursor- oder Statusleistenaktualisierungen unterdrückt werden, sodass sich der Screenreader auf den Zeichen-Eingabestream statt auf Koordinatenaktualisierungen stützt
    • Moderne Frameworks bieten in der Regel keinen „no-cursor“- oder „headless“-Modus und setzen voraus, dass ein sichtbarer Cursor unverzichtbar ist
  • menuconfig: Fokus auf einer einzelnen Spalte

    • Das menuconfig des Linux-Kernels funktioniert, weil es strikt einen Fokus in nur einer Spalte beibehält
    • Es gibt zwar Rahmen und Überschriften, aber der aktive Bereich ist eine vertikale Liste, und der Cursor bleibt auf diese Liste fixiert
    • Er springt nicht zur Aktualisierung einer Uhr nach rechts unten und danach zur Titelaktualisierung nach links oben
    • Die räumliche Komplexität bleibt gering, sodass Screenreader nicht die Orientierung verlieren
  • Irssi: Nutzung von Scrollbereichen

    • Irssi ist nicht zufällig zugänglich, sondern ein Chat-Werkzeug, das seit mehr als 20 Jahren über eine eigene Rendering-Engine VT100-Scrollbereiche nutzt
    • Wenn eine neue Nachricht ankommt, weist es den Terminaltreiber an: „Definiere Zeile 1 bis 23 als Scrollbereich“
    • Danach sendet es den Befehl zum Hochscrollen, das Terminal verschiebt den Inhalt nach oben und zeichnet den neuen Text unten in diesem Bereich ein
    • Auf diese Weise werden Störungen der Eingabezeile minimiert
    • Statt jedes Zeichen auf dem Bildschirm manuell neu zu schreiben, stützt es sich auf Hardware-Funktionen des Terminals
    • Moderne Frameworks ignorieren solche Hardware-Funktionen oft, berechnen stattdessen Zustandsdifferenzen des Bildschirms und schreiben Zeichen neu; das ist rechenintensiver und barrierefeindlich

Die Probleme bei der Bearbeitung von gemini-cli-Issues

  • Google und die Maintainer von gemini-cli wirken zwar so, als nähmen sie Barrierefreiheit ernst, im Repository bleiben aber wichtige Regressionen in der Barrierefreiheit liegen
  • Bei Barrierefreiheitsregressionen wie Issue #3435 und Issue #11305 gibt es weder Diskussion, noch Roadmap, noch Fixes
  • Issue #1553 sollte genau solche Ausfälle der Barrierefreiheit nachverfolgen, wurde aber nicht gelöst und stattdessen automatisch von einem Bot geschlossen
  • Der Bot schließt das Issue mit einer Standardformulierung, wonach es wegen langer Inaktivität und zur Pflege des Backlogs beendet werde
  • Einen Bericht zur Barrierefreiheit zu schließen, nur weil Maintainer monatelang nichts daran getan haben, ist keine Aufräumarbeit, sondern eher das Verbergen von Belegen
  • Es sendet das Signal, dass Bugs verschwinden, wenn man sie nur lange genug ignoriert, während die Software für blinde Nutzer in der Praxis weiterhin unbenutzbar bleibt
  • Die Kennzahl „Closed Issues“ des Projekts mag dadurch besser aussehen, die Barrierefreiheitsprobleme sind damit aber nicht gelöst

Fazit für den Bau barrierefreier Terminal-Werkzeuge

  • Wer Barrierefreiheit bei Terminal-Anwendungen ernst nimmt, sollte aufhören, deklarative UI-Frameworks zu verwenden, die das Terminal wie eine Leinwand behandeln
  • „Moderne“ TUI-Stacks sind darauf optimiert, dass Entwickler Code bequem wie in React schreiben können, und opfern dafür die Fähigkeit der Maschine, Text effizient zu rendern
  • Wenn eine Anwendung nicht sicherstellt, dass Nutzer den Cursor verbergen können, oder auf aggressives Neuzeichnen angewiesen ist, um Spinner und Timer anzuzeigen, wird sie zu einem unzugänglichen Werkzeug
  • Für blinde Nutzer ist ein einfacher, linearer CLI-Stream weitaus besser als eine „smarte“ TUI, die verzögert reagiert, ständig neue Inhalte vorliest und den Cursor über den ganzen Bildschirm verstreut

1 Kommentare

 
GN⁺ 2 시간 전
Lobste.rs-Kommentare
  • Dieser Artikel riecht wie viele andere Blogposts stark nach KI-unterstütztem Schreiben
    LLMs lieben solche Titel: „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“

    • Schon der Titel liest sich wie KI-Massenware. Das Thema selbst ist zwar neu genug, dass eine Spam-Meldung vielleicht überzogen war, aber 1) ich ertrage diesen überall wiederholten Stil nicht mehr und 2) dadurch zweifle ich sogar an der Genauigkeit des Inhalts
      Das hätte ein großartiger Blogpost sein können, aber wenn es wirkt, als hätte der Autor nur eine Gliederung in ChatGPT geworfen und es dann dabei belassen, ist das für Leser und Autor gleichermaßen ein Verlust
    • Eines der Dinge, die ich an LLM-Schreibstil am meisten hasse, ist dieses „The <überhaupt kein etabliertes Konzept>“, wodurch es so wirkt, als sei es ein offizieller Fachbegriff
      Genauso, wenn ein sehr spezielles einmaliges Problem als „klassisches“ Problem bezeichnet wird
  • Wirklich deprimierend. Kurz gesagt: Es gibt zugängliche TUIs wie Irssi, aber moderne TUI-Frameworks ignorieren solche Vorbilder und verlassen sich stattdessen auf Grid-Diffs und Cursorbewegungen
    Screenreader lesen den Inhalt an der Cursorposition vor, wenn sich der Cursor bewegt, wodurch das Ergebnis durcheinandergerät oder es zu massivem Vorlese-Spam kommt

  • Ich bin nicht sicher, ob die technische Erklärung hier vollständig korrekt ist
    Insbesondere unterstützte Ink lange Zeit überhaupt kein inkrementelles Rendering, und die meisten mit Ink gebauten Apps aktivieren es noch immer nicht. Dieses inkrementelle Rendering ist zudem zeilenbasiert und bewegt den Cursor nicht tatsächlich zur Position eines Timers
    Gemini CLI benötigt zum Aktivieren des inkrementellen Renderings die Nutzung eines Alternate Buffers, was deaktiviert wird, wenn der eingebaute screenreaderfreundliche Modus aktiv ist, wie hier zu sehen ist. Die Dokumentation zur entsprechenden Option ist hier
    Außerdem sind Python rich/textual trotz einer langsameren und meist Single-Thread-orientierten Sprache oft deutlich schneller als Ink. Das Berechnen von Diffs über Tausende Zeilen hinweg ist nicht zwangsläufig so langsam und dauert auch keine 10 Sekunden
    Ich zweifle nicht daran, dass die User Experience frustrierend und kaputt ist, aber die konkret angegebene Ursache wirkt womöglich wie eine LLM-Halluzination oder basiert auf unvollständigen Informationen. Das inkrementelle Rendering von Ink funktioniert selbst dann nicht so, wie es beschrieben wird
    In Wirklichkeit ist es wahrscheinlich eher so, dass vollständige Screen-Redraws Screenreader verwirren und zeilenbasierte Redraws dazu führen, dass beliebige zusammenhanglose Textfragmente erneut vorgelesen werden, auch wenn sie mit der Änderung nichts zu tun haben

  • Es ist nicht fair, nur den TUIs die Schuld zu geben
    Das eigentliche Problem ist, dass die Barrierefreiheitsunterstützung fast im gesamten Stack miserabel ist
    Erstens verwenden die meisten GPU-gerenderten Terminalemulatoren überhaupt keine vom System bereitgestellten Accessibility-APIs. Wenn Text per GPU gerendert wird, können Accessibility-Tools ihn nicht „lesen“ und sehen nur ein Bild. Dazu gehören Kitty, Alacritty und WezTerm. Mein Terminal Ghostty ist unter macOS über Accessibility-APIs lesbar, ebenso iTerm2 und Terminal.app
    Zweitens gibt es keinerlei Terminal-Sequenzen oder standardisierte Verfahren, mit denen TUIs Barrierefreiheitsinformationen an den Terminalemulator übergeben könnten. Es bräuchte etwas wie ARIA-artige Annotationen für Terminalzellen, Laufbereiche und Regionen, aber solche Versuche gibt es nicht. Selbst wenn ein TUI den Cursor sauber handhabt, wird es in vielen Anwendungsfällen Probleme geben
    Als Beispiel habe ich in Ghostty an der Integration von OSC133 mit Accessibility-APIs gearbeitet, um jeden Shell-Prompt, jede Eingabe und jeden Befehl nicht nur als einfache Textbox, sondern als strukturell sinnvolle Elemente sichtbar zu machen. Das zeigt, dass Terminal-Spezifikation, TUI und Terminalemulator zusammenpassen müssen
    Der gesamte Stack ist verrottet, und es gibt kaum Leute, die ihn ernsthaft reparieren wollen. Auch ich kann mit meiner begrenzten Zeit nur mein Bestes tun, aber das ist ein riesiges Thema, das sogar Ökosystem-Politik erfordert, und kaum zu bewältigen
    Als Bonus ist die zugleich coole und schreckliche Realität, dass KI hier tatsächlich bei Verbesserungen der Barrierefreiheit hilft. Viele KI-Tools nutzen oder missbrauchen Accessibility-APIs, um Fensterlisten zu lesen und Eingaben auszuführen. Deshalb beginnen mehr Apps, Accessibility-Integration wegen KI-Anwendungsfällen deutlich ernster zu nehmen

    • Das Terminal wird wirklich zu einem kleinen Browser
  • Dass Claude Code und gemini-cli nicht auf readline basieren, macht mich jeden Tag wütend
    Sie haben ein paar ähnliche Tastenbelegungen eingebaut, aber der lange Schwanz vertrauter readline-Shortcuts fehlt
    Anthropic könnte ruhig zugeben, dass die Entscheidung „wir müssen es wie Webentwicklung machen“ ein Fehler war, und wieder bei readline anfangen
    Die Vorstellung, dass die vertraute Entwicklererfahrung der Leute, die solche Tools bauen, wichtiger sei als die vertraute Benutzererfahrung der Leute, die sie benutzen, ist falsch

    • Soweit ich das verstehe, liegt ein großer Teil des Problems darin, dass Ink im Grunde nur mit einem Rendering-Backend zufrieden ist und keine Input-Widgets bereitstellt
      Es gibt tatsächlich kaum bekannte Drittanbieter-Lösungen, die gut gepflegt werden. Wenn man eine flexible Eingabebox braucht, muss man sie von Grund auf selbst bauen
      Im Gegensatz zum hervorragenden Input-Widget von Textual oder einer anderen Bibliothek im JS-Ökosystem, OpenTUI
    • Ist readline nicht unter der GNU-Lizenz? Hat endlich jemand eine Version gebaut, die nicht GPL ist?
      Ich mag LLMs nicht, deshalb ist eine schlechte UI für mich persönlich eher ein Vorteil, aber es könnte Gründe geben, readline nicht zu verwenden
  • Ich glaube, Terminal-Editoren wie kakoune und helix werden es schwer haben, Barrierefreiheitsanforderungen zu erfüllen, sofern sie nicht den Trick des „Cursor-Versteckens“ nutzen
    Selbst dann sind sie wahrscheinlich nicht so zugänglich wie VS Code
    Welche zugänglichen plattformübergreifenden IDE-lites oder IDEs außer VS Code gibt es eigentlich? Die zunehmend feindselige Haltung von VS Code gefällt mir nicht. Vielleicht die IDEs von JetBrains

    • Es gibt emacspeak, das Emacs eine sehr zugängliche Oberfläche gibt
      Der Nachteil ist, dass Emacs selbst zwar plattformübergreifend ist, emacspeak wegen TTS aber möglicherweise schwach von Linux abhängt. Vielleicht auch nicht. Unter Windows habe ich es nie ausprobiert
    • Die erste Frage ist: Barrierefreiheit für wen? Wenn es um Gehörlose geht, braucht man Texte in der jeweiligen Landessprache und die lokale Gebärdensprache. In den USA ist das üblicherweise ASL
      Für blinde Menschen braucht man emacspeak oder die Accessibility-Tools der jeweiligen Plattform für Sehbehinderte
      Barrierefreiheit ist ein Spektrum, keine Checkbox
  • Links hat einen eigenen Braille-Terminalmodus, der gefälschte GUI-Elemente in einfachere Vollbildmenüs umwandelt und die Navigation per Pfeiltasten ebenfalls auf Zeilenbasis umstellt
    Ein weiteres interessantes Beispiel ist edbrowse. Das ist ein Textmodus-Browser des blinden Entwicklers Karl Dahlke, der im Gegensatz zu populäreren Textmodus-Webbrowsern keine TUI verwendet, sondern eine Befehlszeilenschnittstelle im Stil von ed

  • Wenn es sich um das Ink-Framework handelt, ist das wahrscheinlich der Grund, warum die CLI 100 % CPU verbraucht, für immer hängen bleibt und lange Chatverläufe immer wieder neu rendert. Schade