@juliusbrussee/caveman-tui 0.65.2
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 +767 -0
- package/dist/autocomplete.d.ts +52 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +623 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/chord.d.ts +57 -0
- package/dist/chord.d.ts.map +1 -0
- package/dist/chord.js +97 -0
- package/dist/chord.js.map +1 -0
- package/dist/color-depth.d.ts +17 -0
- package/dist/color-depth.d.ts.map +1 -0
- package/dist/color-depth.js +147 -0
- package/dist/color-depth.js.map +1 -0
- package/dist/components/Chapters.d.ts +41 -0
- package/dist/components/Chapters.d.ts.map +1 -0
- package/dist/components/Chapters.js +103 -0
- package/dist/components/Chapters.js.map +1 -0
- package/dist/components/DiffView.d.ts +75 -0
- package/dist/components/DiffView.d.ts.map +1 -0
- package/dist/components/DiffView.js +170 -0
- package/dist/components/DiffView.js.map +1 -0
- package/dist/components/StatusLine.d.ts +135 -0
- package/dist/components/StatusLine.d.ts.map +1 -0
- package/dist/components/StatusLine.js +133 -0
- package/dist/components/StatusLine.js.map +1 -0
- package/dist/components/SubagentOverlay.d.ts +63 -0
- package/dist/components/SubagentOverlay.d.ts.map +1 -0
- package/dist/components/SubagentOverlay.js +124 -0
- package/dist/components/SubagentOverlay.js.map +1 -0
- package/dist/components/box.d.ts +22 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +104 -0
- package/dist/components/box.js.map +1 -0
- package/dist/components/cancellable-loader.d.ts +22 -0
- package/dist/components/cancellable-loader.d.ts.map +1 -0
- package/dist/components/cancellable-loader.js +35 -0
- package/dist/components/cancellable-loader.js.map +1 -0
- package/dist/components/editor.d.ts +244 -0
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/editor.js +1861 -0
- package/dist/components/editor.js.map +1 -0
- package/dist/components/grouped-select-list.d.ts +60 -0
- package/dist/components/grouped-select-list.d.ts.map +1 -0
- package/dist/components/grouped-select-list.js +312 -0
- package/dist/components/grouped-select-list.js.map +1 -0
- package/dist/components/image.d.ts +28 -0
- package/dist/components/image.d.ts.map +1 -0
- package/dist/components/image.js +69 -0
- package/dist/components/image.js.map +1 -0
- package/dist/components/input.d.ts +37 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +426 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/loader.d.ts +26 -0
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/loader.js +67 -0
- package/dist/components/loader.js.map +1 -0
- package/dist/components/markdown.d.ts +95 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +663 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/select-list.d.ts +50 -0
- package/dist/components/select-list.d.ts.map +1 -0
- package/dist/components/select-list.js +159 -0
- package/dist/components/select-list.js.map +1 -0
- package/dist/components/settings-list.d.ts +50 -0
- package/dist/components/settings-list.d.ts.map +1 -0
- package/dist/components/settings-list.js +185 -0
- package/dist/components/settings-list.js.map +1 -0
- package/dist/components/spacer.d.ts +12 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +23 -0
- package/dist/components/spacer.js.map +1 -0
- package/dist/components/spinner.d.ts +35 -0
- package/dist/components/spinner.d.ts.map +1 -0
- package/dist/components/spinner.js +77 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/streaming-markdown.d.ts +39 -0
- package/dist/components/streaming-markdown.d.ts.map +1 -0
- package/dist/components/streaming-markdown.js +137 -0
- package/dist/components/streaming-markdown.js.map +1 -0
- package/dist/components/text.d.ts +19 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +89 -0
- package/dist/components/text.js.map +1 -0
- package/dist/components/truncated-text.d.ts +13 -0
- package/dist/components/truncated-text.d.ts.map +1 -0
- package/dist/components/truncated-text.js +51 -0
- package/dist/components/truncated-text.js.map +1 -0
- package/dist/editor-component.d.ts +39 -0
- package/dist/editor-component.d.ts.map +1 -0
- package/dist/editor-component.js +2 -0
- package/dist/editor-component.js.map +1 -0
- package/dist/fuzzy.d.ts +16 -0
- package/dist/fuzzy.d.ts.map +1 -0
- package/dist/fuzzy.js +107 -0
- package/dist/fuzzy.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/keybindings.d.ts +193 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +174 -0
- package/dist/keybindings.js.map +1 -0
- package/dist/keys.d.ts +170 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +1124 -0
- package/dist/keys.js.map +1 -0
- package/dist/kill-ring.d.ts +28 -0
- package/dist/kill-ring.d.ts.map +1 -0
- package/dist/kill-ring.js +44 -0
- package/dist/kill-ring.js.map +1 -0
- package/dist/notifications.d.ts +35 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +62 -0
- package/dist/notifications.js.map +1 -0
- package/dist/osc52.d.ts +28 -0
- package/dist/osc52.d.ts.map +1 -0
- package/dist/osc52.js +53 -0
- package/dist/osc52.js.map +1 -0
- package/dist/scroll-buffer.d.ts +67 -0
- package/dist/scroll-buffer.d.ts.map +1 -0
- package/dist/scroll-buffer.js +222 -0
- package/dist/scroll-buffer.js.map +1 -0
- package/dist/spinners.d.ts +26 -0
- package/dist/spinners.d.ts.map +1 -0
- package/dist/spinners.js +136 -0
- package/dist/spinners.js.map +1 -0
- package/dist/stdin-buffer.d.ts +48 -0
- package/dist/stdin-buffer.d.ts.map +1 -0
- package/dist/stdin-buffer.js +317 -0
- package/dist/stdin-buffer.js.map +1 -0
- package/dist/sync-output.d.ts +58 -0
- package/dist/sync-output.d.ts.map +1 -0
- package/dist/sync-output.js +79 -0
- package/dist/sync-output.js.map +1 -0
- package/dist/terminal-detect.d.ts +66 -0
- package/dist/terminal-detect.d.ts.map +1 -0
- package/dist/terminal-detect.js +315 -0
- package/dist/terminal-detect.js.map +1 -0
- package/dist/terminal-image.d.ts +68 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +288 -0
- package/dist/terminal-image.js.map +1 -0
- package/dist/terminal.d.ts +105 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +427 -0
- package/dist/terminal.js.map +1 -0
- package/dist/tui.d.ts +268 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +1161 -0
- package/dist/tui.js.map +1 -0
- package/dist/undo-stack.d.ts +17 -0
- package/dist/undo-stack.d.ts.map +1 -0
- package/dist/undo-stack.js +25 -0
- package/dist/undo-stack.js.map +1 -0
- package/dist/utils.d.ts +78 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +960 -0
- package/dist/utils.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-app scroll buffer.
|
|
3
|
+
*
|
|
4
|
+
* Covers cavekit-fullscreen-viewport R5 (buffer semantics), R6 (scroll controls).
|
|
5
|
+
* The buffer owns the session transcript; the TUI renders whatever slice the
|
|
6
|
+
* buffer exposes for the current viewport.
|
|
7
|
+
*/
|
|
8
|
+
import { visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
9
|
+
/**
|
|
10
|
+
* Session transcript with viewport and follow-tail semantics.
|
|
11
|
+
*
|
|
12
|
+
* The buffer stores raw "logical" lines as appended. For rendering, lines are
|
|
13
|
+
* soft-wrapped to the current viewport width producing "display" lines. The
|
|
14
|
+
* viewport is defined in display lines, so the scroll math operates on the
|
|
15
|
+
* post-wrap stream and stays consistent after resize.
|
|
16
|
+
*/
|
|
17
|
+
export class ScrollBuffer {
|
|
18
|
+
logical = [];
|
|
19
|
+
wrapEnabled;
|
|
20
|
+
maxLines;
|
|
21
|
+
// Cache of wrapped lines — invalidated on width change or append.
|
|
22
|
+
wrappedCache = null;
|
|
23
|
+
cachedWidth = 0;
|
|
24
|
+
viewportWidth = 80;
|
|
25
|
+
viewportHeight = 24;
|
|
26
|
+
/** Offset from the top of the wrapped stream (display-line index of viewport top). */
|
|
27
|
+
topOffset = 0;
|
|
28
|
+
_mode = "tail";
|
|
29
|
+
_unseen = 0;
|
|
30
|
+
constructor(opts = {}) {
|
|
31
|
+
this.maxLines = opts.maxLines ?? Number.POSITIVE_INFINITY;
|
|
32
|
+
this.wrapEnabled = opts.wrap !== false;
|
|
33
|
+
}
|
|
34
|
+
append(lines) {
|
|
35
|
+
if (lines.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
this.logical.push(...lines);
|
|
38
|
+
this.trimToMax();
|
|
39
|
+
this.invalidateCache();
|
|
40
|
+
if (this._mode === "tail") {
|
|
41
|
+
this.snapToTail();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this._unseen += this.countDisplayLines(lines);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Replace the last `count` logical lines with `lines`. Used for streaming
|
|
49
|
+
* updates to a single message that grows in-place. Tail/paused behavior
|
|
50
|
+
* mirrors append().
|
|
51
|
+
*/
|
|
52
|
+
replaceTail(count, lines) {
|
|
53
|
+
if (count > 0) {
|
|
54
|
+
const removeCount = Math.min(count, this.logical.length);
|
|
55
|
+
this.logical.splice(this.logical.length - removeCount, removeCount);
|
|
56
|
+
}
|
|
57
|
+
this.logical.push(...lines);
|
|
58
|
+
this.trimToMax();
|
|
59
|
+
this.invalidateCache();
|
|
60
|
+
if (this._mode === "tail") {
|
|
61
|
+
this.snapToTail();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
clear() {
|
|
65
|
+
this.logical = [];
|
|
66
|
+
this.invalidateCache();
|
|
67
|
+
this.topOffset = 0;
|
|
68
|
+
this._mode = "tail";
|
|
69
|
+
this._unseen = 0;
|
|
70
|
+
}
|
|
71
|
+
setViewportHeight(rows) {
|
|
72
|
+
if (rows === this.viewportHeight)
|
|
73
|
+
return;
|
|
74
|
+
this.viewportHeight = Math.max(1, rows);
|
|
75
|
+
if (this._mode === "tail")
|
|
76
|
+
this.snapToTail();
|
|
77
|
+
else
|
|
78
|
+
this.clampTopOffset();
|
|
79
|
+
}
|
|
80
|
+
setViewportWidth(cols) {
|
|
81
|
+
if (cols === this.viewportWidth)
|
|
82
|
+
return;
|
|
83
|
+
this.viewportWidth = Math.max(1, cols);
|
|
84
|
+
this.invalidateCache();
|
|
85
|
+
if (this._mode === "tail")
|
|
86
|
+
this.snapToTail();
|
|
87
|
+
else
|
|
88
|
+
this.clampTopOffset();
|
|
89
|
+
}
|
|
90
|
+
get totalLines() {
|
|
91
|
+
return this.getWrapped().length;
|
|
92
|
+
}
|
|
93
|
+
get viewHeight() {
|
|
94
|
+
return this.viewportHeight;
|
|
95
|
+
}
|
|
96
|
+
get viewWidth() {
|
|
97
|
+
return this.viewportWidth;
|
|
98
|
+
}
|
|
99
|
+
scrollBy(delta) {
|
|
100
|
+
if (delta === 0)
|
|
101
|
+
return;
|
|
102
|
+
const next = this.topOffset + delta;
|
|
103
|
+
const maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);
|
|
104
|
+
const clamped = Math.max(0, Math.min(maxTop, next));
|
|
105
|
+
if (clamped === this.topOffset)
|
|
106
|
+
return;
|
|
107
|
+
this.topOffset = clamped;
|
|
108
|
+
if (this.topOffset >= maxTop) {
|
|
109
|
+
this.setMode("tail");
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.setMode("paused");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
pageUp() {
|
|
116
|
+
this.scrollBy(-Math.max(1, this.viewportHeight - 1));
|
|
117
|
+
}
|
|
118
|
+
pageDown() {
|
|
119
|
+
this.scrollBy(Math.max(1, this.viewportHeight - 1));
|
|
120
|
+
}
|
|
121
|
+
halfPageUp() {
|
|
122
|
+
this.scrollBy(-Math.max(1, Math.floor(this.viewportHeight / 2)));
|
|
123
|
+
}
|
|
124
|
+
halfPageDown() {
|
|
125
|
+
this.scrollBy(Math.max(1, Math.floor(this.viewportHeight / 2)));
|
|
126
|
+
}
|
|
127
|
+
jumpToTop() {
|
|
128
|
+
if (this.topOffset === 0)
|
|
129
|
+
return;
|
|
130
|
+
this.topOffset = 0;
|
|
131
|
+
this.setMode(this.getWrapped().length <= this.viewportHeight ? "tail" : "paused");
|
|
132
|
+
}
|
|
133
|
+
jumpToTail() {
|
|
134
|
+
this.setMode("tail");
|
|
135
|
+
this.snapToTail();
|
|
136
|
+
}
|
|
137
|
+
get isAtTail() {
|
|
138
|
+
const maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);
|
|
139
|
+
return this.topOffset >= maxTop;
|
|
140
|
+
}
|
|
141
|
+
get mode() {
|
|
142
|
+
return this._mode;
|
|
143
|
+
}
|
|
144
|
+
unseenCount() {
|
|
145
|
+
return this._unseen;
|
|
146
|
+
}
|
|
147
|
+
/** Returns exactly viewportHeight display lines (pads with "" when short). */
|
|
148
|
+
render() {
|
|
149
|
+
const wrapped = this.getWrapped();
|
|
150
|
+
const start = Math.max(0, Math.min(wrapped.length, this.topOffset));
|
|
151
|
+
const slice = wrapped.slice(start, start + this.viewportHeight);
|
|
152
|
+
while (slice.length < this.viewportHeight)
|
|
153
|
+
slice.push("");
|
|
154
|
+
return slice;
|
|
155
|
+
}
|
|
156
|
+
// ------------------- internals -------------------
|
|
157
|
+
setMode(next) {
|
|
158
|
+
if (this._mode === next)
|
|
159
|
+
return;
|
|
160
|
+
this._mode = next;
|
|
161
|
+
if (next === "tail") {
|
|
162
|
+
this._unseen = 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
snapToTail() {
|
|
166
|
+
const total = this.getWrapped().length;
|
|
167
|
+
this.topOffset = Math.max(0, total - this.viewportHeight);
|
|
168
|
+
this._unseen = 0;
|
|
169
|
+
}
|
|
170
|
+
clampTopOffset() {
|
|
171
|
+
const maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);
|
|
172
|
+
if (this.topOffset > maxTop)
|
|
173
|
+
this.topOffset = maxTop;
|
|
174
|
+
if (this.topOffset < 0)
|
|
175
|
+
this.topOffset = 0;
|
|
176
|
+
}
|
|
177
|
+
trimToMax() {
|
|
178
|
+
if (!Number.isFinite(this.maxLines))
|
|
179
|
+
return;
|
|
180
|
+
const over = this.logical.length - this.maxLines;
|
|
181
|
+
if (over > 0)
|
|
182
|
+
this.logical.splice(0, over);
|
|
183
|
+
}
|
|
184
|
+
invalidateCache() {
|
|
185
|
+
this.wrappedCache = null;
|
|
186
|
+
}
|
|
187
|
+
getWrapped() {
|
|
188
|
+
if (this.wrappedCache && this.cachedWidth === this.viewportWidth) {
|
|
189
|
+
return this.wrappedCache;
|
|
190
|
+
}
|
|
191
|
+
if (!this.wrapEnabled) {
|
|
192
|
+
this.wrappedCache = [...this.logical];
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const out = [];
|
|
196
|
+
for (const line of this.logical) {
|
|
197
|
+
if (visibleWidth(line) <= this.viewportWidth) {
|
|
198
|
+
out.push(line);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
out.push(...wrapTextWithAnsi(line, this.viewportWidth));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
this.wrappedCache = out;
|
|
205
|
+
}
|
|
206
|
+
this.cachedWidth = this.viewportWidth;
|
|
207
|
+
return this.wrappedCache;
|
|
208
|
+
}
|
|
209
|
+
countDisplayLines(lines) {
|
|
210
|
+
if (!this.wrapEnabled)
|
|
211
|
+
return lines.length;
|
|
212
|
+
let n = 0;
|
|
213
|
+
for (const line of lines) {
|
|
214
|
+
if (visibleWidth(line) <= this.viewportWidth)
|
|
215
|
+
n += 1;
|
|
216
|
+
else
|
|
217
|
+
n += wrapTextWithAnsi(line, this.viewportWidth).length;
|
|
218
|
+
}
|
|
219
|
+
return n;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=scroll-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-buffer.js","sourceRoot":"","sources":["../src/scroll-buffer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * In-app scroll buffer.\n *\n * Covers cavekit-fullscreen-viewport R5 (buffer semantics), R6 (scroll controls).\n * The buffer owns the session transcript; the TUI renders whatever slice the\n * buffer exposes for the current viewport.\n */\n\nimport { visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n\nexport type ScrollMode = \"tail\" | \"paused\";\n\nexport interface ScrollBufferOptions {\n\tmaxLines?: number;\n\twrap?: boolean;\n}\n\n/**\n * Session transcript with viewport and follow-tail semantics.\n *\n * The buffer stores raw \"logical\" lines as appended. For rendering, lines are\n * soft-wrapped to the current viewport width producing \"display\" lines. The\n * viewport is defined in display lines, so the scroll math operates on the\n * post-wrap stream and stays consistent after resize.\n */\nexport class ScrollBuffer {\n\tprivate logical: string[] = [];\n\tprivate wrapEnabled: boolean;\n\tprivate readonly maxLines: number;\n\n\t// Cache of wrapped lines — invalidated on width change or append.\n\tprivate wrappedCache: string[] | null = null;\n\tprivate cachedWidth = 0;\n\n\tprivate viewportWidth = 80;\n\tprivate viewportHeight = 24;\n\n\t/** Offset from the top of the wrapped stream (display-line index of viewport top). */\n\tprivate topOffset = 0;\n\n\tprivate _mode: ScrollMode = \"tail\";\n\tprivate _unseen = 0;\n\n\tconstructor(opts: ScrollBufferOptions = {}) {\n\t\tthis.maxLines = opts.maxLines ?? Number.POSITIVE_INFINITY;\n\t\tthis.wrapEnabled = opts.wrap !== false;\n\t}\n\n\tappend(lines: string[]): void {\n\t\tif (lines.length === 0) return;\n\t\tthis.logical.push(...lines);\n\t\tthis.trimToMax();\n\t\tthis.invalidateCache();\n\n\t\tif (this._mode === \"tail\") {\n\t\t\tthis.snapToTail();\n\t\t} else {\n\t\t\tthis._unseen += this.countDisplayLines(lines);\n\t\t}\n\t}\n\n\t/**\n\t * Replace the last `count` logical lines with `lines`. Used for streaming\n\t * updates to a single message that grows in-place. Tail/paused behavior\n\t * mirrors append().\n\t */\n\treplaceTail(count: number, lines: string[]): void {\n\t\tif (count > 0) {\n\t\t\tconst removeCount = Math.min(count, this.logical.length);\n\t\t\tthis.logical.splice(this.logical.length - removeCount, removeCount);\n\t\t}\n\t\tthis.logical.push(...lines);\n\t\tthis.trimToMax();\n\t\tthis.invalidateCache();\n\n\t\tif (this._mode === \"tail\") {\n\t\t\tthis.snapToTail();\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.logical = [];\n\t\tthis.invalidateCache();\n\t\tthis.topOffset = 0;\n\t\tthis._mode = \"tail\";\n\t\tthis._unseen = 0;\n\t}\n\n\tsetViewportHeight(rows: number): void {\n\t\tif (rows === this.viewportHeight) return;\n\t\tthis.viewportHeight = Math.max(1, rows);\n\t\tif (this._mode === \"tail\") this.snapToTail();\n\t\telse this.clampTopOffset();\n\t}\n\n\tsetViewportWidth(cols: number): void {\n\t\tif (cols === this.viewportWidth) return;\n\t\tthis.viewportWidth = Math.max(1, cols);\n\t\tthis.invalidateCache();\n\t\tif (this._mode === \"tail\") this.snapToTail();\n\t\telse this.clampTopOffset();\n\t}\n\n\tget totalLines(): number {\n\t\treturn this.getWrapped().length;\n\t}\n\n\tget viewHeight(): number {\n\t\treturn this.viewportHeight;\n\t}\n\n\tget viewWidth(): number {\n\t\treturn this.viewportWidth;\n\t}\n\n\tscrollBy(delta: number): void {\n\t\tif (delta === 0) return;\n\t\tconst next = this.topOffset + delta;\n\t\tconst maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);\n\t\tconst clamped = Math.max(0, Math.min(maxTop, next));\n\t\tif (clamped === this.topOffset) return;\n\t\tthis.topOffset = clamped;\n\t\tif (this.topOffset >= maxTop) {\n\t\t\tthis.setMode(\"tail\");\n\t\t} else {\n\t\t\tthis.setMode(\"paused\");\n\t\t}\n\t}\n\n\tpageUp(): void {\n\t\tthis.scrollBy(-Math.max(1, this.viewportHeight - 1));\n\t}\n\n\tpageDown(): void {\n\t\tthis.scrollBy(Math.max(1, this.viewportHeight - 1));\n\t}\n\n\thalfPageUp(): void {\n\t\tthis.scrollBy(-Math.max(1, Math.floor(this.viewportHeight / 2)));\n\t}\n\n\thalfPageDown(): void {\n\t\tthis.scrollBy(Math.max(1, Math.floor(this.viewportHeight / 2)));\n\t}\n\n\tjumpToTop(): void {\n\t\tif (this.topOffset === 0) return;\n\t\tthis.topOffset = 0;\n\t\tthis.setMode(this.getWrapped().length <= this.viewportHeight ? \"tail\" : \"paused\");\n\t}\n\n\tjumpToTail(): void {\n\t\tthis.setMode(\"tail\");\n\t\tthis.snapToTail();\n\t}\n\n\tget isAtTail(): boolean {\n\t\tconst maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);\n\t\treturn this.topOffset >= maxTop;\n\t}\n\n\tget mode(): ScrollMode {\n\t\treturn this._mode;\n\t}\n\n\tunseenCount(): number {\n\t\treturn this._unseen;\n\t}\n\n\t/** Returns exactly viewportHeight display lines (pads with \"\" when short). */\n\trender(): string[] {\n\t\tconst wrapped = this.getWrapped();\n\t\tconst start = Math.max(0, Math.min(wrapped.length, this.topOffset));\n\t\tconst slice = wrapped.slice(start, start + this.viewportHeight);\n\t\twhile (slice.length < this.viewportHeight) slice.push(\"\");\n\t\treturn slice;\n\t}\n\n\t// ------------------- internals -------------------\n\n\tprivate setMode(next: ScrollMode): void {\n\t\tif (this._mode === next) return;\n\t\tthis._mode = next;\n\t\tif (next === \"tail\") {\n\t\t\tthis._unseen = 0;\n\t\t}\n\t}\n\n\tprivate snapToTail(): void {\n\t\tconst total = this.getWrapped().length;\n\t\tthis.topOffset = Math.max(0, total - this.viewportHeight);\n\t\tthis._unseen = 0;\n\t}\n\n\tprivate clampTopOffset(): void {\n\t\tconst maxTop = Math.max(0, this.getWrapped().length - this.viewportHeight);\n\t\tif (this.topOffset > maxTop) this.topOffset = maxTop;\n\t\tif (this.topOffset < 0) this.topOffset = 0;\n\t}\n\n\tprivate trimToMax(): void {\n\t\tif (!Number.isFinite(this.maxLines)) return;\n\t\tconst over = this.logical.length - this.maxLines;\n\t\tif (over > 0) this.logical.splice(0, over);\n\t}\n\n\tprivate invalidateCache(): void {\n\t\tthis.wrappedCache = null;\n\t}\n\n\tprivate getWrapped(): string[] {\n\t\tif (this.wrappedCache && this.cachedWidth === this.viewportWidth) {\n\t\t\treturn this.wrappedCache;\n\t\t}\n\t\tif (!this.wrapEnabled) {\n\t\t\tthis.wrappedCache = [...this.logical];\n\t\t} else {\n\t\t\tconst out: string[] = [];\n\t\t\tfor (const line of this.logical) {\n\t\t\t\tif (visibleWidth(line) <= this.viewportWidth) {\n\t\t\t\t\tout.push(line);\n\t\t\t\t} else {\n\t\t\t\t\tout.push(...wrapTextWithAnsi(line, this.viewportWidth));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.wrappedCache = out;\n\t\t}\n\t\tthis.cachedWidth = this.viewportWidth;\n\t\treturn this.wrappedCache;\n\t}\n\n\tprivate countDisplayLines(lines: string[]): number {\n\t\tif (!this.wrapEnabled) return lines.length;\n\t\tlet n = 0;\n\t\tfor (const line of lines) {\n\t\t\tif (visibleWidth(line) <= this.viewportWidth) n += 1;\n\t\t\telse n += wrapTextWithAnsi(line, this.viewportWidth).length;\n\t\t}\n\t\treturn n;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SpinnerVariant {
|
|
2
|
+
readonly frames: readonly string[];
|
|
3
|
+
readonly interval: number;
|
|
4
|
+
}
|
|
5
|
+
export declare const SPINNERS: {
|
|
6
|
+
readonly dots: SpinnerVariant;
|
|
7
|
+
readonly helix: SpinnerVariant;
|
|
8
|
+
readonly breathe: SpinnerVariant;
|
|
9
|
+
readonly orbit: SpinnerVariant;
|
|
10
|
+
readonly dna: SpinnerVariant;
|
|
11
|
+
readonly waverows: SpinnerVariant;
|
|
12
|
+
readonly snake: SpinnerVariant;
|
|
13
|
+
readonly pulse: SpinnerVariant;
|
|
14
|
+
readonly cascade: SpinnerVariant;
|
|
15
|
+
readonly scan: SpinnerVariant;
|
|
16
|
+
readonly diagswipe: SpinnerVariant;
|
|
17
|
+
readonly fillsweep: SpinnerVariant;
|
|
18
|
+
readonly rain: SpinnerVariant;
|
|
19
|
+
readonly columns: SpinnerVariant;
|
|
20
|
+
readonly sparkle: SpinnerVariant;
|
|
21
|
+
};
|
|
22
|
+
export type SpinnerName = keyof typeof SPINNERS;
|
|
23
|
+
export declare const THINKING_SPINNERS: readonly SpinnerName[];
|
|
24
|
+
export declare const TOOL_SPINNERS: readonly SpinnerName[];
|
|
25
|
+
export declare function getSpinner(name: string | undefined, fallback: SpinnerName): SpinnerVariant;
|
|
26
|
+
//# sourceMappingURL=spinners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinners.d.ts","sourceRoot":"","sources":["../src/spinners.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AA+GD,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;CAgBX,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,QAAQ,CAAC;AAEhD,eAAO,MAAM,iBAAiB,EAAE,SAAS,WAAW,EAQnD,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAQ/C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,WAAW,GAAG,cAAc,CAK1F","sourcesContent":["export interface SpinnerVariant {\n\treadonly frames: readonly string[];\n\treadonly interval: number;\n}\n\nconst dots: SpinnerVariant = {\n\tframes: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n\tinterval: 80,\n};\n\nconst helix: SpinnerVariant = {\n\tframes: [\"⠁\", \"⠂\", \"⠄\", \"⡀\", \"⢀\", \"⠠\", \"⠐\", \"⠈\"],\n\tinterval: 90,\n};\n\nconst breathe: SpinnerVariant = {\n\tframes: [\"·\", \"•\", \"●\", \"•\"],\n\tinterval: 220,\n};\n\nconst orbit: SpinnerVariant = {\n\tframes: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n\tinterval: 110,\n};\n\nconst dna: SpinnerVariant = {\n\tframes: [\"⡀⠀\", \"⠄⠀\", \"⠂⠀\", \"⠁⠀\", \"⠈⠀\", \"⠐⠀\", \"⠠⠀\", \"⢀⠀\"],\n\tinterval: 100,\n};\n\nconst waverows: SpinnerVariant = {\n\tframes: [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\", \"▇\", \"▆\", \"▅\", \"▄\", \"▃\", \"▂\"],\n\tinterval: 90,\n};\n\nconst snake: SpinnerVariant = {\n\tframes: [\n\t\t\"▰▱▱▱▱▱▱\",\n\t\t\"▰▰▱▱▱▱▱\",\n\t\t\"▰▰▰▱▱▱▱\",\n\t\t\"▰▰▰▰▱▱▱\",\n\t\t\"▰▰▰▰▰▱▱\",\n\t\t\"▰▰▰▰▰▰▱\",\n\t\t\"▰▰▰▰▰▰▰\",\n\t\t\"▱▰▰▰▰▰▰\",\n\t\t\"▱▱▰▰▰▰▰\",\n\t\t\"▱▱▱▰▰▰▰\",\n\t\t\"▱▱▱▱▰▰▰\",\n\t\t\"▱▱▱▱▱▰▰\",\n\t\t\"▱▱▱▱▱▱▰\",\n\t\t\"▱▱▱▱▱▱▱\",\n\t],\n\tinterval: 100,\n};\n\nconst pulse: SpinnerVariant = {\n\tframes: [\"○\", \"◔\", \"◑\", \"◕\", \"●\", \"◕\", \"◑\", \"◔\"],\n\tinterval: 130,\n};\n\nconst cascade: SpinnerVariant = {\n\tframes: [\"▏\", \"▎\", \"▍\", \"▌\", \"▋\", \"▊\", \"▉\", \"█\", \"▉\", \"▊\", \"▋\", \"▌\", \"▍\", \"▎\"],\n\tinterval: 90,\n};\n\nconst scan: SpinnerVariant = {\n\tframes: [\n\t\t\"[● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ●]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t],\n\tinterval: 90,\n};\n\nconst diagswipe: SpinnerVariant = {\n\tframes: [\"⠁⠀⠀\", \"⠂⠁⠀\", \"⠄⠂⠁\", \"⡀⠄⠂\", \"⠀⡀⠄\", \"⠀⠀⡀\", \"⠀⠀⠀\"],\n\tinterval: 110,\n};\n\nconst fillsweep: SpinnerVariant = {\n\tframes: [\"▱▱▱▱▱\", \"▰▱▱▱▱\", \"▰▰▱▱▱\", \"▰▰▰▱▱\", \"▰▰▰▰▱\", \"▰▰▰▰▰\", \"▱▰▰▰▰\", \"▱▱▰▰▰\", \"▱▱▱▰▰\", \"▱▱▱▱▰\"],\n\tinterval: 110,\n};\n\nconst rain: SpinnerVariant = {\n\tframes: [\"⡀\", \"⡄\", \"⡆\", \"⡇\", \"⠇\", \"⠃\", \"⠁\", \" \"],\n\tinterval: 90,\n};\n\nconst columns: SpinnerVariant = {\n\tframes: [\"▁ ▁ ▁\", \"▃ ▁ ▁\", \"▅ ▃ ▁\", \"▇ ▅ ▃\", \"█ ▇ ▅\", \"▇ █ ▇\", \"▅ ▇ █\", \"▃ ▅ ▇\", \"▁ ▃ ▅\", \"▁ ▁ ▃\"],\n\tinterval: 110,\n};\n\nconst sparkle: SpinnerVariant = {\n\tframes: [\"·\", \"✦\", \"✧\", \"✦\", \"·\", \" \"],\n\tinterval: 160,\n};\n\nexport const SPINNERS = {\n\tdots,\n\thelix,\n\tbreathe,\n\torbit,\n\tdna,\n\twaverows,\n\tsnake,\n\tpulse,\n\tcascade,\n\tscan,\n\tdiagswipe,\n\tfillsweep,\n\train,\n\tcolumns,\n\tsparkle,\n} as const;\n\nexport type SpinnerName = keyof typeof SPINNERS;\n\nexport const THINKING_SPINNERS: readonly SpinnerName[] = [\n\t\"helix\",\n\t\"breathe\",\n\t\"orbit\",\n\t\"dna\",\n\t\"waverows\",\n\t\"snake\",\n\t\"pulse\",\n];\n\nexport const TOOL_SPINNERS: readonly SpinnerName[] = [\n\t\"cascade\",\n\t\"scan\",\n\t\"diagswipe\",\n\t\"fillsweep\",\n\t\"rain\",\n\t\"columns\",\n\t\"sparkle\",\n];\n\nexport function getSpinner(name: string | undefined, fallback: SpinnerName): SpinnerVariant {\n\tif (name && name in SPINNERS) {\n\t\treturn SPINNERS[name as SpinnerName];\n\t}\n\treturn SPINNERS[fallback];\n}\n"]}
|
package/dist/spinners.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const dots = {
|
|
2
|
+
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
3
|
+
interval: 80,
|
|
4
|
+
};
|
|
5
|
+
const helix = {
|
|
6
|
+
frames: ["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"],
|
|
7
|
+
interval: 90,
|
|
8
|
+
};
|
|
9
|
+
const breathe = {
|
|
10
|
+
frames: ["·", "•", "●", "•"],
|
|
11
|
+
interval: 220,
|
|
12
|
+
};
|
|
13
|
+
const orbit = {
|
|
14
|
+
frames: ["◜", "◠", "◝", "◞", "◡", "◟"],
|
|
15
|
+
interval: 110,
|
|
16
|
+
};
|
|
17
|
+
const dna = {
|
|
18
|
+
frames: ["⡀⠀", "⠄⠀", "⠂⠀", "⠁⠀", "⠈⠀", "⠐⠀", "⠠⠀", "⢀⠀"],
|
|
19
|
+
interval: 100,
|
|
20
|
+
};
|
|
21
|
+
const waverows = {
|
|
22
|
+
frames: ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▂"],
|
|
23
|
+
interval: 90,
|
|
24
|
+
};
|
|
25
|
+
const snake = {
|
|
26
|
+
frames: [
|
|
27
|
+
"▰▱▱▱▱▱▱",
|
|
28
|
+
"▰▰▱▱▱▱▱",
|
|
29
|
+
"▰▰▰▱▱▱▱",
|
|
30
|
+
"▰▰▰▰▱▱▱",
|
|
31
|
+
"▰▰▰▰▰▱▱",
|
|
32
|
+
"▰▰▰▰▰▰▱",
|
|
33
|
+
"▰▰▰▰▰▰▰",
|
|
34
|
+
"▱▰▰▰▰▰▰",
|
|
35
|
+
"▱▱▰▰▰▰▰",
|
|
36
|
+
"▱▱▱▰▰▰▰",
|
|
37
|
+
"▱▱▱▱▰▰▰",
|
|
38
|
+
"▱▱▱▱▱▰▰",
|
|
39
|
+
"▱▱▱▱▱▱▰",
|
|
40
|
+
"▱▱▱▱▱▱▱",
|
|
41
|
+
],
|
|
42
|
+
interval: 100,
|
|
43
|
+
};
|
|
44
|
+
const pulse = {
|
|
45
|
+
frames: ["○", "◔", "◑", "◕", "●", "◕", "◑", "◔"],
|
|
46
|
+
interval: 130,
|
|
47
|
+
};
|
|
48
|
+
const cascade = {
|
|
49
|
+
frames: ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▉", "▊", "▋", "▌", "▍", "▎"],
|
|
50
|
+
interval: 90,
|
|
51
|
+
};
|
|
52
|
+
const scan = {
|
|
53
|
+
frames: [
|
|
54
|
+
"[● ]",
|
|
55
|
+
"[ ● ]",
|
|
56
|
+
"[ ● ]",
|
|
57
|
+
"[ ● ]",
|
|
58
|
+
"[ ● ]",
|
|
59
|
+
"[ ● ]",
|
|
60
|
+
"[ ● ]",
|
|
61
|
+
"[ ● ]",
|
|
62
|
+
"[ ● ]",
|
|
63
|
+
"[ ●]",
|
|
64
|
+
"[ ● ]",
|
|
65
|
+
"[ ● ]",
|
|
66
|
+
"[ ● ]",
|
|
67
|
+
"[ ● ]",
|
|
68
|
+
"[ ● ]",
|
|
69
|
+
"[ ● ]",
|
|
70
|
+
"[ ● ]",
|
|
71
|
+
"[ ● ]",
|
|
72
|
+
],
|
|
73
|
+
interval: 90,
|
|
74
|
+
};
|
|
75
|
+
const diagswipe = {
|
|
76
|
+
frames: ["⠁⠀⠀", "⠂⠁⠀", "⠄⠂⠁", "⡀⠄⠂", "⠀⡀⠄", "⠀⠀⡀", "⠀⠀⠀"],
|
|
77
|
+
interval: 110,
|
|
78
|
+
};
|
|
79
|
+
const fillsweep = {
|
|
80
|
+
frames: ["▱▱▱▱▱", "▰▱▱▱▱", "▰▰▱▱▱", "▰▰▰▱▱", "▰▰▰▰▱", "▰▰▰▰▰", "▱▰▰▰▰", "▱▱▰▰▰", "▱▱▱▰▰", "▱▱▱▱▰"],
|
|
81
|
+
interval: 110,
|
|
82
|
+
};
|
|
83
|
+
const rain = {
|
|
84
|
+
frames: ["⡀", "⡄", "⡆", "⡇", "⠇", "⠃", "⠁", " "],
|
|
85
|
+
interval: 90,
|
|
86
|
+
};
|
|
87
|
+
const columns = {
|
|
88
|
+
frames: ["▁ ▁ ▁", "▃ ▁ ▁", "▅ ▃ ▁", "▇ ▅ ▃", "█ ▇ ▅", "▇ █ ▇", "▅ ▇ █", "▃ ▅ ▇", "▁ ▃ ▅", "▁ ▁ ▃"],
|
|
89
|
+
interval: 110,
|
|
90
|
+
};
|
|
91
|
+
const sparkle = {
|
|
92
|
+
frames: ["·", "✦", "✧", "✦", "·", " "],
|
|
93
|
+
interval: 160,
|
|
94
|
+
};
|
|
95
|
+
export const SPINNERS = {
|
|
96
|
+
dots,
|
|
97
|
+
helix,
|
|
98
|
+
breathe,
|
|
99
|
+
orbit,
|
|
100
|
+
dna,
|
|
101
|
+
waverows,
|
|
102
|
+
snake,
|
|
103
|
+
pulse,
|
|
104
|
+
cascade,
|
|
105
|
+
scan,
|
|
106
|
+
diagswipe,
|
|
107
|
+
fillsweep,
|
|
108
|
+
rain,
|
|
109
|
+
columns,
|
|
110
|
+
sparkle,
|
|
111
|
+
};
|
|
112
|
+
export const THINKING_SPINNERS = [
|
|
113
|
+
"helix",
|
|
114
|
+
"breathe",
|
|
115
|
+
"orbit",
|
|
116
|
+
"dna",
|
|
117
|
+
"waverows",
|
|
118
|
+
"snake",
|
|
119
|
+
"pulse",
|
|
120
|
+
];
|
|
121
|
+
export const TOOL_SPINNERS = [
|
|
122
|
+
"cascade",
|
|
123
|
+
"scan",
|
|
124
|
+
"diagswipe",
|
|
125
|
+
"fillsweep",
|
|
126
|
+
"rain",
|
|
127
|
+
"columns",
|
|
128
|
+
"sparkle",
|
|
129
|
+
];
|
|
130
|
+
export function getSpinner(name, fallback) {
|
|
131
|
+
if (name && name in SPINNERS) {
|
|
132
|
+
return SPINNERS[name];
|
|
133
|
+
}
|
|
134
|
+
return SPINNERS[fallback];
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=spinners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinners.js","sourceRoot":"","sources":["../src/spinners.ts"],"names":[],"mappings":"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","sourcesContent":["export interface SpinnerVariant {\n\treadonly frames: readonly string[];\n\treadonly interval: number;\n}\n\nconst dots: SpinnerVariant = {\n\tframes: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n\tinterval: 80,\n};\n\nconst helix: SpinnerVariant = {\n\tframes: [\"⠁\", \"⠂\", \"⠄\", \"⡀\", \"⢀\", \"⠠\", \"⠐\", \"⠈\"],\n\tinterval: 90,\n};\n\nconst breathe: SpinnerVariant = {\n\tframes: [\"·\", \"•\", \"●\", \"•\"],\n\tinterval: 220,\n};\n\nconst orbit: SpinnerVariant = {\n\tframes: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n\tinterval: 110,\n};\n\nconst dna: SpinnerVariant = {\n\tframes: [\"⡀⠀\", \"⠄⠀\", \"⠂⠀\", \"⠁⠀\", \"⠈⠀\", \"⠐⠀\", \"⠠⠀\", \"⢀⠀\"],\n\tinterval: 100,\n};\n\nconst waverows: SpinnerVariant = {\n\tframes: [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\", \"▇\", \"▆\", \"▅\", \"▄\", \"▃\", \"▂\"],\n\tinterval: 90,\n};\n\nconst snake: SpinnerVariant = {\n\tframes: [\n\t\t\"▰▱▱▱▱▱▱\",\n\t\t\"▰▰▱▱▱▱▱\",\n\t\t\"▰▰▰▱▱▱▱\",\n\t\t\"▰▰▰▰▱▱▱\",\n\t\t\"▰▰▰▰▰▱▱\",\n\t\t\"▰▰▰▰▰▰▱\",\n\t\t\"▰▰▰▰▰▰▰\",\n\t\t\"▱▰▰▰▰▰▰\",\n\t\t\"▱▱▰▰▰▰▰\",\n\t\t\"▱▱▱▰▰▰▰\",\n\t\t\"▱▱▱▱▰▰▰\",\n\t\t\"▱▱▱▱▱▰▰\",\n\t\t\"▱▱▱▱▱▱▰\",\n\t\t\"▱▱▱▱▱▱▱\",\n\t],\n\tinterval: 100,\n};\n\nconst pulse: SpinnerVariant = {\n\tframes: [\"○\", \"◔\", \"◑\", \"◕\", \"●\", \"◕\", \"◑\", \"◔\"],\n\tinterval: 130,\n};\n\nconst cascade: SpinnerVariant = {\n\tframes: [\"▏\", \"▎\", \"▍\", \"▌\", \"▋\", \"▊\", \"▉\", \"█\", \"▉\", \"▊\", \"▋\", \"▌\", \"▍\", \"▎\"],\n\tinterval: 90,\n};\n\nconst scan: SpinnerVariant = {\n\tframes: [\n\t\t\"[● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ●]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t\t\"[ ● ]\",\n\t],\n\tinterval: 90,\n};\n\nconst diagswipe: SpinnerVariant = {\n\tframes: [\"⠁⠀⠀\", \"⠂⠁⠀\", \"⠄⠂⠁\", \"⡀⠄⠂\", \"⠀⡀⠄\", \"⠀⠀⡀\", \"⠀⠀⠀\"],\n\tinterval: 110,\n};\n\nconst fillsweep: SpinnerVariant = {\n\tframes: [\"▱▱▱▱▱\", \"▰▱▱▱▱\", \"▰▰▱▱▱\", \"▰▰▰▱▱\", \"▰▰▰▰▱\", \"▰▰▰▰▰\", \"▱▰▰▰▰\", \"▱▱▰▰▰\", \"▱▱▱▰▰\", \"▱▱▱▱▰\"],\n\tinterval: 110,\n};\n\nconst rain: SpinnerVariant = {\n\tframes: [\"⡀\", \"⡄\", \"⡆\", \"⡇\", \"⠇\", \"⠃\", \"⠁\", \" \"],\n\tinterval: 90,\n};\n\nconst columns: SpinnerVariant = {\n\tframes: [\"▁ ▁ ▁\", \"▃ ▁ ▁\", \"▅ ▃ ▁\", \"▇ ▅ ▃\", \"█ ▇ ▅\", \"▇ █ ▇\", \"▅ ▇ █\", \"▃ ▅ ▇\", \"▁ ▃ ▅\", \"▁ ▁ ▃\"],\n\tinterval: 110,\n};\n\nconst sparkle: SpinnerVariant = {\n\tframes: [\"·\", \"✦\", \"✧\", \"✦\", \"·\", \" \"],\n\tinterval: 160,\n};\n\nexport const SPINNERS = {\n\tdots,\n\thelix,\n\tbreathe,\n\torbit,\n\tdna,\n\twaverows,\n\tsnake,\n\tpulse,\n\tcascade,\n\tscan,\n\tdiagswipe,\n\tfillsweep,\n\train,\n\tcolumns,\n\tsparkle,\n} as const;\n\nexport type SpinnerName = keyof typeof SPINNERS;\n\nexport const THINKING_SPINNERS: readonly SpinnerName[] = [\n\t\"helix\",\n\t\"breathe\",\n\t\"orbit\",\n\t\"dna\",\n\t\"waverows\",\n\t\"snake\",\n\t\"pulse\",\n];\n\nexport const TOOL_SPINNERS: readonly SpinnerName[] = [\n\t\"cascade\",\n\t\"scan\",\n\t\"diagswipe\",\n\t\"fillsweep\",\n\t\"rain\",\n\t\"columns\",\n\t\"sparkle\",\n];\n\nexport function getSpinner(name: string | undefined, fallback: SpinnerName): SpinnerVariant {\n\tif (name && name in SPINNERS) {\n\t\treturn SPINNERS[name as SpinnerName];\n\t}\n\treturn SPINNERS[fallback];\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StdinBuffer buffers input and emits complete sequences.
|
|
3
|
+
*
|
|
4
|
+
* This is necessary because stdin data events can arrive in partial chunks,
|
|
5
|
+
* especially for escape sequences like mouse events. Without buffering,
|
|
6
|
+
* partial sequences can be misinterpreted as regular keypresses.
|
|
7
|
+
*
|
|
8
|
+
* For example, the mouse SGR sequence `\x1b[<35;20;5m` might arrive as:
|
|
9
|
+
* - Event 1: `\x1b`
|
|
10
|
+
* - Event 2: `[<35`
|
|
11
|
+
* - Event 3: `;20;5m`
|
|
12
|
+
*
|
|
13
|
+
* The buffer accumulates these until a complete sequence is detected.
|
|
14
|
+
* Call the `process()` method to feed input data.
|
|
15
|
+
*
|
|
16
|
+
* Based on code from OpenTUI (https://github.com/anomalyco/opentui)
|
|
17
|
+
* MIT License - Copyright (c) 2025 opentui
|
|
18
|
+
*/
|
|
19
|
+
import { EventEmitter } from "events";
|
|
20
|
+
export type StdinBufferOptions = {
|
|
21
|
+
/**
|
|
22
|
+
* Maximum time to wait for sequence completion (default: 10ms)
|
|
23
|
+
* After this time, the buffer is flushed even if incomplete
|
|
24
|
+
*/
|
|
25
|
+
timeout?: number;
|
|
26
|
+
};
|
|
27
|
+
export type StdinBufferEventMap = {
|
|
28
|
+
data: [string];
|
|
29
|
+
paste: [string];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Buffers stdin input and emits complete sequences via the 'data' event.
|
|
33
|
+
* Handles partial escape sequences that arrive across multiple chunks.
|
|
34
|
+
*/
|
|
35
|
+
export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
|
|
36
|
+
private buffer;
|
|
37
|
+
private timeout;
|
|
38
|
+
private readonly timeoutMs;
|
|
39
|
+
private pasteMode;
|
|
40
|
+
private pasteBuffer;
|
|
41
|
+
constructor(options?: StdinBufferOptions);
|
|
42
|
+
process(data: string | Buffer): void;
|
|
43
|
+
flush(): string[];
|
|
44
|
+
clear(): void;
|
|
45
|
+
getBuffer(): string;
|
|
46
|
+
destroy(): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=stdin-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdin-buffer.d.ts","sourceRoot":"","sources":["../src/stdin-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA8MtC,MAAM,MAAM,kBAAkB,GAAG;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACjE,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAc;IAEjC,YAAY,OAAO,GAAE,kBAAuB,EAG3C;IAEM,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiG1C;IAED,KAAK,IAAI,MAAM,EAAE,CAahB;IAED,KAAK,IAAI,IAAI,CAQZ;IAED,SAAS,IAAI,MAAM,CAElB;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * StdinBuffer buffers input and emits complete sequences.\n *\n * This is necessary because stdin data events can arrive in partial chunks,\n * especially for escape sequences like mouse events. Without buffering,\n * partial sequences can be misinterpreted as regular keypresses.\n *\n * For example, the mouse SGR sequence `\\x1b[<35;20;5m` might arrive as:\n * - Event 1: `\\x1b`\n * - Event 2: `[<35`\n * - Event 3: `;20;5m`\n *\n * The buffer accumulates these until a complete sequence is detected.\n * Call the `process()` method to feed input data.\n *\n * Based on code from OpenTUI (https://github.com/anomalyco/opentui)\n * MIT License - Copyright (c) 2025 opentui\n */\n\nimport { EventEmitter } from \"events\";\n\nconst ESC = \"\\x1b\";\nconst BRACKETED_PASTE_START = \"\\x1b[200~\";\nconst BRACKETED_PASTE_END = \"\\x1b[201~\";\n\n/**\n * Check if a string is a complete escape sequence or needs more data\n */\nfunction isCompleteSequence(data: string): \"complete\" | \"incomplete\" | \"not-escape\" {\n\tif (!data.startsWith(ESC)) {\n\t\treturn \"not-escape\";\n\t}\n\n\tif (data.length === 1) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst afterEsc = data.slice(1);\n\n\t// CSI sequences: ESC [\n\tif (afterEsc.startsWith(\"[\")) {\n\t\t// Check for old-style mouse sequence: ESC[M + 3 bytes\n\t\tif (afterEsc.startsWith(\"[M\")) {\n\t\t\t// Old-style mouse needs ESC[M + 3 bytes = 6 total\n\t\t\treturn data.length >= 6 ? \"complete\" : \"incomplete\";\n\t\t}\n\t\treturn isCompleteCsiSequence(data);\n\t}\n\n\t// OSC sequences: ESC ]\n\tif (afterEsc.startsWith(\"]\")) {\n\t\treturn isCompleteOscSequence(data);\n\t}\n\n\t// DCS sequences: ESC P ... ESC \\ (includes XTVersion responses)\n\tif (afterEsc.startsWith(\"P\")) {\n\t\treturn isCompleteDcsSequence(data);\n\t}\n\n\t// APC sequences: ESC _ ... ESC \\ (includes Kitty graphics responses)\n\tif (afterEsc.startsWith(\"_\")) {\n\t\treturn isCompleteApcSequence(data);\n\t}\n\n\t// SS3 sequences: ESC O\n\tif (afterEsc.startsWith(\"O\")) {\n\t\t// ESC O followed by a single character\n\t\treturn afterEsc.length >= 2 ? \"complete\" : \"incomplete\";\n\t}\n\n\t// Meta key sequences: ESC followed by a single character\n\tif (afterEsc.length === 1) {\n\t\treturn \"complete\";\n\t}\n\n\t// Unknown escape sequence - treat as complete\n\treturn \"complete\";\n}\n\n/**\n * Check if CSI sequence is complete\n * CSI sequences: ESC [ ... followed by a final byte (0x40-0x7E)\n */\nfunction isCompleteCsiSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}[`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// Need at least ESC [ and one more character\n\tif (data.length < 3) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst payload = data.slice(2);\n\n\t// CSI sequences end with a byte in the range 0x40-0x7E (@-~)\n\t// This includes all letters and several special characters\n\tconst lastChar = payload[payload.length - 1];\n\tconst lastCharCode = lastChar.charCodeAt(0);\n\n\tif (lastCharCode >= 0x40 && lastCharCode <= 0x7e) {\n\t\t// Special handling for SGR mouse sequences\n\t\t// Format: ESC[<B;X;Ym or ESC[<B;X;YM\n\t\tif (payload.startsWith(\"<\")) {\n\t\t\t// Must have format: <digits;digits;digits[Mm]\n\t\t\tconst mouseMatch = /^<\\d+;\\d+;\\d+[Mm]$/.test(payload);\n\t\t\tif (mouseMatch) {\n\t\t\t\treturn \"complete\";\n\t\t\t}\n\t\t\t// If it ends with M or m but doesn't match the pattern, still incomplete\n\t\t\tif (lastChar === \"M\" || lastChar === \"m\") {\n\t\t\t\t// Check if we have the right structure\n\t\t\t\tconst parts = payload.slice(1, -1).split(\";\");\n\t\t\t\tif (parts.length === 3 && parts.every((p) => /^\\d+$/.test(p))) {\n\t\t\t\t\treturn \"complete\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn \"incomplete\";\n\t\t}\n\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if OSC sequence is complete\n * OSC sequences: ESC ] ... ST (where ST is ESC \\ or BEL)\n */\nfunction isCompleteOscSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}]`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// OSC sequences end with ST (ESC \\) or BEL (\\x07)\n\tif (data.endsWith(`${ESC}\\\\`) || data.endsWith(\"\\x07\")) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if DCS (Device Control String) sequence is complete\n * DCS sequences: ESC P ... ST (where ST is ESC \\)\n * Used for XTVersion responses like ESC P >| ... ESC \\\n */\nfunction isCompleteDcsSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}P`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// DCS sequences end with ST (ESC \\)\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if APC (Application Program Command) sequence is complete\n * APC sequences: ESC _ ... ST (where ST is ESC \\)\n * Used for Kitty graphics responses like ESC _ G ... ESC \\\n */\nfunction isCompleteApcSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}_`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// APC sequences end with ST (ESC \\)\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Split accumulated buffer into complete sequences\n */\nfunction extractCompleteSequences(buffer: string): { sequences: string[]; remainder: string } {\n\tconst sequences: string[] = [];\n\tlet pos = 0;\n\n\twhile (pos < buffer.length) {\n\t\tconst remaining = buffer.slice(pos);\n\n\t\t// Try to extract a sequence starting at this position\n\t\tif (remaining.startsWith(ESC)) {\n\t\t\t// Find the end of this escape sequence\n\t\t\tlet seqEnd = 1;\n\t\t\twhile (seqEnd <= remaining.length) {\n\t\t\t\tconst candidate = remaining.slice(0, seqEnd);\n\t\t\t\tconst status = isCompleteSequence(candidate);\n\n\t\t\t\tif (status === \"complete\") {\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (status === \"incomplete\") {\n\t\t\t\t\tseqEnd++;\n\t\t\t\t} else {\n\t\t\t\t\t// Should not happen when starting with ESC\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (seqEnd > remaining.length) {\n\t\t\t\treturn { sequences, remainder: remaining };\n\t\t\t}\n\t\t} else {\n\t\t\t// Not an escape sequence - take a single character\n\t\t\tsequences.push(remaining[0]!);\n\t\t\tpos++;\n\t\t}\n\t}\n\n\treturn { sequences, remainder: \"\" };\n}\n\nexport type StdinBufferOptions = {\n\t/**\n\t * Maximum time to wait for sequence completion (default: 10ms)\n\t * After this time, the buffer is flushed even if incomplete\n\t */\n\ttimeout?: number;\n};\n\nexport type StdinBufferEventMap = {\n\tdata: [string];\n\tpaste: [string];\n};\n\n/**\n * Buffers stdin input and emits complete sequences via the 'data' event.\n * Handles partial escape sequences that arrive across multiple chunks.\n */\nexport class StdinBuffer extends EventEmitter<StdinBufferEventMap> {\n\tprivate buffer: string = \"\";\n\tprivate timeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate readonly timeoutMs: number;\n\tprivate pasteMode: boolean = false;\n\tprivate pasteBuffer: string = \"\";\n\n\tconstructor(options: StdinBufferOptions = {}) {\n\t\tsuper();\n\t\tthis.timeoutMs = options.timeout ?? 10;\n\t}\n\n\tpublic process(data: string | Buffer): void {\n\t\t// Clear any pending timeout\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\t// Handle high-byte conversion (for compatibility with parseKeypress)\n\t\t// If buffer has single byte > 127, convert to ESC + (byte - 128)\n\t\tlet str: string;\n\t\tif (Buffer.isBuffer(data)) {\n\t\t\tif (data.length === 1 && data[0]! > 127) {\n\t\t\t\tconst byte = data[0]! - 128;\n\t\t\t\tstr = `\\x1b${String.fromCharCode(byte)}`;\n\t\t\t} else {\n\t\t\t\tstr = data.toString();\n\t\t\t}\n\t\t} else {\n\t\t\tstr = data;\n\t\t}\n\n\t\tif (str.length === 0 && this.buffer.length === 0) {\n\t\t\tthis.emit(\"data\", \"\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.buffer += str;\n\n\t\tif (this.pasteMode) {\n\t\t\tthis.pasteBuffer += this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = this.buffer.indexOf(BRACKETED_PASTE_START);\n\t\tif (startIndex !== -1) {\n\t\t\tif (startIndex > 0) {\n\t\t\t\tconst beforePaste = this.buffer.slice(0, startIndex);\n\t\t\t\tconst result = extractCompleteSequences(beforePaste);\n\t\t\t\tfor (const sequence of result.sequences) {\n\t\t\t\t\tthis.emit(\"data\", sequence);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.buffer = this.buffer.slice(startIndex + BRACKETED_PASTE_START.length);\n\t\t\tthis.pasteMode = true;\n\t\t\tthis.pasteBuffer = this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = extractCompleteSequences(this.buffer);\n\t\tthis.buffer = result.remainder;\n\n\t\tfor (const sequence of result.sequences) {\n\t\t\tthis.emit(\"data\", sequence);\n\t\t}\n\n\t\tif (this.buffer.length > 0) {\n\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\tconst flushed = this.flush();\n\n\t\t\t\tfor (const sequence of flushed) {\n\t\t\t\t\tthis.emit(\"data\", sequence);\n\t\t\t\t}\n\t\t\t}, this.timeoutMs);\n\t\t}\n\t}\n\n\tflush(): string[] {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\tif (this.buffer.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst sequences = [this.buffer];\n\t\tthis.buffer = \"\";\n\t\treturn sequences;\n\t}\n\n\tclear(): void {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\t\tthis.buffer = \"\";\n\t\tthis.pasteMode = false;\n\t\tthis.pasteBuffer = \"\";\n\t}\n\n\tgetBuffer(): string {\n\t\treturn this.buffer;\n\t}\n\n\tdestroy(): void {\n\t\tthis.clear();\n\t}\n}\n"]}
|