@cplieger/web-terminal-ui 5.4.2 → 5.4.4
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/features/tabs/index.ts +59 -24
- package/src/features/tabs/strip.ts +26 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-ui",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.4",
|
|
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",
|
|
@@ -45,6 +45,7 @@ import { browserNotifierEnv, createNotifier } from "./notify.js";
|
|
|
45
45
|
import {
|
|
46
46
|
REORDER_MOVE_EPS_PX,
|
|
47
47
|
REORDER_REST_MS,
|
|
48
|
+
REORDER_STILL_MS,
|
|
48
49
|
REORDER_SETTLE_MS,
|
|
49
50
|
REORDER_SHIFT_TRANS,
|
|
50
51
|
REORDER_SLOT_FADE_MS,
|
|
@@ -232,11 +233,17 @@ async function createSessionHonouringRetry(
|
|
|
232
233
|
// First refusal always speaks; after that only every
|
|
233
234
|
// CREATE_RETRY_REANNOUNCE_MS, so a multi-minute wait is neither silent nor
|
|
234
235
|
// a toast storm.
|
|
236
|
+
//
|
|
237
|
+
// The suffix says the page is WAITING, not recovering from a failure. It
|
|
238
|
+
// used to say "retrying", which read as an error loop when the server had
|
|
239
|
+
// only said "not yet" -- and the first announcement fires before any retry
|
|
240
|
+
// has happened at all, so a user two seconds into a boot was told
|
|
241
|
+
// "retrying" about a request that had been made exactly once.
|
|
235
242
|
if (lastAnnouncedAt === 0 || elapsed - lastAnnouncedAt >= CREATE_RETRY_REANNOUNCE_MS) {
|
|
236
243
|
lastAnnouncedAt = elapsed;
|
|
237
|
-
const
|
|
238
|
-
ctx.toast(`${
|
|
239
|
-
ctx.announce(`${
|
|
244
|
+
const reason = err.serverMessage ?? "Server is not ready yet";
|
|
245
|
+
ctx.toast(`${reason}; waiting…`, 8000);
|
|
246
|
+
ctx.announce(`${reason}; waiting…`);
|
|
240
247
|
}
|
|
241
248
|
// Separate from the throttled pair above, and unthrottled: this writes the
|
|
242
249
|
// reason onto the loading OVERLAY, which is the only surface a user can
|
|
@@ -245,7 +252,7 @@ async function createSessionHonouringRetry(
|
|
|
245
252
|
// stacking notifications, so there is no storm to throttle, and repeating
|
|
246
253
|
// the same string is idempotent. This is what turns the black screen of a
|
|
247
254
|
// twenty-minute tools install into a screen that says why it is waiting.
|
|
248
|
-
ctx.loadingReason(`${err.serverMessage ?? "Server is not ready yet"}
|
|
255
|
+
ctx.loadingReason(`${err.serverMessage ?? "Server is not ready yet"}; waiting…`);
|
|
249
256
|
await new Promise((resolve) => {
|
|
250
257
|
window.setTimeout(resolve, err.retryAfterMs ?? CREATE_RETRY_FALLBACK_MS);
|
|
251
258
|
});
|
|
@@ -679,6 +686,9 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
679
686
|
let restTimer: ReturnType<typeof setTimeout> | null = null;
|
|
680
687
|
let restBefore: HTMLElement | null = null;
|
|
681
688
|
let restX: number | null = null;
|
|
689
|
+
// When the pointer last actually MOVED. A stationary dragover this long after it
|
|
690
|
+
// is believed as a stop (see trackRest).
|
|
691
|
+
let restMovedAt = 0;
|
|
682
692
|
// Whether a `drop` fired for the drag in flight. It is the exact signal for
|
|
683
693
|
// "the user released deliberately" as opposed to "the drag was abandoned":
|
|
684
694
|
// Escape and a refused release fire dragend with no drop at all. dragend
|
|
@@ -1943,12 +1953,19 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
1943
1953
|
// travelling pointer rearranges nothing at all; the moment it comes to REST the
|
|
1944
1954
|
// slot opens.
|
|
1945
1955
|
//
|
|
1946
|
-
// Rest is detected
|
|
1947
|
-
//
|
|
1948
|
-
//
|
|
1949
|
-
//
|
|
1950
|
-
//
|
|
1951
|
-
//
|
|
1956
|
+
// Rest is detected from TWO signals, and separating them is what lets the slot
|
|
1957
|
+
// open promptly without a fast sweep committing every slot it crosses:
|
|
1958
|
+
//
|
|
1959
|
+
// - a `dragover` at an unchanged position is POSITIVE evidence of a stop, so it
|
|
1960
|
+
// commits after a short REORDER_STILL_MS confirmation. This is the signal that
|
|
1961
|
+
// normally decides, and the reason the delay is short.
|
|
1962
|
+
// - the absence of events is only a FALLBACK (armRestNet), for a browser that
|
|
1963
|
+
// stops delivering dragover entirely. That one has to out-wait the drag loop's
|
|
1964
|
+
// 350ms cadence, so it is long — and it is rarely what decides.
|
|
1965
|
+
//
|
|
1966
|
+
// One quiet window had to serve both jobs before this, which is why it could not be
|
|
1967
|
+
// shortened: the timer deciding responsiveness was the same one that had to survive
|
|
1968
|
+
// the cadence.
|
|
1952
1969
|
function trackRest(clientX: number): void {
|
|
1953
1970
|
const dragged = draggingEl;
|
|
1954
1971
|
if (!dragged) {
|
|
@@ -1964,21 +1981,39 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
1964
1981
|
return;
|
|
1965
1982
|
}
|
|
1966
1983
|
restBefore = before;
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1984
|
+
const now = Date.now();
|
|
1985
|
+
if (moved) {
|
|
1986
|
+
// Still travelling. Push the net out and wait.
|
|
1987
|
+
restMovedAt = now;
|
|
1988
|
+
armRestNet();
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
// A dragover at an UNCHANGED position is positive evidence that the pointer has
|
|
1992
|
+
// stopped, which is a far stronger signal than the absence of events — and it is
|
|
1993
|
+
// what lets the slot open promptly instead of out-waiting the drag loop's 350ms
|
|
1994
|
+
// cadence. The elapsed check filters the coincidence where one event of a sweep
|
|
1995
|
+
// lands within REORDER_MOVE_EPS_PX of the previous (a reversal, or a frame whose
|
|
1996
|
+
// motion was almost all vertical); a real stop clears it on the next event.
|
|
1997
|
+
if (now - restMovedAt >= REORDER_STILL_MS) {
|
|
1998
|
+
flushRest();
|
|
1999
|
+
return;
|
|
1981
2000
|
}
|
|
2001
|
+
armRestNet(); // too soon to believe; keep the net alive
|
|
2002
|
+
}
|
|
2003
|
+
// armRestNet is the no-events fallback: if dragover stops arriving altogether, no
|
|
2004
|
+
// stationary event will ever confirm the stop, so this commits anyway. Sized to
|
|
2005
|
+
// out-wait the cadence (see REORDER_REST_MS) because it is the one timer that a
|
|
2006
|
+
// fast sweep could otherwise expire between two of its own events.
|
|
2007
|
+
function armRestNet(): void {
|
|
2008
|
+
if (restTimer !== null) {
|
|
2009
|
+
clearTimeout(restTimer);
|
|
2010
|
+
}
|
|
2011
|
+
restTimer = setTimeout(() => {
|
|
2012
|
+
restTimer = null;
|
|
2013
|
+
const target = restBefore;
|
|
2014
|
+
restBefore = null;
|
|
2015
|
+
commitSlot(target);
|
|
2016
|
+
}, REORDER_REST_MS);
|
|
1982
2017
|
}
|
|
1983
2018
|
function cancelRest(): void {
|
|
1984
2019
|
if (restTimer === null) {
|
|
@@ -21,24 +21,35 @@ import { statusPhrase, statusRevealsDot } from "./model.js";
|
|
|
21
21
|
// motion is spelled in — index.ts owns the mechanism, strip.ts owns the values,
|
|
22
22
|
// the same split switcher.ts keeps for the mobile swipe.
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
24
|
+
/** How long (ms) the pointer must have been un-moved before a stationary `dragover` is
|
|
25
|
+
* believed as "stopped".
|
|
26
26
|
*
|
|
27
|
-
* This is
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* This is the number that decides how fast the slot opens, and it is small because a
|
|
28
|
+
* `dragover` at an UNCHANGED position is positive evidence of rest — far better
|
|
29
|
+
* evidence than the absence of events, which is all the fallback below has. The drag
|
|
30
|
+
* loop keeps delivering events while the pointer is held still, so a genuinely stopped
|
|
31
|
+
* pointer announces itself; this only filters the coincidence where one event of a
|
|
32
|
+
* sweep happens to land within REORDER_MOVE_EPS_PX of the previous one (a direction
|
|
33
|
+
* reversal, or a frame where the motion was almost entirely vertical). */
|
|
34
|
+
export const REORDER_STILL_MS = 90;
|
|
35
|
+
|
|
36
|
+
/** Fallback guard (ms): commit the pending slot this long after the last MOVEMENT, for
|
|
37
|
+
* the case where `dragover` stops arriving altogether and no stationary event ever
|
|
38
|
+
* confirms the stop.
|
|
30
39
|
*
|
|
31
|
-
* **
|
|
32
|
-
* guarantees a `dragover` every 350ms — the
|
|
33
|
-
* not per mouse movement — so
|
|
34
|
-
* of a fast sweep, and the strip commits every slot the
|
|
35
|
-
*
|
|
36
|
-
*
|
|
40
|
+
* **Its floor is set by the platform, not by taste, and this is the timer that floor
|
|
41
|
+
* applies to.** HTML5 drag-and-drop only guarantees a `dragover` every 350ms — the drag
|
|
42
|
+
* loop runs on that cadence, not per mouse movement — so a guard at or below it can
|
|
43
|
+
* expire BETWEEN two events of a fast sweep, and the strip then commits every slot the
|
|
44
|
+
* pointer crosses. Measured in production: at 120ms a quick pass over five tabs moved
|
|
45
|
+
* all five.
|
|
37
46
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
47
|
+
* A pure quiet-window design has to make this ONE number carry both jobs, which is why
|
|
48
|
+
* it could not be shortened — the same timer that decides responsiveness was the one
|
|
49
|
+
* that had to out-wait the cadence, so 500ms was safe and felt slow. Splitting rest
|
|
50
|
+
* DETECTION (the stationary event above) from the no-events NET (here) lets each be
|
|
51
|
+
* sized for its own job. This one is rarely the deciding signal at all. */
|
|
52
|
+
export const REORDER_REST_MS = 450;
|
|
42
53
|
|
|
43
54
|
/** Movement (px) between two dragover events below which the pointer counts as still.
|
|
44
55
|
* A hand resting on a mouse is never perfectly still, and treating a 1px tremor as a
|