@cplieger/web-terminal-ui 5.4.4 → 5.4.5
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 +47 -15
- package/src/features/tabs/strip.ts +1 -1
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.5",
|
|
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",
|
|
@@ -744,26 +744,58 @@ export function tabs(opts: TabsOptions = {}): TerminalFeature<TabsApi> {
|
|
|
744
744
|
// name, `list()` reports the real name, and clearing the progress needs no
|
|
745
745
|
// cleanup — the next paint simply stops adding it.
|
|
746
746
|
function relabelAll(): void {
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
|
|
747
|
+
// De-duplicate identical labels with a "(k)" suffix, numbered by the session's
|
|
748
|
+
// AGE — never by its position in the strip.
|
|
749
|
+
//
|
|
750
|
+
// The suffix used to be assigned by encounter order while walking tabList, which
|
|
751
|
+
// made it a property of the SLOT: two tabs both called "workspace" always read
|
|
752
|
+
// "workspace" then "workspace (2)" whichever way round they sat, so the label
|
|
753
|
+
// text stayed put while the sessions moved underneath it. Dragging one of them
|
|
754
|
+
// changed nothing on screen, and the reorder — which had worked correctly all
|
|
755
|
+
// along, server included — was invisible. It also meant dragging tab A could
|
|
756
|
+
// renumber tab B, which is the clearest sign the number belonged to the wrong
|
|
757
|
+
// thing.
|
|
758
|
+
//
|
|
759
|
+
// A label answers "which session is this", so it has to be a property of the
|
|
760
|
+
// session: stable while other tabs move, and travelling with its own tab. Age is
|
|
761
|
+
// that key. createdAt (the server's, tie-broken by id) rather than the local
|
|
762
|
+
// `born` counter, so every device and every reload agrees on which "workspace"
|
|
763
|
+
// is (2) — `born` is adoption order, which races.
|
|
764
|
+
//
|
|
765
|
+
// Numbering is recomputed over the LIVE group each time, so it stays contiguous:
|
|
766
|
+
// closing "(2)" of three renumbers the survivor rather than leaving a hole. That
|
|
767
|
+
// is a renumber with a visible cause the user just performed, which is the one
|
|
768
|
+
// kind that reads as sensible.
|
|
769
|
+
const groups = new Map<string, Tab[]>();
|
|
750
770
|
for (const t of tabList) {
|
|
751
771
|
const { text, fallback } = baseLabel(t);
|
|
752
|
-
|
|
753
|
-
|
|
772
|
+
// Fallback labels are not real names: several untitled tabs all read
|
|
773
|
+
// "New tab" with no numbering, which is the pre-existing behaviour.
|
|
774
|
+
if (fallback) {
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
const group = groups.get(text);
|
|
778
|
+
if (group) {
|
|
779
|
+
group.push(t);
|
|
780
|
+
} else {
|
|
781
|
+
groups.set(text, [t]);
|
|
754
782
|
}
|
|
755
783
|
}
|
|
756
|
-
const
|
|
757
|
-
for (const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
if (!fallback && (counts.get(text) ?? 0) > 1) {
|
|
761
|
-
const k = (seen.get(text) ?? 0) + 1;
|
|
762
|
-
seen.set(text, k);
|
|
763
|
-
if (k > 1) {
|
|
764
|
-
display = `${text} (${String(k)})`;
|
|
765
|
-
}
|
|
784
|
+
const ordinals = new Map<Tab, number>();
|
|
785
|
+
for (const group of groups.values()) {
|
|
786
|
+
if (group.length < 2) {
|
|
787
|
+
continue; // nothing to disambiguate from, so no suffix at all
|
|
766
788
|
}
|
|
789
|
+
[...group]
|
|
790
|
+
.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id))
|
|
791
|
+
.forEach((t, i) => {
|
|
792
|
+
ordinals.set(t, i + 1);
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
for (const t of tabList) {
|
|
796
|
+
const { text } = baseLabel(t);
|
|
797
|
+
const k = ordinals.get(t);
|
|
798
|
+
const display = k !== undefined && k > 1 ? `${text} (${String(k)})` : text;
|
|
767
799
|
t.display = display;
|
|
768
800
|
t.label.textContent = display;
|
|
769
801
|
// The accessible name carries the state and the percentage as well as
|
|
@@ -31,7 +31,7 @@ import { statusPhrase, statusRevealsDot } from "./model.js";
|
|
|
31
31
|
* pointer announces itself; this only filters the coincidence where one event of a
|
|
32
32
|
* sweep happens to land within REORDER_MOVE_EPS_PX of the previous one (a direction
|
|
33
33
|
* reversal, or a frame where the motion was almost entirely vertical). */
|
|
34
|
-
export const REORDER_STILL_MS =
|
|
34
|
+
export const REORDER_STILL_MS = 50;
|
|
35
35
|
|
|
36
36
|
/** Fallback guard (ms): commit the pending slot this long after the last MOVEMENT, for
|
|
37
37
|
* the case where `dragover` stops arriving altogether and no stationary event ever
|