Skip to content

Bug: Input handler persists after remote-to-local mode switch #422

Closed

Description

@ryuan-cw

Bug Description

After switching from remote mode to local mode using double-space, the input handling is broken:

  1. Typed input doesn't get captured entirely
  2. Pressing space in local mode triggers an unintended switch back to remote mode

Root Cause Analysis

The cleanup sequence in claudeRemoteLauncher.ts (lines 444-451) has three issues:

Issue 1: Dead Code (Line 445)

process.stdin.off('data', abort);

The abort function was never registered as a stdin data listener. Line 97 explicitly states:

// Removed catch-all stdin handler - now handled by RemoteModeDisplay keyboard handlers

This line is a no-op and should be removed.

Issue 2: Wrong Cleanup Order (Lines 446-450)

if (process.stdin.isTTY) {
    process.stdin.setRawMode(false);  // Raw mode disabled BEFORE Ink unmounts
}
if (inkInstance) {
    inkInstance.unmount();             // Ink unmount AFTER raw mode change
}

Raw mode is disabled before Ink unmounts. This can interfere with Ink's internal cleanup because Ink expects stdin to be in the state it was configured with.

Issue 3: Orphaned Ink Listener

Ink's useInput hook (in RemoteModeDisplay.tsx line 54) registers internal stdin data listeners. When inkInstance.unmount() is called, Ink may not completely remove these listeners. The orphaned listener remains attached to process.stdin.

How the Bug Manifests

  1. Remote mode sets stdin to raw mode and renders Ink UI with useInput for space key handling
  2. User double-spaces to switch to local mode
  3. Cleanup runs with incorrect order - Ink listener persists
  4. Local mode starts, inherits stdin with orphaned Ink listener still attached
  5. When user types a space, the orphaned listener fires and triggers mode switch

Proposed Fix

Reorder and enhance the cleanup sequence:

} finally {
    // Clean up permission handler
    permissionHandler.reset();

    // Reset Terminal - Order matters!
    // 1. Unmount Ink first while stdin is still in expected state
    if (inkInstance) {
        inkInstance.unmount();
    }

    // 2. Force remove any orphaned stdin listeners (Ink may not clean up completely)
    process.stdin.removeAllListeners('data');

    // 3. Now reset raw mode
    if (process.stdin.isTTY) {
        process.stdin.setRawMode(false);
    }

    messageBuffer.clear();

    // Resolve abort future
    if (abortFuture) {
        abortFuture.resolve(undefined);
    }
}

Files Affected

  • src/claude/claudeRemoteLauncher.ts - Cleanup sequence (lines 444-451)

Verification Steps

  1. Start happy-cli and enter remote mode
  2. Double-space to switch to local mode
  3. Type a sentence containing spaces (e.g., "hello world this is a test")
  4. Verify all characters are captured and spaces don't trigger mode switch

🤖 Generated with Claude Code

Activity

  1. bra1nDump commented on Apr 14, 2026

    @bra1nDump
    Contributor

    tracking this in #613 - same root cause (input handler persists after remote→local switch). closing here to keep discussion in one place.

  2. moved this from Backlog to Done in roadmapon Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions