How to Handle Keyboard Input and Special Key Presses in ego-browser

TLDR: ego-browser exposes a Playwright-style keyboard API via src/driver/keyboard.ts that abstracts Chrome DevTools Protocol (CDP) events into simple methods like press(), type(), and fill(), while automatically handling special keys, modifier combinations, and OS-specific shortcuts.

To handle keyboard input and special key presses in ego-browser, the citrolabs/ego-lite repository provides a dedicated driver that translates high-level commands into Chrome DevTools Protocol (CDP) events. Located in src/driver/keyboard.ts, this module manages virtual-key codes, modifier states, and event dispatching, enabling precise simulation of user typing, shortcuts, and form interactions.

Keyboard Driver Architecture and Special Key Definitions

The keyboard subsystem centers on a symbolic key map and modifier tracking system defined in the core driver file.

The KEYS Map and Virtual Codes

At the heart of src/driver/keyboard.ts lies the KEYS constant, which defines metadata for special keys:

const KEYS = {
  Enter: { vk: 13, key: "Enter", code: "Enter", text: "\r" },
  Tab:   { vk: 9,  key: "Tab",   code: "Tab",   text: "\t" },
  ArrowLeft: { vk: 37, key: "ArrowLeft", code: "ArrowLeft", text: "" },
  // ...
};

Each entry provides the virtual-key code (vk), DOM key identifier, code attribute, and text output. This structure allows the driver to construct accurate CDP events for both printable characters and non-printable navigation keys.

Parsing Modifier Combinations

The helper function parseKeyCombo() splits combination strings such as "Control+a" or "Shift+Tab" into base keys and modifier flags. The driver supports the special "ControlOrMeta" pseudo-modifier, which automatically selects the appropriate flag based on the host operating system—Control for Linux/Windows, Meta for macOS.

A Set named pressedModifiers tracks actively held modifier keys. The internal method activeModifierBits() converts this set into a bit-field representing Control, Shift, Alt, and Meta states, ensuring subsequent events include the correct modifier context.

Dispatching Keyboard Events via CDP

The driver exposes low-level methods that map directly to CDP keyboard events, providing granular control over key state while abstracting transport details.

Low-Level Key State Methods

  • down(keyCombo): Sends a keyDown event via dispatchKeyEvent() and records any modifier key in pressedModifiers.
  • up(keyCombo): Sends a keyUp event and removes the modifier from the active set.
  • press(keyCombo, opts): Executes a complete key press sequence (down followed by up) with an optional delay between actions.

These methods rely on keyEventBase() to construct the common event payload containing the key name, code, virtual-key code, and current modifier bits obtained from activeModifierBits().

CDP Event Forwarding

All keyboard methods ultimately call dispatchKeyEvent(), which forwards the constructed event to the browser through the browserCdp connection provided by src/browser-runtime.ts. This centralized dispatch ensures consistent event formatting and transport handling across the automation layer.

Automating Text Input and Form Interaction

Beyond individual key events, the driver provides convenience methods for common text input scenarios, implemented in src/driver/keyboard.ts and exposed through src/helpers.ts.

Typing Sequences with Modifier Support

The type(text, opts) method iterates through a string character-by-character, automatically determining virtual-key codes for printable characters and injecting the current modifier bits from activeModifierBits(). This enables natural text entry without manually managing key-down and key-up states.

Filling Inputs and Selecting Options

  • fill(selector, value, opts): Focuses the element matching the CSS selector, optionally clears its current value, and types the new text. This composite operation combines focus, clear, and type actions into a single call.
  • select(selector, option, opts): Opens a <select> element and chooses an option by label, value, index, or visible text, simulating the necessary keyboard navigation to reach the target option.

Code Examples

The following patterns demonstrate practical usage according to the citrolabs/ego-lite source code:

// Press a simple special key
await keyboard.press('Enter');

// Execute a modifier shortcut (Ctrl+A)
await keyboard.press('Control+a');

// Hold Shift while typing uppercase text
await keyboard.down('Shift');
await keyboard.type('hello world');
await keyboard.up('Shift');

// Fill a search input and submit
await keyboard.fill('#search', 'ego-browser');
await keyboard.press('Enter');

// Select an option from a dropdown by visible text
await keyboard.select('select#language', 'JavaScript');

Testing and Integration

The keyboard implementation is validated by src/driver/keyboard.test.mjs, which contains unit tests verifying the behavior of down, up, press, type, fill, and select methods. For agent scripts, these methods are injected into the public helper surface defined in src/helpers.ts, making them accessible throughout the browser automation context while leveraging the browserCdp transport from src/browser-runtime.ts.

Summary

  • ego-browser handles keyboard input through a dedicated driver in src/driver/keyboard.ts that abstracts CDP complexity into a Playwright-style API.
  • The KEYS map defines virtual-key codes and DOM properties for special keys like Enter, Tab, and ArrowLeft.
  • Modifier combinations are parsed by parseKeyCombo() and tracked via the pressedModifiers Set, with support for the OS-aware "ControlOrMeta" pseudo-modifier.
  • Low-level methods (down, up, press) provide granular control, while high-level helpers (type, fill, select) automate common workflows.
  • All events route through dispatchKeyEvent() using the browserCdp connection from src/browser-runtime.ts.

Frequently Asked Questions

How does ego-browser handle special keys like Enter or Tab?

Special keys are defined in the KEYS constant within src/driver/keyboard.ts, which maps symbolic names to virtual-key codes, DOM key identifiers, and text values. When you call keyboard.press('Enter'), the driver looks up the key definition and constructs a Chrome DevTools Protocol event with the correct metadata, then dispatches it via dispatchKeyEvent().

Can I use keyboard shortcuts with modifiers like Ctrl+A or Command+C?

Yes. The parseKeyCombo() function supports modifier strings such as "Control+a" or "ControlOrMeta+c". The latter automatically chooses between Control (Linux/Windows) and Meta/Mac (macOS) based on the host operating system. Modifiers are tracked in a pressedModifiers Set to ensure subsequent events carry the correct modifier bits.

What is the difference between type() and fill() in ego-browser?

The type() method sends individual key events for each character in a string, respecting currently held modifiers like Shift. The fill() method is a higher-level helper that focuses an element, optionally clears its existing value, and then types new text. Use fill() for complete form field replacement and type() when you need to simulate realistic keystroke timing or modifier interactions.

Where are the keyboard methods exposed for agent scripts?

While the core implementation lives in src/driver/keyboard.ts, the public API is surfaced through src/helpers.ts. This integration allows agent scripts to access keyboard.press(), keyboard.fill(), and other methods directly. The underlying transport uses browserCdp from src/browser-runtime.ts to communicate with the Chrome DevTools Protocol.

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 →