@cplieger/web-terminal-ui 8.6.0-dev.8 → 8.6.0-dev.9
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/README.md +1 -1
- package/css/00-tokens.css +8 -7
- package/css/30-tabs.css +15 -17
- package/css/31-switcher.css +1 -1
- package/package.json +1 -1
- package/src/features/tabs/index.ts +12 -18
- package/src/features/tabs/model.ts +29 -29
- package/src/features/tabs/strip.ts +1 -1
- package/src/presets/agent-tabbed.ts +6 -8
- package/src/presets/tabbed.ts +5 -4
- package/src/presets.ts +8 -7
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ web-terminal-ui is built for a shell or a coding agent served by web-terminal-en
|
|
|
14
14
|
|
|
15
15
|
- One `createTerminal()` call builds the UI inside an element you provide. Four presets cover a single, a touch and two tabbed layouts.
|
|
16
16
|
- On a touchscreen it adds a key bar with Tab, Esc, arrows, Enter and a sticky Ctrl, a long-press paste menu and iOS soft-keyboard handling.
|
|
17
|
-
- Tabs show activity dots from the program's `OSC 9;4` progress
|
|
17
|
+
- Tabs show activity dots from the program's `OSC 7501` status, `OSC 9;4` progress and classified `OSC 9` notifications, and raise browser notifications. Two tabs can sit side by side. A full-page terminal gives each tab and each split its own page address, and names the browser tab after the active tab, followed by the page's own title: `fix build · Web Terminal`.
|
|
18
18
|
- Native text selection, IME input and dictation work, and predictive echo hides latency.
|
|
19
19
|
|
|
20
20
|
Consider [xterm.js](https://github.com/xtermjs/xterm.js) if you want a terminal component for any PTY backend, such as node-pty. It has an optional GPU-accelerated renderer and a set of addons, and VS Code uses it. To build a UI of your own on web-terminal-engine, depend on the engine alone.
|
package/css/00-tokens.css
CHANGED
|
@@ -80,11 +80,12 @@
|
|
|
80
80
|
--status-working: #52a9fe; /* agent working (waves): the same blue as the default --accent */
|
|
81
81
|
--status-done: #22c55e; /* turn finished: solid green */
|
|
82
82
|
--status-input: #fb923c; /* action required: solid orange + ring */
|
|
83
|
-
/*
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
/* The warning and failed statuses (STATUS_WARNING and STATUS_FAILED in
|
|
84
|
+
features/tabs/model.ts name their sources). Both render as the SAME object
|
|
85
|
+
as working -- identical glow + travelling wave, one shared rule and one
|
|
86
|
+
shared pair of keyframes (30-tabs.css) -- differing ONLY in hue, because
|
|
87
|
+
OSC 9;4 models one progress indicator with several states rather than three
|
|
88
|
+
unrelated dots. Neither wears a ring: the frozen hard ring is
|
|
88
89
|
--status-input's exclusive meaning ("blocked, waiting on you"), and neither
|
|
89
90
|
warning nor error asks the user to act.
|
|
90
91
|
LIGHTNESS SEPARATION IS LOAD-BEARING, NOT DECORATIVE. Three states that
|
|
@@ -100,8 +101,8 @@
|
|
|
100
101
|
indistinguishable to a colour-blind user, whatever their hues. warning is
|
|
101
102
|
YELLOW, deliberately not amber -- amber would collide with --status-input's
|
|
102
103
|
orange (L 75.8%), which sits between working and warning. */
|
|
103
|
-
--status-warning: #facc15; /*
|
|
104
|
-
--status-failed: #dc2626; /*
|
|
104
|
+
--status-warning: #facc15; /* warning: yellow disc, working's wave */
|
|
105
|
+
--status-failed: #dc2626; /* a reported error: red disc, working's wave; crashed reuses it solid and static */
|
|
105
106
|
--radius: 0.75rem;
|
|
106
107
|
--radius-inner: 0.5rem;
|
|
107
108
|
/* Small square icon buttons (e.g. the 24px tab-close x): at that size the
|
package/css/30-tabs.css
CHANGED
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
width: 300px;
|
|
108
108
|
flex: 0 1 auto;
|
|
109
109
|
min-width: 100px;
|
|
110
|
-
/* Positioning context for the chip's own overlay: the
|
|
111
|
-
|
|
110
|
+
/* Positioning context for the chip's own overlay: the determinate progress
|
|
111
|
+
bar sits on its bottom edge (see .wt-progress-bar). Nothing else
|
|
112
112
|
inside a chip is absolutely positioned, and the strip's two root-level
|
|
113
113
|
overlays (.wt-tab-ghost, .wt-tab-menu) are children of .wt-root, not of a
|
|
114
114
|
chip, so this cannot capture them. */
|
|
@@ -660,13 +660,13 @@
|
|
|
660
660
|
}
|
|
661
661
|
|
|
662
662
|
/* ===== Activity dots: one .wt-status-dot vocabulary for the strip, the
|
|
663
|
-
switcher and the overview rows. Eight states told apart without
|
|
664
|
-
(WCAG 1.4.1, by shape and motion): three animated progress hues
|
|
665
|
-
warning, failed) spread across lightness, a ringed disc (input), two
|
|
666
|
-
discs (done, crashed) and two hollow ones (idle, exited)
|
|
667
|
-
|
|
668
|
-
(.wt-reports, set by paintStatusDot)
|
|
669
|
-
presumeReports option; data-status is set either way. */
|
|
663
|
+
switcher and the overview rows. Eight built-in states told apart without
|
|
664
|
+
colour (WCAG 1.4.1, by shape and motion): three animated progress hues
|
|
665
|
+
(working, warning, failed) spread across lightness, a ringed disc (input), two
|
|
666
|
+
static discs (done, crashed) and two hollow ones (idle, exited); any other
|
|
667
|
+
status keeps the default hollow dot below. A dot shows once its session
|
|
668
|
+
reports activity (.wt-reports, set by paintStatusDot) or from creation under
|
|
669
|
+
tabs' presumeReports option; data-status is set either way. */
|
|
670
670
|
:where(.wt-root) .wt-status-dot {
|
|
671
671
|
display: none;
|
|
672
672
|
width: 9px;
|
|
@@ -688,12 +688,10 @@
|
|
|
688
688
|
animation: none;
|
|
689
689
|
}
|
|
690
690
|
|
|
691
|
-
/* ===== Determinate progress bar (OSC 9;4
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
by all three chip sites (desktop strip chip, mobile active row, expanded list
|
|
696
|
-
row) because it ships in the one chipContent builder.
|
|
691
|
+
/* ===== Determinate progress bar (the session's percentage: OSC 7501 progress, else OSC 9;4) =====
|
|
692
|
+
One 2px line on the chip's bottom edge, shared by all three chip sites
|
|
693
|
+
(desktop strip chip, mobile active row, expanded list row) because it ships
|
|
694
|
+
in the one chipContent builder.
|
|
697
695
|
|
|
698
696
|
The ACCENT colour, not a per-state tint: the bar answers "how far", the dot
|
|
699
697
|
answers "in what state", and tinting the bar per state would say the same
|
|
@@ -746,8 +744,8 @@
|
|
|
746
744
|
the element), which read as a blink and coupled the layers — an element's
|
|
747
745
|
opacity always multiplies onto its pseudos, so true independence requires
|
|
748
746
|
animating disc COLORS, not element opacity. Server-side this is driven by the
|
|
749
|
-
|
|
750
|
-
the prompt.
|
|
747
|
+
program's own status reports, so it tracks agent activity, not the user
|
|
748
|
+
typing at the prompt.
|
|
751
749
|
|
|
752
750
|
WORKING, WARNING AND FAILED SHARE THIS RULE, AND BOTH KEYFRAMES, VERBATIM.
|
|
753
751
|
OSC 9;4 models ONE progress indicator with several states (1/3 active,
|
package/css/31-switcher.css
CHANGED
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
process crashed or reported an error. Those four only (CueStatus) — the rule
|
|
361
361
|
the user set is "only states that want the user, like a notification", so the
|
|
362
362
|
ongoing progress states (working / warning) never raise it. `failed` DOES,
|
|
363
|
-
because
|
|
363
|
+
because a reported error is a result the program parked rather than a phase in
|
|
364
364
|
progress; the override below is what keeps it looking like a notification here.
|
|
365
365
|
Reuses the exact .wt-status-dot rules (30-tabs.css, --status-* tokens) so it
|
|
366
366
|
matches the per-tab dots. Hidden until the tabs feature sets data-status;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-ui",
|
|
3
|
-
"version": "8.6.0-dev.
|
|
3
|
+
"version": "8.6.0-dev.9",
|
|
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",
|
|
@@ -330,14 +330,13 @@ class SettleCreateFailed extends Error {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
/** Read the layout, then list, then (when nothing live is listed) create one
|
|
333
|
-
* session, starting the turn again after any 503, so a
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* reason. */
|
|
333
|
+
* session, starting the turn again after any 503, so a create refused
|
|
334
|
+
* mid-restore never adds a tab beside the restored ones. A non-503 layout read
|
|
335
|
+
* proves the host was not restoring when it was served, so a list issued after
|
|
336
|
+
* it sees the whole restored set. Past `CREATE_RETRY_MAX_TOTAL_MS` a refused
|
|
337
|
+
* layout read or list returns the last good list, or rethrows the 503; a create
|
|
338
|
+
* that fails for good, a spent 503 included, throws `SettleCreateFailed`
|
|
339
|
+
* carrying the list, an abort its reason. */
|
|
341
340
|
async function settleSessionSet(
|
|
342
341
|
api: SessionAPIClient,
|
|
343
342
|
ctx: TerminalContext,
|
|
@@ -1068,9 +1067,7 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
1068
1067
|
const prefersReduce = (): boolean =>
|
|
1069
1068
|
win.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1070
1069
|
|
|
1071
|
-
// Recomputes every display label with de-duplication.
|
|
1072
|
-
// is applied at render time and stored nowhere, so de-duplication and the
|
|
1073
|
-
// rename field see real names and clearing it needs no cleanup.
|
|
1070
|
+
// Recomputes every display label with de-duplication.
|
|
1074
1071
|
function relabelAll(): void {
|
|
1075
1072
|
// The "(k)" suffix is numbered by the session's AGE, never by its slot:
|
|
1076
1073
|
// numbered by encounter order, the labels stayed put while the sessions
|
|
@@ -1140,11 +1137,8 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
1140
1137
|
return foldedCueStatus(statusOf(t), t.activity);
|
|
1141
1138
|
}
|
|
1142
1139
|
|
|
1143
|
-
/** shownProgress is a tab's percentage as it may currently be DISPLAYED
|
|
1144
|
-
*
|
|
1145
|
-
* one the progress channel owns. Those are the only two ways a percentage
|
|
1146
|
-
* stops showing — the program's own OSC 9;4;0 (an explicit -1) and a status
|
|
1147
|
-
* from another channel; see renderedProgress for why there is no third. */
|
|
1140
|
+
/** shownProgress is a tab's percentage as it may currently be DISPLAYED;
|
|
1141
|
+
* renderedProgress owns when that is. */
|
|
1148
1142
|
function shownProgress(t: Tab): number {
|
|
1149
1143
|
return renderedProgress(statusOf(t), t.progress);
|
|
1150
1144
|
}
|
|
@@ -3344,8 +3338,8 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
3344
3338
|
t.seenSeq = seen;
|
|
3345
3339
|
closeReadSurfaces(t);
|
|
3346
3340
|
}
|
|
3347
|
-
// A program that
|
|
3348
|
-
// permission request for the next user gesture.
|
|
3341
|
+
// A program that reports activity may also post an OSC 9 notification:
|
|
3342
|
+
// arm the permission request for the next user gesture.
|
|
3349
3343
|
if (reports) {
|
|
3350
3344
|
ctx.shell.notifications.arm();
|
|
3351
3345
|
}
|
|
@@ -19,7 +19,9 @@ export type StatusRecord = SessionInfo & {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
// The status vocabulary is module-private so a status's MEANING has one home:
|
|
22
|
-
// every consumer asks a predicate below instead of comparing strings.
|
|
22
|
+
// every consumer asks a predicate below instead of comparing strings. The
|
|
23
|
+
// protocol states named below are the built-in sources; a classified OSC 9
|
|
24
|
+
// notification may set any status.
|
|
23
25
|
//
|
|
24
26
|
// An exited session is viewable history that can never produce output again, so
|
|
25
27
|
// session selection prefers live sessions everywhere.
|
|
@@ -30,11 +32,14 @@ const STATUS_EXITED = "exited";
|
|
|
30
32
|
* restart never paints as a failure. Terminal, like "exited". */
|
|
31
33
|
const STATUS_CRASHED = "crashed";
|
|
32
34
|
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
+
/** An error the program reported: OSC 7501 state `error`, or OSC 9;4 progress
|
|
36
|
+
* state 2 (iTerm2 semantics). A STATE that persists until the program reports
|
|
37
|
+
* another, or the process dies. */
|
|
35
38
|
const STATUS_FAILED = "failed";
|
|
36
39
|
|
|
37
|
-
/** OSC 9;4 progress state 4, the warning state (iTerm2 semantics).
|
|
40
|
+
/** OSC 9;4 progress state 4, the warning state (iTerm2 semantics). OSC 9;4
|
|
41
|
+
* cannot set it once the session has sent an OSC 7501 report, until a
|
|
42
|
+
* terminal reset (RIS): OSC 7501 outranks OSC 9;4 until a RIS forgets it. */
|
|
38
43
|
const STATUS_WARNING = "warning";
|
|
39
44
|
|
|
40
45
|
/** Whether the session's process is GONE, whichever way it went: reloading onto
|
|
@@ -51,8 +56,9 @@ export function statusRevealsDot(status: string): boolean {
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
/** The human wording for a status, for BOTH the dot's tooltip and the tab's
|
|
54
|
-
* accessible name, so they cannot disagree.
|
|
55
|
-
*
|
|
59
|
+
* accessible name, so they cannot disagree. A status outside the built-in
|
|
60
|
+
* vocabulary (a host classifier's, or a newer server's) falls back to the raw
|
|
61
|
+
* value rather than being hidden. */
|
|
56
62
|
export function statusPhrase(status: string): string {
|
|
57
63
|
switch (status) {
|
|
58
64
|
case "working":
|
|
@@ -94,22 +100,16 @@ export function normalizeProgress(value: unknown): number {
|
|
|
94
100
|
return Math.min(100, Math.round(value));
|
|
95
101
|
}
|
|
96
102
|
|
|
97
|
-
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/** The percentage a status may SHOW. Two things clear it: the program's own
|
|
106
|
-
* OSC 9;4;0 (arriving as PROGRESS_ABSENT) and a status the channel does not own
|
|
107
|
-
* (a state-4 percentage persists under a done latch, which state 4 does not
|
|
108
|
-
* clear, and would otherwise paint a bar beside a done dot). Deliberately
|
|
109
|
-
* no third clear: 100% is a STATE that persists, and a timeout would assert a
|
|
110
|
-
* change the program never reported. */
|
|
103
|
+
/** The percentage a status may SHOW. Under any live status it is the server's
|
|
104
|
+
* value, because the status and the percentage can come from different
|
|
105
|
+
* channels: OSC 7501 or a notification can set the status while OSC 9;4
|
|
106
|
+
* supplies the percentage. Two things clear it: the server's PROGRESS_ABSENT
|
|
107
|
+
* (the program cleared or never reported a percentage, e.g. OSC 9;4;0) and an
|
|
108
|
+
* ended process, whose percentage nothing will ever clear. No timeout clears
|
|
109
|
+
* it: 100% is a STATE that persists, and a timeout would assert a change the
|
|
110
|
+
* program never reported. */
|
|
111
111
|
export function renderedProgress(status: string, progress: number): number {
|
|
112
|
-
return
|
|
112
|
+
return isEndedStatus(status) ? PROGRESS_ABSENT : progress;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// The secondary-activity vocabulary: a host-reported background activity that
|
|
@@ -221,8 +221,8 @@ export function cueIconName(status: CueStatus): "input" | "done" | "alert" {
|
|
|
221
221
|
|
|
222
222
|
/** The cue statuses meaning the turn is OVER WITHOUT INCIDENT, the only ones a
|
|
223
223
|
* live background task may blank. `done` is the whole set: every other cue must
|
|
224
|
-
* reach the viewer AT ONCE, and `failed`
|
|
225
|
-
* turn
|
|
224
|
+
* reach the viewer AT ONCE, and `failed` is an error the program reported,
|
|
225
|
+
* not a clean end of the turn. */
|
|
226
226
|
const SETTLED_CUES: ReadonlySet<string> = new Set<CueStatus>(["done"]);
|
|
227
227
|
|
|
228
228
|
/** The status the CUE surfaces see: the session's own, unless a settled turn's
|
|
@@ -380,10 +380,9 @@ export interface Tab {
|
|
|
380
380
|
/** The chip's determinate progress bar (the 2px line on its bottom edge).
|
|
381
381
|
* Present on every chip, hidden until a percentage exists. */
|
|
382
382
|
progressEl: HTMLElement;
|
|
383
|
-
/** The last
|
|
383
|
+
/** The last percentage the server reported for this session, or
|
|
384
384
|
* PROGRESS_ABSENT. Held raw: whether it is currently SHOWN is a render-time
|
|
385
|
-
* question (renderedProgress)
|
|
386
|
-
* any title field. */
|
|
385
|
+
* question (renderedProgress). */
|
|
387
386
|
progress: number;
|
|
388
387
|
/** The chip's secondary activity mark (the rounded-square ring beside the
|
|
389
388
|
* status dot). Present on every chip, and costing no layout until a state
|
|
@@ -403,9 +402,10 @@ export interface Tab {
|
|
|
403
402
|
* offset away, and it stopped meaning the same line once the session produced
|
|
404
403
|
* output while backgrounded. In memory only. */
|
|
405
404
|
view: ViewMemory | null;
|
|
406
|
-
/** Sticky: true once this session emitted a genuine activity signal (OSC
|
|
407
|
-
*
|
|
408
|
-
*
|
|
405
|
+
/** Sticky: true once this session emitted a genuine activity signal (OSC 7501
|
|
406
|
+
* status, OSC 9;4 progress or a classified OSC 9 notification). Its activity
|
|
407
|
+
* dot is shown only while true; a session that never reports activity (a
|
|
408
|
+
* plain shell) keeps a clean, dot-less tab. Fed from the server's
|
|
409
409
|
* reportsActivity via applyStatus. */
|
|
410
410
|
reports: boolean;
|
|
411
411
|
/** The user's pinned name (the server's `pinnedTitle` wire field). Outranks
|
|
@@ -235,7 +235,7 @@ export function paintStatusDot(el: HTMLElement, status: string, reports: boolean
|
|
|
235
235
|
* a tooltip worded by activityPhrase. No state REMOVES the attribute, which is
|
|
236
236
|
* what collapses the mark's footprint to zero. The reveal is the mark's OWN
|
|
237
237
|
* state, not the status dot's reportsActivity gate: a background run is a fact
|
|
238
|
-
* independent of whether the program ever
|
|
238
|
+
* independent of whether the program ever reported status. */
|
|
239
239
|
export function paintActivityMark(el: HTMLElement, state: string, count: number): void {
|
|
240
240
|
const phrase = activityPhrase(state, count);
|
|
241
241
|
if (phrase === "") {
|
|
@@ -10,14 +10,12 @@ import { type TabbedPresetOptions, buildTabbed } from "./tabbed.js";
|
|
|
10
10
|
/** Tabbed UI for an agent shell. Same features as presetTabbed, tuned for
|
|
11
11
|
* sessions that ARE agents:
|
|
12
12
|
* - `presumeReports`: every session's program is an agent that WILL report
|
|
13
|
-
* OSC 9;4 progress
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* plain shell never grows
|
|
18
|
-
*
|
|
19
|
-
* status classifier that maps OSC 9 notifications to done/needs-input. Tab
|
|
20
|
-
* names are server-side too but are not agent-specific — see buildTabbed. */
|
|
13
|
+
* activity (OSC 7501 status, OSC 9;4 progress or a classified OSC 9
|
|
14
|
+
* notification), so the idle activity dot
|
|
15
|
+
* shows from tab creation instead of popping in when the agent first
|
|
16
|
+
* reports; the server's sticky reportsActivity flag then merely confirms.
|
|
17
|
+
* (presetTabbed keeps the evidence-driven reveal: a plain shell never grows
|
|
18
|
+
* a meaningless dot.) */
|
|
21
19
|
export function presetAgentTabbed(opts: TabbedPresetOptions = {}): TerminalFeature<unknown>[] {
|
|
22
20
|
return buildTabbed(true, opts);
|
|
23
21
|
}
|
package/src/presets/tabbed.ts
CHANGED
|
@@ -29,10 +29,11 @@ export interface TabbedPresetOptions {
|
|
|
29
29
|
* toolbar opened from the tab bar's keyboard button, the activity monitor (the
|
|
30
30
|
* status SSE), tabs wired to both, and animations. The monitor is always
|
|
31
31
|
* included: a tab's dot shows only once its session reports activity (OSC
|
|
32
|
-
* 9;4
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* 7501 status, OSC 9;4 progress or a classified OSC 9 notification), or from
|
|
33
|
+
* creation under presumeReports.
|
|
34
|
+
* The toolbar and monitor precede tabs, which reads their APIs via ctx.use.
|
|
35
|
+
* Titles are the engine's: a browser that re-derived one would disagree with
|
|
36
|
+
* the server and every other client. Shared with presetAgentTabbed. */
|
|
36
37
|
export function buildTabbed(
|
|
37
38
|
agentShell: boolean,
|
|
38
39
|
opts: TabbedPresetOptions = {},
|
package/src/presets.ts
CHANGED
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
* The tabbed presets (presetTabbed / presetAgentTabbed) share the same feature
|
|
12
12
|
* set (both include the activity monitor, which drives the per-tab activity
|
|
13
13
|
* dot). They differ in one flag, `presumeReports`: presetAgentTabbed presumes
|
|
14
|
-
* every session's program is an agent that WILL report OSC
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* every session's program is an agent that WILL report activity (OSC 7501
|
|
15
|
+
* status, OSC 9;4 progress or a classified OSC 9 notification), so the idle dot
|
|
16
|
+
* shows from tab creation instead of appearing seconds later on the first
|
|
17
|
+
* report, while presetTabbed keeps the evidence-driven reveal so a plain shell
|
|
18
|
+
* never grows a meaningless dot.
|
|
19
|
+
* Session NAMES are not a preset concern: the engine resolves them, including a
|
|
20
|
+
* title its host pushes. Which status a session holds is the server's: the UI
|
|
21
|
+
* renders the status it is sent.
|
|
21
22
|
*
|
|
22
23
|
* @module
|
|
23
24
|
*/
|