run method
Runs the IDE until the user quits, restoring the terminal afterwards.
Implementation
Future<void> run() async {
_terminal.enter();
final decoder = KeyDecoder();
StreamSubscription<List<int>>? inputSub;
StreamSubscription<void>? resizeSub;
try {
// Load the tree root and git state before the first paint.
_repo = await GitRepo.discover(_workspace, rootPath);
await _tree.init();
await _refreshGit();
_render();
// Input is handled asynchronously (file ops await the workspace), so the
// subscription is paused while a chunk is processed and resumed after, to
// serialise key handling and keep ordering deterministic.
inputSub = _terminal.input.listen(
(bytes) async {
inputSub!.pause();
for (final key in decoder.decode(bytes)) {
// Never let an unexpected error from a key handler crash or freeze
// the TUI: surface it in the status bar and keep the loop alive.
try {
await _handleKey(key);
} on Object catch (e) {
_setMessage('Unexpected error: $e', isError: true);
}
if (_done.isCompleted) break;
}
if (!_done.isCompleted) _render();
if (!_done.isCompleted) inputSub.resume();
},
onError: (_) => _finish(),
onDone: _finish,
);
resizeSub = _terminal.resizeEvents.listen((_) {
_terminal.invalidate();
_render();
});
await _done.future;
} finally {
await inputSub?.cancel();
await resizeSub?.cancel();
await _terminalPanel?.dispose();
if (_confirm != null && !_confirm!.completer.isCompleted) {
_confirm!.completer.complete(false);
}
_agentBackend?.close();
await _workspace.close();
_terminal.leave();
}
}