c6be1f5a7a
The terminal raw-mode toggle is refcounted per-stdin (a WeakMap shared across controllers) so one app's unmount can't drop raw mode while another still needs it — vue's deliberate improvement over Ink, whose per-App counts let the first unmount disable raw for everyone. But the "data" input listener was ALSO gated on that shared refcount, so when two apps (separate createApp/stdout) shared one stdin, only the first app's handleData ever attached and the second was permanently deaf — and it didn't even self-heal when the first unmounted (Ink at least does, via its per-App readable listeners). Make the "data" listener (and its synchronous clearInputState cleanup: parser reset, pending-escape flush, listener detach) PER-CONTROLLER, gated on this controller's own localRefs, while keeping the raw-mode enable/disable on the shared refcount. Because vue uses the "data" (push) event, every listener gets every chunk, so both apps now receive input — strictly better than Ink's "readable" (pull) model where the first-registered listener drains the buffer. Single-app behavior is byte-identical: with one controller localRefs and the shared refs move 1:1, so the same-tick swap (parser reset + listener re-attach) fires at exactly the same moments as before. Test: two apps sharing one stdin both receive a keystroke, and the second keeps receiving after the first unmounts while raw mode stays enabled (shared ref); raw mode disables only when the last app unmounts. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>