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

> Learn to handle keyboard input and special key presses in ego-browser with its Playwright-style keyboard API. Use simple methods for typing, pressing keys, and managing modifiers effortlessly.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-30

---

**TLDR:** ego-browser exposes a Playwright-style `keyboard` API via [`src/driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/keyboard.ts) lies the `KEYS` constant, which defines metadata for special keys:

```typescript
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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/keyboard.ts) and exposed through [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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:

```typescript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), making them accessible throughout the browser automation context while leveraging the `browserCdp` transport from [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts).

## Summary

- **ego-browser** handles keyboard input through a dedicated driver in [`src/driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/keyboard.ts), the public API is surfaced through [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) to communicate with the Chrome DevTools Protocol.