@cplieger/web-terminal-ui 8.3.2 → 8.3.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/web-terminal-ui",
3
- "version": "8.3.2",
3
+ "version": "8.3.3",
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",
@@ -926,6 +926,18 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
926
926
  const el = termInput();
927
927
  inputFocusedAtPress = el !== null && doc.activeElement === el;
928
928
  }
929
+ // Read at the button's own pointerdown, before the press moves focus onto it.
930
+ function keyboardAtPressOf(btn: HTMLElement): () => boolean {
931
+ let held = false;
932
+ btn.addEventListener(
933
+ "pointerdown",
934
+ () => {
935
+ held = doc.activeElement === termInput();
936
+ },
937
+ { passive: true },
938
+ );
939
+ return () => held;
940
+ }
929
941
  function keyboardParkedOnChrome(): boolean {
930
942
  const active = doc.activeElement;
931
943
  return bar.contains(active) || switcher.contains(active);
@@ -1018,9 +1030,11 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
1018
1030
  collapseSwitcher();
1019
1031
  switchTo(t.id);
1020
1032
  });
1021
- pick(row, ".wt-switcher-row-close").addEventListener("click", (e) => {
1033
+ const rowClose = pick(row, ".wt-switcher-row-close");
1034
+ const heldKeyboard = keyboardAtPressOf(rowClose);
1035
+ rowClose.addEventListener("click", (e) => {
1022
1036
  e.stopPropagation();
1023
- void close_(t.id);
1037
+ void dropTab(t.id, true, heldKeyboard());
1024
1038
  });
1025
1039
  return row;
1026
1040
  }
@@ -1476,9 +1490,10 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
1476
1490
  switchTo(target.id);
1477
1491
  target.el.focus();
1478
1492
  });
1493
+ const heldKeyboard = keyboardAtPressOf(close);
1479
1494
  close.addEventListener("click", (e) => {
1480
1495
  e.stopPropagation();
1481
- void close_(tab.id);
1496
+ void dropTab(tab.id, true, heldKeyboard());
1482
1497
  });
1483
1498
  // Middle-click closes the tab (#8). Suppress the middle-click default on
1484
1499
  // mousedown so the browser's autoscroll/paste affordance does not fire.
@@ -2449,8 +2464,10 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
2449
2464
  // its pane shows next. remote=true also DELETEs the server session (a user
2450
2465
  // close); remote=false is a local drop for a session the server already
2451
2466
  // ended (an SSE removed event or a poll that no longer lists it), so no
2452
- // redundant DELETE is sent.
2453
- async function dropTab(id: string, remote: boolean): Promise<void> {
2467
+ // redundant DELETE is sent. keyboardHeld: the terminal input held the
2468
+ // keyboard when the press on this tab's close button began, so focus goes
2469
+ // back to it once the button that took it is gone.
2470
+ async function dropTab(id: string, remote: boolean, keyboardHeld = false): Promise<void> {
2454
2471
  const idx = tabList.findIndex((t) => t.id === id);
2455
2472
  if (idx < 0) {
2456
2473
  return;
@@ -2539,7 +2556,7 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
2539
2556
  replacement = create();
2540
2557
  }
2541
2558
  }
2542
- if (typing) {
2559
+ if (typing || keyboardHeld) {
2543
2560
  focusInput();
2544
2561
  }
2545
2562
  if (remote) {