From eeed3fdba023a79ae4796e8d4d098e8ea832f848 Mon Sep 17 00:00:00 2001 From: Chris Hendrickson Date: Sat, 25 Apr 2026 14:22:09 -0400 Subject: [PATCH] fix(tui): pause/resume stdin subscription around external editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io.stdin is a single-subscription stream — calling listen() a second time after cancel() throws 'Stream has already been listened to'. Use keySub.pause() before launching the editor and keySub.resume() after it exits instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/kanban/lib/src/commands/tui_command.dart | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/kanban/lib/src/commands/tui_command.dart b/packages/kanban/lib/src/commands/tui_command.dart index e16dab8..a128712 100644 --- a/packages/kanban/lib/src/commands/tui_command.dart +++ b/packages/kanban/lib/src/commands/tui_command.dart @@ -302,9 +302,7 @@ class TuiCommand extends DewCommand { } // Key stream — raw bytes converted to Key values without blocking. - // Declared as var so it can be cancelled and re-created around external editor. - StreamSubscription> keySub; - keySub = io.stdin.listen((bytes) { + final keySub = io.stdin.listen((bytes) { for (final key in _parseKeys(bytes)) { if (!events.isClosed) events.add(_TuiKey(key)); } @@ -649,7 +647,7 @@ class TuiCommand extends DewCommand { '${io.Directory.systemTemp.path}/dew_edit_${es.ticket.id}.md', ); await tmpFile.writeAsString(es.body); - await keySub.cancel(); + keySub.pause(); console.rawMode = false; console.showCursor(); console.clearScreen(); @@ -661,13 +659,9 @@ class TuiCommand extends DewCommand { await proc.exitCode; es.body = await tmpFile.readAsString(); await tmpFile.delete(); - keySub = io.stdin.listen((bytes) { - for (final key in _parseKeys(bytes)) { - if (!events.isClosed) events.add(_TuiKey(key)); - } - }); console.rawMode = true; console.hideCursor(); + keySub.resume(); default: break; }