@cplieger/web-terminal-engine 3.5.0 → 3.7.0

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/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from "./types.js";
2
2
  export { LineStore } from "./store.js";
3
- export type { WindowState, StoreChanges } from "./store.js";
3
+ export type { WindowState, StoreChanges, StoreSnapshot } from "./store.js";
4
4
  export * as render from "./render.js";
5
+ export type { ViewMemory } from "./render.js";
5
6
  export * as keyboard from "./keyboard.js";
6
7
  export * as toolbar from "./toolbar.js";
7
8
  export * as scroll from "./scroll.js";
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Gap geometry over the store's retained absolute indices.
3
+ *
4
+ * The store's held lines form an INTERVAL SET (docs/paged-scrollback.md §5.2):
5
+ * a live tail plus zero or more browse-cache runs below it, with gaps between
6
+ * them. This module answers only where the GAPS are, and it derives them from
7
+ * the retained KEY SET.
8
+ *
9
+ * It must read the key set, not the cache's membership: a hole can exist inside
10
+ * the live tail — an `unsupported` eviction-gap resume legally leaves two
11
+ * disjoint tail runs — and geometry drawn from cache membership alone would
12
+ * join adjacent rows across such a hole with no marker, silently splicing
13
+ * unrelated regions of output.
14
+ *
15
+ * BROWSE MEMBERSHIP is deliberately not modelled here. The store keeps it as a
16
+ * plain `Set` of keys, because every question it is asked (is the reader on
17
+ * cache, how many rows, which to evict) is a per-key question, and answering
18
+ * those from ranges meant walking numeric spans the store need not even hold —
19
+ * the replay-jump band spans every index a reconnecting client is missing.
20
+ * Classification also cannot be DERIVED from geometry: a fetched page can sit
21
+ * flush against the tail with no numeric gap at all.
22
+ *
23
+ * Every interval is half-open: `[lo, hi)` holds `hi - lo` indices and `hi` is
24
+ * one past the last. Sets are SORTED and DISJOINT with no touching pair.
25
+ */
26
+
27
+ /** A half-open range of absolute line indices: `[lo, hi)`. */
28
+ export interface Interval {
29
+ /** First index in the range. */
30
+ lo: number;
31
+ /** One past the last index in the range. */
32
+ hi: number;
33
+ }
34
+
35
+ /**
36
+ * Coalesce a set of retained absolute indices into sorted, disjoint intervals.
37
+ * This is the GAP source: the complement of the returned runs (between the
38
+ * lowest and highest retained index) is exactly the set of holes the fetch
39
+ * trigger may target and the renderer must mark.
40
+ */
41
+ export function retainedIntervals(keys: Iterable<number>): Interval[] {
42
+ const sorted = [...keys].sort((a, b) => a - b);
43
+ const out: Interval[] = [];
44
+ let run: Interval | null = null;
45
+ for (const k of sorted) {
46
+ if (run !== null && k === run.hi) {
47
+ run.hi = k + 1; // contiguous: extend
48
+ continue;
49
+ }
50
+ if (run !== null && k < run.hi) {
51
+ continue; // duplicate key (defensive; a Map cannot produce one)
52
+ }
53
+ if (run !== null) {
54
+ out.push(run);
55
+ }
56
+ run = { lo: k, hi: k + 1 };
57
+ }
58
+ if (run !== null) {
59
+ out.push(run);
60
+ }
61
+ return out;
62
+ }
63
+
64
+ /**
65
+ * The holes between a set's intervals: for `[[0,10), [20,30)]` the single gap
66
+ * `[10,20)`. Only INTERIOR gaps are returned — the frontier below the lowest
67
+ * retained index is a pseudo-gap the store derives from `pagingFloor` instead,
68
+ * because its lower edge is a policy question (what is still worth requesting)
69
+ * rather than a geometric one.
70
+ */
71
+ export function interiorGaps(set: readonly Interval[]): Interval[] {
72
+ const out: Interval[] = [];
73
+ for (let i = 1; i < set.length; i++) {
74
+ const prev = set[i - 1];
75
+ const cur = set[i];
76
+ if (prev !== undefined && cur !== undefined && cur.lo > prev.hi) {
77
+ out.push({ lo: prev.hi, hi: cur.lo });
78
+ }
79
+ }
80
+ return out;
81
+ }