@@ -52,6 +52,11 @@ const Terminal = forwardRef<TerminalHandle, TerminalProps>(
5252 const requestConfirmRef = useRef ( requestConfirm ) ;
5353 useEffect ( ( ) => { requestConfirmRef . current = requestConfirm ; } , [ requestConfirm ] ) ;
5454
55+ // Command history
56+ const commandHistory = useRef < string [ ] > ( [ ] ) ;
57+ const historyIndex = useRef ( - 1 ) ;
58+ const savedInput = useRef ( '' ) ;
59+
5560 // Exec mode refs — active while a Java process is running
5661 const execStdinCallback = useRef < ( ( data : string ) => void ) | null > ( null ) ;
5762 const execKillCallback = useRef < ( ( ) => void ) | null > ( null ) ;
@@ -175,11 +180,62 @@ const Terminal = forwardRef<TerminalHandle, TerminalProps>(
175180 }
176181
177182 // ── Normal command mode ──
183+
184+ // Arrow keys arrive as escape sequences
185+ if ( data === '\x1b[A' || data === '\x1b[B' ) {
186+ const history = commandHistory . current ;
187+ if ( history . length === 0 ) return ;
188+
189+ if ( data === '\x1b[A' ) {
190+ // Up — go back in history
191+ if ( historyIndex . current === - 1 ) {
192+ // Save whatever the user was typing
193+ savedInput . current = inputBuffer . current ;
194+ historyIndex . current = history . length - 1 ;
195+ } else if ( historyIndex . current > 0 ) {
196+ historyIndex . current -- ;
197+ } else {
198+ return ; // Already at oldest
199+ }
200+ } else {
201+ // Down — go forward in history
202+ if ( historyIndex . current === - 1 ) return ; // Nothing to navigate
203+ if ( historyIndex . current < history . length - 1 ) {
204+ historyIndex . current ++ ;
205+ } else {
206+ // Past newest — restore saved input
207+ historyIndex . current = - 1 ;
208+ }
209+ }
210+
211+ // Erase the current input on screen
212+ const eraseLen = inputBuffer . current . length ;
213+ if ( eraseLen > 0 ) term . write ( '\b \b' . repeat ( eraseLen ) ) ;
214+
215+ // Replace with history entry or saved input
216+ const replacement = historyIndex . current === - 1
217+ ? savedInput . current
218+ : history [ historyIndex . current ] ;
219+ inputBuffer . current = replacement ;
220+ term . write ( replacement ) ;
221+ return ;
222+ }
223+
178224 if ( code === 13 ) {
179225 // Enter
180226 term . write ( '\r\n' ) ;
181227 const raw = inputBuffer . current . trim ( ) ;
182228 inputBuffer . current = '' ;
229+
230+ // Push to history (skip duplicates of the last entry)
231+ if ( raw && raw !== commandHistory . current [ commandHistory . current . length - 1 ] ) {
232+ commandHistory . current . push ( raw ) ;
233+ // Cap at 100 entries
234+ if ( commandHistory . current . length > 100 ) commandHistory . current . shift ( ) ;
235+ }
236+ historyIndex . current = - 1 ;
237+ savedInput . current = '' ;
238+
183239 const parts = raw . split ( / \s + / ) ;
184240 const cmd = parts [ 0 ] ?. toLowerCase ( ) ?? '' ;
185241 const arg = parts . slice ( 1 ) . join ( ' ' ) ;
@@ -374,6 +430,8 @@ const Terminal = forwardRef<TerminalHandle, TerminalProps>(
374430 inputBuffer . current = inputBuffer . current . slice ( 0 , - 1 ) ;
375431 term . write ( '\b \b' ) ;
376432 }
433+ } else if ( code === 27 ) {
434+ // Escape sequence — ignore (Left/Right/etc already handled above)
377435 } else if ( code >= 32 ) {
378436 // Printable chars
379437 inputBuffer . current += data ;
0 commit comments