@@ -11,7 +11,8 @@ import { IDisposable, Terminal } from '@xterm/xterm';
1111/**
1212 * Lightweight xterm wrapper that is decoupled from the backend and is optimized
1313 * for screen-reader experiments. It renders a static intro text and mirrors any
14- * user input back to the terminal with a simple prompt (`> `).
14+ * user input back to the terminal, including a delayed echo so assistive tech can
15+ * be tested without talking to the backend.
1516 */
1617@Component ( {
1718 selector : 'app-mud-screenreader-client' ,
@@ -25,16 +26,16 @@ export class MudScreenreaderClientComponent
2526 private readonly terminal = new Terminal ( {
2627 fontFamily : 'JetBrainsMono, monospace' ,
2728 theme : { background : '#000' , foreground : '#ccc' } ,
28- // cursorBlink: true,
29+ cursorBlink : true ,
2930 screenReaderMode : true ,
3031 } ) ;
3132
3233 private readonly fitAddon = new FitAddon ( ) ;
3334 private readonly terminalDisposables : IDisposable [ ] = [ ] ;
3435 private readonly resizeObs = new ResizeObserver ( ( ) => this . handleResize ( ) ) ;
3536
36- private readonly promptLabel = '> ' ;
3737 private currentInput = '' ;
38+ private readonly pendingEchoTimeouts : number [ ] = [ ] ;
3839
3940 @ViewChild ( 'hostRef' , { static : true } )
4041 private readonly terminalRef ! : ElementRef < HTMLDivElement > ;
@@ -46,7 +47,6 @@ export class MudScreenreaderClientComponent
4647 this . resizeObs . observe ( this . terminalRef . nativeElement ) ;
4748
4849 this . renderStaticIntro ( ) ;
49- this . renderPrompt ( ) ;
5050 this . terminal . focus ( ) ;
5151
5252 this . terminalDisposables . push (
@@ -56,6 +56,8 @@ export class MudScreenreaderClientComponent
5656
5757 ngOnDestroy ( ) : void {
5858 this . resizeObs . disconnect ( ) ;
59+ this . pendingEchoTimeouts . forEach ( ( timeoutId ) => clearTimeout ( timeoutId ) ) ;
60+ this . pendingEchoTimeouts . length = 0 ;
5961
6062 this . terminalDisposables . forEach ( ( disposable ) => disposable . dispose ( ) ) ;
6163 this . terminal . dispose ( ) ;
@@ -103,21 +105,19 @@ export class MudScreenreaderClientComponent
103105
104106 private commitInput ( ) : void {
105107 this . terminal . write ( '\r\n' ) ;
106- this . terminal . writeln ( `Eingabe: ${ this . currentInput } ` ) ;
108+ const inputSnapshot = this . currentInput ;
109+ this . terminal . writeln ( `Eingabe: ${ inputSnapshot } ` ) ;
110+ this . scheduleEcho ( inputSnapshot ) ;
107111 this . currentInput = '' ;
108- this . renderPrompt ( ) ;
109- }
110-
111- private renderPrompt ( ) : void {
112- this . terminal . write ( this . promptLabel ) ;
112+ this . terminal . writeln ( '' ) ;
113113 }
114114
115115 private renderStaticIntro ( ) : void {
116116 this . terminal . writeln ( 'Willkommen zum barrierefreien Testlauf.' ) ;
117117 this . terminal . writeln ( 'Die Verbindung zum Server ist deaktiviert.' ) ;
118118 this . terminal . writeln ( '' ) ;
119119 this . terminal . writeln (
120- 'Tippen Sie Ihre Eingabe nach dem Prompt (>) und bestätigen Sie mit Enter.' ,
120+ 'Tippen Sie Ihre Eingabe und bestaetigen Sie mit Enter.' ,
121121 ) ;
122122 this . terminal . writeln ( '' ) ;
123123 }
@@ -126,4 +126,16 @@ export class MudScreenreaderClientComponent
126126 const code = char . charCodeAt ( 0 ) ;
127127 return code >= 0x20 && code !== 0x7f ;
128128 }
129+
130+ private scheduleEcho ( message : string ) : void {
131+ const timeoutId = window . setTimeout ( ( ) => {
132+ this . terminal . writeln ( `Echo: ${ message } ` ) ;
133+ const idx = this . pendingEchoTimeouts . indexOf ( timeoutId ) ;
134+ if ( idx >= 0 ) {
135+ this . pendingEchoTimeouts . splice ( idx , 1 ) ;
136+ }
137+ } , 5000 ) ;
138+
139+ this . pendingEchoTimeouts . push ( timeoutId ) ;
140+ }
129141}
0 commit comments