@@ -20,11 +20,23 @@ import {
2020 renderBufferWithCursor ,
2121 buildInitPromptSubmission ,
2222 buildPromptDraftFromSessionMessage ,
23+ dispatchTerminalInput ,
2324 disableTerminalExtendedKeys ,
2425 enableTerminalExtendedKeys ,
26+ EMPTY_BUFFER ,
27+ insertText ,
28+ backspace ,
2529} from "../ui" ;
2630import type { SessionMessage , SkillInfo } from "../session" ;
2731
32+ function collectDispatchedInput ( data : string ) {
33+ const events : ReturnType < typeof parseTerminalInput > [ ] = [ ] ;
34+ dispatchTerminalInput ( data , ( input , key ) => {
35+ events . push ( { input, key } ) ;
36+ } ) ;
37+ return events ;
38+ }
39+
2840test ( "parseTerminalInput treats DEL bytes as backspace" , ( ) => {
2941 const { input, key } = parseTerminalInput ( "\u007F" ) ;
3042 assert . equal ( input , "" ) ;
@@ -72,6 +84,45 @@ test("parseTerminalInput keeps DEL payload for meta+backspace", () => {
7284 assert . equal ( key . backspace , false ) ;
7385} ) ;
7486
87+ test ( "dispatchTerminalInput splits iOS CJK composition packets" , ( ) => {
88+ const events = collectDispatchedInput ( "가\u007F나" ) ;
89+ assert . equal ( events . length , 3 ) ;
90+ assert . equal ( events [ 0 ] ?. input , "가" ) ;
91+ assert . equal ( events [ 1 ] ?. input , "" ) ;
92+ assert . equal ( events [ 1 ] ?. key . backspace , true ) ;
93+ assert . equal ( events [ 2 ] ?. input , "나" ) ;
94+ } ) ;
95+
96+ test ( "dispatchTerminalInput applies multi-step CJK composition to the prompt buffer" , ( ) => {
97+ let state = EMPTY_BUFFER ;
98+ dispatchTerminalInput ( "ㄱ\u007F가\u007F각" , ( input , key ) => {
99+ if ( key . backspace ) {
100+ state = backspace ( state ) ;
101+ return ;
102+ }
103+ state = insertText ( state , input ) ;
104+ } ) ;
105+
106+ assert . equal ( state . text , "각" ) ;
107+ assert . equal ( state . cursor , 1 ) ;
108+ } ) ;
109+
110+ test ( "dispatchTerminalInput preserves meta+backspace as one event" , ( ) => {
111+ const events = collectDispatchedInput ( "\u001B\u007F" ) ;
112+ assert . equal ( events . length , 1 ) ;
113+ assert . equal ( events [ 0 ] ?. input , "\u007F" ) ;
114+ assert . equal ( events [ 0 ] ?. key . meta , true ) ;
115+ assert . equal ( events [ 0 ] ?. key . backspace , false ) ;
116+ assert . equal ( events [ 0 ] ?. key . escape , false ) ;
117+ } ) ;
118+
119+ test ( "dispatchTerminalInput emits consecutive backspaces from one packet" , ( ) => {
120+ const events = collectDispatchedInput ( "\u007F\u007F" ) ;
121+ assert . equal ( events . length , 2 ) ;
122+ assert . equal ( events [ 0 ] ?. key . backspace , true ) ;
123+ assert . equal ( events [ 1 ] ?. key . backspace , true ) ;
124+ } ) ;
125+
75126test ( "parseTerminalInput keeps BS payload for meta+backspace" , ( ) => {
76127 const { input, key } = parseTerminalInput ( "\u001B\b" ) ;
77128 assert . equal ( input , "\b" ) ;
0 commit comments