- यह मान लेना कि terminal apps टेक्स्ट-आधारित होने के कारण स्वाभाविक रूप से accessible होते हैं, आधुनिक TUI में टूट जाता है; Ink, Bubble Tea, tcell जैसे frameworks screen reader users के लिए और अधिक शत्रुतापूर्ण माहौल बना सकते हैं
- CLI में output
stdin/stdout की linear stream के रूप में समयक्रम में जुड़ता जाता है, जबकि TUI terminal को character-cell आधारित 2D grid की तरह संभालता है, जिससे screen readers के लिए flow का पीछा करना कठिन हो जाता है
gemini-cli में Ink React component tree को terminal grid के हिसाब से फिर से render करता है, और spinner, timer, तथा conversation history के बीच cursor घुमाते हुए Speakup और NVDA में बार-बार पढ़ना, crash, और input lag पैदा कर सकता है
nano, vim, menuconfig, Irssi जैसे पुराने tools cursor hiding, single-column focus, और VT100 scroll region के उपयोग से coordinate update के शोर को घटाते हैं और input line में हस्तक्षेप कम करते हैं
- accessible terminal tools बनाने के लिए terminal को canvas की तरह संभालने वाले declarative UI frameworks और aggressive re-rendering से बचना होगा, और simple, linear CLI stream के करीब behavior सुनिश्चित करना होगा
“टेक्स्ट है, इसलिए accessible है” — यह गलतफ़हमी
- यह धारणा कि terminal पर चलने वाले applications स्वाभाविक रूप से accessible होते हैं, वास्तविक उपयोग स्थितियों से मेल नहीं खाती
- यह अपेक्षा कि graphics, जटिल DOM, या WebGL canvas न होने से screen readers raw ASCII text को आसानी से समझ लेंगे, आधुनिक TUI में टूट जाती है
- Ink(JS/React), Bubble Tea(Go), tcell जैसे terminal UI frameworks developer experience (DX) को बेहतर बनाने की कोशिश करते हैं, लेकिन दृष्टिबाधित users के लिए और अधिक शत्रुतापूर्ण वातावरण बना सकते हैं
- accessibility के मामले में आधुनिक TUI कई बार खराब तरह से implement किए गए graphical interface से भी बदतर होते हैं
CLI और TUI के बीच संरचनात्मक अंतर
-
CLI: linear stream
- CLI
stdin/stdout के आधार पर काम करता है; command डालने पर result नीचे जुड़ता है और cursor नीचे चला जाता है
- output linear और time-ordered रूप में जमा होता है, इसलिए Speakup जैसे kernel-level screen readers के लिए यह उपयुक्त है
-
TUI: 2D grid
- TUI terminal window को text stream की तरह नहीं, बल्कि ऐसी 2D grid की तरह देखता है जिसमें हर character cell एक pixel की तरह इस्तेमाल होता है
- समयगत flow को छोड़कर spatial layout को प्राथमिकता देने से यह screen readers के लिए कठिन संरचना बन जाती है
gemini-cli में दिखने वाली समस्याएँ
gemini-cli Node.js और Ink framework से बना एक tool है, और ऊपर से यह एक साधारण chat interface जैसा दिखता है
- अंदर ही अंदर Ink React component tree को terminal grid के हिसाब से ढालने की कोशिश करता है
- Speakup(Linux) या NVDA(Windows) के साथ इस्तेमाल करने पर application सिर्फ fail नहीं करता, बल्कि screen reader पर लगातार पढ़े जाने वाली चीज़ें उछालता रहता है
-
responsive canvas की तरह व्यवहार करने वाली screen
- framework screen को responsive canvas की तरह मानता है, इसलिए हर update re-rendering करवाता है
- जब AI “सोच रहा” होता है, तो timer या spinner update करने के लिए hardware cursor को timer की जगह ले जाया जाता है, नया समय लिखा जाता है, फिर उसे वापस मूल जगह लाया जाता है
- visual users के लिए यह तुरंत गुजर जाने वाली क्रिया है, लेकिन screen reader users को यह “Responding... Time elapsed 1s... Responding... Time elapsed 2s...” की तरह बार-बार सुनाई दे सकती है
- cursor status display, spinner, और conversation history के बीच पलभर के लिए घूमता है, और Speakup उस क्षण cursor के नीचे मौजूद content को पढ़ने की कोशिश करता है
- नतीजतन timer updates और conversation fragments आपस में मिल जाते हैं, जिससे वास्तव में टाइप की जा रही चीज़ पर ध्यान केंद्रित करना मुश्किल हो जाता है
-
NVDA और paste करते समय होने वाली अस्थिरता
- Windows में NVDA के साथ terminal खोलकर, Linux मशीन पर SSH से जुड़ने के बाद
screen session में text paste करने पर NVDA तुरंत crash कर सकता है या system बहुत अस्थिर हो सकता है
- हर character टाइप करने या text paste करने पर application state बदलती है, और framework तय करता है कि interface को फिर से render करना चाहिए
- अगर conversation history state में शामिल है, तो framework हजारों lines के text layout को तुरंत फिर से draw या recalculate करने की कोशिश कर सकता है
- conversation messages जितने ज़्यादा होंगे, यह समस्या उतनी ही अधिक बार होगी
- dynamic content notifications से बचने के लिए
Insert+5 combination भी इस समस्या से नहीं बचा पाता
-
input lag loop
- Ink जैसे frameworks जब Node.js जैसे single-threaded environment में चलते हैं, तो history बढ़ने के साथ performance गिरावट बढ़ती जाती है
- बड़े text blocks paste करने पर हजारों lines का diff calculate करना पड़ सकता है
- system screen को फिर से draw करने का तरीका निकालने में व्यस्त हो जाता है, इसलिए input processing देर से होती है
- एक key दबाने पर भी character दोबारा दिखने में अधिकतम 10 सेकंड तक लग सकते हैं
पुराने tools क्यों काम करते हैं
nano, vim, menuconfig जैसे tools इसलिए उपयोगी नहीं हैं कि वे हमेशा accessibility में परफ़ेक्ट हों
- असली बात यह है कि ये tools cursor को पूरी तरह छिपा सकते हैं, या cursor position tracking से पैदा होने वाले शोर को कम कर सकते हैं
-
nano और vim: cursor छिपाना
nano को --constantshow जैसे cursor position दिखाने वाले option के साथ चलाना, या vim को बिना खास settings के इस्तेमाल करना usability को खराब कर सकता है
- अगर cursor दिखाई दे रहा हो और tracking चालू हो, तो Speakup character echo की जगह cursor position updates को प्राथमिकता देता है
- user अगर “a” टाइप करे तो उसे “a” की जगह “Column 2” सुनाई दे सकता है, और “b” टाइप करने पर “Column 3”
- इन पुराने tools को इस तरह configure किया जा सकता है कि visual cursor या status bar updates दब जाएँ, जिससे screen reader coordinate updates की बजाय character input stream पर निर्भर रह सके
- आधुनिक frameworks आमतौर पर “no-cursor” या “headless” mode नहीं देते, और मान लेते हैं कि visual cursor अनिवार्य है
-
menuconfig: single-column focus
- Linux kernel का
menuconfig इसलिए काम करता है क्योंकि यह कड़ाई से single-column focus बनाए रखता है
- borders और titles होने के बावजूद active area एक vertical list ही रहती है, और cursor उसी list में स्थिर रहता है
- cursor clock update के लिए नीचे-दाएँ और title update के लिए ऊपर-बाएँ नहीं घूमता
- spatial complexity कम रहने से screen reader अपना रास्ता नहीं खोता
-
Irssi: scroll region का उपयोग
- Irssi संयोग से accessible नहीं है; यह 20 साल से अधिक समय से custom rendering engine के साथ VT100 scroll region का उपयोग करने वाला chat tool है
- नया message आने पर यह terminal driver को निर्देश देता है: “row 1 से row 23 तक को scroll region के रूप में define करो”
- फिर “scroll up” command भेजी जाती है, terminal content को ऊपर खिसका देता है, और उस region के नीचे नए text को draw करता है
- यह तरीका input line के साथ हस्तक्षेप को न्यूनतम करता है
- screen के हर character को हाथ से फिर से लिखने की बजाय यह terminal की hardware capabilities पर निर्भर करता है
- आधुनिक frameworks इन hardware features को नज़रअंदाज़ करके screen state diff calculate कर characters फिर से लिखने का तरीका अपनाते हैं, जो computationally अधिक महँगा और accessibility के लिए शत्रुतापूर्ण है
gemini-cli issues को संभालने की समस्या
- Google और
gemini-cli maintainers देखने में accessibility की परवाह करते लगते हैं, लेकिन repository में महत्वपूर्ण accessibility regressions को छोड़ा हुआ है
- Issue #3435 और Issue #11305 जैसे accessibility regressions पर न चर्चा है, न roadmap, न fix
- Issue #1553 इस तरह की accessibility failures को track करने के लिए था, लेकिन यह unresolved रहा और bot ने इसे अपने-आप बंद कर दिया
- bot ने issue बंद करते समय वही सामान्य संदेश दिया कि काफी समय से activity नहीं हुई और backlog manage करने के लिए इसे बंद किया जा रहा है
- सिर्फ इसलिए accessibility reports बंद कर देना कि maintainers ने महीनों तक उन्हें नहीं छुआ, सफ़ाई नहीं बल्कि सबूत छिपाने जैसा है
- इससे यह संकेत जाता है कि अगर किसी bug को काफ़ी देर तक नज़रअंदाज़ करो, तो वह मानो मौजूद ही नहीं रहता, जबकि वास्तविक software दृष्टिबाधित users के लिए अब भी अनुपयोगी बना रहता है
- project के “Closed Issues” metrics बेहतर दिख सकते हैं, लेकिन accessibility problems हल नहीं होतीं
accessible terminal tools बनाने के लिए निष्कर्ष
- अगर terminal applications में accessibility सचमुच महत्वपूर्ण है, तो terminal को canvas की तरह संभालने वाले declarative UI frameworks का उपयोग बंद करना होगा
- “modern” TUI stacks को इस तरह optimize किया गया है कि developers React-जैसा code आसानी से लिख सकें, और इसके बदले machines की efficient text rendering क्षमता की कुर्बानी दी गई है
- अगर application यह सुनिश्चित नहीं कर सकता कि user cursor छिपा सके, या spinner और timer दिखाने के लिए aggressive re-rendering पर निर्भर है, तो वह एक inaccessible tool बन जाता है
- दृष्टिबाधित users के लिए ऐसा simple, linear CLI stream कहीं बेहतर है, बजाय उस “smart” TUI के जो lag करता है, लगातार पढ़े जाने वाला content उछालता रहता है, और cursor को पूरी screen पर बिखेर देता है
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”
यह एक शानदार ब्लॉग पोस्ट हो सकती थी, लेकिन अगर लगता है कि लेखक ने बस आउटलाइन ChatGPT में डालकर काम खत्म कर दिया, तो इससे पाठक और लेखक दोनों का नुकसान होता है
किसी बहुत ही विशेष, एकबारगी समस्या को “क्लासिक” समस्या कहना भी वैसा ही है
यह सच में उदास करने वाला है। सार यह है कि Irssi जैसे सुलभ TUI मौजूद हैं, लेकिन आधुनिक TUI framework ऐसी मिसालों को नज़रअंदाज़ करके grid diffing और cursor movement पर निर्भर करते हैं
screen reader cursor के हिलने पर उस जगह की सामग्री पढ़ते हैं, इसलिए नतीजा पूरी तरह गड़बड़ हो जाता है या बहुत ज़्यादा पढ़कर सुनाने वाला spam पैदा होता है
यहाँ दी गई तकनीकी व्याख्या पूरी तरह सही है या नहीं, इस पर संदेह है
खासकर Ink लंबे समय तक incremental rendering को बिल्कुल support नहीं करता था, और Ink का उपयोग करने वाले ज़्यादातर app अब भी इसे enable नहीं करते। वह incremental rendering भी line-based है, इसलिए यह cursor को असली timer position पर नहीं ले जाता
Gemini CLI में incremental rendering चालू करने के लिए alternate buffer का उपयोग ज़रूरी है, और built-in screen reader friendly mode चालू होने पर यह disable हो जाता है। संबंधित option का दस्तावेज़ यहाँ है
जोड़ने की बात यह भी है कि Python का rich/textual, अधिक धीमी और प्रायः single-threaded language पर होने के बावजूद, कई बार Ink से कहीं तेज़ होता है। हज़ारों लाइनों का diff निकालना अनिवार्य रूप से इतना धीमा नहीं होता, और न ही इसमें 10 सेकंड लगने चाहिए
user experience निराशाजनक और टूटा हुआ है, इस पर संदेह नहीं, लेकिन जो सटीक कारण बताए गए हैं वे LLM hallucination हो सकते हैं या अधूरी जानकारी पर आधारित हो सकते हैं। Ink का incremental rendering, चालू होने पर भी, बताए गए तरीके से काम नहीं करता
असल में full-screen redraw screen reader को भ्रमित करने की ज़्यादा संभावना रखता है, और line-based redraw से बदलाव से असंबंधित, बेतरतीब टूटे हुए text fragment दोबारा पढ़े जा सकते हैं
सिर्फ TUI को दोष देना ठीक नहीं है
असली समस्या यह है कि लगभग पूरे stack में accessibility support बहुत खराब है
पहली बात, ज़्यादातर GPU-rendering terminal emulator system-provided accessibility API का बिल्कुल उपयोग नहीं करते। जब text GPU से render होता है, तो accessibility tool उसे “पढ़” नहीं सकते और वह सिर्फ image जैसा दिखता है। Kitty, Alacritty, WezTerm इसी श्रेणी में आते हैं। मेरा terminal Ghostty macOS पर accessibility API से पढ़ा जा सकता है, और iTerm2 तथा Terminal.app भी ऐसा कर सकते हैं
दूसरी बात, TUI के लिए accessibility जानकारी terminal emulator तक पहुँचाने वाला कोई terminal sequence या standard movement है ही नहीं। terminal cell, run, region के लिए ARIA-जैसी annotation के समकक्ष किसी चीज़ की ज़रूरत है, लेकिन ऐसा कोई प्रयास नहीं है। भले ही TUI cursor को अच्छी तरह संभाले, कई use case में समस्या फिर भी आएगी
उदाहरण के तौर पर, Ghostty में OSC133 और accessibility API को जोड़कर हर shell prompt, input, command को साधारण text box नहीं बल्कि संरचनात्मक रूप से अर्थपूर्ण element के रूप में उजागर करने का काम किया गया है। इससे पता चलता है कि terminal spec, TUI और terminal emulator—तीनों को साथ में काम करना होगा
पूरा stack सड़ चुका है, और इसे सच में ठीक करने की कोशिश करने वाले लोग भी लगभग नहीं हैं। मेरे पास भी समय सीमित है, इसलिए मैं बस अपनी पूरी कोशिश कर सकता हूँ, लेकिन यह इतना बड़ा विषय है कि इसमें ecosystem politics तक शामिल है, इसलिए इसे संभालना मुश्किल है
बोनस के तौर पर, दिलचस्प लेकिन भयानक सच्चाई यह है कि यहाँ AI accessibility सुधारने में मदद भी कर रहा है। बहुत से AI tool accessibility API का उपयोग या दुरुपयोग करके window list पढ़ते हैं और input करते हैं। इसलिए AI use case की वजह से ज़्यादा app अब accessibility integration को कहीं अधिक गंभीरता से लेने लगे हैं
Claude Code और gemini-cli के readline-based न होने से मुझे हर दिन गुस्सा आता है
कुछ मिलती-जुलती keybinding डाली गई हैं, लेकिन परिचित readline shortcut की लंबी tail गायब है
Anthropic यह मानकर कि “इसे web development जैसा बनाना चाहिए” एक गलती थी, readline से फिर से शुरू कर सकता है
यह सोचना गलत है कि ऐसे tool बनाने वाले developer के लिए परिचित dev experience, उन tool का उपयोग करने वाले user के लिए परिचित user experience से ज़्यादा महत्वपूर्ण है
वास्तव में, अच्छी तरह maintain होने वाले चर्चित third-party समाधान भी लगभग नहीं हैं। अगर आपको flexible input box चाहिए, तो आपको शुरू से खुद बनाना पड़ता है
इसकी तुलना Textual के शानदार Input widget या JS ecosystem की दूसरी library OpenTUI से की जा सकती है
मुझे LLM पसंद नहीं हैं, इसलिए UI का खराब होना व्यक्तिगत रूप से मुझे फ़ायदा लगता है, लेकिन readline का उपयोग न करने के पीछे कोई वजह हो सकती है
kakoune, helix जैसे terminal editor शायद accessibility मानकों को पार करने में मुश्किल पाएँगे, जब तक वे “cursor छिपाने” जैसी तरकीब न अपनाएँ
फिर भी, संभावना है कि वे VS Code जितने सुलभ न हों
VS Code के अलावा कौन-सा सुलभ cross-platform IDE-lite या IDE है? VS Code का धीरे-धीरे अधिक शत्रुतापूर्ण होता रवैया मुझे पसंद नहीं है। JetBrains IDE हो सकता है
कमी यह है कि Emacs खुद तो cross-platform है, लेकिन TTS की वजह से emacspeak की Linux पर कुछ निर्भरता हो सकती है। या शायद नहीं भी। मैंने इसे Windows पर कभी आज़माया नहीं
अगर यह दृष्टिबाधित लोगों के लिए accessibility है, तो emacspeak या platform के accessibility tool की ज़रूरत होगी
accessibility एक spectrum है, कोई checkbox नहीं
Links में अलग Braille terminal mode है, जो नकली GUI elements को अधिक सरल full-screen menu में बदल देता है और arrow-key navigation को भी line-by-line बना देता है
एक और दिलचस्प उदाहरण edbrowse है। यह दृष्टिबाधित Karl Dahlke द्वारा बनाया गया text-mode browser है, जो अधिक लोकप्रिय text-mode web browser से अलग TUI का उपयोग नहीं करता, बल्कि ed-style command-line interface का उपयोग करता है
अगर यह Ink framework है, तो शायद यही वजह है कि CLI CPU 100% इस्तेमाल करता है, हमेशा के लिए अटक जाता है, और लंबा chat history बार-बार redraw करता रहता है। दुखद है