@@ -4,6 +4,7 @@ import 'dart:async';
44import 'dart:convert' ;
55
66import 'package:flutter/material.dart' ;
7+ import 'package:flutter/services.dart' ;
78import 'package:flutter_riverpod/flutter_riverpod.dart' ;
89import 'package:intl/intl.dart' ;
910
@@ -43,10 +44,27 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
4344 bool _busy = false ;
4445 bool _suppressRawLines = false ;
4546 bool _showCommands = true ;
47+ final _history = < String > [];
48+ int _historyIndex = - 1 ;
49+ String _savedInput = '' ;
4650
4751 @override
4852 void initState () {
4953 super .initState ();
54+ _commandFocus.onKeyEvent = (node, event) {
55+ if (event is ! KeyDownEvent && event is ! KeyRepeatEvent ) {
56+ return KeyEventResult .ignored;
57+ }
58+ if (event.logicalKey == LogicalKeyboardKey .arrowUp) {
59+ _historyUp ();
60+ return KeyEventResult .handled;
61+ }
62+ if (event.logicalKey == LogicalKeyboardKey .arrowDown) {
63+ _historyDown ();
64+ return KeyEventResult .handled;
65+ }
66+ return KeyEventResult .ignored;
67+ };
5068 _eventSub = ref.listenManual (latestEventsProvider, (_, next) {
5169 if (! mounted) return ;
5270 if (_mode == ConsoleMode .directSerialRepeater) return ;
@@ -87,6 +105,12 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
87105 if (_busy) return ;
88106 if (text.isEmpty && _mode != ConsoleMode .directSerialRepeater) return ;
89107 if (! mounted) return ;
108+ if (text.isNotEmpty && (_history.isEmpty || _history.last != text)) {
109+ _history.add (text);
110+ if (_history.length > 100 ) _history.removeAt (0 );
111+ }
112+ _historyIndex = - 1 ;
113+ _savedInput = '' ;
90114 setState (() => _busy = true );
91115 final txLabel =
92116 _mode == ConsoleMode .directSerialRepeater && _serialDeviceName != null
@@ -148,7 +172,12 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
148172 if (! mounted) return ;
149173 _append ('ERR' , '$error ' );
150174 } finally {
151- if (mounted) setState (() => _busy = false );
175+ if (mounted) {
176+ setState (() => _busy = false );
177+ WidgetsBinding .instance.addPostFrameCallback ((_) {
178+ if (mounted) _commandFocus.requestFocus ();
179+ });
180+ }
152181 }
153182 }
154183
@@ -624,6 +653,37 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
624653 _commandFocus.requestFocus ();
625654 }
626655
656+ void _historyUp () {
657+ if (_history.isEmpty) return ;
658+ if (_historyIndex == - 1 ) {
659+ _savedInput = _commandController.text;
660+ _historyIndex = _history.length - 1 ;
661+ } else if (_historyIndex > 0 ) {
662+ _historyIndex-- ;
663+ } else {
664+ return ;
665+ }
666+ final cmd = _history[_historyIndex];
667+ _commandController.text = cmd;
668+ _commandController.selection = TextSelection .collapsed (offset: cmd.length);
669+ }
670+
671+ void _historyDown () {
672+ if (_historyIndex == - 1 ) return ;
673+ if (_historyIndex < _history.length - 1 ) {
674+ _historyIndex++ ;
675+ final cmd = _history[_historyIndex];
676+ _commandController.text = cmd;
677+ _commandController.selection =
678+ TextSelection .collapsed (offset: cmd.length);
679+ } else {
680+ _historyIndex = - 1 ;
681+ _commandController.text = _savedInput;
682+ _commandController.selection =
683+ TextSelection .collapsed (offset: _savedInput.length);
684+ }
685+ }
686+
627687 Future <void > _openSerialConfig () async {
628688 if (_serialDeviceName == null ) return ;
629689 final console = ref.read (rawSerialConsoleProvider);
0 commit comments