Terminal I/O Contract¶
This document defines Rezi's terminal input contract.
Contract path:
- Raw bytes arrive from a real PTY/ConPTY session.
- Zireael normalizes bytes into ZREV batches.
- Rezi parses those batches with
parseEventBatchV1. - App/runtime routing consumes parsed events.
All behavior in this document is covered by deterministic integration tests in:
packages/node/src/__e2e__/terminal_io_contract.e2e.test.tspackages/node/src/__e2e__/fixtures/terminal-io-contract-target.ts
Constants¶
- Key codes follow
@rezi-ui/core/keybindings/include/zr/zr_event.h. - Modifier bitmask:
SHIFT=1CTRL=2ALT=4META=8
Keyboard Contract¶
Byte-to-event mapping¶
| Input bytes | Expected parsed event(s) |
|---|---|
ESC [ 1 ; 5 A |
{ kind: "key", key: ZR_KEY_UP, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ Z |
{ kind: "key", key: ZR_KEY_TAB, mods: ZR_MOD_SHIFT, action: "down" } |
ESC [ 9 ; 5 u |
{ kind: "key", key: ZR_KEY_TAB, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 13 ; 5 u |
{ kind: "key", key: ZR_KEY_ENTER, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 127 ; 5 u |
{ kind: "key", key: ZR_KEY_BACKSPACE, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 97 ; 3 u |
Alt/Meta text policy fallback: ESC key prefix then payload ('a' text or equivalent Alt key payload event) |
ESC [ 98 ; 9 u |
Alt/Meta text policy fallback: ESC key prefix then payload ('b' text or equivalent Meta key payload event) |
ESC ambiguity/incomplete policy¶
- Incomplete supported escape prefixes are buffered.
- If a sequence is completed in a later read, it resolves as the completed key event.
- If a supported prefix remains incomplete at flush, fallback is deterministic:
- emit
ESCkey - emit remaining bytes as text scalars (example:
ESC [->ESCkey then'['text)
Bracketed Paste Contract¶
Framing¶
- Begin marker:
ESC [ 200 ~ - End marker:
ESC [ 201 ~ - Payload between markers produces one
pasteevent: { kind:"paste", bytes:<exact payload bytes> }
Missing end marker¶
- Missing end marker must not wedge input.
- Engine may finalize and emit a best-effort
pasteevent with captured bytes, or drop the incomplete paste. - Subsequent key/text input must continue normally.
Max paste size behavior¶
- Paste capture is bounded by engine paste buffer capacity.
- On overrun, the oversized paste is dropped (no truncated
pasteevent is emitted). - Input stream continues after paste end or idle flush.
Focus Contract¶
Focus in/out¶
| Input bytes | Expected parsed event |
|---|---|
ESC [ I |
{ kind:"key", key:30 /*FOCUS_IN*/, mods:0, action:"down" } |
ESC [ O |
{ kind:"key", key:31 /*FOCUS_OUT*/, mods:0, action:"down" } |
Gating¶
- Focus events are emitted when terminal capabilities report focus support.
- Rezi integration coverage asserts capability-gated suppression (
ZIREAEL_CAP_FOCUS_EVENTS=0). - Native runtime config gating (
enableFocusEvents) is implemented by Zireael platform config.
Mouse Contract (SGR)¶
| Input bytes | Expected parsed event |
|---|---|
ESC [ < 0 ; 300 ; 400 M |
{ kind:"mouse", mouseKind:3 /*down*/, x:299, y:399, buttons:1 } |
ESC [ < 0 ; 300 ; 400 m |
{ kind:"mouse", mouseKind:4 /*up*/, x:299, y:399, buttons:1 } |
ESC [ < 64 ; 400 ; 500 M |
{ kind:"mouse", mouseKind:5 /*wheel*/, x:399, y:499, wheelY:1 } |
Notes:
- SGR coordinates are 1-based on wire and normalized to 0-based in events.
- High coordinates must remain stable (no 223-column legacy clipping).
Resize Contract¶
- Engine emits an initial resize event at startup.
- Subsequent terminal size changes emit
resizeevents with latest cols/rows. - Ordering expectation:
- initial resize appears before later explicit resize updates
- observed size values match PTY resize requests
Split Reads / Partial Sequences¶
Examples (explicitly tested):
- Split complete sequence across reads:
- read #1:
ESC [ - read #2:
A - expected output: one
UPkey event, no prematureESCfallback before completion. - Split incomplete sequence flushed without completion:
- read #1:
ESC [ - no completion bytes
- expected output after flush:
ESCkey event, then'['text event. - Split paste begin/content without end marker:
- read #1:
ESC [ 200 ~ xyz - no end marker
- expected output: input remains live (and may include a best-effort paste flush).
Platform Coverage¶
- Linux/macOS: full contract suite runs through real PTY.
- Windows: ConPTY-guarded test covers at least arrows/modifiers + bracketed paste.
- If ConPTY path is unavailable in environment, tests skip with explicit reason.
Startup input safety note (emoji width probe)¶
- By default, Rezi does not perform startup CPR probing for emoji width.
- CPR probing is opt-in (
ZRUI_EMOJI_WIDTH_PROBE=1) because probing temporarily reads stdin bytes and can race startup-time input streams. - The default contract therefore keeps startup input handling deterministic and unaffected by width probing.