
---
title: Input Handling System
description: Complete input handling architecture with facade pattern, key processing, and command mode
category: core-systems
status: review
last_updated: 2025-01-15
version: 1.0.0
tags:
  - level: advanced
  - component: input
  - status: stable
related:
  - ../architecture/system-architecture.md
  - ../architecture/event-system.md
  - ./terminal-rendering.md
  - ../reference/paste-detection-system.md
---

# Input Handling System

## Overview

The Input Handling System manages all user interaction with the Kollabor CLI application, implementing a Facade pattern that coordinates 8 specialized components for comprehensive input processing. The system handles keyboard input, paste detection, command mode, modal interactions, hook registration, display updates, and status modal rendering through a unified interface.

## Purpose

This document explains:
- InputHandler Facade architecture and responsibilities
- The 8 specialized components it coordinates
- Key processing flow from keyboard to application events
- Command mode and slash command handling
- Paste detection and placeholder generation
- Modal lifecycle management
- Display control and pause/resume functionality

## Scope

**Covered**:
- InputHandler Facade component
- InputLoopManager (main loop, platform I/O, paste detection)
- KeyPressHandler (key processing, Enter/Escape handling)
- CommandModeHandler (slash commands, command menus)
- ModalController (all modal types)
- HookRegistrar (event hook registration)
- DisplayController (display updates, pause/resume)
- PasteProcessor (paste detection, placeholders)
- StatusModalRenderer (status modal line generation)

**Not Covered**:
- Terminal rendering (see: [Terminal Rendering System](./terminal-rendering.md))
- Modal rendering details (see: [Modal System Guide](../reference/modal-system-guide.md))
- Message display (see: [LLM Message Flow](../reference/llm-message-flow.md))

## Prerequisites

- Understanding of terminal raw mode vs. cooked mode
- Knowledge of event system (see: [Event System Architecture](../architecture/event-system.md))
- Familiarity with plugin system (see: [Plugin System Architecture](./plugin-system.md))
- Understanding of Facade design pattern

## Table of Contents

- [Architecture Overview](#architecture-overview)
- [InputHandler Facade](#inputhandler-facade)
- [InputLoopManager](#inputloopmanager)
- [KeyPressHandler](#keypresshandler)
- [CommandModeHandler](#commandmodehandler)
- [ModalController](#modalcontroller)
- [HookRegistrar](#hookregistrar)
- [DisplayController](#displaycontroller)
- [PasteProcessor](#pasteprocessor)
- [StatusModalRenderer](#statusmodalrenderer)
- [Complete Input Flow](#complete-input-flow)
- [Examples](#examples)
- [Troubleshooting](#troubleshooting)
- [Related Documents](#related-documents)
- [References](#references)

---

## Architecture Overview

The Input Handling System implements a Facade pattern that coordinates 8 specialized components:

```mermaid
graph TB
    subgraph "Input Handler Facade"
        IH[InputHandler]
    end
    
    subgraph "Specialized Components"
        ILM[InputLoopManager]
        KPH[KeyPressHandler]
        CMH[CommandModeHandler]
        MC[ModalController]
        HR[HookRegistrar]
        DC[DisplayController]
        PP[PasteProcessor]
        SMR[StatusModalRenderer]
    end
    
    subgraph "External Systems"
        EB[Event Bus]
        TR[Terminal Renderer]
        MM[Modal Manager]
        UI[UI Components]
    end
    
    IH --> ILM
    IH --> KPH
    IH --> CMH
    IH --> MC
    IH --> HR
    IH --> DC
    IH --> PP
    IH --> SMR
    
    ILM --> PP
    ILM --> UI
    KPH --> IH
    KPH --> EB
    CMH --> EB
    MC --> MM
    HR --> EB
    DC --> TR
    SMR --> MM
```

### Component Responsibilities

| Component | File | Responsibilities |
|-----------|-------|-----------------|
| **InputHandler** | Facade entry point | Coordinates all input handling components |
| **InputLoopManager** | Main loop, platform I/O | Manages input loop, reads keys, detects paste |
| **KeyPressHandler** | Key processing | Processes key presses, Enter/Escape handling, event emission |
| **CommandModeHandler** | Command mode | Slash commands, command menus, filtering |
| **ModalController** | Modal lifecycle | All modal types (overlay, status, live) |
| **HookRegistrar** | Hook management | Registers hooks for events |
| **DisplayController** | Display control | Pause/resume rendering, display updates |
| **PasteProcessor** | Paste detection | Detects pastes, generates placeholders |
| **StatusModalRenderer** | Status modal | Generates status modal lines |

---

## InputHandler Facade

**File**: `kollabor/io/input/input_handler.py`

The InputHandler serves as the central facade for all input handling operations, coordinating between specialized components while providing a simple interface to the rest of the application.

### Facade Pattern Implementation

```python
class InputHandler:
    """Facade for input handling system.
    
    Coordinates 8 specialized components:
    - InputLoopManager: Main input loop
    - KeyPressHandler: Key press processing
    - CommandModeHandler: Command mode
    - ModalController: Modal lifecycle
    - HookRegistrar: Hook registration
    - DisplayController: Display control
    - PasteProcessor: Paste detection
    - StatusModalRenderer: Status modal rendering
    """
    
    def __init__(
        self,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager,
        **kwargs
    ):
        """Initialize input handler facade.
        
        Args:
            event_bus: Event bus for hook registration
            renderer: Terminal renderer for display control
            config: Configuration manager
            **kwargs: Additional dependencies
        """
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        # Initialize specialized components
        self.input_loop_manager = InputLoopManager(self, event_bus, renderer, config)
        self.key_press_handler = KeyPressHandler(self, event_bus, renderer, config)
        self.command_mode_handler = CommandModeHandler(self, event_bus, renderer, config)
        self.modal_controller = ModalController(self, event_bus, renderer, config)
        self.hook_registrar = HookRegistrar(self, event_bus, renderer, config)
        self.display_controller = DisplayController(self, event_bus, renderer, config)
        self.paste_processor = PasteProcessor(self, event_bus, renderer, config)
        self.status_modal_renderer = StatusModalRenderer(self, event_bus, renderer, config)
```

### Facade Methods

```python
# Main input operations
async def start_input_loop(self) -> None:
    """Start the main input loop."""
    await self.input_loop_manager.start()

async def stop_input_loop(self) -> None:
    """Stop the main input loop."""
    await self.input_loop_manager.stop()

# Key processing
def handle_key_press(self, key: str) -> None:
    """Handle a key press."""
    self.key_press_handler.handle_key_press(key)

# Command mode operations
async def enter_command_mode(self) -> None:
    """Enter command mode."""
    await self.command_mode_handler.enter_command_mode()

async def exit_command_mode(self) -> None:
    """Exit command mode."""
    await self.command_mode_handler.exit_command_mode()

# Modal operations
async def enter_modal_mode(self, modal_type: str, **kwargs) -> None:
    """Enter modal mode."""
    await self.modal_controller.enter_modal_mode(modal_type, **kwargs)

async def exit_modal_mode(self) -> None:
    """Exit modal mode."""
    await self.modal_controller.exit_modal_mode()

# Display control
async def pause_rendering(self) -> None:
    """Pause terminal rendering."""
    await self.display_controller.pause_rendering()

async def resume_rendering(self) -> None:
    """Resume terminal rendering."""
    await self.display_controller.resume_rendering()

# Hook management
async def register_hooks(self) -> None:
    """Register input handling hooks."""
    await self.hook_registrar.register_hooks()
```

---

## InputLoopManager

**File**: `kollabor/io/input/input_loop_manager.py`

The InputLoopManager manages the main input loop, platform-specific I/O, and paste detection coordination.

### Main Input Loop

```python
class InputLoopManager:
    """Manages main input loop and platform I/O."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.running = False
        self.input_loop_task = None
        
        # Platform-specific I/O
        self.platform_io = self._init_platform_io()
    
    async def start(self) -> None:
        """Start the main input loop."""
        if self.running:
            return  # Already running
        
        self.running = True
        self.input_loop_task = asyncio.create_task(self._input_loop())
        
        logger.info("Input loop started")
    
    async def stop(self) -> None:
        """Stop the main input loop."""
        if not self.running:
            return  # Not running
        
        self.running = False
        
        if self.input_loop_task:
            self.input_loop_task.cancel()
            try:
                await self.input_loop_task
            except asyncio.CancelledError:
                pass
        
        logger.info("Input loop stopped")
    
    async def _input_loop(self) -> None:
        """Main input loop."""
        while self.running:
            try:
                # Read key from platform I/O
                key = await self.platform_io.read_key()
                
                if key:
                    # Process key press
                    self.input_handler.handle_key_press(key)
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Input loop error: {e}", exc_info=True)
                await asyncio.sleep(0.1)  # Brief pause on error
```

### Platform I/O

```python
def _init_platform_io(self) -> PlatformIO:
    """Initialize platform-specific I/O.
    
    Returns platform-specific I/O handler.
    """
    import platform
    
    if platform.system() == "Windows":
        return WindowsPlatformIO()
    elif platform.system() == "Darwin":
        return MacOSPlatformIO()
    else:  # Linux and others
        return LinuxPlatformIO()

class PlatformIO:
    """Base class for platform-specific I/O."""
    
    async def read_key(self) -> Optional[str]:
        """Read a single key from terminal.
        
        Returns:
            Key character or special key name (e.g., "Enter", "Escape")
        """
        raise NotImplementedError
```

### Paste Detection Coordination

```python
async def _input_loop(self) -> None:
    """Main input loop with paste detection."""
    last_key_time = time.time()
    key_buffer = []
    
    while self.running:
        try:
            key = await self.platform_io.read_key()
            
            if key:
                current_time = time.time()
                
                # Check for paste (rapid character input)
                if current_time - last_key_time < 0.01:  # 10ms threshold
                    key_buffer.append(key)
                else:
                    if key_buffer:
                        # Paste detected
                        await self.paste_processor.handle_paste(''.join(key_buffer))
                        key_buffer.clear()
                    
                    # Single key press
                    self.input_handler.handle_key_press(key)
                
                last_key_time = current_time
        
        except asyncio.CancelledError:
            break
```

---

## KeyPressHandler

**File**: `kollabor/io/input/key_press_handler.py`

The KeyPressHandler processes all key presses, handling Enter/Escape specially and emitting appropriate events.

### Key Processing Flow

```python
class KeyPressHandler:
    """Processes key presses and emits events."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.buffer_manager = BufferManager()
        self.key_parser = KeyParser()
    
    def handle_key_press(self, key: str) -> None:
        """Handle a key press.
        
        Args:
            key: Key character or special key name
        """
        # Emit KEY_PRESS event
        self.event_bus.fire_event(Event(
            type=EventType.KEY_PRESS,
            data={"key": key},
            source="key_press_handler"
        ))
        
        # Handle special keys
        if key == "Enter":
            self._handle_enter()
        elif key == "Escape":
            self._handle_escape()
        elif key == "Ctrl+C":
            self._handle_ctrl_c()
        elif key == "Ctrl+L":
            self._handle_ctrl_l()
        else:
            # Regular character input
            self._handle_regular_key(key)
```

### Enter Key Handling

```python
def _handle_enter(self) -> None:
    """Handle Enter key press.
    
    Submits user input for processing.
    """
    # Get user message
    message = self.buffer_manager.get_content_and_clear()
    
    # Clear active area
    self.renderer.clear_active_area()
    
    # Exit raw mode temporarily for display
    with self.renderer.terminal_state.temporarily_exit_raw_mode():
        # Emit USER_INPUT event
        asyncio.create_task(
            self.event_bus.emit_with_hooks(
                EventType.USER_INPUT,
                {"message": message},
                source="key_press_handler"
            )
        )
```

### Escape Key Handling

```python
def _handle_escape(self) -> None:
    """Handle Escape key press.
    
    Exits command mode, modal mode, or cancels LLM processing.
    """
    # Priority: Modal > Command Mode > LLM Cancel
    
    # Check if in modal mode
    if self.modal_controller.in_modal_mode:
        self.modal_controller.exit_modal_mode()
        return
    
    # Check if in command mode
    if self.command_mode_handler.in_command_mode:
        self.command_mode_handler.exit_command_mode()
        return
    
    # Cancel LLM processing
    await self.event_bus.emit_with_hooks(
        EventType.CANCEL_REQUEST,
        {
            "reason": "user_escape",
            "source": "key_press_handler"
        },
        source="key_press_handler"
    )
```

### Regular Key Handling

```python
def _handle_regular_key(self, key: str) -> None:
    """Handle regular character input.
    
    Args:
        key: Single character
    """
    # Check if in command mode
    if self.command_mode_handler.in_command_mode:
        self.command_mode_handler.handle_key(key)
    elif self.modal_controller.in_modal_mode:
        self.modal_controller.handle_key(key)
    else:
        # Regular input mode
        self.buffer_manager.add_character(key)
        self._trigger_input_render()
```

### Input Render Trigger

```python
def _trigger_input_render(self) -> None:
    """Trigger INPUT_RENDER event."""
    asyncio.create_task(
        self.event_bus.emit_with_hooks(
            EventType.INPUT_RENDER,
            {
                "input_buffer": self.buffer_manager.get_content(),
                "cursor_position": self.buffer_manager.get_cursor_position()
            },
            source="key_press_handler"
        )
    )
```

---

## CommandModeHandler

**File**: `kollabor/io/input/command_mode_handler.py`

The CommandModeHandler manages slash commands and the command menu interface.

### Command Mode Entry/Exit

```python
class CommandModeHandler:
    """Handles command mode and slash commands."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.in_command_mode = False
        self.command_buffer = ""
        self.available_commands = []
        self.filtered_commands = []
        self.selected_index = 0
    
    async def enter_command_mode(self) -> None:
        """Enter command mode (slash commands)."""
        if self.in_command_mode:
            return  # Already in command mode
        
        self.in_command_mode = True
        self.command_buffer = "/"
        
        # Get available commands
        self.available_commands = self._get_available_commands()
        self.filtered_commands = self.available_commands
        self.selected_index = 0
        
        # Emit event
        await self.event_bus.emit_with_hooks(
            EventType.COMMAND_MENU_SHOW,
            {
                "available_commands": self.available_commands,
                "filter_text": ""
            },
            source="command_mode_handler"
        )
        
        # Trigger render
        await self._trigger_command_render()
    
    async def exit_command_mode(self) -> None:
        """Exit command mode."""
        if not self.in_command_mode:
            return  # Not in command mode
        
        self.in_command_mode = False
        self.command_buffer = ""
        
        # Emit event
        await self.event_bus.emit_with_hooks(
            EventType.COMMAND_MENU_HIDE,
            {
                "reason": "manual_exit"
            },
            source="command_mode_handler"
        )
```

### Command Filtering

```python
def _filter_commands(self, filter_text: str) -> List[CommandDefinition]:
    """Filter commands based on input.
    
    Args:
        filter_text: Text to filter commands
        
    Returns:
            List of filtered commands
    """
    filter_text = filter_text.lower()
    
    # Remove leading slash for filtering
    if filter_text.startswith('/'):
        filter_text = filter_text[1:]
    
    filtered = []
    for command in self.available_commands:
        # Filter by name, description, or aliases
        matches = (
            filter_text in command.name.lower() or
            filter_text in command.description.lower() or
            any(filter_text in alias.lower() for alias in command.aliases)
        )
        
        if matches:
            filtered.append(command)
    
    return filtered
```

### Command Menu Navigation

```python
def _handle_navigation_key(self, key: str) -> None:
    """Handle navigation keys in command menu.
    
    Args:
        key: Navigation key (Up, Down, Enter, Escape)
    """
    if key == "Up":
        self.selected_index = max(0, self.selected_index - 1)
    elif key == "Down":
        self.selected_index = min(
            len(self.filtered_commands) - 1,
            self.selected_index + 1
        )
    elif key == "Enter":
        self._execute_selected_command()
    elif key == "Escape":
        asyncio.create_task(self.exit_command_mode())
    
    self._trigger_command_render()
```

### Command Execution

```python
def _execute_selected_command(self) -> None:
    """Execute the selected command."""
    if not self.filtered_commands:
        return  # No commands
    
    selected_command = self.filtered_commands[self.selected_index]
    
    # Emit execute event
    asyncio.create_task(
        self.event_bus.emit_with_hooks(
            EventType.COMMAND_MENU_EXECUTE,
            {
                "command": selected_command,
                "command_def": selected_command
            },
            source="command_mode_handler"
        )
    )
    
    # Execute command handler
    asyncio.create_task(
        self._run_command_handler(selected_command)
    )
    
    # Exit command mode
    asyncio.create_task(self.exit_command_mode())

async def _run_command_handler(self, command: CommandDefinition) -> None:
    """Run command handler.
    
    Args:
        command: Command to execute
    """
    try:
        # Execute command
        result = await command.handler(SlashCommand(
            name=command.name,
            args=[],
            raw_input=self.command_buffer
        ))
        
        # Emit completion event
        await self.event_bus.emit_with_hooks(
            EventType.COMMAND_MENU_COMPLETE,
            {
                "command": command,
                "result": result
            },
            source="command_mode_handler"
        )
        
        # Display result if needed
        if result.display_type not in ["modal", "none"]:
            await self.event_bus.emit_with_hooks(
                EventType.COMMAND_OUTPUT_DISPLAY,
                {
                    "message": result.message,
                    "display_type": result.display_type,
                    "success": result.success
                },
                source="command_mode_handler"
            )
    
    except Exception as e:
        # Emit error event
        await self.event_bus.emit_with_hooks(
            EventType.COMMAND_MENU_ERROR,
            {
                "command": command,
                "error_type": "handler_error",
                "result": CommandResult(
                    success=False,
                    message=f"Error: {str(e)}",
                    display_type="error"
                )
            },
            source="command_mode_handler"
        )
```

---

## ModalController

**File**: `kollabor/ui/modal_controller.py`

The ModalController manages all modal types (overlay, status, live).

### Modal Lifecycle

```python
class ModalController:
    """Manages modal lifecycle for all modal types."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.in_modal_mode = False
        self.current_modal = None
        self.modal_history = []
    
    async def enter_modal_mode(
        self,
        modal_type: str,
        **kwargs
    ) -> None:
        """Enter modal mode.
        
        Args:
            modal_type: Type of modal ("overlay", "status", "live")
            **kwargs: Modal-specific configuration
        """
        if self.in_modal_mode:
            # Push to history for nesting
            self.modal_history.append(self.current_modal)
        
        self.in_modal_mode = True
        
        # Create appropriate modal
        if modal_type == "overlay":
            self.current_modal = OverlayModal(**kwargs)
        elif modal_type == "status":
            self.current_modal = StatusModal(**kwargs)
        elif modal_type == "live":
            self.current_modal = LiveModal(**kwargs)
        else:
            logger.error(f"Unknown modal type: {modal_type}")
            return
        
        # Pause rendering
        await self.display_controller.pause_rendering()
        
        # Show modal
        await self.current_modal.show()
    
    async def exit_modal_mode(self) -> None:
        """Exit modal mode."""
        if not self.in_modal_mode:
            return  # Not in modal mode
        
        # Hide current modal
        if self.current_modal:
            await self.current_modal.hide()
        
        # Resume rendering
        await self.display_controller.resume_rendering()
        
        # Restore previous modal if any
        if self.modal_history:
            self.current_modal = self.modal_history.pop()
        else:
            self.in_modal_mode = False
            self.current_modal = None
```

### Modal Key Handling

```python
def _handle_modal_key(self, key: str) -> None:
    """Handle key press in modal mode.
    
    Args:
        key: Key pressed
    """
    if not self.current_modal:
        return  # No modal
    
    # Delegate to current modal
    handled = await self.current_modal.handle_key(key)
    
    if not handled:
        # Modal didn't handle key, check for Escape
        if key == "Escape":
            asyncio.create_task(self.exit_modal_mode())
```

---

## HookRegistrar

**File**: `kollabor/io/input/hook_registrar.py`

The HookRegistrar manages input-related event hook registration.

### Hook Registration

```python
class HookRegistrar:
    """Registers input-related event hooks."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
    
    async def register_hooks(self) -> None:
        """Register all input-related hooks."""
        
        # KEY_PRESS hook
        await self.event_bus.register_hook(
            Hook(
                name="log_key_press",
                plugin_name="input_system",
                event_type=EventType.KEY_PRESS,
                priority=HookPriority.POSTPROCESSING.value,
                callback=self._log_key_press,
                timeout=10,
                error_action="continue"
            )
        )
        
        # USER_INPUT hook
        await self.event_bus.register_hook(
            Hook(
                name="validate_user_input",
                plugin_name="input_system",
                event_type=EventType.USER_INPUT_PRE,
                priority=HookPriority.PREPROCESSING.value,
                callback=self._validate_user_input,
                timeout=30,
                error_action="stop"  # Cancel event on validation failure
            )
        )
        
        # INPUT_RENDER hook
        await self.event_bus.register_hook(
            Hook(
                name="format_input_render",
                plugin_name="input_system",
                event_type=EventType.INPUT_RENDER,
                priority=HookPriority.DISPLAY.value,
                callback=self._format_input_render,
                timeout=10,
                error_action="continue"
            )
        )
```

### Hook Callbacks

```python
async def _log_key_press(
    self,
    data: Dict[str, Any],
    event: Event
) -> Dict[str, Any]:
    """Log key press events.
    
    Args:
        data: Event data
        event: Event object
        
    Returns:
        Hook result
    """
    key = data.get('key', '')
    logger.debug(f"Key press: {key}")
    return {"logged": True}

async def _validate_user_input(
    self,
    data: Dict[str, Any],
    event: Event
) -> Dict[str, Any]:
    """Validate user input before processing.
    
    Args:
        data: Event data
        event: Event object
        
    Returns:
        Hook result (can cancel event)
    """
    message = data.get('message', '')
    
    # Validate message length
    max_length = self.config.get('input.max_message_length', 10000)
    if len(message) > max_length:
        event.cancelled = True
        return {
            "cancelled": True,
            "reason": "message_too_long"
        }
    
    return {"validated": True}

async def _format_input_render(
    self,
    data: Dict[str, Any],
    event: Event
) -> Dict[str, Any]:
    """Format input for rendering.
    
    Args:
        data: Event data
        event: Event object
        
    Returns:
        Hook result with formatted lines
    """
    input_buffer = data.get('input_buffer', '')
    
    # Format input line
    formatted_line = f"> {input_buffer}"
    
    return {
        "fancy_input_lines": [formatted_line]
    }
```

---

## DisplayController

**File**: `kollabor/io/input/display_controller.py`

The DisplayController manages terminal rendering pause/resume and display updates.

### Rendering Control

```python
class DisplayController:
    """Controls terminal rendering pause/resume."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.rendering_paused = False
        self.render_lock = asyncio.Lock()
    
    async def pause_rendering(self) -> None:
        """Pause terminal rendering.
        
        Used during modal display or external viewer.
        """
        async with self.render_lock:
            if self.rendering_paused:
                return  # Already paused
            
            self.rendering_paused = True
            
            # Stop render loop
            await self.renderer.stop_render_loop()
            
            logger.debug("Rendering paused")
    
    async def resume_rendering(self) -> None:
        """Resume terminal rendering.
        
        Called after modal closed or external viewer exited.
        """
        async with self.render_lock:
            if not self.rendering_paused:
                return  # Not paused
            
            self.rendering_paused = False
            
            # Start render loop
            await self.renderer.start_render_loop()
            
            logger.debug("Rendering resumed")
    
    async def force_display_update(self) -> None:
        """Force immediate display update.
        
        Bypasses render loop for immediate update.
        """
        # Clear dirty regions to force re-render
        self.renderer.invalidate_cache()
        
        # Force single frame render
        await self.renderer.render_frame(force_render=True)
```

---

## PasteProcessor

**File**: `kollabor/io/input/paste_processor.py`

The PasteProcessor detects paste events and generates placeholders.

### Paste Detection

```python
class PasteProcessor:
    """Detects and processes paste events."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
        
        self.paste_threshold = 0.01  # 10ms between keys
        self.min_paste_length = 5  # Minimum characters
        self.last_key_time = 0
        self.paste_buffer = []
    
    async def handle_paste(self, paste_content: str) -> None:
        """Handle detected paste.
        
        Args:
            paste_content: Pasted content
        """
        # Check minimum length
        if len(paste_content) < self.min_paste_length:
            # Treat as individual keys
            for char in paste_content:
                self.input_handler.handle_key_press(char)
            return
        
        # Emit paste event
        await self.event_bus.emit_with_hooks(
            EventType.PASTE_DETECTED,
            {
                "content": paste_content,
                "length": len(paste_content)
            },
            source="paste_processor"
        )
        
        # Generate placeholder
        placeholder = self._generate_placeholder(paste_content)
        
        # Insert placeholder into buffer
        self.input_handler.buffer_manager.insert_placeholder(placeholder)
        
        # Store paste content for expansion
        self._store_paste_content(placeholder, paste_content)
```

### Placeholder Generation

```python
def _generate_placeholder(self, content: str) -> str:
    """Generate placeholder for paste content.
    
    Args:
        content: Content to generate placeholder for
        
    Returns:
        Placeholder string (e.g., "<paste:abc123>")
    """
    import hashlib
    
    # Generate short hash
    content_hash = hashlib.md5(content.encode()).hexdigest()[:6]
    
    # Get content type hint
    content_type = self._detect_content_type(content)
    
    # Generate placeholder
    placeholder = f"<paste:{content_type}:{content_hash}>"
    
    return placeholder

def _detect_content_type(self, content: str) -> str:
    """Detect content type for placeholder.
    
    Args:
        content: Content to analyze
        
    Returns:
        Content type string
    """
    if content.startswith('http://') or content.startswith('https://'):
        return "url"
    elif '\n' in content:
        return "multiline"
    elif content.startswith('/'):
        return "command"
    else:
        return "text"
```

---

## StatusModalRenderer

**File**: `kollabor/io/input/status_modal_renderer.py`

The StatusModalRenderer generates status modal line content.

### Status Line Generation

```python
class StatusModalRenderer:
    """Generates status modal line content."""
    
    def __init__(
        self,
        input_handler: InputHandler,
        event_bus: EventBus,
        renderer: TerminalRenderer,
        config: ConfigManager
    ):
        self.input_handler = input_handler
        self.event_bus = event_bus
        self.renderer = renderer
        self.config = config
    
    def get_status_modal_lines(self) -> List[str]:
        """Get status modal lines.
        
        Returns:
            List of status lines to display in modal
        """
        lines = []
        
        # Add application status
        lines.append(f"Application: Running")
        
        # Add input status
        if self.input_handler.command_mode_handler.in_command_mode:
            lines.append("Command Mode: Active")
        elif self.input_handler.modal_controller.in_modal_mode:
            lines.append("Modal Mode: Active")
        else:
            lines.append("Input Mode: Normal")
        
        # Add LLM status
        llm_status = self._get_llm_status()
        lines.append(f"LLM: {llm_status}")
        
        # Add plugin status
        plugin_status = self._get_plugin_status()
        lines.append(f"Plugins: {plugin_status}")
        
        return lines
    
    def _get_llm_status(self) -> str:
        """Get LLM status."""
        # Check if LLM is processing
        if self.renderer.thinking:
            return "Processing..."
        else:
            return "Idle"
    
    def _get_plugin_status(self) -> str:
        """Get plugin status."""
        # Get plugin statistics
        stats = self.event_bus.get_hook_status()
        
        total = stats.get('total_hooks', 0)
        active = stats.get('status_counts', {}).get('working', 0)
        
        return f"{active}/{total} active"
```

---

## Complete Input Flow

### From Key Press to Event

```mermaid
sequenceDiagram
    participant K as Keyboard
    participant ILM as InputLoopManager
    participant KPH as KeyPressHandler
    participant EB as Event Bus
    participant Cmd as CommandModeHandler
    participant MC as ModalController
    
    K->>ILM: Key press
    ILM->>ILM: Detect paste?
    alt Paste detected
        ILM->>PP: handle_paste()
        PP-->>ILM: placeholder
        ILM->>KPH: handle_key(placeholder)
    else Normal key
        ILM->>KPH: handle_key_press(key)
    end
    
    KPH->>KPH: Check special keys?
    alt Enter
        KPH->>KPH: Get buffer content
        KPH->>EB: emit USER_INPUT
    else Escape
        KPH->>MC: exit_modal_mode()
        KPH->>Cmd: exit_command_mode()
        KPH->>EB: emit CANCEL_REQUEST
    else Regular
        KPH->>KPH: In command mode?
        alt Yes
            KPH->>Cmd: handle_key(key)
        else In modal?
            alt Yes
                KPH->>MC: handle_key(key)
            else Normal input
                KPH->>KPH: Add to buffer
                KPH->>EB: emit INPUT_RENDER
        end
    end
```

### Command Mode Flow

```mermaid
sequenceDiagram
    participant K as Keyboard
    participant Cmd as CommandModeHandler
    participant EB as Event Bus
    participant CR as CommandRegistry
    
    K->>Cmd: Type "/"
    Cmd->>Cmd: enter_command_mode()
    Cmd->>EB: emit COMMAND_MENU_SHOW
    
    K->>Cmd: Type filter text
    Cmd->>Cmd: filter_commands(text)
    Cmd->>Cmd: Trigger render
    
    K->>Cmd: Navigate menu (Up/Down)
    Cmd->>Cmd: Update selection
    Cmd->>Cmd: Trigger render
    
    K->>Cmd: Press Enter
    Cmd->>Cmd: execute_selected_command()
    Cmd->>EB: emit COMMAND_MENU_EXECUTE
    Cmd->>CR: Run handler
    Cmd->>EB: emit COMMAND_COMPLETE
    
    K->>Cmd: Press Escape
    Cmd->>Cmd: exit_command_mode()
    Cmd->>EB: emit COMMAND_MENU_HIDE
```

---

## Examples

### Example 1: Creating Custom Key Handler

```python
class MyPlugin:
    """Plugin with custom key handling."""
    
    async def register_hooks(self):
        """Register key press hook."""
        await self.event_bus.register_hook(
            Hook(
                name="custom_key_handler",
                plugin_name=self.name,
                event_type=EventType.KEY_PRESS,
                priority=HookPriority.PREPROCESSING.value,
                callback=self._handle_custom_key,
                timeout=10
            )
        )
    
    async def _handle_custom_key(
        self,
        data: Dict[str, Any],
        event: Event
    ) -> Dict[str, Any]:
        """Handle custom key press."""
        key = data.get('key', '')
        
        # Handle custom key combination
        if key == "Ctrl+P":
            # Custom action
            await self._do_custom_action()
            return {"handled": True}
        
        return {"handled": False}
```

### Example 2: Custom Command

```python
async def initialize(self, event_bus, config, **kwargs):
    """Initialize and register custom command."""
    command_registry = kwargs.get('command_registry')
    
    command = CommandDefinition(
        name="mycommand",
        description="My custom command",
        handler=self._handle_mycommand,
        plugin_name=self.name,
        category=CommandCategory.CUSTOM,
        subcommands=[
            SubcommandInfo("list", "", "List items"),
            SubcommandInfo("add", "<item>", "Add an item")
        ]
    )
    
    command_registry.register_command(command)

async def _handle_mycommand(self, command: SlashCommand) -> CommandResult:
    """Handle my custom command."""
    args = command.args
    
    if not args:
        return CommandResult(
            success=True,
            message="Use: /mycommand list|add <item>",
            display_type="info"
        )
    
    subcommand = args[0].lower()
    
    if subcommand == "list":
        return await self._list_items()
    elif subcommand == "add":
        return await self._add_item(args[1:])
    else:
        return CommandResult(
            success=False,
            message=f"Unknown subcommand: {subcommand}",
            display_type="error"
        )
```

### Example 3: Custom Modal

```python
async def _show_custom_modal(self) -> None:
    """Show custom modal."""
    await self.modal_controller.enter_modal_mode(
        "overlay",
        title="Custom Modal",
        content="This is a custom modal",
        width=60,
        height=10,
        actions=[
            {"key": "Enter", "label": "OK", "action": "ok"},
            {"key": "Escape", "label": "Cancel", "action": "cancel"}
        ]
    )
```

---

## Troubleshooting

### Issue: Key Presses Not Registering

**Symptoms**: Typing shows no response

**Diagnosis**:

```python
# Check input loop status
print(f"Input loop running: {self.input_loop_manager.running}")

# Check platform I/O
print(f"Platform I/O: {self.input_loop_manager.platform_io}")

# Check event bus hooks
status = self.event_bus.get_hook_status()
print(f"Key press hooks: {status.get('key_press', {})}")
```

**Solutions**:
1. Verify input loop is running
2. Check platform I/O initialization
3. Verify terminal is in raw mode
4. Check for blocked hooks

### Issue: Command Mode Not Working

**Symptoms**: Typing `/` doesn't enter command mode

**Diagnosis**:

```python
# Check command mode status
print(f"Command mode: {self.command_mode_handler.in_command_mode}")
print(f"Available commands: {len(self.command_mode_handler.available_commands)}")

# Check key press handler
print(f"Key press handler: {self.key_press_handler}")
```

**Solutions**:
1. Verify command registry is loaded
2. Check command definitions are valid
3. Verify key press handler is receiving `/` key
4. Check for conflicting hooks

### Issue: Paste Detection Not Working

**Symptoms**: Paste treated as individual characters

**Diagnosis**:

```python
# Check paste threshold
print(f"Paste threshold: {self.paste_processor.paste_threshold}")

# Check platform I/O timing
import time
start = time.time()
# ... simulate key press ...
elapsed = time.time() - start
print(f"Time between keys: {elapsed}s")
```

**Solutions**:
1. Adjust paste threshold in config
2. Verify platform I/O is timing key presses correctly
3. Check minimum paste length setting

### Issue: Modal Not Showing

**Symptoms**: enter_modal_mode() called but modal doesn't appear

**Diagnosis**:

```python
# Check modal status
print(f"In modal mode: {self.modal_controller.in_modal_mode}")
print(f"Current modal: {self.modal_controller.current_modal}")

# Check display controller
print(f"Rendering paused: {self.display_controller.rendering_paused}")

# Check modal renderer
print(f"Modal renderer: {self.renderer.modal_renderer}")
```

**Solutions**:
1. Verify rendering is paused
2. Check modal renderer is initialized
3. Verify modal type is supported
4. Check for exceptions in modal.show()

---

## Related Documents

- **[Terminal Rendering System](./terminal-rendering.md)** - Terminal display and formatting
- **[Event System Architecture](../architecture/event-system.md)** - Event bus and hooks
- **[Plugin System Architecture](./plugin-system.md)** - Plugin lifecycle and discovery
- **[Paste Detection System](../reference/paste-detection-system.md)** - Paste detection details
- **[Modal System Guide](../reference/modal-system-guide.md)** - Modal rendering system

---

## References

**Source Files**:
- `kollabor/io/input/input_handler.py` - Main input handler facade
- `kollabor/io/input/input_loop_manager.py` - Main input loop
- `kollabor/io/input/key_press_handler.py` - Key press processing
- `kollabor/io/input/command_mode_handler.py` - Command mode management
- `kollabor/ui/modal_controller.py` - Modal lifecycle
- `kollabor/io/input/hook_registrar.py` - Hook registration
- `kollabor/io/input/display_controller.py` - Display control
- `kollabor/io/input/paste_processor.py` - Paste detection
- `kollabor/io/input/status_modal_renderer.py` - Status modal

**Related Specifications**:
- `docs/specs/documentation-overhaul-spec.md` - Documentation specification

**External Resources**:
- [ANSI Escape Codes](https://en.wikipedia.org/wiki/ANSI_escape_code) - Terminal control
- [TTY Raw Mode](https://en.wikipedia.org/wiki/POSIX_terminal_interface) - Raw mode details

---

*Last Updated: 2025-01-15*  
*Status: Review - Ready for technical review*  
*Version: 1.0.0*
