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

The Ego-Lite keyboard driver in 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:

  • 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 (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
// 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:

  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
// 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

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

Pattern 2: Type into focused element

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 (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
// 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 (lines 741–746), where they're mounted under the keyboard namespace:

// 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.


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) 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.

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 →