@cplieger/web-terminal-ui 8.2.0 → 8.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/package.json +1 -1
- package/src/kernel/pane.ts +47 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-ui",
|
|
3
|
-
"version": "8.2.
|
|
3
|
+
"version": "8.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Reference touch-first browser UI for @cplieger/web-terminal-engine: a composable kernel + feature modules (tabs, activity monitor, mobile toolbar, context menu, IME, predictive echo, viewport handling) and CSS. One createTerminal() call.",
|
|
6
6
|
"license": "MPL-2.0",
|
package/src/kernel/pane.ts
CHANGED
|
@@ -36,6 +36,8 @@ import type {
|
|
|
36
36
|
|
|
37
37
|
const { mapKeyboardEvent, bracketTextForPaste, prepareTextForTerminal } = keyboard;
|
|
38
38
|
|
|
39
|
+
type GridSize = ReturnType<TerminalEngine["renderer"]["computeSize"]>;
|
|
40
|
+
|
|
39
41
|
const DEFAULT_WS_PATH = "/ws";
|
|
40
42
|
// The family that carries the CELL METRICS, alone, never --font-mono's list:
|
|
41
43
|
// WebKit settles load() over a list as soon as the FIRST family covering the
|
|
@@ -468,7 +470,7 @@ function buildPaneInto(
|
|
|
468
470
|
function updateMousePointer(): void {
|
|
469
471
|
termWrap.classList.toggle("wt-mouse-app", engine.modes.getMouseMode() !== 0);
|
|
470
472
|
}
|
|
471
|
-
function measurableSize():
|
|
473
|
+
function measurableSize(): GridSize | null {
|
|
472
474
|
// A pane hidden by a closed split is `display: none`: the cell width it would
|
|
473
475
|
// measure is 0, and the first render after a reveal would lay out on it.
|
|
474
476
|
if (!fontsLoaded || hidden || viewport.isInTransition()) {
|
|
@@ -477,11 +479,25 @@ function buildPaneInto(
|
|
|
477
479
|
engine.renderer.updateFontMetrics();
|
|
478
480
|
return engine.renderer.computeSize();
|
|
479
481
|
}
|
|
482
|
+
let announced: GridSize | null = null;
|
|
483
|
+
let heldGrow = false;
|
|
484
|
+
function announce(size: GridSize): void {
|
|
485
|
+
announced = size;
|
|
486
|
+
heldGrow = false;
|
|
487
|
+
engine.connection.sendResize();
|
|
488
|
+
}
|
|
489
|
+
function releaseHeldGrow(): void {
|
|
490
|
+
const size = measurableSize();
|
|
491
|
+
if (size !== null) {
|
|
492
|
+
announce(size);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
480
495
|
function maybeSendFirstResize(): void {
|
|
481
|
-
|
|
496
|
+
const size = wsOpen ? measurableSize() : null;
|
|
497
|
+
if (size === null) {
|
|
482
498
|
return;
|
|
483
499
|
}
|
|
484
|
-
|
|
500
|
+
announce(size);
|
|
485
501
|
}
|
|
486
502
|
|
|
487
503
|
const engine: TerminalEngine = createTerminalEngine({
|
|
@@ -496,6 +512,9 @@ function buildPaneInto(
|
|
|
496
512
|
},
|
|
497
513
|
onUserScrollChange: (scrolledUp) => {
|
|
498
514
|
bus.emit("scroll:state", { scrolledUp });
|
|
515
|
+
if (heldGrow && !scrolledUp) {
|
|
516
|
+
releaseHeldGrow();
|
|
517
|
+
}
|
|
499
518
|
},
|
|
500
519
|
callbacks: {
|
|
501
520
|
computeSize: () => engine.renderer.computeSize(),
|
|
@@ -513,6 +532,11 @@ function buildPaneInto(
|
|
|
513
532
|
if (fontsLoaded) {
|
|
514
533
|
markReady();
|
|
515
534
|
}
|
|
535
|
+
// The engine applies a frame to the bound store before this callback,
|
|
536
|
+
// and the alternate screen has no scrollback to hold a size for.
|
|
537
|
+
if (heldGrow && engine.renderer.boundStore().isAlt()) {
|
|
538
|
+
releaseHeldGrow();
|
|
539
|
+
}
|
|
516
540
|
bus.emit("wire:screen", msg);
|
|
517
541
|
} else if (msg.type === "title") {
|
|
518
542
|
// A shell clears its window title when it redraws its prompt after
|
|
@@ -971,11 +995,24 @@ function buildPaneInto(
|
|
|
971
995
|
box: root,
|
|
972
996
|
keyboard: services.keyboardInsets,
|
|
973
997
|
scroll: engine.scroll,
|
|
974
|
-
onSettled() {
|
|
998
|
+
onSettled(wasAtBottom) {
|
|
975
999
|
// The settle is the one resize a keyboard slide or rotation should cost;
|
|
976
1000
|
// sendResize deduplicates.
|
|
977
|
-
|
|
978
|
-
|
|
1001
|
+
const size = measurableSize();
|
|
1002
|
+
if (size !== null) {
|
|
1003
|
+
// A program repaints on resize, and that repaint can erase or reflow the
|
|
1004
|
+
// scrollback being read, so a taller box waits until the reader follows.
|
|
1005
|
+
if (
|
|
1006
|
+
!wasAtBottom &&
|
|
1007
|
+
announced !== null &&
|
|
1008
|
+
size.cols === announced.cols &&
|
|
1009
|
+
size.rows > announced.rows &&
|
|
1010
|
+
!engine.renderer.boundStore().isAlt()
|
|
1011
|
+
) {
|
|
1012
|
+
heldGrow = true;
|
|
1013
|
+
} else {
|
|
1014
|
+
announce(size);
|
|
1015
|
+
}
|
|
979
1016
|
}
|
|
980
1017
|
composition.positionCompositionView();
|
|
981
1018
|
},
|
|
@@ -1039,10 +1076,11 @@ function buildPaneInto(
|
|
|
1039
1076
|
// The gate opened on fallback metrics and the swap period is infinite, so
|
|
1040
1077
|
// one corrective announce once the bytes land; sendResize deduplicates.
|
|
1041
1078
|
void doc.fonts.ready.then(() => {
|
|
1042
|
-
|
|
1079
|
+
const size = destroyed ? null : measurableSize();
|
|
1080
|
+
if (size === null) {
|
|
1043
1081
|
return;
|
|
1044
1082
|
}
|
|
1045
|
-
|
|
1083
|
+
announce(size);
|
|
1046
1084
|
});
|
|
1047
1085
|
}, onFontSettled);
|
|
1048
1086
|
} catch (err) {
|
|
@@ -1353,7 +1391,7 @@ function buildPaneInto(
|
|
|
1353
1391
|
clearActiveSession,
|
|
1354
1392
|
announceSize() {
|
|
1355
1393
|
if (!destroyed && fontsLoaded) {
|
|
1356
|
-
engine.
|
|
1394
|
+
announce(engine.renderer.computeSize());
|
|
1357
1395
|
}
|
|
1358
1396
|
},
|
|
1359
1397
|
focus: focusTerminal,
|