@valyrianjs/terminal 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ansi.d.ts +2 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +12 -0
- package/dist/ansi.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -2
- package/dist/events.js.map +1 -1
- package/dist/keymap.d.ts.map +1 -1
- package/dist/keymap.js +4 -2
- package/dist/keymap.js.map +1 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +2 -1
- package/dist/layout.js.map +1 -1
- package/dist/mouse.d.ts +6 -0
- package/dist/mouse.d.ts.map +1 -1
- package/dist/mouse.js +30 -16
- package/dist/mouse.js.map +1 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +8 -1
- package/dist/primitives.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +184 -27
- package/dist/render.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +13 -5
- package/dist/runtime.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +323 -83
- package/dist/session.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +3 -0
- package/dist/theme.js.map +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +18 -4
- package/dist/tree.js.map +1 -1
- package/dist/types.d.ts +38 -4
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +13 -6
- package/docs/cookbook.md +1 -1
- package/docs/interaction-model.md +7 -5
- package/docs/primitive-gallery.md +7 -3
- package/llms-full.txt +28 -15
- package/package.json +1 -1
- package/src/ansi.ts +12 -0
- package/src/events.ts +4 -2
- package/src/keymap.ts +4 -2
- package/src/layout.ts +2 -1
- package/src/mouse.ts +31 -15
- package/src/primitives.ts +8 -1
- package/src/render.ts +199 -28
- package/src/runtime.ts +13 -5
- package/src/session.ts +341 -79
- package/src/theme.ts +3 -0
- package/src/tree.ts +19 -4
- package/src/types.ts +45 -3
package/docs/api-reference.md
CHANGED
|
@@ -239,7 +239,7 @@ Props:
|
|
|
239
239
|
|
|
240
240
|
Draws a clipped region over the current frame. `margin` sets the distance from the overlay to the frame edges; numbers use cells and percentage strings such as `"10%"` resolve per axis. Use `{ x, y }` when the two axes need different margins. `trapFocus` keeps traversal inside the overlay while it is active.
|
|
241
241
|
|
|
242
|
-
Overlay calculates its size from the available frame, stays centered by its margins, and keeps app routing and pane movement in your app layer.
|
|
242
|
+
Overlay calculates its size from the available frame, stays centered by its margins, and keeps app routing and pane movement in your app layer. Direct sibling overlays use source order: later overlays paint above earlier overlays, receive pointer hits first, and lead trapped focus traversal. Overlay styles paint the full assigned surface, including empty cells behind child controls.
|
|
243
243
|
|
|
244
244
|
### `FocusScope`
|
|
245
245
|
|
|
@@ -336,11 +336,17 @@ Props:
|
|
|
336
336
|
- `pointerCapture?: boolean`
|
|
337
337
|
- `items?: T[]`
|
|
338
338
|
- `virtualized?: boolean`
|
|
339
|
+
- `height?: number`
|
|
339
340
|
- `itemHeight?: 1`
|
|
340
341
|
- `overscan?: number`
|
|
342
|
+
- `wrap?: boolean`
|
|
343
|
+
- `itemKey?(item, index): string | number`
|
|
344
|
+
- `showActive?: boolean`
|
|
341
345
|
- shared visual props: `style`, `styles`, `state`
|
|
342
|
-
- `
|
|
346
|
+
- `children?(item, ctx): any`
|
|
347
|
+
- `renderItem?(item, index): any`
|
|
343
348
|
- `onchange?(event)`
|
|
349
|
+
- `onviewportchange?(event)`
|
|
344
350
|
- `onpress?(event)`
|
|
345
351
|
- `ondoublepress?(event)`
|
|
346
352
|
- `oncontextpress?(event)`
|
|
@@ -352,12 +358,13 @@ Props:
|
|
|
352
358
|
|
|
353
359
|
Payloads:
|
|
354
360
|
|
|
355
|
-
- `TerminalListChangeEventPayload<T>` - `{ type: "change", id, index, value }`
|
|
356
|
-
- `
|
|
357
|
-
- `
|
|
361
|
+
- `TerminalListChangeEventPayload<T>` - `{ type: "change", id, index, key?, value, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
362
|
+
- `TerminalListViewportChangeEventPayload` - `{ type: "viewportchange", id, offset, rows, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
363
|
+
- `TerminalListPressEventPayload<T>` - `{ type: "press" | "doublepress" | "contextpress", id, index, key?, value, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
364
|
+
- `TerminalListPointerEventPayload<T>` - `{ type: "hover" | "rowenter" | "rowleave", id, row, index, key?, value, x, y }`
|
|
358
365
|
- `TerminalCaptureEventPayload` - `{ type: "capturestart" | "captureend", id, source, row, x, y }`
|
|
359
366
|
|
|
360
|
-
Supports selection, activation, double press on the same row, context press for the target row, hover payloads, optional virtualization, and pointer capture.
|
|
367
|
+
Supports internal active row movement, internal selection, activation, double press on the same row, context press for the target row, hover payloads, optional wrapping, optional virtualization, viewport scroll, and pointer capture. `Up`/`Down`, `PageUp`, `PageDown`, `Home`, and `End` are built-in list navigation keys; `Left` and `Right` stay available for application bindings. Keyboard navigation moves the active row and emits `change`; `Enter` selects and activates the active row. Pointer click selects and activates the clicked row. A quick second primary click on the same row emits `doublepress` without a second `press`. The default renderer displays the active row as `list.current`; when the selected row differs from the active row, it displays the selected row as `list.selected`. A JSX `children` callback is the recommended item renderer and wins over `renderItem` when both are present. `children` receives `ctx.active` and `ctx.selected` separately. `renderItem` remains as a compatibility alias with the legacy `(item, index)` signature. `wrap` wraps long default-rendered item text to the list width. Virtualized lists receive the full `items` array and calculate the visible viewport internally; use `itemKey` for stable event identity when items can reorder. Observe the resulting active row, selected row, and viewport through the list event payloads. Run [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx) with `bun examples/docs/virtualized-list-workbench.tsx` to inspect those values from events in one bounded split layout.
|
|
361
368
|
|
|
362
369
|
### `ScrollView`
|
|
363
370
|
|
package/docs/cookbook.md
CHANGED
|
@@ -84,7 +84,7 @@ console.log(session.output());
|
|
|
84
84
|
session.destroy();
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
For large lists, add `virtualized` and place the list in a bounded region such as a filled pane or split.
|
|
87
|
+
For large lists, add `virtualized` and place the list in a bounded region such as a filled pane or split. The virtualized workbench shows the normal uncontrolled API with `itemKey`, the `children` callback, event observation, and internal active, selected, and viewport state: [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx). Run it with `bun examples/docs/virtualized-list-workbench.tsx`, move active rows with `J/K` or `Up`/`Down`, jump with `PageUp`, `PageDown`, `Home`, or `End`, select with `Enter`, and quit with `Ctrl+C`.
|
|
88
88
|
|
|
89
89
|
## Local state to Valyrian state module bridge
|
|
90
90
|
|
|
@@ -93,15 +93,17 @@ A quick second primary mouse press keeps the normal activation behavior and also
|
|
|
93
93
|
|
|
94
94
|
### `List`
|
|
95
95
|
|
|
96
|
-
`List` models row selection and activation.
|
|
96
|
+
`List` models active row movement, optional visible selection, viewport scroll, and activation. In virtualized mode the component receives the full `items` array and owns the viewport; wheel input moves the viewport, while keyboard list commands move the active row and emit `change`. A JSX `children` callback receives `(item, ctx)` with `ctx.active`, `ctx.selected`, `ctx.index`, `ctx.key`, and `ctx.viewportIndex`; it takes precedence over `renderItem`. Use `itemKey` when rows have stable identities across reorder or filtering.
|
|
97
97
|
|
|
98
98
|
Supported behavior includes:
|
|
99
99
|
|
|
100
|
-
- `UP` and `
|
|
101
|
-
- `
|
|
102
|
-
- `
|
|
100
|
+
- `UP` and `DOWN` move the active row and emit `onchange`
|
|
101
|
+
- `PageUp`, `PageDown`, `Home`, and `End` jump the active row and keep it visible
|
|
102
|
+
- `LEFT` and `RIGHT` have no default list command, so applications can bind them
|
|
103
|
+
- `ENTER` selects and activates the active row
|
|
104
|
+
- mouse click selects and activates the target row
|
|
103
105
|
- mouse hover reports row details when mouse input is connected
|
|
104
|
-
- quick primary mouse
|
|
106
|
+
- quick primary mouse clicks on the same row emit `ondoublepress` without a second `onpress`
|
|
105
107
|
- secondary mouse presses emit `oncontextpress` for the target row
|
|
106
108
|
|
|
107
109
|
Main handlers:
|
|
@@ -228,16 +228,20 @@ Use `List` for menus, choosers, command lists, and item browsers.
|
|
|
228
228
|
|
|
229
229
|
**What it is:** A focusable collection control with selection and press events.
|
|
230
230
|
**Use it for:** menus, choosers, command lists, and item browsers.
|
|
231
|
-
**Core props:** `id`, `items`, `renderItem`, `virtualized`, `onchange`, `onpress`, `ondoublepress`, `oncontextpress`.
|
|
231
|
+
**Core props:** `id`, `items`, `children` callback, `renderItem`, `itemKey`, `virtualized`, `showActive`, `onchange`, `onviewportchange`, `onpress`, `ondoublepress`, `oncontextpress`. `List` owns its active row, selected row, and viewport state internally; observe those values from event payloads.
|
|
232
232
|
|
|
233
233
|
**Minimal example:**
|
|
234
234
|
|
|
235
235
|
```tsx
|
|
236
|
-
<List id="menu" items={["Inbox", "Done"]}
|
|
236
|
+
<List id="menu" items={["Inbox", "Done"]} itemKey={(item) => item}>
|
|
237
|
+
{(item, ctx) => `${ctx.active ? "› " : " "}${item}`}
|
|
238
|
+
</List>
|
|
237
239
|
```
|
|
238
240
|
|
|
239
241
|
**Complete demo:** [`examples/docs/primitive-data-explorer.tsx`](../examples/docs/primitive-data-explorer.tsx). Run it with `bun examples/docs/primitive-data-explorer.tsx` and quit with `Ctrl+C`.
|
|
240
242
|
|
|
243
|
+
**Virtualized demo:** [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx). Run it with `bun examples/docs/virtualized-list-workbench.tsx`, move active rows with `J/K` or `Up`/`Down`, jump with `PageUp`, `PageDown`, `Home`, or `End`, select with `Enter`, and quit with `Ctrl+C`.
|
|
244
|
+
|
|
241
245
|
### `Table`
|
|
242
246
|
|
|
243
247
|
Use `Table` for compact data detail and aligned text pairs.
|
|
@@ -332,7 +336,7 @@ Use `Overlay` for command panels, dialogs, palettes, and focused chooser surface
|
|
|
332
336
|
|
|
333
337
|
**What it is:** A positioned layer over the current frame.
|
|
334
338
|
**Use it for:** command panels, dialogs, palettes, and focused chooser surfaces.
|
|
335
|
-
**Core props:** `id`, `margin`, `trapFocus`, `style`. `margin` is required and accepts a number, a percentage string, or `{ x, y }` with number or percentage-string values. When `Overlay` is the root passed to `renderTerminal()`, pass exact `{ cols, rows }` as the second argument so margin can resolve against terminal dimensions.
|
|
339
|
+
**Core props:** `id`, `margin`, `trapFocus`, `style`. `margin` is required and accepts a number, a percentage string, or `{ x, y }` with number or percentage-string values. When `Overlay` is the root passed to `renderTerminal()`, pass exact `{ cols, rows }` as the second argument so margin can resolve against terminal dimensions. Later sibling overlays paint and receive pointer/focus routing above earlier overlays. Overlay background styles cover their full assigned surface, not just text cells.
|
|
336
340
|
|
|
337
341
|
**Minimal example:**
|
|
338
342
|
|
package/llms-full.txt
CHANGED
|
@@ -666,7 +666,7 @@ console.log(session.output());
|
|
|
666
666
|
session.destroy();
|
|
667
667
|
```
|
|
668
668
|
|
|
669
|
-
For large lists, add `virtualized` and place the list in a bounded region such as a filled pane or split.
|
|
669
|
+
For large lists, add `virtualized` and place the list in a bounded region such as a filled pane or split. The virtualized workbench shows the normal uncontrolled API with `itemKey`, the `children` callback, event observation, and internal active, selected, and viewport state: [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx). Run it with `bun examples/docs/virtualized-list-workbench.tsx`, move active rows with `J/K` or `Up`/`Down`, jump with `PageUp`, `PageDown`, `Home`, or `End`, select with `Enter`, and quit with `Ctrl+C`.
|
|
670
670
|
|
|
671
671
|
## Local state to Valyrian state module bridge
|
|
672
672
|
|
|
@@ -1196,16 +1196,20 @@ Use `List` for menus, choosers, command lists, and item browsers.
|
|
|
1196
1196
|
|
|
1197
1197
|
**What it is:** A focusable collection control with selection and press events.
|
|
1198
1198
|
**Use it for:** menus, choosers, command lists, and item browsers.
|
|
1199
|
-
**Core props:** `id`, `items`, `renderItem`, `virtualized`, `onchange`, `onpress`, `ondoublepress`, `oncontextpress`.
|
|
1199
|
+
**Core props:** `id`, `items`, `children` callback, `renderItem`, `itemKey`, `virtualized`, `showActive`, `onchange`, `onviewportchange`, `onpress`, `ondoublepress`, `oncontextpress`. `List` owns its active row, selected row, and viewport state internally; observe those values from event payloads.
|
|
1200
1200
|
|
|
1201
1201
|
**Minimal example:**
|
|
1202
1202
|
|
|
1203
1203
|
```tsx
|
|
1204
|
-
<List id="menu" items={["Inbox", "Done"]}
|
|
1204
|
+
<List id="menu" items={["Inbox", "Done"]} itemKey={(item) => item}>
|
|
1205
|
+
{(item, ctx) => `${ctx.active ? "› " : " "}${item}`}
|
|
1206
|
+
</List>
|
|
1205
1207
|
```
|
|
1206
1208
|
|
|
1207
1209
|
**Complete demo:** [`examples/docs/primitive-data-explorer.tsx`](../examples/docs/primitive-data-explorer.tsx). Run it with `bun examples/docs/primitive-data-explorer.tsx` and quit with `Ctrl+C`.
|
|
1208
1210
|
|
|
1211
|
+
**Virtualized demo:** [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx). Run it with `bun examples/docs/virtualized-list-workbench.tsx`, move active rows with `J/K` or `Up`/`Down`, jump with `PageUp`, `PageDown`, `Home`, or `End`, select with `Enter`, and quit with `Ctrl+C`.
|
|
1212
|
+
|
|
1209
1213
|
### `Table`
|
|
1210
1214
|
|
|
1211
1215
|
Use `Table` for compact data detail and aligned text pairs.
|
|
@@ -1300,7 +1304,7 @@ Use `Overlay` for command panels, dialogs, palettes, and focused chooser surface
|
|
|
1300
1304
|
|
|
1301
1305
|
**What it is:** A positioned layer over the current frame.
|
|
1302
1306
|
**Use it for:** command panels, dialogs, palettes, and focused chooser surfaces.
|
|
1303
|
-
**Core props:** `id`, `margin`, `trapFocus`, `style`. `margin` is required and accepts a number, a percentage string, or `{ x, y }` with number or percentage-string values. When `Overlay` is the root passed to `renderTerminal()`, pass exact `{ cols, rows }` as the second argument so margin can resolve against terminal dimensions.
|
|
1307
|
+
**Core props:** `id`, `margin`, `trapFocus`, `style`. `margin` is required and accepts a number, a percentage string, or `{ x, y }` with number or percentage-string values. When `Overlay` is the root passed to `renderTerminal()`, pass exact `{ cols, rows }` as the second argument so margin can resolve against terminal dimensions. Later sibling overlays paint and receive pointer/focus routing above earlier overlays. Overlay background styles cover their full assigned surface, not just text cells.
|
|
1304
1308
|
|
|
1305
1309
|
**Minimal example:**
|
|
1306
1310
|
|
|
@@ -1436,15 +1440,17 @@ A quick second primary mouse press keeps the normal activation behavior and also
|
|
|
1436
1440
|
|
|
1437
1441
|
### `List`
|
|
1438
1442
|
|
|
1439
|
-
`List` models row selection and activation.
|
|
1443
|
+
`List` models active row movement, optional visible selection, viewport scroll, and activation. In virtualized mode the component receives the full `items` array and owns the viewport; wheel input moves the viewport, while keyboard list commands move the active row and emit `change`. A JSX `children` callback receives `(item, ctx)` with `ctx.active`, `ctx.selected`, `ctx.index`, `ctx.key`, and `ctx.viewportIndex`; it takes precedence over `renderItem`. Use `itemKey` when rows have stable identities across reorder or filtering.
|
|
1440
1444
|
|
|
1441
1445
|
Supported behavior includes:
|
|
1442
1446
|
|
|
1443
|
-
- `UP` and `
|
|
1444
|
-
- `
|
|
1445
|
-
- `
|
|
1447
|
+
- `UP` and `DOWN` move the active row and emit `onchange`
|
|
1448
|
+
- `PageUp`, `PageDown`, `Home`, and `End` jump the active row and keep it visible
|
|
1449
|
+
- `LEFT` and `RIGHT` have no default list command, so applications can bind them
|
|
1450
|
+
- `ENTER` selects and activates the active row
|
|
1451
|
+
- mouse click selects and activates the target row
|
|
1446
1452
|
- mouse hover reports row details when mouse input is connected
|
|
1447
|
-
- quick primary mouse
|
|
1453
|
+
- quick primary mouse clicks on the same row emit `ondoublepress` without a second `onpress`
|
|
1448
1454
|
- secondary mouse presses emit `oncontextpress` for the target row
|
|
1449
1455
|
|
|
1450
1456
|
Main handlers:
|
|
@@ -5172,7 +5178,7 @@ Props:
|
|
|
5172
5178
|
|
|
5173
5179
|
Draws a clipped region over the current frame. `margin` sets the distance from the overlay to the frame edges; numbers use cells and percentage strings such as `"10%"` resolve per axis. Use `{ x, y }` when the two axes need different margins. `trapFocus` keeps traversal inside the overlay while it is active.
|
|
5174
5180
|
|
|
5175
|
-
Overlay calculates its size from the available frame, stays centered by its margins, and keeps app routing and pane movement in your app layer.
|
|
5181
|
+
Overlay calculates its size from the available frame, stays centered by its margins, and keeps app routing and pane movement in your app layer. Direct sibling overlays use source order: later overlays paint above earlier overlays, receive pointer hits first, and lead trapped focus traversal. Overlay styles paint the full assigned surface, including empty cells behind child controls.
|
|
5176
5182
|
|
|
5177
5183
|
### `FocusScope`
|
|
5178
5184
|
|
|
@@ -5269,11 +5275,17 @@ Props:
|
|
|
5269
5275
|
- `pointerCapture?: boolean`
|
|
5270
5276
|
- `items?: T[]`
|
|
5271
5277
|
- `virtualized?: boolean`
|
|
5278
|
+
- `height?: number`
|
|
5272
5279
|
- `itemHeight?: 1`
|
|
5273
5280
|
- `overscan?: number`
|
|
5281
|
+
- `wrap?: boolean`
|
|
5282
|
+
- `itemKey?(item, index): string | number`
|
|
5283
|
+
- `showActive?: boolean`
|
|
5274
5284
|
- shared visual props: `style`, `styles`, `state`
|
|
5275
|
-
- `
|
|
5285
|
+
- `children?(item, ctx): any`
|
|
5286
|
+
- `renderItem?(item, index): any`
|
|
5276
5287
|
- `onchange?(event)`
|
|
5288
|
+
- `onviewportchange?(event)`
|
|
5277
5289
|
- `onpress?(event)`
|
|
5278
5290
|
- `ondoublepress?(event)`
|
|
5279
5291
|
- `oncontextpress?(event)`
|
|
@@ -5285,12 +5297,13 @@ Props:
|
|
|
5285
5297
|
|
|
5286
5298
|
Payloads:
|
|
5287
5299
|
|
|
5288
|
-
- `TerminalListChangeEventPayload<T>` - `{ type: "change", id, index, value }`
|
|
5289
|
-
- `
|
|
5290
|
-
- `
|
|
5300
|
+
- `TerminalListChangeEventPayload<T>` - `{ type: "change", id, index, key?, value, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
5301
|
+
- `TerminalListViewportChangeEventPayload` - `{ type: "viewportchange", id, offset, rows, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
5302
|
+
- `TerminalListPressEventPayload<T>` - `{ type: "press" | "doublepress" | "contextpress", id, index, key?, value, activeIndex, selectedIndex, viewportOffset, viewportRows }`
|
|
5303
|
+
- `TerminalListPointerEventPayload<T>` - `{ type: "hover" | "rowenter" | "rowleave", id, row, index, key?, value, x, y }`
|
|
5291
5304
|
- `TerminalCaptureEventPayload` - `{ type: "capturestart" | "captureend", id, source, row, x, y }`
|
|
5292
5305
|
|
|
5293
|
-
Supports selection, activation, double press on the same row, context press for the target row, hover payloads, optional virtualization, and pointer capture.
|
|
5306
|
+
Supports internal active row movement, internal selection, activation, double press on the same row, context press for the target row, hover payloads, optional wrapping, optional virtualization, viewport scroll, and pointer capture. `Up`/`Down`, `PageUp`, `PageDown`, `Home`, and `End` are built-in list navigation keys; `Left` and `Right` stay available for application bindings. Keyboard navigation moves the active row and emits `change`; `Enter` selects and activates the active row. Pointer click selects and activates the clicked row. A quick second primary click on the same row emits `doublepress` without a second `press`. The default renderer displays the active row as `list.current`; when the selected row differs from the active row, it displays the selected row as `list.selected`. A JSX `children` callback is the recommended item renderer and wins over `renderItem` when both are present. `children` receives `ctx.active` and `ctx.selected` separately. `renderItem` remains as a compatibility alias with the legacy `(item, index)` signature. `wrap` wraps long default-rendered item text to the list width. Virtualized lists receive the full `items` array and calculate the visible viewport internally; use `itemKey` for stable event identity when items can reorder. Observe the resulting active row, selected row, and viewport through the list event payloads. Run [`examples/docs/virtualized-list-workbench.tsx`](../examples/docs/virtualized-list-workbench.tsx) with `bun examples/docs/virtualized-list-workbench.tsx` to inspect those values from events in one bounded split layout.
|
|
5294
5307
|
|
|
5295
5308
|
### `ScrollView`
|
|
5296
5309
|
|
package/package.json
CHANGED
package/src/ansi.ts
CHANGED
|
@@ -5,6 +5,8 @@ export const ANSI_ENTER_ALTERNATE_SCREEN = "\u001b[?1049h";
|
|
|
5
5
|
export const ANSI_EXIT_ALTERNATE_SCREEN = "\u001b[?1049l";
|
|
6
6
|
export const ANSI_HIDE_CURSOR = "\u001b[?25l";
|
|
7
7
|
export const ANSI_SHOW_CURSOR = "\u001b[?25h";
|
|
8
|
+
export const ANSI_ENABLE_MOUSE_REPORTING = "\u001b[?1000h\u001b[?1002h\u001b[?1006h";
|
|
9
|
+
export const ANSI_DISABLE_MOUSE_REPORTING = "\u001b[?1002l\u001b[?1000l\u001b[?1006l";
|
|
8
10
|
|
|
9
11
|
type AnsiFrameOptions = {
|
|
10
12
|
showCursor?: boolean;
|
|
@@ -112,16 +114,26 @@ function renderAnsiLine(line: string, spans: TerminalStyleSpan[], y: number, the
|
|
|
112
114
|
output += char;
|
|
113
115
|
visibleColumn += 1;
|
|
114
116
|
|
|
117
|
+
let closedSpan = false;
|
|
115
118
|
for (const span of rowSpans) {
|
|
116
119
|
if (span.x2 === visibleColumn) {
|
|
117
120
|
if (span.kind !== "focus") {
|
|
118
121
|
output += spanAnsiClose(span, theme);
|
|
122
|
+
closedSpan = true;
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
for (const span of rowSpans) {
|
|
123
127
|
if (span.x2 === visibleColumn && span.kind === "focus") {
|
|
124
128
|
output += spanAnsiClose(span, theme);
|
|
129
|
+
closedSpan = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (closedSpan && visibleColumn <= line.length && line[visibleColumn - 1] === " ") {
|
|
133
|
+
for (const span of rowSpans) {
|
|
134
|
+
if (span.x1 < visibleColumn && span.x2 > visibleColumn) {
|
|
135
|
+
output += spanAnsiOpen(span, theme);
|
|
136
|
+
}
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
}
|
package/src/events.ts
CHANGED
|
@@ -151,9 +151,11 @@ export function parseTerminalKey(chunk: string | Uint8Array) {
|
|
|
151
151
|
if (value === "\u001b[1;3C" || value === "\u001bf") return "ALT_RIGHT";
|
|
152
152
|
if (value === "\u001b[1;3D" || value === "\u001bb") return "ALT_LEFT";
|
|
153
153
|
if (value === "\u001b[3~") return "DELETE";
|
|
154
|
+
if (value === "\u001b[5~") return "PAGEUP";
|
|
155
|
+
if (value === "\u001b[6~") return "PAGEDOWN";
|
|
154
156
|
if (value === "\u0008" || value === "\u007f") return "BACKSPACE";
|
|
155
|
-
if (value === "\u001b[H") return "HOME";
|
|
156
|
-
if (value === "\u001b[F") return "END";
|
|
157
|
+
if (value === "\u001b[H" || value === "\u001b[1~") return "HOME";
|
|
158
|
+
if (value === "\u001b[F" || value === "\u001b[4~") return "END";
|
|
157
159
|
if (value === "\u0001") return "CTRL_A";
|
|
158
160
|
if (value === "\u0003") return "CTRL_C";
|
|
159
161
|
if (value === "\u000b") return "CTRL_K";
|
package/src/keymap.ts
CHANGED
|
@@ -32,9 +32,11 @@ export const defaultTerminalKeyBindings: TerminalKeyBinding[] = [
|
|
|
32
32
|
{ key: "ENTER", command: { id: "button.press" }, scope: "button", when: { focusedTag: "terminal-button" } },
|
|
33
33
|
{ key: "SPACE", command: { id: "button.press" }, scope: "button", when: { focusedTag: "terminal-button" } },
|
|
34
34
|
{ key: "UP", command: { id: "list.prev" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
35
|
-
{ key: "LEFT", command: { id: "list.prev" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
36
35
|
{ key: "DOWN", command: { id: "list.next" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
37
|
-
{ key: "
|
|
36
|
+
{ key: "PAGEUP", command: { id: "list.pageUp" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
37
|
+
{ key: "PAGEDOWN", command: { id: "list.pageDown" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
38
|
+
{ key: "HOME", command: { id: "list.home" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
39
|
+
{ key: "END", command: { id: "list.end" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
38
40
|
{ key: "ENTER", command: { id: "list.press" }, scope: "list", when: { focusedTag: "terminal-list" } },
|
|
39
41
|
{ key: "UP", command: { id: "scroll.up" }, scope: "scroll", when: { focusedTag: "terminal-scroll" } },
|
|
40
42
|
{ key: "DOWN", command: { id: "scroll.down" }, scope: "scroll", when: { focusedTag: "terminal-scroll" } }
|
package/src/layout.ts
CHANGED
|
@@ -25,7 +25,8 @@ export function shiftFrame(frame: TerminalFrame, dx: number, dy: number): Termin
|
|
|
25
25
|
x2: box.x2 + dx,
|
|
26
26
|
y1: box.y1 + dy,
|
|
27
27
|
y2: box.y2 + dy,
|
|
28
|
-
textStartX: typeof box.textStartX === "number" ? box.textStartX + dx : undefined
|
|
28
|
+
textStartX: typeof box.textStartX === "number" ? box.textStartX + dx : undefined,
|
|
29
|
+
contentY: typeof box.contentY === "number" ? box.contentY + dy : undefined
|
|
29
30
|
})),
|
|
30
31
|
cursor: frame.cursor ? { x: frame.cursor.x + dx, y: frame.cursor.y + dy } : null,
|
|
31
32
|
spans: frame.spans.map((span) => ({ ...span, x1: span.x1 + dx, x2: span.x2 + dx, y: span.y + dy }))
|
package/src/mouse.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { parseTerminalKey } from "./events.js";
|
|
|
2
2
|
|
|
3
3
|
import type { ParsedTerminalInput, TerminalHitbox } from "./types.js";
|
|
4
4
|
|
|
5
|
-
const nestedParentTags = new Set(["terminal-box", "terminal-view"]);
|
|
5
|
+
const nestedParentTags = new Set(["terminal-box", "terminal-view", "terminal-list"]);
|
|
6
6
|
|
|
7
7
|
function containsHitbox(parent: TerminalHitbox, child: TerminalHitbox) {
|
|
8
8
|
return child.x1 >= parent.x1 && child.x2 <= parent.x2 && child.y1 >= parent.y1 && child.y2 <= parent.y2;
|
|
@@ -12,24 +12,40 @@ function samePointerLayer(left: TerminalHitbox, right: TerminalHitbox) {
|
|
|
12
12
|
return (left.pointerLayer ?? 0) === (right.pointerLayer ?? 0);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function parseMouseMatch(match: RegExpExecArray): Extract<ParsedTerminalInput, { type: "mouse" }> {
|
|
16
|
+
const code = Number(match[1]);
|
|
17
|
+
let action: "press" | "release" | "drag" | "wheel-up" | "wheel-down" = match[4] === "M" ? "press" : "release";
|
|
18
|
+
if (code >= 64) {
|
|
19
|
+
action = code === 64 ? "wheel-up" : "wheel-down";
|
|
20
|
+
} else if (code >= 32 && match[4] === "M") {
|
|
21
|
+
action = "drag";
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
type: "mouse",
|
|
25
|
+
button: code,
|
|
26
|
+
x: Number(match[2]),
|
|
27
|
+
y: Number(match[3]),
|
|
28
|
+
action
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseTerminalMousePrefix(chunk: string | Uint8Array): { input: Extract<ParsedTerminalInput, { type: "mouse" }>; rest: string } | null {
|
|
33
|
+
const value = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
34
|
+
const match = /^\u001b\[<(\d+);(\d+);(\d+)([Mm])/.exec(value);
|
|
35
|
+
if (!match) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
input: parseMouseMatch(match),
|
|
40
|
+
rest: value.slice(match[0].length)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
15
44
|
export function parseTerminalInput(chunk: string | Uint8Array): ParsedTerminalInput {
|
|
16
45
|
const value = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
17
46
|
const match = /^\u001b\[<(\d+);(\d+);(\d+)([Mm])$/.exec(value);
|
|
18
47
|
if (match) {
|
|
19
|
-
|
|
20
|
-
let action: "press" | "release" | "drag" | "wheel-up" | "wheel-down" = match[4] === "M" ? "press" : "release";
|
|
21
|
-
if (code >= 64) {
|
|
22
|
-
action = code === 64 ? "wheel-up" : "wheel-down";
|
|
23
|
-
} else if (code >= 32 && match[4] === "M") {
|
|
24
|
-
action = "drag";
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
type: "mouse",
|
|
28
|
-
button: code,
|
|
29
|
-
x: Number(match[2]),
|
|
30
|
-
y: Number(match[3]),
|
|
31
|
-
action
|
|
32
|
-
};
|
|
48
|
+
return parseMouseMatch(match);
|
|
33
49
|
}
|
|
34
50
|
return { type: "key", key: parseTerminalKey(value) };
|
|
35
51
|
}
|
package/src/primitives.ts
CHANGED
|
@@ -49,7 +49,14 @@ export const Editor = primitive<TerminalEditorProps>("terminal-editor");
|
|
|
49
49
|
export const Button = primitive<TerminalButtonProps>("terminal-button");
|
|
50
50
|
export const ScrollView = primitive<TerminalScrollViewProps>("terminal-scroll");
|
|
51
51
|
export const LogView = primitive<TerminalLogViewProps>("terminal-log-view");
|
|
52
|
-
export const List =
|
|
52
|
+
export const List = function TerminalList(props: TerminalPrimitiveComponentProps<TerminalListProps<any>> | null | undefined, children: any[]) {
|
|
53
|
+
const nextProps = { ...(props || {}) } as Record<string, any>;
|
|
54
|
+
if (children.length === 1 && typeof children[0] === "function") {
|
|
55
|
+
nextProps.__childrenRenderer = children[0];
|
|
56
|
+
return v("terminal-list", nextProps);
|
|
57
|
+
}
|
|
58
|
+
return v("terminal-list", nextProps, ...children);
|
|
59
|
+
} as TerminalListComponent;
|
|
53
60
|
export const Table = primitive<TerminalTableProps>("terminal-table");
|
|
54
61
|
export const Row = primitive<TerminalRowProps>("terminal-row");
|
|
55
62
|
export const Td = primitive<TerminalTdProps>("terminal-td");
|