@caupulican/pi-tui 0.81.27 → 0.81.30
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/dist/kill-ring.d.ts +4 -7
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js +45 -2
- package/dist/kill-ring.js.map +1 -1
- package/dist/undo-stack.d.ts +4 -6
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js +32 -2
- package/dist/undo-stack.js.map +1 -1
- package/package.json +1 -1
package/dist/kill-ring.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ring buffer for Emacs-style kill/yank operations.
|
|
3
|
-
*
|
|
4
|
-
* Tracks killed (deleted) text entries. Consecutive kills can accumulate
|
|
5
|
-
* into a single entry. Supports yank (paste most recent) and yank-pop
|
|
6
|
-
* (cycle through older entries).
|
|
7
|
-
*/
|
|
8
1
|
export declare class KillRing {
|
|
9
2
|
private ring;
|
|
3
|
+
private totalBytes;
|
|
4
|
+
private readonly maxEntries;
|
|
5
|
+
private readonly maxBytes;
|
|
6
|
+
constructor(maxEntries?: number, maxBytes?: number);
|
|
10
7
|
/**
|
|
11
8
|
* Add text to the kill ring.
|
|
12
9
|
*
|
package/dist/kill-ring.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kill-ring.d.ts","sourceRoot":"","sources":["../src/kill-ring.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"kill-ring.d.ts","sourceRoot":"","sources":["../src/kill-ring.ts"],"names":[],"mappings":"AA8BA,qBAAa,QAAQ;IACpB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,UAAU,GAAE,MAAsC,EAAE,QAAQ,GAAE,MAAoC,EAG7G;IAED;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAmBzE;IAED,wDAAwD;IACxD,IAAI,IAAI,MAAM,GAAG,SAAS,CAEzB;IAED,uDAAuD;IACvD,MAAM,IAAI,IAAI,CAKb;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;CACD","sourcesContent":["/**\n * Ring buffer for Emacs-style kill/yank operations.\n *\n * Tracks killed (deleted) text entries. Consecutive kills can accumulate\n * into a single entry. Supports yank (paste most recent) and yank-pop\n * (cycle through older entries).\n */\nconst DEFAULT_MAX_KILL_RING_ENTRIES = 60;\nconst DEFAULT_MAX_KILL_RING_BYTES = 1024 * 1024;\n\nfunction positiveLimit(value: number, fallback: number): number {\n\treturn Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;\n}\n\nfunction clampUtf8(value: string, maxBytes: number, keepEnd: boolean): string {\n\tif (Buffer.byteLength(value, \"utf-8\") <= maxBytes) return value;\n\tlet low = 0;\n\tlet high = value.length;\n\twhile (low < high) {\n\t\tconst middle = Math.ceil((low + high) / 2);\n\t\tconst candidate = keepEnd ? value.slice(value.length - middle) : value.slice(0, middle);\n\t\tif (Buffer.byteLength(candidate, \"utf-8\") <= maxBytes) low = middle;\n\t\telse high = middle - 1;\n\t}\n\tlet result = keepEnd ? value.slice(value.length - low) : value.slice(0, low);\n\tif (keepEnd && result.length > 0 && /[\\uDC00-\\uDFFF]/.test(result[0])) result = result.slice(1);\n\tif (!keepEnd && result.length > 0 && /[\\uD800-\\uDBFF]/.test(result.at(-1) ?? \"\")) result = result.slice(0, -1);\n\treturn result;\n}\n\nexport class KillRing {\n\tprivate ring: string[] = [];\n\tprivate totalBytes = 0;\n\tprivate readonly maxEntries: number;\n\tprivate readonly maxBytes: number;\n\n\tconstructor(maxEntries: number = DEFAULT_MAX_KILL_RING_ENTRIES, maxBytes: number = DEFAULT_MAX_KILL_RING_BYTES) {\n\t\tthis.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_KILL_RING_ENTRIES);\n\t\tthis.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_KILL_RING_BYTES);\n\t}\n\n\t/**\n\t * Add text to the kill ring.\n\t *\n\t * @param text - The killed text to add\n\t * @param opts - Push options\n\t * @param opts.prepend - If accumulating, prepend (backward deletion) or append (forward deletion)\n\t * @param opts.accumulate - Merge with the most recent entry instead of creating a new one\n\t */\n\tpush(text: string, opts: { prepend: boolean; accumulate?: boolean }): void {\n\t\tif (!text) return;\n\n\t\tif (opts.accumulate && this.ring.length > 0) {\n\t\t\tconst last = this.ring.pop()!;\n\t\t\tthis.totalBytes -= Buffer.byteLength(last, \"utf-8\");\n\t\t\tconst combined = opts.prepend ? text + last : last + text;\n\t\t\tconst bounded = clampUtf8(combined, this.maxBytes, !opts.prepend);\n\t\t\tthis.ring.push(bounded);\n\t\t\tthis.totalBytes += Buffer.byteLength(bounded, \"utf-8\");\n\t\t} else {\n\t\t\tconst bounded = clampUtf8(text, this.maxBytes, true);\n\t\t\tthis.ring.push(bounded);\n\t\t\tthis.totalBytes += Buffer.byteLength(bounded, \"utf-8\");\n\t\t}\n\t\twhile (this.ring.length > this.maxEntries || this.totalBytes > this.maxBytes) {\n\t\t\tconst removed = this.ring.shift();\n\t\t\tif (removed !== undefined) this.totalBytes -= Buffer.byteLength(removed, \"utf-8\");\n\t\t}\n\t}\n\n\t/** Get most recent entry without modifying the ring. */\n\tpeek(): string | undefined {\n\t\treturn this.ring.length > 0 ? this.ring[this.ring.length - 1] : undefined;\n\t}\n\n\t/** Move last entry to front (for yank-pop cycling). */\n\trotate(): void {\n\t\tif (this.ring.length > 1) {\n\t\t\tconst last = this.ring.pop()!;\n\t\t\tthis.ring.unshift(last);\n\t\t}\n\t}\n\n\tget length(): number {\n\t\treturn this.ring.length;\n\t}\n}\n"]}
|
package/dist/kill-ring.js
CHANGED
|
@@ -5,8 +5,40 @@
|
|
|
5
5
|
* into a single entry. Supports yank (paste most recent) and yank-pop
|
|
6
6
|
* (cycle through older entries).
|
|
7
7
|
*/
|
|
8
|
+
const DEFAULT_MAX_KILL_RING_ENTRIES = 60;
|
|
9
|
+
const DEFAULT_MAX_KILL_RING_BYTES = 1024 * 1024;
|
|
10
|
+
function positiveLimit(value, fallback) {
|
|
11
|
+
return Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;
|
|
12
|
+
}
|
|
13
|
+
function clampUtf8(value, maxBytes, keepEnd) {
|
|
14
|
+
if (Buffer.byteLength(value, "utf-8") <= maxBytes)
|
|
15
|
+
return value;
|
|
16
|
+
let low = 0;
|
|
17
|
+
let high = value.length;
|
|
18
|
+
while (low < high) {
|
|
19
|
+
const middle = Math.ceil((low + high) / 2);
|
|
20
|
+
const candidate = keepEnd ? value.slice(value.length - middle) : value.slice(0, middle);
|
|
21
|
+
if (Buffer.byteLength(candidate, "utf-8") <= maxBytes)
|
|
22
|
+
low = middle;
|
|
23
|
+
else
|
|
24
|
+
high = middle - 1;
|
|
25
|
+
}
|
|
26
|
+
let result = keepEnd ? value.slice(value.length - low) : value.slice(0, low);
|
|
27
|
+
if (keepEnd && result.length > 0 && /[\uDC00-\uDFFF]/.test(result[0]))
|
|
28
|
+
result = result.slice(1);
|
|
29
|
+
if (!keepEnd && result.length > 0 && /[\uD800-\uDBFF]/.test(result.at(-1) ?? ""))
|
|
30
|
+
result = result.slice(0, -1);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
8
33
|
export class KillRing {
|
|
9
34
|
ring = [];
|
|
35
|
+
totalBytes = 0;
|
|
36
|
+
maxEntries;
|
|
37
|
+
maxBytes;
|
|
38
|
+
constructor(maxEntries = DEFAULT_MAX_KILL_RING_ENTRIES, maxBytes = DEFAULT_MAX_KILL_RING_BYTES) {
|
|
39
|
+
this.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_KILL_RING_ENTRIES);
|
|
40
|
+
this.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_KILL_RING_BYTES);
|
|
41
|
+
}
|
|
10
42
|
/**
|
|
11
43
|
* Add text to the kill ring.
|
|
12
44
|
*
|
|
@@ -20,10 +52,21 @@ export class KillRing {
|
|
|
20
52
|
return;
|
|
21
53
|
if (opts.accumulate && this.ring.length > 0) {
|
|
22
54
|
const last = this.ring.pop();
|
|
23
|
-
this.
|
|
55
|
+
this.totalBytes -= Buffer.byteLength(last, "utf-8");
|
|
56
|
+
const combined = opts.prepend ? text + last : last + text;
|
|
57
|
+
const bounded = clampUtf8(combined, this.maxBytes, !opts.prepend);
|
|
58
|
+
this.ring.push(bounded);
|
|
59
|
+
this.totalBytes += Buffer.byteLength(bounded, "utf-8");
|
|
24
60
|
}
|
|
25
61
|
else {
|
|
26
|
-
this.
|
|
62
|
+
const bounded = clampUtf8(text, this.maxBytes, true);
|
|
63
|
+
this.ring.push(bounded);
|
|
64
|
+
this.totalBytes += Buffer.byteLength(bounded, "utf-8");
|
|
65
|
+
}
|
|
66
|
+
while (this.ring.length > this.maxEntries || this.totalBytes > this.maxBytes) {
|
|
67
|
+
const removed = this.ring.shift();
|
|
68
|
+
if (removed !== undefined)
|
|
69
|
+
this.totalBytes -= Buffer.byteLength(removed, "utf-8");
|
|
27
70
|
}
|
|
28
71
|
}
|
|
29
72
|
/** Get most recent entry without modifying the ring. */
|
package/dist/kill-ring.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kill-ring.js","sourceRoot":"","sources":["../src/kill-ring.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,OAAO,QAAQ;IACZ,IAAI,GAAa,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"kill-ring.js","sourceRoot":"","sources":["../src/kill-ring.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ring buffer for Emacs-style kill/yank operations.\n *\n * Tracks killed (deleted) text entries. Consecutive kills can accumulate\n * into a single entry. Supports yank (paste most recent) and yank-pop\n * (cycle through older entries).\n */\nconst DEFAULT_MAX_KILL_RING_ENTRIES = 60;\nconst DEFAULT_MAX_KILL_RING_BYTES = 1024 * 1024;\n\nfunction positiveLimit(value: number, fallback: number): number {\n\treturn Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;\n}\n\nfunction clampUtf8(value: string, maxBytes: number, keepEnd: boolean): string {\n\tif (Buffer.byteLength(value, \"utf-8\") <= maxBytes) return value;\n\tlet low = 0;\n\tlet high = value.length;\n\twhile (low < high) {\n\t\tconst middle = Math.ceil((low + high) / 2);\n\t\tconst candidate = keepEnd ? value.slice(value.length - middle) : value.slice(0, middle);\n\t\tif (Buffer.byteLength(candidate, \"utf-8\") <= maxBytes) low = middle;\n\t\telse high = middle - 1;\n\t}\n\tlet result = keepEnd ? value.slice(value.length - low) : value.slice(0, low);\n\tif (keepEnd && result.length > 0 && /[\\uDC00-\\uDFFF]/.test(result[0])) result = result.slice(1);\n\tif (!keepEnd && result.length > 0 && /[\\uD800-\\uDBFF]/.test(result.at(-1) ?? \"\")) result = result.slice(0, -1);\n\treturn result;\n}\n\nexport class KillRing {\n\tprivate ring: string[] = [];\n\tprivate totalBytes = 0;\n\tprivate readonly maxEntries: number;\n\tprivate readonly maxBytes: number;\n\n\tconstructor(maxEntries: number = DEFAULT_MAX_KILL_RING_ENTRIES, maxBytes: number = DEFAULT_MAX_KILL_RING_BYTES) {\n\t\tthis.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_KILL_RING_ENTRIES);\n\t\tthis.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_KILL_RING_BYTES);\n\t}\n\n\t/**\n\t * Add text to the kill ring.\n\t *\n\t * @param text - The killed text to add\n\t * @param opts - Push options\n\t * @param opts.prepend - If accumulating, prepend (backward deletion) or append (forward deletion)\n\t * @param opts.accumulate - Merge with the most recent entry instead of creating a new one\n\t */\n\tpush(text: string, opts: { prepend: boolean; accumulate?: boolean }): void {\n\t\tif (!text) return;\n\n\t\tif (opts.accumulate && this.ring.length > 0) {\n\t\t\tconst last = this.ring.pop()!;\n\t\t\tthis.totalBytes -= Buffer.byteLength(last, \"utf-8\");\n\t\t\tconst combined = opts.prepend ? text + last : last + text;\n\t\t\tconst bounded = clampUtf8(combined, this.maxBytes, !opts.prepend);\n\t\t\tthis.ring.push(bounded);\n\t\t\tthis.totalBytes += Buffer.byteLength(bounded, \"utf-8\");\n\t\t} else {\n\t\t\tconst bounded = clampUtf8(text, this.maxBytes, true);\n\t\t\tthis.ring.push(bounded);\n\t\t\tthis.totalBytes += Buffer.byteLength(bounded, \"utf-8\");\n\t\t}\n\t\twhile (this.ring.length > this.maxEntries || this.totalBytes > this.maxBytes) {\n\t\t\tconst removed = this.ring.shift();\n\t\t\tif (removed !== undefined) this.totalBytes -= Buffer.byteLength(removed, \"utf-8\");\n\t\t}\n\t}\n\n\t/** Get most recent entry without modifying the ring. */\n\tpeek(): string | undefined {\n\t\treturn this.ring.length > 0 ? this.ring[this.ring.length - 1] : undefined;\n\t}\n\n\t/** Move last entry to front (for yank-pop cycling). */\n\trotate(): void {\n\t\tif (this.ring.length > 1) {\n\t\t\tconst last = this.ring.pop()!;\n\t\t\tthis.ring.unshift(last);\n\t\t}\n\t}\n\n\tget length(): number {\n\t\treturn this.ring.length;\n\t}\n}\n"]}
|
package/dist/undo-stack.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic undo stack with clone-on-push semantics.
|
|
3
|
-
*
|
|
4
|
-
* Stores deep clones of state snapshots. Popped snapshots are returned
|
|
5
|
-
* directly (no re-cloning) since they are already detached.
|
|
6
|
-
*/
|
|
7
1
|
export declare class UndoStack<S> {
|
|
8
2
|
private stack;
|
|
3
|
+
private totalBytes;
|
|
4
|
+
private readonly maxEntries;
|
|
5
|
+
private readonly maxBytes;
|
|
6
|
+
constructor(maxEntries?: number, maxBytes?: number);
|
|
9
7
|
/** Push a deep clone of the given state onto the stack. */
|
|
10
8
|
push(state: S): void;
|
|
11
9
|
/** Pop and return the most recent snapshot, or undefined if empty. */
|
package/dist/undo-stack.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"undo-stack.d.ts","sourceRoot":"","sources":["../src/undo-stack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"undo-stack.d.ts","sourceRoot":"","sources":["../src/undo-stack.ts"],"names":[],"mappings":"AAoBA,qBAAa,SAAS,CAAC,CAAC;IACvB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,UAAU,GAAE,MAAiC,EAAE,QAAQ,GAAE,MAA+B,EAGnG;IAED,2DAA2D;IAC3D,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAanB;IAED,sEAAsE;IACtE,GAAG,IAAI,CAAC,GAAG,SAAS,CAKnB;IAED,4BAA4B;IAC5B,KAAK,IAAI,IAAI,CAGZ;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;CACD","sourcesContent":["import { deserialize, serialize } from \"node:v8\";\n\n/**\n * Generic undo stack with clone-on-push semantics.\n *\n * Stores deep clones of state snapshots. Popped snapshots are returned\n * directly (no re-cloning) since they are already detached.\n */\nconst DEFAULT_MAX_UNDO_ENTRIES = 100;\nconst DEFAULT_MAX_UNDO_BYTES = 8 * 1024 * 1024;\n\ninterface UndoEntry<S> {\n\tstate: S;\n\tbytes: number;\n}\n\nfunction positiveLimit(value: number, fallback: number): number {\n\treturn Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;\n}\n\nexport class UndoStack<S> {\n\tprivate stack: UndoEntry<S>[] = [];\n\tprivate totalBytes = 0;\n\tprivate readonly maxEntries: number;\n\tprivate readonly maxBytes: number;\n\n\tconstructor(maxEntries: number = DEFAULT_MAX_UNDO_ENTRIES, maxBytes: number = DEFAULT_MAX_UNDO_BYTES) {\n\t\tthis.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_UNDO_ENTRIES);\n\t\tthis.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_UNDO_BYTES);\n\t}\n\n\t/** Push a deep clone of the given state onto the stack. */\n\tpush(state: S): void {\n\t\tconst encoded = serialize(state);\n\t\tconst bytes = encoded.byteLength;\n\t\tif (bytes > this.maxBytes) {\n\t\t\tthis.clear();\n\t\t\treturn;\n\t\t}\n\t\tthis.stack.push({ state: deserialize(encoded) as S, bytes });\n\t\tthis.totalBytes += bytes;\n\t\twhile (this.stack.length > this.maxEntries || this.totalBytes > this.maxBytes) {\n\t\t\tconst removed = this.stack.shift();\n\t\t\tif (removed) this.totalBytes -= removed.bytes;\n\t\t}\n\t}\n\n\t/** Pop and return the most recent snapshot, or undefined if empty. */\n\tpop(): S | undefined {\n\t\tconst entry = this.stack.pop();\n\t\tif (!entry) return undefined;\n\t\tthis.totalBytes -= entry.bytes;\n\t\treturn entry.state;\n\t}\n\n\t/** Remove all snapshots. */\n\tclear(): void {\n\t\tthis.stack.length = 0;\n\t\tthis.totalBytes = 0;\n\t}\n\n\tget length(): number {\n\t\treturn this.stack.length;\n\t}\n}\n"]}
|
package/dist/undo-stack.js
CHANGED
|
@@ -1,22 +1,52 @@
|
|
|
1
|
+
import { deserialize, serialize } from "node:v8";
|
|
1
2
|
/**
|
|
2
3
|
* Generic undo stack with clone-on-push semantics.
|
|
3
4
|
*
|
|
4
5
|
* Stores deep clones of state snapshots. Popped snapshots are returned
|
|
5
6
|
* directly (no re-cloning) since they are already detached.
|
|
6
7
|
*/
|
|
8
|
+
const DEFAULT_MAX_UNDO_ENTRIES = 100;
|
|
9
|
+
const DEFAULT_MAX_UNDO_BYTES = 8 * 1024 * 1024;
|
|
10
|
+
function positiveLimit(value, fallback) {
|
|
11
|
+
return Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;
|
|
12
|
+
}
|
|
7
13
|
export class UndoStack {
|
|
8
14
|
stack = [];
|
|
15
|
+
totalBytes = 0;
|
|
16
|
+
maxEntries;
|
|
17
|
+
maxBytes;
|
|
18
|
+
constructor(maxEntries = DEFAULT_MAX_UNDO_ENTRIES, maxBytes = DEFAULT_MAX_UNDO_BYTES) {
|
|
19
|
+
this.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_UNDO_ENTRIES);
|
|
20
|
+
this.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_UNDO_BYTES);
|
|
21
|
+
}
|
|
9
22
|
/** Push a deep clone of the given state onto the stack. */
|
|
10
23
|
push(state) {
|
|
11
|
-
|
|
24
|
+
const encoded = serialize(state);
|
|
25
|
+
const bytes = encoded.byteLength;
|
|
26
|
+
if (bytes > this.maxBytes) {
|
|
27
|
+
this.clear();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.stack.push({ state: deserialize(encoded), bytes });
|
|
31
|
+
this.totalBytes += bytes;
|
|
32
|
+
while (this.stack.length > this.maxEntries || this.totalBytes > this.maxBytes) {
|
|
33
|
+
const removed = this.stack.shift();
|
|
34
|
+
if (removed)
|
|
35
|
+
this.totalBytes -= removed.bytes;
|
|
36
|
+
}
|
|
12
37
|
}
|
|
13
38
|
/** Pop and return the most recent snapshot, or undefined if empty. */
|
|
14
39
|
pop() {
|
|
15
|
-
|
|
40
|
+
const entry = this.stack.pop();
|
|
41
|
+
if (!entry)
|
|
42
|
+
return undefined;
|
|
43
|
+
this.totalBytes -= entry.bytes;
|
|
44
|
+
return entry.state;
|
|
16
45
|
}
|
|
17
46
|
/** Remove all snapshots. */
|
|
18
47
|
clear() {
|
|
19
48
|
this.stack.length = 0;
|
|
49
|
+
this.totalBytes = 0;
|
|
20
50
|
}
|
|
21
51
|
get length() {
|
|
22
52
|
return this.stack.length;
|
package/dist/undo-stack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"undo-stack.js","sourceRoot":"","sources":["../src/undo-stack.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,SAAS;IACb,KAAK,
|
|
1
|
+
{"version":3,"file":"undo-stack.js","sourceRoot":"","sources":["../src/undo-stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,wBAAwB,GAAG,GAAG,CAAC;AACrC,MAAM,sBAAsB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAO/C,SAAS,aAAa,CAAC,KAAa,EAAE,QAAgB,EAAU;IAC/D,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAAA,CACvF;AAED,MAAM,OAAO,SAAS;IACb,KAAK,GAAmB,EAAE,CAAC;IAC3B,UAAU,GAAG,CAAC,CAAC;IACN,UAAU,CAAS;IACnB,QAAQ,CAAS;IAElC,YAAY,UAAU,GAAW,wBAAwB,EAAE,QAAQ,GAAW,sBAAsB,EAAE;QACrG,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IAAA,CAChE;IAED,2DAA2D;IAC3D,IAAI,CAAC,KAAQ,EAAQ;QACpB,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;QACjC,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC/E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,OAAO;gBAAE,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAC/C,CAAC;IAAA,CACD;IAED,sEAAsE;IACtE,GAAG,GAAkB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC;QAC/B,OAAO,KAAK,CAAC,KAAK,CAAC;IAAA,CACnB;IAED,4BAA4B;IAC5B,KAAK,GAAS;QACb,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;IAAA,CACpB;IAED,IAAI,MAAM,GAAW;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAAA,CACzB;CACD","sourcesContent":["import { deserialize, serialize } from \"node:v8\";\n\n/**\n * Generic undo stack with clone-on-push semantics.\n *\n * Stores deep clones of state snapshots. Popped snapshots are returned\n * directly (no re-cloning) since they are already detached.\n */\nconst DEFAULT_MAX_UNDO_ENTRIES = 100;\nconst DEFAULT_MAX_UNDO_BYTES = 8 * 1024 * 1024;\n\ninterface UndoEntry<S> {\n\tstate: S;\n\tbytes: number;\n}\n\nfunction positiveLimit(value: number, fallback: number): number {\n\treturn Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;\n}\n\nexport class UndoStack<S> {\n\tprivate stack: UndoEntry<S>[] = [];\n\tprivate totalBytes = 0;\n\tprivate readonly maxEntries: number;\n\tprivate readonly maxBytes: number;\n\n\tconstructor(maxEntries: number = DEFAULT_MAX_UNDO_ENTRIES, maxBytes: number = DEFAULT_MAX_UNDO_BYTES) {\n\t\tthis.maxEntries = positiveLimit(maxEntries, DEFAULT_MAX_UNDO_ENTRIES);\n\t\tthis.maxBytes = positiveLimit(maxBytes, DEFAULT_MAX_UNDO_BYTES);\n\t}\n\n\t/** Push a deep clone of the given state onto the stack. */\n\tpush(state: S): void {\n\t\tconst encoded = serialize(state);\n\t\tconst bytes = encoded.byteLength;\n\t\tif (bytes > this.maxBytes) {\n\t\t\tthis.clear();\n\t\t\treturn;\n\t\t}\n\t\tthis.stack.push({ state: deserialize(encoded) as S, bytes });\n\t\tthis.totalBytes += bytes;\n\t\twhile (this.stack.length > this.maxEntries || this.totalBytes > this.maxBytes) {\n\t\t\tconst removed = this.stack.shift();\n\t\t\tif (removed) this.totalBytes -= removed.bytes;\n\t\t}\n\t}\n\n\t/** Pop and return the most recent snapshot, or undefined if empty. */\n\tpop(): S | undefined {\n\t\tconst entry = this.stack.pop();\n\t\tif (!entry) return undefined;\n\t\tthis.totalBytes -= entry.bytes;\n\t\treturn entry.state;\n\t}\n\n\t/** Remove all snapshots. */\n\tclear(): void {\n\t\tthis.stack.length = 0;\n\t\tthis.totalBytes = 0;\n\t}\n\n\tget length(): number {\n\t\treturn this.stack.length;\n\t}\n}\n"]}
|
package/package.json
CHANGED