$ rezi
Protocol

Cursor Protocol

Rezi uses the SET_CURSOR command to control the terminal cursor position, visibility, shape, and blink state from the TypeScript renderer.

Rezi uses the SET_CURSOR command to control the terminal cursor position, visibility, shape, and blink state from the TypeScript renderer.

SET_CURSOR command

Opcode: 7 (OP_SET_CURSOR)

Total size: 20 bytes (8-byte command header + 12-byte payload)

Payload layout

OffsetSizeTypeFieldDescription
84i32x0-based cell column; -1 = leave unchanged
124i32y0-based cell row; -1 = leave unchanged
161u8shapeCursor shape constant
171u8visible1 = show, 0 = hide
181u8blink1 = blinking, 0 = steady
191u8reserved0Must be 0

The command is 20 bytes total, which is 4-byte aligned (no padding needed).

Special values

  • x = -1 or y = -1: The engine leaves that coordinate unchanged from the previous frame.
  • visible = 0: Hides the cursor until a later command sets visible = 1.

Cursor shape constants

The shape field accepts the following values, defined in packages/core/src/abi.ts:

ConstantValueDescription
ZR_CURSOR_SHAPE_BLOCK0Block cursor (full cell)
ZR_CURSOR_SHAPE_UNDERLINE1Underline cursor (bottom of cell)
ZR_CURSOR_SHAPE_BAR2Bar cursor (left edge of cell)

The TypeScript type is:

export type CursorShape = 0 | 1 | 2;

If the terminal does not support cursor shaping, the engine ignores shape and uses the terminal default cursor appearance.

Runtime behavior

  • The widget renderer resolves cursor targets from focused inputs/editors and emits SET_CURSOR when visible.
  • If no widget requests a cursor, the renderer emits a hide cursor command.
  • Node/Bun defaults to drawlist v1, so cursor protocol support is available by default.

Default cursor shapes

The framework provides context-specific defaults in CURSOR_DEFAULTS:

ContextShapeBlinkUse case
input2 (BAR)trueText input fields
selection0 (BLOCK)trueSelection-mode cursors
staticUnderline1 (UNDERLINE)falseNon-blinking indicator
import { CURSOR_DEFAULTS } from "@rezi-ui/core";

// CURSOR_DEFAULTS.input       => { shape: 2, blink: true }
// CURSOR_DEFAULTS.selection   => { shape: 0, blink: true }
// CURSOR_DEFAULTS.staticUnderline => { shape: 1, blink: false }

See also

On this page