@xp266/dshtui 0.1.6 → 0.1.7
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/lib/dialog.mjs +1 -1
- package/lib/index.mjs +3 -1
- package/lib/{surface-B7w3R_kh.mjs → surface-DSDGGCSN.mjs} +150 -137
- package/package.json +1 -1
- package/src/terminal/cursor-shape.ts +4 -1
- package/src/terminal/mouse.ts +19 -2
- package/src/ui/app.tsx +6 -0
package/lib/dialog.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "./prod-react-Dzof4qJx.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { L as panelAnchorRow, c as asTextItem, i as Dialog, l as registerWidget, n as panelLegend, o as rowHeight, r as CloseGuardContext, s as wrapStatusLines, t as PanelSurface } from "./surface-DSDGGCSN.mjs";
|
|
3
3
|
export { CloseGuardContext, Dialog, PanelSurface, asTextItem, panelAnchorRow, panelLegend, registerWidget, rowHeight, wrapStatusLines };
|
package/lib/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./prod-react-Dzof4qJx.mjs";
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as colToCharIndex, A as writeCursorShape, At as colorLevel, B as hintBlockTop, C as useCaret, Ct as permissionModeInfo, D as SelectableText, Dt as subscribePalettes, E as markKeymapConsumed, Et as setThemeMode, F as inputContentContains, Ft as installCrashHandlers, G as segmentsKey, H as inputStatusRow, I as messageBackgroundWidth, It as warn, J as sliceByColumns, K as wrapSegments, L as panelAnchorRow, M as isMouseResidue, Mt as env, N as writeMouseDisable, Nt as configureLogs, O as SelectionContext, Ot as themeMode, P as hintSpan, Pt as error, Q as charWidth, R as panelContains, S as caretNonceText, St as paletteColor, T as isKeyConsumed, Tt as replacePalettes, U as glyphs, V as inputFrameTop, W as mergeRuns, X as selectedRange, Y as clampFocusRow, Z as toScreenSelection, _ as editDeleteWordRight, _t as registerFieldKind, a as DIALOG_MAX_HEIGHT, at as truncate, b as caretNonceBold, bt as specialFieldFactory, ct as charactersChipLabel, d as editCursorLeft, dt as fieldStyleOf, et as errorText, f as editCursorRight, ft as hasFieldChar, g as editDeleteWordLeft, gt as linesChipLabel, h as editDelete, ht as isFieldChar, i as Dialog, it as textWidth, j as createMouseController, jt as setColorLevel, k as Region, kt as keyedRegistry, l as registerWidget, lt as fieldExpandOf, m as editCursorWordRight, mt as imageChipLabel, n as panelLegend, nt as locToPoint, ot as wrapLines, p as editCursorWordLeft, pt as hasFieldSlots, q as chromeSelectionText, r as CloseGuardContext, rt as segmentGraphemes, st as allocateField, t as PanelSurface, tt as lineBreaks, u as editBackspace, ut as fieldSlotOf, v as editInsert, vt as releaseFields, w as beginKeyEvent, wt as registerPalette, x as caretNonceColor, xt as COLORS, y as CapText, yt as releaseUnreferenced, z as scrollbarColumn } from "./surface-DSDGGCSN.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { readFileSync, statSync } from "node:fs";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
@@ -9142,6 +9142,8 @@ function App({ bridge, screen, themeTick = 0 }) {
|
|
|
9142
9142
|
}
|
|
9143
9143
|
if (!exiting.current) {
|
|
9144
9144
|
exiting.current = true;
|
|
9145
|
+
writeCursorShape("reset");
|
|
9146
|
+
writeMouseDisable();
|
|
9145
9147
|
process.kill(process.pid, "SIGINT");
|
|
9146
9148
|
setTimeout(() => process.exit(0), FORCE_EXIT_DELAY_MS).unref();
|
|
9147
9149
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { Box, Text, useCursor, useInput, usePaste, useStdout } from "ink";
|
|
@@ -1298,6 +1298,154 @@ function scrollbarColumn(columns) {
|
|
|
1298
1298
|
function messageBackgroundWidth(width) {
|
|
1299
1299
|
return width - 2 - 3 - 1;
|
|
1300
1300
|
}
|
|
1301
|
+
//#endregion
|
|
1302
|
+
//#region src/terminal/mouse.ts
|
|
1303
|
+
const MOUSE_ENABLE_SEQUENCES = "\x1B[?1003l\x1B[?1000h\x1B[?1002h\x1B[?1006h";
|
|
1304
|
+
const MOUSE_DISABLE_SEQUENCES = "\x1B[?1003l\x1B[?1000l\x1B[?1002l\x1B[?1006l";
|
|
1305
|
+
/**
|
|
1306
|
+
* Terminal-mode sequences must reach the device even when the process exits
|
|
1307
|
+
* right after: on Windows, TTY writes through process.stdout are
|
|
1308
|
+
* asynchronous and a trailing restore sequence is silently dropped, leaving
|
|
1309
|
+
* the terminal in mouse-tracking mode. fd 1 writes are synchronous.
|
|
1310
|
+
*/
|
|
1311
|
+
function writeStdoutRaw(text) {
|
|
1312
|
+
try {
|
|
1313
|
+
writeSync(1, text);
|
|
1314
|
+
} catch {
|
|
1315
|
+
process.stdout.write(text);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
function writeMouseEnable() {
|
|
1319
|
+
writeStdoutRaw(MOUSE_ENABLE_SEQUENCES);
|
|
1320
|
+
}
|
|
1321
|
+
function writeMouseDisable() {
|
|
1322
|
+
writeStdoutRaw(MOUSE_DISABLE_SEQUENCES);
|
|
1323
|
+
}
|
|
1324
|
+
const MAX_BUFFER = 64;
|
|
1325
|
+
const SGR_PREFIX = "\x1B[<";
|
|
1326
|
+
function partialPrefixLength(buffer) {
|
|
1327
|
+
for (let keep = Math.min(2, buffer.length); keep > 0; keep--) if (SGR_PREFIX.startsWith(buffer.slice(-keep))) return keep;
|
|
1328
|
+
return 0;
|
|
1329
|
+
}
|
|
1330
|
+
function createMouseParser(onEvent) {
|
|
1331
|
+
let buffer = "";
|
|
1332
|
+
const pressedButtons = /* @__PURE__ */ new Set();
|
|
1333
|
+
function parseSequenceAt(str, offset) {
|
|
1334
|
+
if (!str.startsWith("\x1B[<", offset)) return null;
|
|
1335
|
+
let index = offset + 3;
|
|
1336
|
+
const values = [];
|
|
1337
|
+
let current = 0;
|
|
1338
|
+
let hasDigit = false;
|
|
1339
|
+
let terminator = "";
|
|
1340
|
+
for (; index < str.length; index++) {
|
|
1341
|
+
const ch = str[index];
|
|
1342
|
+
if (ch >= "0" && ch <= "9") {
|
|
1343
|
+
current = current * 10 + (ch.charCodeAt(0) - 48);
|
|
1344
|
+
hasDigit = true;
|
|
1345
|
+
} else if (ch === ";" && hasDigit) {
|
|
1346
|
+
values.push(current);
|
|
1347
|
+
current = 0;
|
|
1348
|
+
hasDigit = false;
|
|
1349
|
+
} else if (ch === "M" || ch === "m") {
|
|
1350
|
+
if (!hasDigit) return null;
|
|
1351
|
+
values.push(current);
|
|
1352
|
+
terminator = ch;
|
|
1353
|
+
break;
|
|
1354
|
+
} else return null;
|
|
1355
|
+
}
|
|
1356
|
+
if (terminator === "") return null;
|
|
1357
|
+
if (values.length !== 3) return null;
|
|
1358
|
+
return {
|
|
1359
|
+
event: decodeSgrEvent(values[0], values[1], values[2], terminator, pressedButtons),
|
|
1360
|
+
consumed: index - offset + 1
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
return { feed(chunk) {
|
|
1364
|
+
buffer += chunk;
|
|
1365
|
+
while (true) {
|
|
1366
|
+
const start = buffer.indexOf("\x1B[<");
|
|
1367
|
+
if (start === -1) {
|
|
1368
|
+
const keep = partialPrefixLength(buffer);
|
|
1369
|
+
buffer = keep === 0 ? "" : buffer.slice(-keep);
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
if (start > 0) {
|
|
1373
|
+
buffer = buffer.slice(start);
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
if (buffer.length > MAX_BUFFER) {
|
|
1377
|
+
buffer = "";
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const parsed = parseSequenceAt(buffer, 0);
|
|
1381
|
+
if (parsed === null) return;
|
|
1382
|
+
const { event, consumed } = parsed;
|
|
1383
|
+
buffer = buffer.slice(consumed);
|
|
1384
|
+
onEvent(event);
|
|
1385
|
+
}
|
|
1386
|
+
} };
|
|
1387
|
+
}
|
|
1388
|
+
function decodeSgrEvent(rawButtonCode, wireX, wireY, pressRelease, pressed) {
|
|
1389
|
+
const button = rawButtonCode & 3;
|
|
1390
|
+
const isScroll = (rawButtonCode & 64) !== 0;
|
|
1391
|
+
const isMotion = (rawButtonCode & 32) !== 0;
|
|
1392
|
+
const modifiers = {
|
|
1393
|
+
shift: (rawButtonCode & 4) !== 0,
|
|
1394
|
+
alt: (rawButtonCode & 8) !== 0,
|
|
1395
|
+
ctrl: (rawButtonCode & 16) !== 0
|
|
1396
|
+
};
|
|
1397
|
+
let type;
|
|
1398
|
+
let scrollDirection;
|
|
1399
|
+
if (isScroll) {
|
|
1400
|
+
type = "scroll";
|
|
1401
|
+
scrollDirection = button === 0 ? "up" : "down";
|
|
1402
|
+
} else if (isMotion) type = pressed.size > 0 ? "drag" : "move";
|
|
1403
|
+
else if (pressRelease === "M") {
|
|
1404
|
+
type = "down";
|
|
1405
|
+
if (button !== 3) pressed.add(button);
|
|
1406
|
+
} else {
|
|
1407
|
+
type = "up";
|
|
1408
|
+
if (button === 3) pressed.clear();
|
|
1409
|
+
else pressed.delete(button);
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
type,
|
|
1413
|
+
button: button === 3 ? 0 : button,
|
|
1414
|
+
x: wireX - 1,
|
|
1415
|
+
y: wireY - 1,
|
|
1416
|
+
modifiers,
|
|
1417
|
+
...scrollDirection === void 0 ? {} : { scrollDirection }
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function isMouseResidue(input) {
|
|
1421
|
+
return input.includes("\x1B") || /^\[<\d+(;\d+)*[Mm]$/.test(input);
|
|
1422
|
+
}
|
|
1423
|
+
function createMouseController(onEvent) {
|
|
1424
|
+
const parser = createMouseParser(onEvent);
|
|
1425
|
+
const onData = (chunk) => parser.feed(chunk.toString("latin1"));
|
|
1426
|
+
return {
|
|
1427
|
+
enable() {
|
|
1428
|
+
writeMouseEnable();
|
|
1429
|
+
process.stdin.on("data", onData);
|
|
1430
|
+
},
|
|
1431
|
+
disable() {
|
|
1432
|
+
writeMouseDisable();
|
|
1433
|
+
process.stdin.off("data", onData);
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
//#endregion
|
|
1438
|
+
//#region src/terminal/cursor-shape.ts
|
|
1439
|
+
const SEQUENCES = {
|
|
1440
|
+
beam: "\x1B[1 q",
|
|
1441
|
+
block: "\x1B[2 q",
|
|
1442
|
+
reset: "\x1B[0 q",
|
|
1443
|
+
hide: "\x1B[?25l",
|
|
1444
|
+
show: "\x1B[?25h"
|
|
1445
|
+
};
|
|
1446
|
+
function writeCursorShape(shape) {
|
|
1447
|
+
writeStdoutRaw(SEQUENCES[shape]);
|
|
1448
|
+
}
|
|
1301
1449
|
const RegionContext = createContext({
|
|
1302
1450
|
x: 0,
|
|
1303
1451
|
y: 0
|
|
@@ -1435,129 +1583,6 @@ function renderSegments(segments, start, end, baseColor) {
|
|
|
1435
1583
|
return out;
|
|
1436
1584
|
}
|
|
1437
1585
|
//#endregion
|
|
1438
|
-
//#region src/terminal/mouse.ts
|
|
1439
|
-
const MOUSE_ENABLE_SEQUENCES = "\x1B[?1003l\x1B[?1000h\x1B[?1002h\x1B[?1006h";
|
|
1440
|
-
const MOUSE_DISABLE_SEQUENCES = "\x1B[?1003l\x1B[?1000l\x1B[?1002l\x1B[?1006l";
|
|
1441
|
-
function writeMouseEnable() {
|
|
1442
|
-
process.stdout.write(MOUSE_ENABLE_SEQUENCES);
|
|
1443
|
-
}
|
|
1444
|
-
function writeMouseDisable() {
|
|
1445
|
-
process.stdout.write(MOUSE_DISABLE_SEQUENCES);
|
|
1446
|
-
}
|
|
1447
|
-
const MAX_BUFFER = 64;
|
|
1448
|
-
const SGR_PREFIX = "\x1B[<";
|
|
1449
|
-
function partialPrefixLength(buffer) {
|
|
1450
|
-
for (let keep = Math.min(2, buffer.length); keep > 0; keep--) if (SGR_PREFIX.startsWith(buffer.slice(-keep))) return keep;
|
|
1451
|
-
return 0;
|
|
1452
|
-
}
|
|
1453
|
-
function createMouseParser(onEvent) {
|
|
1454
|
-
let buffer = "";
|
|
1455
|
-
const pressedButtons = /* @__PURE__ */ new Set();
|
|
1456
|
-
function parseSequenceAt(str, offset) {
|
|
1457
|
-
if (!str.startsWith("\x1B[<", offset)) return null;
|
|
1458
|
-
let index = offset + 3;
|
|
1459
|
-
const values = [];
|
|
1460
|
-
let current = 0;
|
|
1461
|
-
let hasDigit = false;
|
|
1462
|
-
let terminator = "";
|
|
1463
|
-
for (; index < str.length; index++) {
|
|
1464
|
-
const ch = str[index];
|
|
1465
|
-
if (ch >= "0" && ch <= "9") {
|
|
1466
|
-
current = current * 10 + (ch.charCodeAt(0) - 48);
|
|
1467
|
-
hasDigit = true;
|
|
1468
|
-
} else if (ch === ";" && hasDigit) {
|
|
1469
|
-
values.push(current);
|
|
1470
|
-
current = 0;
|
|
1471
|
-
hasDigit = false;
|
|
1472
|
-
} else if (ch === "M" || ch === "m") {
|
|
1473
|
-
if (!hasDigit) return null;
|
|
1474
|
-
values.push(current);
|
|
1475
|
-
terminator = ch;
|
|
1476
|
-
break;
|
|
1477
|
-
} else return null;
|
|
1478
|
-
}
|
|
1479
|
-
if (terminator === "") return null;
|
|
1480
|
-
if (values.length !== 3) return null;
|
|
1481
|
-
return {
|
|
1482
|
-
event: decodeSgrEvent(values[0], values[1], values[2], terminator, pressedButtons),
|
|
1483
|
-
consumed: index - offset + 1
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
return { feed(chunk) {
|
|
1487
|
-
buffer += chunk;
|
|
1488
|
-
while (true) {
|
|
1489
|
-
const start = buffer.indexOf("\x1B[<");
|
|
1490
|
-
if (start === -1) {
|
|
1491
|
-
const keep = partialPrefixLength(buffer);
|
|
1492
|
-
buffer = keep === 0 ? "" : buffer.slice(-keep);
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
if (start > 0) {
|
|
1496
|
-
buffer = buffer.slice(start);
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
|
-
if (buffer.length > MAX_BUFFER) {
|
|
1500
|
-
buffer = "";
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
const parsed = parseSequenceAt(buffer, 0);
|
|
1504
|
-
if (parsed === null) return;
|
|
1505
|
-
const { event, consumed } = parsed;
|
|
1506
|
-
buffer = buffer.slice(consumed);
|
|
1507
|
-
onEvent(event);
|
|
1508
|
-
}
|
|
1509
|
-
} };
|
|
1510
|
-
}
|
|
1511
|
-
function decodeSgrEvent(rawButtonCode, wireX, wireY, pressRelease, pressed) {
|
|
1512
|
-
const button = rawButtonCode & 3;
|
|
1513
|
-
const isScroll = (rawButtonCode & 64) !== 0;
|
|
1514
|
-
const isMotion = (rawButtonCode & 32) !== 0;
|
|
1515
|
-
const modifiers = {
|
|
1516
|
-
shift: (rawButtonCode & 4) !== 0,
|
|
1517
|
-
alt: (rawButtonCode & 8) !== 0,
|
|
1518
|
-
ctrl: (rawButtonCode & 16) !== 0
|
|
1519
|
-
};
|
|
1520
|
-
let type;
|
|
1521
|
-
let scrollDirection;
|
|
1522
|
-
if (isScroll) {
|
|
1523
|
-
type = "scroll";
|
|
1524
|
-
scrollDirection = button === 0 ? "up" : "down";
|
|
1525
|
-
} else if (isMotion) type = pressed.size > 0 ? "drag" : "move";
|
|
1526
|
-
else if (pressRelease === "M") {
|
|
1527
|
-
type = "down";
|
|
1528
|
-
if (button !== 3) pressed.add(button);
|
|
1529
|
-
} else {
|
|
1530
|
-
type = "up";
|
|
1531
|
-
if (button === 3) pressed.clear();
|
|
1532
|
-
else pressed.delete(button);
|
|
1533
|
-
}
|
|
1534
|
-
return {
|
|
1535
|
-
type,
|
|
1536
|
-
button: button === 3 ? 0 : button,
|
|
1537
|
-
x: wireX - 1,
|
|
1538
|
-
y: wireY - 1,
|
|
1539
|
-
modifiers,
|
|
1540
|
-
...scrollDirection === void 0 ? {} : { scrollDirection }
|
|
1541
|
-
};
|
|
1542
|
-
}
|
|
1543
|
-
function isMouseResidue(input) {
|
|
1544
|
-
return input.includes("\x1B") || /^\[<\d+(;\d+)*[Mm]$/.test(input);
|
|
1545
|
-
}
|
|
1546
|
-
function createMouseController(onEvent) {
|
|
1547
|
-
const parser = createMouseParser(onEvent);
|
|
1548
|
-
const onData = (chunk) => parser.feed(chunk.toString("latin1"));
|
|
1549
|
-
return {
|
|
1550
|
-
enable() {
|
|
1551
|
-
writeMouseEnable();
|
|
1552
|
-
process.stdin.on("data", onData);
|
|
1553
|
-
},
|
|
1554
|
-
disable() {
|
|
1555
|
-
writeMouseDisable();
|
|
1556
|
-
process.stdin.off("data", onData);
|
|
1557
|
-
}
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
//#endregion
|
|
1561
1586
|
//#region src/ui/key-arbiter.ts
|
|
1562
1587
|
/**
|
|
1563
1588
|
* Key-event suppression for ink's broadcast-based useInput.
|
|
@@ -1626,18 +1651,6 @@ function caretNonceBold(cursor) {
|
|
|
1626
1651
|
return Math.floor(cursor / 480) % 2 === 0;
|
|
1627
1652
|
}
|
|
1628
1653
|
//#endregion
|
|
1629
|
-
//#region src/terminal/cursor-shape.ts
|
|
1630
|
-
const SEQUENCES = {
|
|
1631
|
-
beam: "\x1B[1 q",
|
|
1632
|
-
block: "\x1B[2 q",
|
|
1633
|
-
reset: "\x1B[0 q",
|
|
1634
|
-
hide: "\x1B[?25l",
|
|
1635
|
-
show: "\x1B[?25h"
|
|
1636
|
-
};
|
|
1637
|
-
function writeCursorShape(shape) {
|
|
1638
|
-
process.stdout.write(SEQUENCES[shape]);
|
|
1639
|
-
}
|
|
1640
|
-
//#endregion
|
|
1641
1654
|
//#region src/ui/chrome/caps.tsx
|
|
1642
1655
|
function CapText({ background, top, width }) {
|
|
1643
1656
|
if (glyphs.halfBlockCaps) return /* @__PURE__ */ jsx(Text, {
|
|
@@ -3123,4 +3136,4 @@ function PanelSurface({ columns, rows, body, bodyStart, background, blockWidth }
|
|
|
3123
3136
|
}) });
|
|
3124
3137
|
}
|
|
3125
3138
|
//#endregion
|
|
3126
|
-
export {
|
|
3139
|
+
export { colToCharIndex as $, writeCursorShape as A, colorLevel as At, hintBlockTop as B, useCaret as C, permissionModeInfo as Ct, SelectableText as D, subscribePalettes as Dt, markKeymapConsumed as E, setThemeMode as Et, inputContentContains as F, installCrashHandlers as Ft, segmentsKey as G, inputStatusRow as H, messageBackgroundWidth as I, warn as It, sliceByColumns as J, wrapSegments as K, panelAnchorRow as L, isMouseResidue as M, env as Mt, writeMouseDisable as N, configureLogs as Nt, SelectionContext as O, themeMode as Ot, hintSpan as P, error as Pt, charWidth as Q, panelContains as R, caretNonceText as S, paletteColor as St, isKeyConsumed as T, replacePalettes as Tt, glyphs as U, inputFrameTop as V, mergeRuns as W, selectedRange as X, clampFocusRow as Y, toScreenSelection as Z, editDeleteWordRight as _, registerFieldKind as _t, DIALOG_MAX_HEIGHT as a, truncate as at, caretNonceBold as b, specialFieldFactory as bt, asTextItem as c, charactersChipLabel as ct, editCursorLeft as d, fieldStyleOf as dt, errorText as et, editCursorRight as f, hasFieldChar as ft, editDeleteWordLeft as g, linesChipLabel as gt, editDelete as h, isFieldChar as ht, Dialog as i, textWidth as it, createMouseController as j, setColorLevel as jt, Region as k, keyedRegistry as kt, registerWidget as l, fieldExpandOf as lt, editCursorWordRight as m, imageChipLabel as mt, panelLegend as n, locToPoint as nt, rowHeight as o, wrapLines as ot, editCursorWordLeft as p, hasFieldSlots as pt, chromeSelectionText as q, CloseGuardContext as r, segmentGraphemes as rt, wrapStatusLines as s, allocateField as st, PanelSurface as t, lineBreaks as tt, editBackspace as u, fieldSlotOf as ut, editInsert as v, releaseFields as vt, beginKeyEvent as w, registerPalette as wt, caretNonceColor as x, COLORS as xt, CapText as y, releaseUnreferenced as yt, scrollbarColumn as z };
|
package/package.json
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
import { writeStdoutRaw } from './mouse.ts'
|
|
3
|
+
|
|
1
4
|
const SEQUENCES = {
|
|
2
5
|
beam: '\x1b[1 q',
|
|
3
6
|
block: '\x1b[2 q',
|
|
@@ -9,5 +12,5 @@ const SEQUENCES = {
|
|
|
9
12
|
export type CursorShape = keyof typeof SEQUENCES
|
|
10
13
|
|
|
11
14
|
export function writeCursorShape(shape: CursorShape): void {
|
|
12
|
-
|
|
15
|
+
writeStdoutRaw(SEQUENCES[shape])
|
|
13
16
|
}
|
package/src/terminal/mouse.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { writeSync } from 'node:fs'
|
|
2
|
+
|
|
1
3
|
export type MouseEventType = 'down' | 'up' | 'drag' | 'move' | 'scroll'
|
|
2
4
|
|
|
3
5
|
export interface MouseEventData {
|
|
@@ -12,12 +14,27 @@ export interface MouseEventData {
|
|
|
12
14
|
const MOUSE_ENABLE_SEQUENCES = '\x1b[?1003l\x1b[?1000h\x1b[?1002h\x1b[?1006h'
|
|
13
15
|
const MOUSE_DISABLE_SEQUENCES = '\x1b[?1003l\x1b[?1000l\x1b[?1002l\x1b[?1006l'
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Terminal-mode sequences must reach the device even when the process exits
|
|
19
|
+
* right after: on Windows, TTY writes through process.stdout are
|
|
20
|
+
* asynchronous and a trailing restore sequence is silently dropped, leaving
|
|
21
|
+
* the terminal in mouse-tracking mode. fd 1 writes are synchronous.
|
|
22
|
+
*/
|
|
23
|
+
export function writeStdoutRaw(text: string): void {
|
|
24
|
+
try {
|
|
25
|
+
writeSync(1, text)
|
|
26
|
+
} catch {
|
|
27
|
+
// A full stdout buffer (EAGAIN) can only be retried asynchronously.
|
|
28
|
+
process.stdout.write(text)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
export function writeMouseEnable(): void {
|
|
16
|
-
|
|
33
|
+
writeStdoutRaw(MOUSE_ENABLE_SEQUENCES)
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
export function writeMouseDisable(): void {
|
|
20
|
-
|
|
37
|
+
writeStdoutRaw(MOUSE_DISABLE_SEQUENCES)
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
interface ParsedSequence {
|
package/src/ui/app.tsx
CHANGED
|
@@ -9,6 +9,8 @@ import type { ChatBridge } from '../chat/bridge.ts'
|
|
|
9
9
|
import type { AgentActivity } from '../chat/store.ts'
|
|
10
10
|
import { rowIndexFor, selectionText } from './message/layout.ts'
|
|
11
11
|
import { glyphs } from '../terminal/glyphs.ts'
|
|
12
|
+
import { writeMouseDisable } from '../terminal/mouse.ts'
|
|
13
|
+
import { writeCursorShape } from '../terminal/cursor-shape.ts'
|
|
12
14
|
import { chromeSelectionText } from './selection-registry.ts'
|
|
13
15
|
import type { ScreenCapture } from '../terminal/screen.ts'
|
|
14
16
|
import type { LineSelection } from '../model/selection.ts'
|
|
@@ -332,6 +334,10 @@ export function App({ bridge, screen, themeTick = 0 }: AppProps) {
|
|
|
332
334
|
}
|
|
333
335
|
if (!exiting.current) {
|
|
334
336
|
exiting.current = true
|
|
337
|
+
// Restore terminal modes synchronously before signaling: on Windows
|
|
338
|
+
// the queued async writes of the dispose path can be lost at exit.
|
|
339
|
+
writeCursorShape('reset')
|
|
340
|
+
writeMouseDisable()
|
|
335
341
|
process.kill(process.pid, 'SIGINT')
|
|
336
342
|
setTimeout(() => process.exit(0), FORCE_EXIT_DELAY_MS).unref()
|
|
337
343
|
}
|