Skip to content

Commit 57aafeb

Browse files
Merge pull request #190 from RtlZeroMemory/codex/remove-v1-cursor
refactor(cursor): remove v1 cursor paths and make native cursor default
2 parents cee18ba + 730ae54 commit 57aafeb

40 files changed

+200
-521
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format is based on Keep a Changelog and the project follows Semantic Version
1818

1919
### Features
2020

21+
- **cursor/protocol**: Removed cursor v1/v2 toggle flags (`useV2Cursor`, `useDrawlistV2`); native cursor protocol is now the default runtime path and backend drawlist version `1` is rejected.
2122
- **runtime**: Widget protocol registry centralizes widget capability detection.
2223
- **events**: Extended `RoutedAction` union (`toggle`, `select`, `rowPress`, `change`, `activate`, `scroll`) now surfaces through `UiEvent`.
2324
- **overlays**: Unified overlay system; dropdowns and toasts register in `LayerRegistry`.

docs/backend/engine-config.md

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ type AppConfig = Readonly<{
2727
fpsCap?: number;
2828
maxEventBytes?: number;
2929
maxDrawlistBytes?: number;
30-
useV2Cursor?: boolean;
3130
drawlistValidateParams?: boolean;
3231
drawlistReuseOutputBuffer?: boolean;
3332
drawlistEncodedStringCacheCap?: number;
@@ -87,35 +86,6 @@ config: {
8786
}
8887
```
8988

90-
### useV2Cursor
91-
92-
| Detail | Value |
93-
|----------|-------|
94-
| Type | `boolean` |
95-
| Default | `false` |
96-
97-
Enables the v2 cursor protocol, which adds a `SET_CURSOR` command to the
98-
drawlist. When enabled, the native engine can set the terminal cursor position
99-
and shape directly, which is required for proper cursor rendering in `Input`
100-
widgets.
101-
102-
Both the app config and the backend must agree on this setting. If you set
103-
`useV2Cursor: true` in the app config, the backend must also be created with
104-
drawlist v2+ support, or the runtime will throw a validation error.
105-
106-
The default Node/Bun backend supports cursor v2 (drawlist v5 by default). You
107-
only need to change backend configuration if you intentionally force an older
108-
drawlist version.
109-
110-
```typescript
111-
import { createNodeApp } from "@rezi-ui/node";
112-
113-
const app = createNodeApp({
114-
initialState: {},
115-
config: { useV2Cursor: true },
116-
});
117-
```
118-
11989
### drawlistValidateParams
12090

12191
| Detail | Value |
@@ -207,7 +177,6 @@ config: {
207177
| `fpsCap` | `60` |
208178
| `maxEventBytes` | `1048576` (1 MiB) |
209179
| `maxDrawlistBytes` | `2097152` (2 MiB) |
210-
| `useV2Cursor` | `false` |
211180
| `drawlistValidateParams` | `true` (app runtime default) |
212181
| `drawlistReuseOutputBuffer` | `true` |
213182
| `drawlistEncodedStringCacheCap` | `1024` |

docs/backend/node.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@ const app = createNodeApp({
1717
executionMode: "auto",
1818
fpsCap: 60,
1919
maxEventBytes: 1 << 20,
20-
useV2Cursor: false,
2120
},
2221
});
2322
```
2423

2524
`createNodeApp` is the recommended path because it keeps core/backend config
2625
knobs aligned:
2726

28-
- `useV2Cursor` and drawlist v2 are paired automatically.
2927
- `maxEventBytes` is applied to both app parsing and backend transport buffers.
3028
- `fpsCap` is the single scheduling knob.
3129
- `executionMode: "auto"` resolves to inline when `fpsCap <= 30`, worker otherwise.

docs/getting-started/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const app = createNodeApp<State>({ initialState: { count: 0 } });
106106

107107
- `createNodeApp<State>` creates a typed application instance with a compatible Node/Bun backend
108108
- `initialState` provides the starting application state
109-
- An optional `config` object controls runtime knobs (`fpsCap`, `maxEventBytes`, `useV2Cursor`)
109+
- An optional `config` object controls runtime knobs (`fpsCap`, `maxEventBytes`, `maxDrawlistBytes`)
110110

111111
### Defining the View
112112

docs/guide/debugging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const app = createAppWithInspectorOverlay({
6969

7070
Overlay sections include:
7171
- Focus summary (`focusedId`, active zone, active trap)
72-
- Cursor target summary (position, shape, blink intent when cursor v2 is active)
72+
- Cursor target summary (position, shape, blink intent)
7373
- Damage + frame summary (`mode`, rect/cell counts, commit/layout/incremental state)
7474
- Frame timing rows (`drawlistBytes`, `diffBytesEmitted`, `usDrawlist`, `usDiff`, `usWrite`)
7575
- Event routing breadcrumbs (last event kind, keybindings vs widget routing path, last action)

docs/guide/lifecycle-and-updates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type State = { count: number };
1414

1515
const app = createNodeApp<State>({
1616
initialState: { count: 0 },
17-
config: { fpsCap: 60, maxEventBytes: 1 << 20, useV2Cursor: false },
17+
config: { fpsCap: 30, maxEventBytes: 1 << 20 },
1818
});
1919

2020
app.view((state) => ui.text(`Count: ${state.count}`));

docs/guide/record-replay.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Top-level required fields:
3535
| `maxDrawlistBytes` | `number` (non-negative integer) |
3636
| `maxFrames` | `number` (non-negative integer) |
3737
| `fpsCap` | `number` (positive integer) |
38-
| `cursorProtocolVersion` | `1 \| 2` |
38+
| `cursorProtocolVersion` | `2` |
3939

4040
### `capsSnapshot`
4141

@@ -44,7 +44,7 @@ Top-level required fields:
4444
| `terminalCaps` | `TerminalCaps \| null` |
4545
| `backendCaps.maxEventBytes` | `number` (non-negative integer) |
4646
| `backendCaps.fpsCap` | `number` (positive integer) |
47-
| `backendCaps.cursorProtocolVersion` | `1 \| 2` |
47+
| `backendCaps.cursorProtocolVersion` | `2` |
4848

4949
### `timingModel`
5050

docs/packages/node.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,13 @@ const app = createNodeApp({
4040
executionMode: "auto",
4141
fpsCap: 60,
4242
maxEventBytes: 1 << 20,
43-
useV2Cursor: false,
4443
},
4544
});
4645
```
4746

4847
`createNodeApp` is the default path because it keeps core/backend config in
4948
lockstep:
5049

51-
- `useV2Cursor` <-> drawlist v2
5250
- app/backend `maxEventBytes`
5351
- app/backend `fpsCap`
5452

docs/protocol/cursor-v2.md

Lines changed: 0 additions & 167 deletions
This file was deleted.

docs/protocol/cursor.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Cursor Protocol
2+
3+
Rezi uses the **SET_CURSOR** command to control the terminal cursor position,
4+
visibility, shape, and blink state from the TypeScript renderer.
5+
6+
## SET_CURSOR command
7+
8+
**Opcode:** 7 (`OP_SET_CURSOR`)
9+
10+
**Total size:** 20 bytes (8-byte command header + 12-byte payload)
11+
12+
### Payload layout
13+
14+
| Offset | Size | Type | Field | Description |
15+
|--------|------|------|-------|-------------|
16+
| 8 | 4 | `i32` | `x` | 0-based cell column; `-1` = leave unchanged |
17+
| 12 | 4 | `i32` | `y` | 0-based cell row; `-1` = leave unchanged |
18+
| 16 | 1 | `u8` | `shape` | Cursor shape constant |
19+
| 17 | 1 | `u8` | `visible` | `1` = show, `0` = hide |
20+
| 18 | 1 | `u8` | `blink` | `1` = blinking, `0` = steady |
21+
| 19 | 1 | `u8` | `reserved0` | Must be `0` |
22+
23+
The command is 20 bytes total, which is 4-byte aligned (no padding needed).
24+
25+
### Special values
26+
27+
- **x = -1 or y = -1:** The engine leaves that coordinate unchanged from the previous frame.
28+
- **visible = 0:** Hides the cursor until a later command sets `visible = 1`.
29+
30+
## Cursor shape constants
31+
32+
The `shape` field accepts the following values, defined in `packages/core/src/abi.ts`:
33+
34+
| Constant | Value | Description |
35+
|----------|-------|-------------|
36+
| `ZR_CURSOR_SHAPE_BLOCK` | `0` | Block cursor (full cell) |
37+
| `ZR_CURSOR_SHAPE_UNDERLINE` | `1` | Underline cursor (bottom of cell) |
38+
| `ZR_CURSOR_SHAPE_BAR` | `2` | Bar cursor (left edge of cell) |
39+
40+
The TypeScript type is:
41+
42+
```typescript
43+
export type CursorShape = 0 | 1 | 2;
44+
```
45+
46+
!!! note
47+
If the terminal does not support cursor shaping, the engine ignores `shape`
48+
and uses the terminal default cursor appearance.
49+
50+
## Runtime behavior
51+
52+
- The widget renderer resolves cursor targets from focused inputs/editors and
53+
emits `SET_CURSOR` when visible.
54+
- If no widget requests a cursor, the renderer emits a hide cursor command.
55+
- Node/Bun defaults to drawlist v5, so cursor protocol support is available by
56+
default.
57+
58+
## Default cursor shapes
59+
60+
The framework provides context-specific defaults in `CURSOR_DEFAULTS`:
61+
62+
| Context | Shape | Blink | Use case |
63+
|---------|-------|-------|----------|
64+
| `input` | `2` (BAR) | `true` | Text input fields |
65+
| `selection` | `0` (BLOCK) | `true` | Selection-mode cursors |
66+
| `staticUnderline` | `1` (UNDERLINE) | `false` | Non-blinking indicator |
67+
68+
```typescript
69+
import { CURSOR_DEFAULTS } from "@rezi-ui/core";
70+
71+
// CURSOR_DEFAULTS.input => { shape: 2, blink: true }
72+
// CURSOR_DEFAULTS.selection => { shape: 0, blink: true }
73+
// CURSOR_DEFAULTS.staticUnderline => { shape: 1, blink: false }
74+
```
75+
76+
## See also
77+
78+
- [Drawlists (ZRDL)](zrdl.md) -- full ZRDL format reference
79+
- [Versioning](versioning.md) -- protocol and ABI version table

0 commit comments

Comments
 (0)