@vincemakes/kiso-tui 0.15.9 → 0.15.10
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/compositor.d.ts +26 -0
- package/dist/compositor.js +103 -1
- package/package.json +2 -2
package/dist/compositor.d.ts
CHANGED
|
@@ -212,6 +212,32 @@ export declare class Body {
|
|
|
212
212
|
* live region's first row) — NEVER at the formula's committed-count
|
|
213
213
|
* top: external writes (the CLI's console.error CRLF) can shift the
|
|
214
214
|
* committed content down, and a formula-top ED0 would clear it. */
|
|
215
|
+
/**
|
|
216
|
+
* REL-0152-D18 — a window DRAG is one resize, not forty.
|
|
217
|
+
*
|
|
218
|
+
* SIGWINCH fires continuously while a drag is in progress — every few
|
|
219
|
+
* pixels is another signal — and this used to answer each one at once
|
|
220
|
+
* with an erase and a full repaint. A real terminal REFLOWS on a width
|
|
221
|
+
* change and pushes the rows it displaces into its scrollback, so
|
|
222
|
+
* every one of those repaints deposited another copy of the screen
|
|
223
|
+
* into the history. The owner's two-second drag left six identical
|
|
224
|
+
* copies of the same tool block in the transcript.
|
|
225
|
+
*
|
|
226
|
+
* Nothing clever can undo that afterwards: the scrollback is not ours
|
|
227
|
+
* to rewrite. The only fix is to not paint into the middle of a drag.
|
|
228
|
+
*
|
|
229
|
+
* The geometry is adopted IMMEDIATELY — every cap and bound is read
|
|
230
|
+
* from `#opts` at frame time, so the model is already at the new size
|
|
231
|
+
* and nothing is computed against a stale width. What waits is the
|
|
232
|
+
* PAINT. When the signals stop, one erase and one repaint.
|
|
233
|
+
*
|
|
234
|
+
* The competitor does not have this problem, for a structural reason
|
|
235
|
+
* worth naming rather than envying: it runs on the alternate screen,
|
|
236
|
+
* which has no scrollback to accumulate into. kiso is on the primary
|
|
237
|
+
* screen deliberately — the transcript IS the terminal's own
|
|
238
|
+
* scrollback, which is the product's whole claim — so it pays for
|
|
239
|
+
* that choice here, and has to pay carefully.
|
|
240
|
+
*/
|
|
215
241
|
onResize(): void;
|
|
216
242
|
/** W18: the status row's right-aligned hint is part of the status
|
|
217
243
|
* state — the compacting row passes "esc to cancel" (the affordance
|
package/dist/compositor.js
CHANGED
|
@@ -64,6 +64,10 @@ const SPINNER_MS = 200; // the spinner cadence — a ONE-SHOT re-armed on demand
|
|
|
64
64
|
/** REL-0152-R1: a held row that has never been painted, so the first
|
|
65
65
|
* frame writes every row rather than trusting an empty string. */
|
|
66
66
|
const NOT_PAINTED = "\u0000never";
|
|
67
|
+
/** REL-0152-D18 — how long a drag has to be quiet before it is over.
|
|
68
|
+
* Long enough that a continuous drag never crosses it, short enough
|
|
69
|
+
* that a single resize still feels immediate. */
|
|
70
|
+
const RESIZE_SETTLE_MS = 80;
|
|
67
71
|
const CHROME_ROWS = 4; // box top + input + box bottom + status — the design §03 chrome (V6-3; the box is W6)
|
|
68
72
|
/** W20 — the whole-table-replace comparison: the live task block only
|
|
69
73
|
* redraws when the items actually changed (the task extension's
|
|
@@ -141,6 +145,11 @@ export class Body {
|
|
|
141
145
|
#scrolledOff = 0;
|
|
142
146
|
/** REL-0152-R1: this frame is the repaint after a SIGWINCH. */
|
|
143
147
|
#resizeFrame = false;
|
|
148
|
+
/** REL-0152-D18: a drag's signals are still arriving. */
|
|
149
|
+
#resizePending = false;
|
|
150
|
+
/** REL-0152-D19: the inherited terminal has not been released yet. */
|
|
151
|
+
#needsReset = false;
|
|
152
|
+
#resizeTimer = null;
|
|
144
153
|
#lastH = 0;
|
|
145
154
|
// KC1 §6: the composer's recorded extent — the row count the last
|
|
146
155
|
// frame drew (exit's clear walks it) and the row its CHA parked the
|
|
@@ -868,6 +877,42 @@ export class Body {
|
|
|
868
877
|
return;
|
|
869
878
|
this.#docked = true;
|
|
870
879
|
this.#guardOutput();
|
|
880
|
+
// REL-0152-D19: RELEASE the terminal we were handed, before the
|
|
881
|
+
// first frame.
|
|
882
|
+
//
|
|
883
|
+
// The dock resets on the way OUT and reset nothing on the way IN,
|
|
884
|
+
// so every session began in whatever state the previous occupant
|
|
885
|
+
// left. For THIS product that is not an edge case: kiso's claim is
|
|
886
|
+
// that it survives kill -9, which makes "the last instance died
|
|
887
|
+
// without running its teardown" a supported and advertised way to
|
|
888
|
+
// arrive here.
|
|
889
|
+
//
|
|
890
|
+
// Both of these confine writes to a SUB-REGION of the screen,
|
|
891
|
+
// which is the only way content can survive at a column the dock
|
|
892
|
+
// paints — every chrome row is exactly W wide and written from
|
|
893
|
+
// column 1, so an erase plus a W-wide write covers the row end to
|
|
894
|
+
// end unless the write cannot reach the ends.
|
|
895
|
+
//
|
|
896
|
+
// ESC[r the scroll region back to the whole screen
|
|
897
|
+
// ESC[?69l left/right margin mode OFF (DECLRMM) — ESC[r does
|
|
898
|
+
// NOT release margins, which is why resetting only the
|
|
899
|
+
// region on exit was never enough
|
|
900
|
+
//
|
|
901
|
+
// And two that a KILLED kiso leaves set, which REL-0152-D14
|
|
902
|
+
// introduced and this is the first thing to defend against it: a
|
|
903
|
+
// frame turns autowrap off and the cursor invisible and restores
|
|
904
|
+
// both at its end, so a process that dies BETWEEN those two points
|
|
905
|
+
// hands the shell — and the next kiso — a terminal with wrapping
|
|
906
|
+
// off and no cursor. `kill -9` cannot be caught, so the entry is
|
|
907
|
+
// the only place this can be repaired, and a product whose claim
|
|
908
|
+
// is that it survives kill -9 has to repair it there.
|
|
909
|
+
//
|
|
910
|
+
// ESC[?7h autowrap back to its default
|
|
911
|
+
// ESC[?25h the cursor visible again
|
|
912
|
+
//
|
|
913
|
+
// Idempotent, sixteen bytes, once per session, and correct whether
|
|
914
|
+
// or not anything was actually left set.
|
|
915
|
+
this.#needsReset = true;
|
|
871
916
|
this.#attachResize();
|
|
872
917
|
this.#fullRedraw = true;
|
|
873
918
|
this.#dirty = true;
|
|
@@ -877,6 +922,13 @@ export class Body {
|
|
|
877
922
|
* chrome rows cleared, the cursor home at the input line. */
|
|
878
923
|
exit() {
|
|
879
924
|
this.#unguard?.();
|
|
925
|
+
// REL-0152-D18: a drag in flight must not repaint into a torn-down
|
|
926
|
+
// dock — the timer outlives the compositor otherwise.
|
|
927
|
+
if (this.#resizeTimer !== null) {
|
|
928
|
+
clearTimeout(this.#resizeTimer);
|
|
929
|
+
this.#resizeTimer = null;
|
|
930
|
+
}
|
|
931
|
+
this.#resizePending = false;
|
|
880
932
|
// TUI2-R3v2 ②: the mouse disable rides the SAME teardown as CSI r,
|
|
881
933
|
// and rides it BEFORE the docked guard — an un-docked compositor is
|
|
882
934
|
// exactly the state a superseded or half-torn-down one is in, and
|
|
@@ -1001,9 +1053,50 @@ export class Body {
|
|
|
1001
1053
|
* live region's first row) — NEVER at the formula's committed-count
|
|
1002
1054
|
* top: external writes (the CLI's console.error CRLF) can shift the
|
|
1003
1055
|
* committed content down, and a formula-top ED0 would clear it. */
|
|
1056
|
+
/**
|
|
1057
|
+
* REL-0152-D18 — a window DRAG is one resize, not forty.
|
|
1058
|
+
*
|
|
1059
|
+
* SIGWINCH fires continuously while a drag is in progress — every few
|
|
1060
|
+
* pixels is another signal — and this used to answer each one at once
|
|
1061
|
+
* with an erase and a full repaint. A real terminal REFLOWS on a width
|
|
1062
|
+
* change and pushes the rows it displaces into its scrollback, so
|
|
1063
|
+
* every one of those repaints deposited another copy of the screen
|
|
1064
|
+
* into the history. The owner's two-second drag left six identical
|
|
1065
|
+
* copies of the same tool block in the transcript.
|
|
1066
|
+
*
|
|
1067
|
+
* Nothing clever can undo that afterwards: the scrollback is not ours
|
|
1068
|
+
* to rewrite. The only fix is to not paint into the middle of a drag.
|
|
1069
|
+
*
|
|
1070
|
+
* The geometry is adopted IMMEDIATELY — every cap and bound is read
|
|
1071
|
+
* from `#opts` at frame time, so the model is already at the new size
|
|
1072
|
+
* and nothing is computed against a stale width. What waits is the
|
|
1073
|
+
* PAINT. When the signals stop, one erase and one repaint.
|
|
1074
|
+
*
|
|
1075
|
+
* The competitor does not have this problem, for a structural reason
|
|
1076
|
+
* worth naming rather than envying: it runs on the alternate screen,
|
|
1077
|
+
* which has no scrollback to accumulate into. kiso is on the primary
|
|
1078
|
+
* screen deliberately — the transcript IS the terminal's own
|
|
1079
|
+
* scrollback, which is the product's whole claim — so it pays for
|
|
1080
|
+
* that choice here, and has to pay carefully.
|
|
1081
|
+
*/
|
|
1004
1082
|
onResize() {
|
|
1005
1083
|
if (!this.#isActive())
|
|
1006
1084
|
return;
|
|
1085
|
+
this.#resizePending = true;
|
|
1086
|
+
if (this.#resizeTimer !== null)
|
|
1087
|
+
clearTimeout(this.#resizeTimer);
|
|
1088
|
+
this.#resizeTimer = setTimeout(() => {
|
|
1089
|
+
this.#resizeTimer = null;
|
|
1090
|
+
this.#settleResize();
|
|
1091
|
+
}, RESIZE_SETTLE_MS);
|
|
1092
|
+
if (this.#resizeTimer.unref !== undefined)
|
|
1093
|
+
this.#resizeTimer.unref();
|
|
1094
|
+
}
|
|
1095
|
+
/** The one repaint a drag earns, once its signals have stopped. */
|
|
1096
|
+
#settleResize() {
|
|
1097
|
+
if (!this.#resizePending || !this.#isActive())
|
|
1098
|
+
return;
|
|
1099
|
+
this.#resizePending = false;
|
|
1007
1100
|
const H = this.#opts.height();
|
|
1008
1101
|
const liveRows = this.#lastLiveRows > 0 ? this.#lastLiveRows : 3;
|
|
1009
1102
|
const from = Math.max(1, (this.#lastH > 0 ? this.#lastH : H) - liveRows + 1);
|
|
@@ -1011,7 +1104,7 @@ export class Body {
|
|
|
1011
1104
|
this.#fullRedraw = true;
|
|
1012
1105
|
this.#resizeFrame = true;
|
|
1013
1106
|
this.#dirty = true;
|
|
1014
|
-
this.render();
|
|
1107
|
+
this.render();
|
|
1015
1108
|
}
|
|
1016
1109
|
/** W18: the status row's right-aligned hint is part of the status
|
|
1017
1110
|
* state — the compacting row passes "esc to cancel" (the affordance
|
|
@@ -1373,6 +1466,15 @@ export class Body {
|
|
|
1373
1466
|
// Restored at the end of every frame: prose written OUTSIDE a
|
|
1374
1467
|
// frame (bodyLog, an error) is ordinary output and must still
|
|
1375
1468
|
// wrap, and the shell inherits the terminal when kiso exits.
|
|
1469
|
+
if (this.#needsReset) {
|
|
1470
|
+
// REL-0152-D19: released as the FIRST bytes of the FIRST frame
|
|
1471
|
+
// rather than as a write of its own. A separate write would be
|
|
1472
|
+
// one more thing between the dock coming up and its first
|
|
1473
|
+
// frame, and the PTY gates that wait on the boot stream feel
|
|
1474
|
+
// it; inside the frame it is four sequences and no new event.
|
|
1475
|
+
this.#needsReset = false;
|
|
1476
|
+
out.push("\x1b[r\x1b[?69l\x1b[?7h\x1b[?25h");
|
|
1477
|
+
}
|
|
1376
1478
|
out.push("\x1b[?7l");
|
|
1377
1479
|
out.push(this.#conservative ? "\x1b[?25l" : "\x1b[?2026h"); // D1: sync ON, or cursor-hide where 2026 is dead bytes
|
|
1378
1480
|
// A8: the bottom-anchored window (the model's last H rows) shifts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.10",
|
|
4
4
|
"description": "kiso tui \u2014 the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@vincemakes/kiso-tui-cells": "0.15.
|
|
38
|
+
"@vincemakes/kiso-tui-cells": "0.15.10"
|
|
39
39
|
}
|
|
40
40
|
}
|