@cplieger/web-terminal-ui 5.6.0 → 5.6.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 +2 -2
- package/src/dom-isolation-setup.ts +32 -0
- package/src/features/tabs/index.ts +22 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-ui",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"fast-check": "4.9.0",
|
|
58
58
|
"happy-dom": "20.11.2",
|
|
59
59
|
"html-validate": "11.6.2",
|
|
60
|
-
"knip": "6.32.
|
|
60
|
+
"knip": "6.32.2",
|
|
61
61
|
"prettier": "3.9.6",
|
|
62
62
|
"stylelint": "17.14.1",
|
|
63
63
|
"stylelint-config-standard": "40.0.0",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Per-test cleanup of DOM state that outlives a test file.
|
|
2
|
+
//
|
|
3
|
+
// Loaded via vitest's `setupFiles` alongside fc-strict-setup, because
|
|
4
|
+
// `vitest.config.ts` deliberately sets `isolate: false`: test FILES share one
|
|
5
|
+
// happy-dom instance inside a worker thread, which is what makes the suite fast
|
|
6
|
+
// and also what lets one file's leftovers reach another's assertions.
|
|
7
|
+
//
|
|
8
|
+
// The document selection is the state that actually bites, for two compounding
|
|
9
|
+
// reasons. Several suites end with a live selection on purpose (the kernel's
|
|
10
|
+
// mouse-selection and type-to-focus suites assert that a gesture PRESERVED one,
|
|
11
|
+
// and contextMenu's dismissal suite clicks a real Select All). And happy-dom does
|
|
12
|
+
// not apply the selection fixup a real browser does, so a range whose nodes were
|
|
13
|
+
// removed by `document.body.replaceChildren()` keeps reporting its old text:
|
|
14
|
+
// measured rangeCount 1, isCollapsed false, and toString() unchanged after the
|
|
15
|
+
// nodes were detached.
|
|
16
|
+
//
|
|
17
|
+
// Left alone, that reached `context-menu.test.ts`'s "omits Copy with nothing
|
|
18
|
+
// selected" case, which found text to copy and failed. Order-dependently, so it
|
|
19
|
+
// passed every local run and every PR check, then failed once on main where the
|
|
20
|
+
// thread layout put the files in a different order. Clearing here rather than in
|
|
21
|
+
// each suite keeps the guarantee independent of which files a worker happens to
|
|
22
|
+
// pair up.
|
|
23
|
+
import { beforeEach } from "vitest";
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
// Node-environment files (the default; DOM files opt in per file) have no
|
|
27
|
+
// window, and nothing to clean.
|
|
28
|
+
if (typeof window === "undefined") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
window.getSelection()?.removeAllRanges();
|
|
32
|
+
});
|
|
@@ -2561,6 +2561,20 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
2561
2561
|
if (activeId !== null) {
|
|
2562
2562
|
return;
|
|
2563
2563
|
}
|
|
2564
|
+
// ...but only once the bootstrap has adopted its list. Before that the
|
|
2565
|
+
// view is PARTIAL, and deterministically so: tabs subscribes to the status
|
|
2566
|
+
// stream during setup, before resolveInitialSession issues its
|
|
2567
|
+
// GET /api/sessions, so the SSE snapshot's one-event-per-session arrives
|
|
2568
|
+
// first and this ran on a strip holding only whichever session the server
|
|
2569
|
+
// sent first. Activating it both picked a tab on incomplete information and
|
|
2570
|
+
// silently disabled the saved-tab restore: the bootstrap ladder's first act
|
|
2571
|
+
// is to return early when activeId is already set, so localStorage was
|
|
2572
|
+
// never read and every reload landed on the oldest tab. Boot activation
|
|
2573
|
+
// belongs to that ladder alone, which is the only site with the whole list
|
|
2574
|
+
// AND the saved id; this stays the runtime repair it was written to be.
|
|
2575
|
+
if (!started) {
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2564
2578
|
const first = tabList.find((t) => !isEndedStatus(statusOf(t))) ?? tabList[0];
|
|
2565
2579
|
if (first) {
|
|
2566
2580
|
switchTo(first.id);
|
|
@@ -4025,10 +4039,14 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
4025
4039
|
}
|
|
4026
4040
|
// From here on, tabs added at runtime (create / adopt) animate in.
|
|
4027
4041
|
started = true;
|
|
4028
|
-
//
|
|
4029
|
-
//
|
|
4030
|
-
//
|
|
4031
|
-
//
|
|
4042
|
+
// Something already activated a tab while the list was in flight. The
|
|
4043
|
+
// chrome mounts synchronously, so a user can tap a chip (or a delivered
|
|
4044
|
+
// notification can be clicked) before this resolves, and an explicit
|
|
4045
|
+
// gesture outranks the saved id — return null and leave that switch alone;
|
|
4046
|
+
// the kernel sees connectionInitiated and leaves the loading overlay to
|
|
4047
|
+
// the normal ready path. ensureActive is deliberately NOT one of these
|
|
4048
|
+
// callers during boot (see its `started` gate): when it was, the SSE
|
|
4049
|
+
// snapshot won this race on every load and the ladder below never ran.
|
|
4032
4050
|
if (activeId !== null) {
|
|
4033
4051
|
return null;
|
|
4034
4052
|
}
|