# How the Ego-Lite Keyboard Driver Handles `fill`, `press`, `pressSequentially`, and `selectOption`

> Explore how the Ego-Lite keyboard driver simplifies form interactions with fill, press, pressSequentially, and selectOption. Learn about its Playwright-style helpers and CDP command integration.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-02

---

**The Ego-Lite keyboard driver in [`package/ego-browser/src/driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/keyboard.ts) provides Playwright-style form interaction helpers that wrap Chrome DevTools Protocol (CDP) commands with synthetic DOM event fallbacks.**

This article examines the implementation of four core keyboard methods in the **ego-lite** browser automation framework. The driver balances reliability (via CDP's `Input.dispatchKeyEvent`) with graceful degradation (via injected JavaScript probes), delivering a robust API for text entry, keystrokes, and dropdown selection.

---

## Keyboard Driver Architecture

The keyboard driver follows a layered design with shared utilities at the base and high-level helpers built on top.

### Key Definitions and Modifier Handling

Core constants are defined early in [`keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/keyboard.ts):

- **`KEYS`** (lines 22–40): Maps semantic names like `Enter`, `Backspace`, and `Tab` to their corresponding key values
- **`MODIFIER_BITS`** (lines 80–85): Assigns bit flags to `Alt`, `Control`, `Meta`, and `Shift`
- **`pressedModifiers`** (lines 98–99): A runtime `Set` tracking which modifiers are currently active

The `parseKeyCombo()` function (lines 100–130) splits strings like `"Ctrl+Shift+A"` into a base key and a numeric modifier mask. When dispatching events, `activeModifierBits()` combines this parsed mask with any currently-pressed modifiers.

### Low-Level CDP Integration

Two mechanisms ensure keystrokes reach the browser:

- **`dispatchKeyEvent()`** (lines 30–37): Sends raw `Input.dispatchKeyEvent` CDP commands with configurable timeout
- **Key probe system** (`installKeyProbe()` at lines 39–66, `finishKeyProbe()` at lines 67–77): Injects a synthetic `keydown` listener to detect when CDP events fail to register, triggering fallback DOM events

---

## `fill(selector, value, options)`: Rapid Form Population

The `fill` method provides the fastest way to replace an input's contents. In [`package/ego-browser/src/driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/keyboard.ts) (lines 82–123), it executes five sequential operations:

1. **Element resolution**: Optionally waits for `selector` via `waitForSelector`
2. **Focus**: Calls `focus(selector)` using `Runtime.callFunctionOn`
3. **Clear existing content** (when `clearFirst` is true): Detects content-editable or `<input>` elements, clears them, and fires `input` with `deleteContentBackward`
4. **Text insertion**: Uses `Input.insertText` CDP command for immediate population
5. **Event completion**: Emits final `input` and `change` events to trigger form validation

```javascript
// Fill an input, clearing it first (default behavior)
await page.fill('#email', 'user@example.com');

```

The `clearFirst` option defaults to `true`, making `fill` behave like a complete replacement operation rather than an append.

---

## `press(keyCombo)`: Single Keystroke Dispatch

The `press` method handles individual keys and modifier combinations. Implementation spans lines 102–144 in [`keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/keyboard.ts):

1. **Parse and build**: `parseKeyCombo()` extracts base key and modifiers; constructs key-definition object
2. **Editing commands**: `editingCommandsForKey()` (lines 64–77) maps keys to browser editing actions (`selectAll`, `deleteBackward`, `deleteForward`)
3. **Event sequence**: Dispatches `keydown` (with optional `text` and `commands`), pauses for `INPUT_EVENT_DELAY_MS`, then dispatches `keyup`
4. **Verification**: Uses probe logic (lines 136–144); if the key goes unobserved, falls back to synthetic DOM events

```javascript
// Press Ctrl+A to select all text
await page.keyboard.press('ControlOrMeta+a');

```

The `ControlOrMeta` alias automatically selects `Control` on Linux/Windows and `Meta` on macOS, ensuring cross-platform scripts work without modification.

---

## `pressSequentially(selectorOrText, textOrOptions?, options?)`: Character-by-Character Typing

The `pressSequentially` method simulates realistic user typing with configurable delays. Located at lines 42–56, it supports two calling conventions:

**Pattern 1: Target element explicitly**

```javascript
await page.keyboard.pressSequentially('#search', 'hello world');

```

**Pattern 2: Type into focused element**

```javascript
await page.keyboard.pressSequentially('hello world', { delay: 50 });

```

Implementation details:

- `focusWithTimeout()` ensures the target element is active before typing begins
- Iterates the string, calling `press(char)` for each character
- Optionally awaits `state.sleep(delay)` between keystrokes when `delay` option is provided

The delay parameter mimics human typing patterns, useful for triggering JavaScript auto-complete or validation that responds to individual keystrokes.

---

## `selectOption(selector, values)`: Dropdown Manipulation

The `selectOption` method programmatically controls `<select>` elements. In [`keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/keyboard.ts) (lines 33–57), it:

1. Resolves the `<select>` element and executes a script via `Runtime.callFunctionOn`
2. Normalizes the `values` argument—accepts single strings, arrays, or objects with `value`, `label`, or `index` properties
3. Clears existing selections and marks matching `<option>` elements as selected
4. Fires `input` and `change` events to notify JavaScript listeners
5. Returns an array of selected option values

```javascript
// Select multiple options using mixed value types
await page.selectOption('#countries', [
  'us',
  { label: 'Canada' },
  { index: 3 }
]);

```

The flexible value format allows scripts to target options by their underlying value, visible label, or positional index.

---

## Integration with User-Facing API

These driver methods are exposed to automation scripts through [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) (lines 741–746), where they're mounted under the `keyboard` namespace:

```javascript
// helpers.ts re-exports
keyboard: {
  press: keyboard.press.bind(keyboard),
  pressSequentially: keyboard.pressSequentially.bind(keyboard),
  // ... plus page-level shortcuts
}

```

The `page.fill()` and `page.selectOption()` shortcuts delegate directly to the driver methods while handling additional element-waiting logic.

---

## Summary

- **`fill`** combines CDP's `insertText` with element clearing and event emission for complete input replacement
- **`press`** parses key combinations, dispatches CDP events with editing commands, and falls back to synthetic events via the probe system
- **`pressSequentially`** types character-by-character with optional delays, supporting both targeted and focused-element modes
- **`selectOption`** executes JavaScript in the browser to manipulate `<select>` elements and trigger change notifications

All four methods share the driver's core infrastructure: modifier tracking, CDP command dispatch, and the key-probe fallback mechanism implemented in [`package/ego-browser/src/driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/keyboard.ts).

---

## Frequently Asked Questions

### What is the difference between `fill` and `pressSequentially` in ego-lite?

**`fill` replaces content immediately using CDP's `insertText`**—no individual keystrokes are simulated, making it faster but less realistic. **`pressSequentially` types character-by-character** with optional delays, triggering JavaScript event handlers that respond to each keystroke. Use `fill` for speed; use `pressSequentially` when page behavior depends on key event sequences.

### How does ego-lite handle keyboard shortcuts across operating systems?

The driver defines **platform-agnostic aliases** like `ControlOrMeta` in `KEYS` (lines 22–40). When `press` parses a key combination, it resolves these aliases to the appropriate modifier bit (`Control` on Windows/Linux, `Meta` on macOS) via `MODIFIER_BITS` (lines 80–85). Scripts using these aliases work cross-platform without modification.

### What happens when CDP keyboard events fail to register?

The **key probe system** (`installKeyProbe` at lines 39–66) injects a temporary JavaScript `keydown` listener before dispatch. `finishKeyProbe` (lines 67–77) checks whether the listener observed the event; if not, the driver falls back to synthetic DOM events through `dispatchKeyEvent`. This dual-path approach ensures compatibility with iframe-heavy or event-intercepting pages.

### Can `selectOption` handle multi-select elements?

Yes. The `values` parameter accepts arrays, and the implementation (lines 46–57 in [`keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/keyboard.ts)) clears then repopulates selected options for both single and multiple selection modes. The method returns all currently selected values, allowing verification of the final state.