How ego-browser Manages Keyboard Input, Form Filling, and the selectOption Function

Ego-browser synthesizes Chrome DevTools Protocol (CDP) keyboard events through its keyboard.ts driver to simulate real user typing, while selectOption manipulates DOM properties directly and dispatches native change events.

Ego-browser, part of the citrolabs/ego-lite repository, provides a robust automation layer for web interaction through synthetic keyboard events. Understanding how ego-browser handles keyboard input, form filling, and the selectOption function reveals the precise architecture behind its reliable form automation capabilities. The implementation spans multiple TypeScript modules that coordinate CDP commands with DOM manipulation to replicate authentic user behavior.

Core Architecture of ego-browser Keyboard Input

The keyboard subsystem delegates high-level commands to low-level CDP events through four interconnected components.

The Keyboard Driver (src/driver/keyboard.ts)

The keyboard.ts file defines the KEYS map (lines 22-41) that translates special keys like Enter (vk: 13) and Tab (vk: 9) into virtual-key codes, and implements the core event dispatching logic. It maintains a pressedModifiers Set (line 100) to track Shift, Control, Alt, and Meta states across keystrokes, enabling complex shortcuts like Control+A.

Helper Functions (src/helpers.ts)

User-facing methods like fill, press, and selectOption reside in src/helpers.ts, providing a simplified API that agents consume directly. These helpers delegate to the keyboard driver while handling selector resolution through withHandle and normalizing input parameters before sending commands to the browser.

CDP Integration Layer (src/browser-runtime.ts)

The driver sends synthetic events via browserCdp.send('Input.dispatchKeyEvent', ...), transforming JavaScript key definitions into browser-native input events that trigger appropriate DOM listeners. This layer bridges the gap between Node.js automation scripts and the Chrome DevTools Protocol.

How ego-browser Handles Form Filling

The fill method implements a three-step workflow to simulate authentic user input, ensuring fields are properly focused and modified before text entry begins.

The Three-Step Workflow

First, withHandle(selector) in src/driver/element-ops.ts resolves the target element to a JSHandle. Second, if clearFirst is enabled in FillOptions, the driver sends Control+A followed by Delete to empty the field. Third, pressSequentially iterates through each character, dispatching keydown, optional keypress, and keyup events via CDP.

Typing Delays and Editing Commands

The driver respects INPUT_EVENT_DELAY_MS (default 25ms) between keystrokes, configurable through PressSequentiallyOptions.delay. The editingCommandsForKey function (lines 64-78) detects shortcuts like Ctrl+A and translates them into appropriate DOM editing actions such as selectAll and deleteForward.

// Example: Fill with field clearing and custom delay
await fill('#email', 'user@example.com', { 
  clearFirst: true,
  delay: 50  // 50ms between keystrokes
});

The selectOption Function Implementation

Located in src/driver/keyboard.ts, selectOption handles <select> elements and ARIA comboboxes without simulating individual keystrokes, instead manipulating the DOM directly for reliability.

Option Matching Logic

The function accepts SelectOption values, which can be a string (matching option value), number (treated as index), or an object with value, label, or index properties. It evaluates a script in the page context to match options against element.options, supporting multiple selection strategies:

type SelectOption =
  | string
  | number
  | { value?: string; label?: string; index?: number };

Change Event Dispatching

After setting opt.selected = true for matched options, the function dispatches a bubbling change event via element.dispatchEvent(new Event('change', { bubbles: true })). This ensures React, Vue, and vanilla JavaScript listeners trigger correctly, mimicking genuine user interaction with dropdown menus.

// Select by label, value, and index simultaneously
await selectOption('#country', [
  { label: 'Canada' },
  { value: 'usa' },
  5  // sixth option in the list
]);

Practical Implementation Examples

Basic Text Input with Special Keys

// Fill and submit a form
await fill('#username', 'alice', { clearFirst: true });
await keyboard.type('#password', 's3cr3t');
await keyboard.press('#password', 'Enter');

Multi-Select Dropdown Handling

When targeting multi-select elements, pass an array to select multiple options at once. The driver sets each option's selected property and fires a single change event after processing all selections.

// Select multiple colors from a multi-select dropdown
await selectOption('#colors', [
  'red',           // matches option value
  { label: 'Blue' },
  2                // third option by index
]);

Direct Keyboard Control

For complex interactions, access the keyboard driver directly to manage focus and modifiers:

// Tab navigation between fields
await keyboard.press('#field1', 'Tab');
await keyboard.type('#field2', 'text with\nnewlines');

Summary

  • ego-browser synthesizes keyboard events through CDP Input.dispatchKeyEvent calls in src/driver/keyboard.ts, mapping characters to virtual-key codes with proper modifier tracking
  • The form filling workflow supports optional field clearing via FillOptions.clearFirst and configurable typing delays via PressSequentiallyOptions
  • selectOption manipulates DOM properties directly in src/driver/keyboard.ts and dispatches native change events rather than simulating keystrokes, supporting value, label, and index matching strategies
  • All keyboard actions resolve selectors through withHandle in src/driver/element-ops.ts before interacting with elements, ensuring stable element references

Frequently Asked Questions

How does ego-browser simulate typing delays between keystrokes?

The driver uses INPUT_EVENT_DELAY_MS (25ms default) in pressSequentially, configurable via PressSequentiallyOptions.delay. This creates realistic timing between keydown, keypress, and keyup CDP events sent through browserCdp.send('Input.dispatchKeyEvent', ...).

Can selectOption handle multi-select elements?

Yes. When passed an array of values, selectOption sets selected = true on each matching option and fires a single change event after all selections are applied, supporting standard HTML multiple-select fields in src/driver/keyboard.ts.

What CDP command does ego-browser use for keyboard events?

The keyboard driver calls browserCdp.send('Input.dispatchKeyEvent', ...) defined in src/browser-runtime.ts, sending structured key event data including virtual key codes, key identifiers, and modifier states to the browser.

How does the fill command clear existing text?

When clearFirst: true is passed in FillOptions, the driver sends a Control+A shortcut followed by Delete via editingCommandsForKey (lines 64-78), selecting all text and removing it before typing the new value.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →