@code-yeongyu/senpi-tui 2026.9.29 → 2026.10.1-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 +38 -0
- package/dist/autocomplete.d.ts +5 -0
- package/dist/autocomplete.js +38 -14
- package/dist/colors.d.ts +73 -0
- package/dist/colors.js +273 -0
- package/dist/components/box.js +5 -3
- package/dist/components/editor.d.ts +4 -1
- package/dist/components/editor.js +23 -8
- package/dist/components/latex.js +5 -0
- package/dist/editor-component.d.ts +7 -2
- package/dist/fuzzy.js +23 -22
- package/dist/index.d.ts +6 -3
- package/dist/index.js +5 -2
- package/dist/native-platform.d.ts +2 -0
- package/dist/oklab.d.ts +43 -0
- package/dist/oklab.js +210 -0
- package/dist/slash-command-autocomplete.js +3 -0
- package/dist/terminal-capabilities.js +1 -1
- package/dist/terminal-colors.d.ts +19 -2
- package/dist/terminal-colors.js +12 -7
- package/dist/terminal-image.d.ts +3 -1
- package/dist/terminal-image.js +33 -9
- package/dist/terminal.d.ts +3 -0
- package/dist/terminal.js +15 -8
- package/dist/tui-alt-screen.d.ts +12 -7
- package/dist/tui-alt-screen.js +37 -17
- package/dist/tui.d.ts +18 -17
- package/dist/tui.js +79 -62
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +108 -60
- package/dist/wheel-scroll.d.ts +23 -0
- package/dist/wheel-scroll.js +70 -0
- package/native/clipboard.h +40 -2
- package/native/darwin/README.md +1 -1
- package/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/native/darwin/src/darwin-platform.m +39 -0
- package/native/napi.h +2 -0
- package/package.json +3 -2
- package/dist/alt-screen-search.d.ts.map +0 -1
- package/dist/alt-screen-search.js.map +0 -1
- package/dist/autocomplete.d.ts.map +0 -1
- package/dist/autocomplete.js.map +0 -1
- package/dist/components/alt-screen-flash.d.ts.map +0 -1
- package/dist/components/alt-screen-flash.js.map +0 -1
- package/dist/components/box.d.ts.map +0 -1
- package/dist/components/box.js.map +0 -1
- package/dist/components/cancellable-loader.d.ts.map +0 -1
- package/dist/components/cancellable-loader.js.map +0 -1
- package/dist/components/editor-line-render.d.ts.map +0 -1
- package/dist/components/editor-line-render.js.map +0 -1
- package/dist/components/editor.d.ts.map +0 -1
- package/dist/components/editor.js.map +0 -1
- package/dist/components/h-stack.d.ts.map +0 -1
- package/dist/components/h-stack.js.map +0 -1
- package/dist/components/image.d.ts.map +0 -1
- package/dist/components/image.js.map +0 -1
- package/dist/components/input.d.ts.map +0 -1
- package/dist/components/input.js.map +0 -1
- package/dist/components/latex.d.ts.map +0 -1
- package/dist/components/latex.js.map +0 -1
- package/dist/components/loader.d.ts.map +0 -1
- package/dist/components/loader.js.map +0 -1
- package/dist/components/markdown.d.ts.map +0 -1
- package/dist/components/markdown.js.map +0 -1
- package/dist/components/mouse-region.d.ts.map +0 -1
- package/dist/components/mouse-region.js.map +0 -1
- package/dist/components/scroll-view.d.ts.map +0 -1
- package/dist/components/scroll-view.js.map +0 -1
- package/dist/components/select-list.d.ts.map +0 -1
- package/dist/components/select-list.js.map +0 -1
- package/dist/components/settings-list.d.ts.map +0 -1
- package/dist/components/settings-list.js.map +0 -1
- package/dist/components/spacer.d.ts.map +0 -1
- package/dist/components/spacer.js.map +0 -1
- package/dist/components/stack.d.ts.map +0 -1
- package/dist/components/stack.js.map +0 -1
- package/dist/components/text.d.ts.map +0 -1
- package/dist/components/text.js.map +0 -1
- package/dist/components/truncated-text.d.ts.map +0 -1
- package/dist/components/truncated-text.js.map +0 -1
- package/dist/components/v-stack.d.ts.map +0 -1
- package/dist/components/v-stack.js.map +0 -1
- package/dist/dollar-invocation-autocomplete.d.ts.map +0 -1
- package/dist/dollar-invocation-autocomplete.js.map +0 -1
- package/dist/editor-component.d.ts.map +0 -1
- package/dist/editor-component.js.map +0 -1
- package/dist/fuzzy.d.ts.map +0 -1
- package/dist/fuzzy.js.map +0 -1
- package/dist/image-markers.d.ts.map +0 -1
- package/dist/image-markers.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/keybindings.d.ts.map +0 -1
- package/dist/keybindings.js.map +0 -1
- package/dist/keys.d.ts.map +0 -1
- package/dist/keys.js.map +0 -1
- package/dist/kill-ring.d.ts.map +0 -1
- package/dist/kill-ring.js.map +0 -1
- package/dist/layout-node.d.ts.map +0 -1
- package/dist/layout-node.js.map +0 -1
- package/dist/layout.d.ts.map +0 -1
- package/dist/layout.js.map +0 -1
- package/dist/mouse-input.d.ts.map +0 -1
- package/dist/mouse-input.js.map +0 -1
- package/dist/mux.d.ts.map +0 -1
- package/dist/mux.js.map +0 -1
- package/dist/native-modifiers.d.ts.map +0 -1
- package/dist/native-modifiers.js.map +0 -1
- package/dist/native-module-path.d.ts.map +0 -1
- package/dist/native-module-path.js.map +0 -1
- package/dist/native-platform.d.ts.map +0 -1
- package/dist/native-platform.js.map +0 -1
- package/dist/paste-markers.d.ts.map +0 -1
- package/dist/paste-markers.js.map +0 -1
- package/dist/slash-command-autocomplete.d.ts.map +0 -1
- package/dist/slash-command-autocomplete.js.map +0 -1
- package/dist/stderr-observer.d.ts.map +0 -1
- package/dist/stderr-observer.js.map +0 -1
- package/dist/stdin-buffer.d.ts.map +0 -1
- package/dist/stdin-buffer.js.map +0 -1
- package/dist/terminal-capabilities.d.ts.map +0 -1
- package/dist/terminal-capabilities.js.map +0 -1
- package/dist/terminal-colors.d.ts.map +0 -1
- package/dist/terminal-colors.js.map +0 -1
- package/dist/terminal-image.d.ts.map +0 -1
- package/dist/terminal-image.js.map +0 -1
- package/dist/terminal-text.d.ts.map +0 -1
- package/dist/terminal-text.js.map +0 -1
- package/dist/terminal.d.ts.map +0 -1
- package/dist/terminal.js.map +0 -1
- package/dist/tmux-cursor-query.d.ts.map +0 -1
- package/dist/tmux-cursor-query.js.map +0 -1
- package/dist/tmux-focus.d.ts.map +0 -1
- package/dist/tmux-focus.js.map +0 -1
- package/dist/tmux-image-capability.d.ts.map +0 -1
- package/dist/tmux-image-capability.js.map +0 -1
- package/dist/tmux-image-probe.d.ts.map +0 -1
- package/dist/tmux-image-probe.js.map +0 -1
- package/dist/tui-alt-screen.d.ts.map +0 -1
- package/dist/tui-alt-screen.js.map +0 -1
- package/dist/tui-main-screen.d.ts.map +0 -1
- package/dist/tui-main-screen.js.map +0 -1
- package/dist/tui.d.ts.map +0 -1
- package/dist/tui.js.map +0 -1
- package/dist/undo-stack.d.ts.map +0 -1
- package/dist/undo-stack.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/word-navigation.d.ts.map +0 -1
- package/dist/word-navigation.js.map +0 -1
package/dist/utils.js
CHANGED
|
@@ -67,6 +67,10 @@ export function __widthCacheStats() {
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
export const cjkBreakRegex = /[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}\p{Script_Extensions=Bopomofo}]/u;
|
|
70
|
+
// CJK letters remain part of words and paths; only punctuation can separate prose from completions.
|
|
71
|
+
export const cjkPunctuationRegex = new RegExp(`(?:(?=\\p{Punctuation})${cjkBreakRegex.source}|[,.:;!?()[]{}“”‘’…—])`, "u");
|
|
72
|
+
export const autocompleteSeparatorRegex = new RegExp(`(?:\\s|${cjkPunctuationRegex.source})`, "u");
|
|
73
|
+
export const autocompleteBoundaryRegex = new RegExp(`(?:^|${autocompleteSeparatorRegex.source})`, "u");
|
|
70
74
|
function isPrintableAscii(str) {
|
|
71
75
|
for (let i = 0; i < str.length; i++) {
|
|
72
76
|
const code = str.charCodeAt(i);
|
|
@@ -166,8 +170,12 @@ function finalizeTruncatedResult(prefix, prefixWidth, ellipsis, ellipsisWidth, m
|
|
|
166
170
|
* check to avoid running the RGI_Emoji regex unnecessarily.
|
|
167
171
|
*/
|
|
168
172
|
function graphemeWidth(segment) {
|
|
169
|
-
if (segment ===
|
|
170
|
-
|
|
173
|
+
if (segment.length === 1) {
|
|
174
|
+
const code = segment.charCodeAt(0);
|
|
175
|
+
if (code >= 0x20 && code <= 0x7e)
|
|
176
|
+
return 1;
|
|
177
|
+
if (code === 0x09)
|
|
178
|
+
return 3;
|
|
171
179
|
}
|
|
172
180
|
// Some marks occupy cells even without a base character.
|
|
173
181
|
if (terminalSpacingMarkRegex.test(segment)) {
|
|
@@ -229,9 +237,11 @@ export function visibleWidth(str) {
|
|
|
229
237
|
if (str.length === 0) {
|
|
230
238
|
return 0;
|
|
231
239
|
}
|
|
232
|
-
// Fast path:
|
|
233
|
-
|
|
234
|
-
|
|
240
|
+
// Fast path: printable ASCII, tabs, and ANSI escape sequences. Styled lines take this path, so
|
|
241
|
+
// re-rendering after a theme change does not run grapheme segmentation on every line.
|
|
242
|
+
const asciiWidth = asciiVisibleWidth(str);
|
|
243
|
+
if (asciiWidth !== -1) {
|
|
244
|
+
return asciiWidth;
|
|
235
245
|
}
|
|
236
246
|
// Check cache
|
|
237
247
|
const cachedCurrent = widthCacheCurrent.get(str);
|
|
@@ -251,22 +261,26 @@ export function visibleWidth(str) {
|
|
|
251
261
|
if (str.includes("\t")) {
|
|
252
262
|
clean = clean.replace(/\t/g, " ");
|
|
253
263
|
}
|
|
254
|
-
|
|
264
|
+
let escapeIndex = clean.indexOf("\x1b");
|
|
265
|
+
if (escapeIndex !== -1) {
|
|
255
266
|
// Strip supported ANSI/OSC/APC escape sequences in one pass.
|
|
256
267
|
// This covers CSI styling/cursor codes, OSC hyperlinks and prompt markers,
|
|
257
268
|
// and APC sequences like CURSOR_MARKER.
|
|
258
269
|
let stripped = "";
|
|
259
|
-
let
|
|
260
|
-
while (
|
|
261
|
-
const
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
let copyFrom = 0;
|
|
271
|
+
while (escapeIndex !== -1) {
|
|
272
|
+
const length = ansiCodeLength(clean, escapeIndex);
|
|
273
|
+
if (length > 0) {
|
|
274
|
+
stripped += clean.slice(copyFrom, escapeIndex);
|
|
275
|
+
escapeIndex += length;
|
|
276
|
+
copyFrom = escapeIndex;
|
|
265
277
|
}
|
|
266
|
-
|
|
267
|
-
|
|
278
|
+
else {
|
|
279
|
+
escapeIndex++;
|
|
280
|
+
}
|
|
281
|
+
escapeIndex = clean.indexOf("\x1b", escapeIndex);
|
|
268
282
|
}
|
|
269
|
-
clean = stripped;
|
|
283
|
+
clean = stripped + clean.slice(copyFrom);
|
|
270
284
|
}
|
|
271
285
|
// Calculate width
|
|
272
286
|
let width = 0;
|
|
@@ -394,30 +408,52 @@ export function coalesceAdjacentSgr(line) {
|
|
|
394
408
|
* Extract ANSI escape sequences from a string at the given position.
|
|
395
409
|
*/
|
|
396
410
|
export function extractAnsiCode(str, pos) {
|
|
397
|
-
|
|
398
|
-
|
|
411
|
+
const length = ansiCodeLength(str, pos);
|
|
412
|
+
return length > 0 ? { code: str.substring(pos, pos + length), length } : null;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Width of a string made of printable ASCII, tabs, and ANSI escape sequences, or -1 if it contains
|
|
416
|
+
* anything else. Matches `visibleWidth` for those strings without allocating.
|
|
417
|
+
*/
|
|
418
|
+
function asciiVisibleWidth(str) {
|
|
419
|
+
let width = 0;
|
|
420
|
+
let i = 0;
|
|
421
|
+
while (i < str.length) {
|
|
422
|
+
const code = str.charCodeAt(i);
|
|
423
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
424
|
+
width++;
|
|
425
|
+
i++;
|
|
426
|
+
}
|
|
427
|
+
else if (code === 0x09) {
|
|
428
|
+
width += 3;
|
|
429
|
+
i++;
|
|
430
|
+
}
|
|
431
|
+
else if (code === 0x1b) {
|
|
432
|
+
const length = ansiCodeLength(str, i);
|
|
433
|
+
if (length === 0)
|
|
434
|
+
return -1;
|
|
435
|
+
i += length;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
return -1;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return width;
|
|
442
|
+
}
|
|
443
|
+
/** Length of the ANSI/OSC/APC escape sequence starting at `pos`, or 0 if there is none. */
|
|
444
|
+
function ansiCodeLength(str, pos) {
|
|
445
|
+
if (pos >= str.length || str.charCodeAt(pos) !== 0x1b)
|
|
446
|
+
return 0;
|
|
399
447
|
const next = str[pos + 1];
|
|
400
448
|
// CSI sequence: ESC [ ... m/G/K/H/J
|
|
401
449
|
if (next === "[") {
|
|
402
|
-
let j = pos + 2;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
// OSC sequence: ESC ] ... BEL or ESC ] ... ST (ESC \)
|
|
410
|
-
// Used for hyperlinks (OSC 8), window titles, etc.
|
|
411
|
-
if (next === "]") {
|
|
412
|
-
let j = pos + 2;
|
|
413
|
-
while (j < str.length) {
|
|
414
|
-
if (str[j] === "\x07")
|
|
415
|
-
return { code: str.substring(pos, j + 1), length: j + 1 - pos };
|
|
416
|
-
if (str[j] === "\x1b" && str[j + 1] === "\\")
|
|
417
|
-
return { code: str.substring(pos, j + 2), length: j + 2 - pos };
|
|
418
|
-
j++;
|
|
450
|
+
for (let j = pos + 2; j < str.length; j++) {
|
|
451
|
+
const c = str.charCodeAt(j);
|
|
452
|
+
// m, G, K, H, J
|
|
453
|
+
if (c === 0x6d || c === 0x47 || c === 0x4b || c === 0x48 || c === 0x4a)
|
|
454
|
+
return j + 1 - pos;
|
|
419
455
|
}
|
|
420
|
-
return
|
|
456
|
+
return 0;
|
|
421
457
|
}
|
|
422
458
|
// DCS sequence: ESC P ... ST (ESC \)
|
|
423
459
|
// Used for tmux passthrough (ESC P tmux; ... ESC \) whose payload doubles
|
|
@@ -426,32 +462,33 @@ export function extractAnsiCode(str, pos) {
|
|
|
426
462
|
if (next === "P") {
|
|
427
463
|
let j = pos + 2;
|
|
428
464
|
while (j < str.length) {
|
|
429
|
-
if (str
|
|
465
|
+
if (str.charCodeAt(j) === 0x1b) {
|
|
430
466
|
if (str[j + 1] === "\\")
|
|
431
|
-
return
|
|
432
|
-
if (str
|
|
467
|
+
return j + 2 - pos;
|
|
468
|
+
if (str.charCodeAt(j + 1) === 0x1b) {
|
|
433
469
|
j += 2;
|
|
434
470
|
continue;
|
|
435
471
|
}
|
|
436
472
|
}
|
|
437
473
|
j++;
|
|
438
474
|
}
|
|
439
|
-
return
|
|
475
|
+
return 0;
|
|
440
476
|
}
|
|
477
|
+
// OSC sequence: ESC ] ... BEL or ESC ] ... ST (ESC \)
|
|
478
|
+
// Used for hyperlinks (OSC 8), window titles, etc.
|
|
441
479
|
// APC sequence: ESC _ ... BEL or ESC _ ... ST (ESC \)
|
|
442
480
|
// Used for cursor marker and application-specific commands
|
|
443
|
-
if (next === "_") {
|
|
444
|
-
let j = pos + 2;
|
|
445
|
-
|
|
446
|
-
if (
|
|
447
|
-
return
|
|
448
|
-
if (
|
|
449
|
-
return
|
|
450
|
-
j++;
|
|
481
|
+
if (next === "]" || next === "_") {
|
|
482
|
+
for (let j = pos + 2; j < str.length; j++) {
|
|
483
|
+
const c = str.charCodeAt(j);
|
|
484
|
+
if (c === 0x07)
|
|
485
|
+
return j + 1 - pos;
|
|
486
|
+
if (c === 0x1b && str[j + 1] === "\\")
|
|
487
|
+
return j + 2 - pos;
|
|
451
488
|
}
|
|
452
|
-
return
|
|
489
|
+
return 0;
|
|
453
490
|
}
|
|
454
|
-
return
|
|
491
|
+
return 0;
|
|
455
492
|
}
|
|
456
493
|
function parseOsc8Hyperlink(ansiCode) {
|
|
457
494
|
if (!ansiCode.startsWith("\x1b]8;")) {
|
|
@@ -726,16 +763,17 @@ class AnsiCodeTracker {
|
|
|
726
763
|
}
|
|
727
764
|
}
|
|
728
765
|
function updateTrackerFromText(text, tracker) {
|
|
729
|
-
let i =
|
|
730
|
-
while (i
|
|
731
|
-
const
|
|
732
|
-
if (
|
|
733
|
-
tracker.process(
|
|
734
|
-
i +=
|
|
766
|
+
let i = text.indexOf("\x1b");
|
|
767
|
+
while (i !== -1) {
|
|
768
|
+
const length = ansiCodeLength(text, i);
|
|
769
|
+
if (length > 0) {
|
|
770
|
+
tracker.process(text.substring(i, i + length));
|
|
771
|
+
i += length;
|
|
735
772
|
}
|
|
736
773
|
else {
|
|
737
774
|
i++;
|
|
738
775
|
}
|
|
776
|
+
i = text.indexOf("\x1b", i);
|
|
739
777
|
}
|
|
740
778
|
}
|
|
741
779
|
/** Return only the background color active at the end of an ANSI-styled string. */
|
|
@@ -744,6 +782,10 @@ export function getActiveBackgroundAnsi(text) {
|
|
|
744
782
|
updateTrackerFromText(text, tracker);
|
|
745
783
|
return tracker.getActiveBackgroundCode();
|
|
746
784
|
}
|
|
785
|
+
function* graphemeSegments(text) {
|
|
786
|
+
for (const { segment } of graphemeSegmenter.segment(text))
|
|
787
|
+
yield segment;
|
|
788
|
+
}
|
|
747
789
|
/**
|
|
748
790
|
* Split text into words while keeping ANSI codes attached.
|
|
749
791
|
*/
|
|
@@ -769,13 +811,19 @@ function splitIntoTokensWithAnsi(text) {
|
|
|
769
811
|
i += ansiResult.length;
|
|
770
812
|
continue;
|
|
771
813
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
814
|
+
// Visible text runs up to the next escape sequence.
|
|
815
|
+
let end = text.indexOf("\x1b", i + 1);
|
|
816
|
+
while (end !== -1 && ansiCodeLength(text, end) === 0) {
|
|
817
|
+
end = text.indexOf("\x1b", end + 1);
|
|
775
818
|
}
|
|
776
|
-
|
|
819
|
+
if (end === -1)
|
|
820
|
+
end = text.length;
|
|
821
|
+
const chunk = text.slice(i, end);
|
|
822
|
+
// Printable ASCII characters are single graphemes, so skip the segmenter for them.
|
|
823
|
+
const ascii = isPrintableAscii(chunk);
|
|
824
|
+
for (const segment of ascii ? chunk : graphemeSegments(chunk)) {
|
|
777
825
|
const segmentIsSpace = segment === " ";
|
|
778
|
-
if (!segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
826
|
+
if (!ascii && !segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
779
827
|
flushCurrent();
|
|
780
828
|
const token = pendingAnsi + segment;
|
|
781
829
|
pendingAnsi = "";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Lines moved per mouse-wheel event, or `"auto"` to accelerate fast wheel spins. */
|
|
2
|
+
export type WheelScrollLines = number | "auto";
|
|
3
|
+
/**
|
|
4
|
+
* Converts wheel events into line counts.
|
|
5
|
+
*
|
|
6
|
+
* In `"auto"` mode on terminals that do not accelerate wheel input, the count follows event
|
|
7
|
+
* velocity: an isolated notch moves one line, while a fast spin moves up to six lines per event.
|
|
8
|
+
* For example, notches 100 ms apart move 1 line each, 50 ms apart move 2, and 20 ms apart move 5.
|
|
9
|
+
*/
|
|
10
|
+
export declare class WheelScrollAccelerator {
|
|
11
|
+
private lines;
|
|
12
|
+
private readonly accelerate;
|
|
13
|
+
private lastTime;
|
|
14
|
+
private lastDirection;
|
|
15
|
+
private averageGap;
|
|
16
|
+
private carry;
|
|
17
|
+
constructor(lines?: WheelScrollLines, accelerate?: boolean);
|
|
18
|
+
setLines(lines: WheelScrollLines): void;
|
|
19
|
+
/** Return the positive line count for a wheel event in `direction` at time `now` (milliseconds). */
|
|
20
|
+
next(direction: -1 | 1, now: number): number;
|
|
21
|
+
private reset;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=wheel-scroll.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Several events closer than this belong to one physical notch (Ghostty emits them ~4 ms apart)
|
|
2
|
+
// or come from a high-resolution source. They move one line each and do not accelerate.
|
|
3
|
+
const BURST_GAP_MS = 5;
|
|
4
|
+
// A pause longer than this ends a scroll gesture.
|
|
5
|
+
const GESTURE_GAP_MS = 200;
|
|
6
|
+
// Average event gap that maps to one line per event. Faster events scale up proportionally.
|
|
7
|
+
const REFERENCE_GAP_MS = 100;
|
|
8
|
+
const MAX_AUTO_LINES = 6;
|
|
9
|
+
/**
|
|
10
|
+
* Local macOS terminals receive wheel and trackpad deltas that the OS has already accelerated,
|
|
11
|
+
* and they emit one event per line. Other platforms, and SSH sessions where the client platform
|
|
12
|
+
* is unknown, usually send one event per wheel notch.
|
|
13
|
+
*/
|
|
14
|
+
function terminalAcceleratesWheel() {
|
|
15
|
+
const env = process.env;
|
|
16
|
+
return (process.platform === "darwin" &&
|
|
17
|
+
env.SSH_CONNECTION === undefined &&
|
|
18
|
+
env.SSH_CLIENT === undefined &&
|
|
19
|
+
env.SSH_TTY === undefined);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Converts wheel events into line counts.
|
|
23
|
+
*
|
|
24
|
+
* In `"auto"` mode on terminals that do not accelerate wheel input, the count follows event
|
|
25
|
+
* velocity: an isolated notch moves one line, while a fast spin moves up to six lines per event.
|
|
26
|
+
* For example, notches 100 ms apart move 1 line each, 50 ms apart move 2, and 20 ms apart move 5.
|
|
27
|
+
*/
|
|
28
|
+
export class WheelScrollAccelerator {
|
|
29
|
+
constructor(lines = "auto", accelerate = !terminalAcceleratesWheel()) {
|
|
30
|
+
this.lastTime = Number.NEGATIVE_INFINITY;
|
|
31
|
+
this.lastDirection = 0;
|
|
32
|
+
this.carry = 0;
|
|
33
|
+
this.lines = lines;
|
|
34
|
+
this.accelerate = accelerate;
|
|
35
|
+
}
|
|
36
|
+
setLines(lines) {
|
|
37
|
+
this.lines = lines;
|
|
38
|
+
this.reset();
|
|
39
|
+
}
|
|
40
|
+
/** Return the positive line count for a wheel event in `direction` at time `now` (milliseconds). */
|
|
41
|
+
next(direction, now) {
|
|
42
|
+
if (this.lines !== "auto")
|
|
43
|
+
return Number.isFinite(this.lines) ? Math.max(1, Math.floor(this.lines)) : 1;
|
|
44
|
+
if (!this.accelerate)
|
|
45
|
+
return 1;
|
|
46
|
+
const gap = now - this.lastTime;
|
|
47
|
+
const sameGesture = direction === this.lastDirection && gap <= GESTURE_GAP_MS;
|
|
48
|
+
this.lastTime = now;
|
|
49
|
+
this.lastDirection = direction;
|
|
50
|
+
if (!sameGesture) {
|
|
51
|
+
this.averageGap = undefined;
|
|
52
|
+
this.carry = 0;
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
if (gap < BURST_GAP_MS)
|
|
56
|
+
return 1;
|
|
57
|
+
this.averageGap = this.averageGap === undefined ? gap : (this.averageGap + gap) / 2;
|
|
58
|
+
const lines = Math.min(MAX_AUTO_LINES, Math.max(1, REFERENCE_GAP_MS / this.averageGap)) + this.carry;
|
|
59
|
+
const whole = Math.floor(lines);
|
|
60
|
+
this.carry = lines - whole;
|
|
61
|
+
return whole;
|
|
62
|
+
}
|
|
63
|
+
reset() {
|
|
64
|
+
this.lastTime = Number.NEGATIVE_INFINITY;
|
|
65
|
+
this.lastDirection = 0;
|
|
66
|
+
this.averageGap = undefined;
|
|
67
|
+
this.carry = 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=wheel-scroll.js.map
|
package/native/clipboard.h
CHANGED
|
@@ -7,8 +7,17 @@
|
|
|
7
7
|
#include <string.h>
|
|
8
8
|
#endif
|
|
9
9
|
|
|
10
|
-
typedef enum { CLIPBOARD_TEXT, CLIPBOARD_IMAGE, CLIPBOARD_WRITE } clipboard_operation;
|
|
11
|
-
|
|
10
|
+
typedef enum { CLIPBOARD_TEXT, CLIPBOARD_IMAGE, CLIPBOARD_WRITE, CLIPBOARD_FILES } clipboard_operation;
|
|
11
|
+
// CLIPBOARD_PATHS stores concatenated, NUL-terminated UTF-8 paths in clipboard_job.data.
|
|
12
|
+
typedef enum {
|
|
13
|
+
CLIPBOARD_UNAVAILABLE,
|
|
14
|
+
CLIPBOARD_EMPTY,
|
|
15
|
+
CLIPBOARD_UTF8,
|
|
16
|
+
CLIPBOARD_LATIN1,
|
|
17
|
+
CLIPBOARD_UTF16,
|
|
18
|
+
CLIPBOARD_BUFFER,
|
|
19
|
+
CLIPBOARD_PATHS
|
|
20
|
+
} clipboard_format;
|
|
12
21
|
|
|
13
22
|
typedef struct {
|
|
14
23
|
napi_async_work work;
|
|
@@ -63,6 +72,33 @@ static void PI_NAPI_CALL execute_clipboard_work(napi_env env, void* data) {
|
|
|
63
72
|
clipboard_execute(data);
|
|
64
73
|
}
|
|
65
74
|
|
|
75
|
+
static int create_clipboard_paths_value(
|
|
76
|
+
napi_env env,
|
|
77
|
+
const clipboard_job* job,
|
|
78
|
+
napi_create_string_utf8_fn create_string,
|
|
79
|
+
napi_value* result
|
|
80
|
+
) {
|
|
81
|
+
napi_create_array_fn create_array = (napi_create_array_fn)node_symbol("napi_create_array");
|
|
82
|
+
napi_set_element_fn set_element = (napi_set_element_fn)node_symbol("napi_set_element");
|
|
83
|
+
if (!create_array || !set_element || create_array(env, result) != 0) return 1;
|
|
84
|
+
|
|
85
|
+
const char* cursor = job->data;
|
|
86
|
+
const char* end = cursor + job->length;
|
|
87
|
+
for (uint32_t index = 0; cursor < end; index++) {
|
|
88
|
+
const char* terminator = cursor;
|
|
89
|
+
while (terminator < end && *terminator != '\0') terminator++;
|
|
90
|
+
if (terminator == end) return 1;
|
|
91
|
+
|
|
92
|
+
napi_value item = 0;
|
|
93
|
+
if (create_string(env, cursor, (size_t)(terminator - cursor), &item) != 0 ||
|
|
94
|
+
set_element(env, *result, index, item) != 0) {
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
cursor = terminator + 1;
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
66
102
|
static void PI_NAPI_CALL complete_clipboard_work(napi_env env, int status, void* data) {
|
|
67
103
|
clipboard_job* job = data;
|
|
68
104
|
napi_create_string_utf8_fn create_string = (napi_create_string_utf8_fn)node_symbol("napi_create_string_utf8");
|
|
@@ -71,6 +107,8 @@ static void PI_NAPI_CALL complete_clipboard_work(napi_env env, int status, void*
|
|
|
71
107
|
if (!job->error && job->operation != CLIPBOARD_WRITE) {
|
|
72
108
|
if (job->format == CLIPBOARD_EMPTY) {
|
|
73
109
|
result = null_value(env);
|
|
110
|
+
} else if (job->format == CLIPBOARD_PATHS) {
|
|
111
|
+
status = create_clipboard_paths_value(env, job, create_string, &result);
|
|
74
112
|
} else if (job->format == CLIPBOARD_BUFFER) {
|
|
75
113
|
napi_create_buffer_copy_fn create_buffer = (napi_create_buffer_copy_fn)node_symbol("napi_create_buffer_copy");
|
|
76
114
|
status = create_buffer(env, job->length, job->data, 0, &result);
|
package/native/darwin/README.md
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -66,6 +66,40 @@ static void clipboard_execute(clipboard_job* job) {
|
|
|
66
66
|
} else {
|
|
67
67
|
clipboard_copy(job, text.UTF8String, [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding], CLIPBOARD_UTF8);
|
|
68
68
|
}
|
|
69
|
+
} else if (job->operation == CLIPBOARD_FILES) {
|
|
70
|
+
NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]]
|
|
71
|
+
options:@{NSPasteboardURLReadingFileURLsOnlyKey: @YES}];
|
|
72
|
+
size_t total = 0;
|
|
73
|
+
for (NSURL* url in urls) {
|
|
74
|
+
const char* path = url.fileSystemRepresentation;
|
|
75
|
+
if (!path) continue;
|
|
76
|
+
size_t length = strlen(path);
|
|
77
|
+
if (length == SIZE_MAX || total > SIZE_MAX - length - 1) {
|
|
78
|
+
job->error = "Clipboard file paths are too large";
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
total += length + 1;
|
|
82
|
+
}
|
|
83
|
+
if (total == 0) {
|
|
84
|
+
job->format = CLIPBOARD_EMPTY;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
char* paths = clipboard_alloc(total);
|
|
88
|
+
if (!paths) {
|
|
89
|
+
job->error = "Out of memory";
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
char* cursor = paths;
|
|
93
|
+
for (NSURL* url in urls) {
|
|
94
|
+
const char* path = url.fileSystemRepresentation;
|
|
95
|
+
if (!path) continue;
|
|
96
|
+
size_t length = strlen(path);
|
|
97
|
+
memcpy(cursor, path, length + 1);
|
|
98
|
+
cursor += length + 1;
|
|
99
|
+
}
|
|
100
|
+
job->data = paths;
|
|
101
|
+
job->length = total;
|
|
102
|
+
job->format = CLIPBOARD_PATHS;
|
|
69
103
|
} else {
|
|
70
104
|
if (![pasteboard availableTypeFromArray:@[ NSPasteboardTypePNG, NSPasteboardTypeTIFF ]]) {
|
|
71
105
|
job->format = CLIPBOARD_EMPTY;
|
|
@@ -84,10 +118,15 @@ static void clipboard_execute(clipboard_job* job) {
|
|
|
84
118
|
}
|
|
85
119
|
}
|
|
86
120
|
|
|
121
|
+
static napi_value PI_NAPI_CALL get_clipboard_file_paths(napi_env env, napi_callback_info info) {
|
|
122
|
+
return queue_clipboard(env, info, CLIPBOARD_FILES);
|
|
123
|
+
}
|
|
124
|
+
|
|
87
125
|
PI_NAPI_EXPORT napi_value napi_register_module_v1(napi_env env, napi_value exports) {
|
|
88
126
|
set_function_export(env, exports, "isModifierPressed", is_modifier_pressed);
|
|
89
127
|
set_function_export(env, exports, "getText", get_clipboard_text);
|
|
90
128
|
set_function_export(env, exports, "setText", set_clipboard_text);
|
|
91
129
|
set_function_export(env, exports, "getImage", get_clipboard_image);
|
|
130
|
+
set_function_export(env, exports, "getFilePaths", get_clipboard_file_paths);
|
|
92
131
|
return exports;
|
|
93
132
|
}
|
package/native/napi.h
CHANGED
|
@@ -26,6 +26,7 @@ typedef void* napi_deferred;
|
|
|
26
26
|
typedef void (PI_NAPI_CALL *napi_async_execute_callback)(napi_env, void*);
|
|
27
27
|
typedef void (PI_NAPI_CALL *napi_async_complete_callback)(napi_env, int, void*);
|
|
28
28
|
typedef int (PI_NAPI_CALL *napi_create_async_work_fn)(napi_env, napi_value, napi_value, napi_async_execute_callback, napi_async_complete_callback, void*, napi_async_work*);
|
|
29
|
+
typedef int (PI_NAPI_CALL *napi_create_array_fn)(napi_env, napi_value*);
|
|
29
30
|
typedef int (PI_NAPI_CALL *napi_async_work_fn)(napi_env, napi_async_work);
|
|
30
31
|
typedef int (PI_NAPI_CALL *napi_create_promise_fn)(napi_env, napi_deferred*, napi_value*);
|
|
31
32
|
typedef int (PI_NAPI_CALL *napi_settle_deferred_fn)(napi_env, napi_deferred, napi_value);
|
|
@@ -41,6 +42,7 @@ typedef int (PI_NAPI_CALL *napi_get_value_fn)(napi_env, napi_value*);
|
|
|
41
42
|
typedef int (PI_NAPI_CALL *napi_get_value_string_utf8_fn)(napi_env, napi_value, char*, size_t, size_t*);
|
|
42
43
|
typedef int (PI_NAPI_CALL *napi_get_value_string_utf16_fn)(napi_env, napi_value, uint16_t*, size_t, size_t*);
|
|
43
44
|
typedef int (PI_NAPI_CALL *napi_set_named_property_fn)(napi_env, napi_value, const char*, napi_value);
|
|
45
|
+
typedef int (PI_NAPI_CALL *napi_set_element_fn)(napi_env, napi_value, uint32_t, napi_value);
|
|
44
46
|
typedef int (PI_NAPI_CALL *napi_throw_error_fn)(napi_env, const char*, const char*);
|
|
45
47
|
|
|
46
48
|
static void* node_symbol(const char* name) {
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-tui",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1-2",
|
|
4
4
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"clean": "shx rm -rf dist",
|
|
9
|
-
"build": "
|
|
9
|
+
"build": "tsc -p tsconfig.build.json",
|
|
10
10
|
"build:native:darwin": "native/darwin/build.sh",
|
|
11
11
|
"build:native:linux": "native/linux/build.sh",
|
|
12
12
|
"build:native:win32": "node native/win32/build.mjs",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist/**/*",
|
|
19
|
+
"!dist/**/*.map",
|
|
19
20
|
"native/napi.h",
|
|
20
21
|
"native/clipboard.h",
|
|
21
22
|
"native/win32/prebuilds/**/*.node",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"alt-screen-search.d.ts","sourceRoot":"","sources":["../src/alt-screen-search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAmBrD,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACnC;AAwHD,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,gGAAgG;AAChG,qBAAa,oBAAoB;IAChC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,OAAO,CAA8B;IAE7C,MAAM,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAqBrE;CACD;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAG1G;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAI9E;AAED,qBAAa,wBAAyB,YAAW,SAAS,EAAE,SAAS;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAInB;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA6C;IACnF,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,0BAA0B,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACtC,qBAAqB,GAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,MAAuB,EAIlF;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5C;IAED,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAKxE;IAED,6BAA6B,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,OAAO,CAIpE;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAK9B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkE9B;CACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"alt-screen-search.js","sourceRoot":"","sources":["../src/alt-screen-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { Input } from \"./components/input.ts\";\nimport { getKeybindings } from \"./keybindings.ts\";\nimport type { Component, Focusable } from \"./tui.ts\";\nimport { getGraphemeSegmenter, stripTerminalSequences, truncateToWidth, visibleWidth } from \"./utils.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface SearchSourceSpan {\n\ttextStart: number;\n\ttextEnd: number;\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n\tlinearColumns: boolean;\n}\n\ninterface SearchCorpus {\n\ttext: string;\n\tspans: SearchSourceSpan[];\n}\n\nexport interface AltScreenSearchSegment {\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n}\n\nexport interface AltScreenSearchMatch {\n\tsegments: AltScreenSearchSegment[];\n}\n\nconst PRINTABLE_ASCII = /^[\\x20-\\x7e]*$/;\n\nfunction buildSearchCorpus(lines: readonly string[]): SearchCorpus {\n\tconst chunks: string[] = [];\n\tconst spans: SearchSourceSpan[] = [];\n\tlet textLength = 0;\n\tlet pendingSeparator = false;\n\n\tconst appendSeparator = (): void => {\n\t\tif (!pendingSeparator) return;\n\t\tchunks.push(\" \");\n\t\ttextLength += 1;\n\t\tpendingSeparator = false;\n\t};\n\n\tfor (let row = 0; row < lines.length; row++) {\n\t\tconst line = stripTerminalSequences(lines[row] ?? \"\");\n\t\tlet column = 0;\n\n\t\t// Rendered transcripts are overwhelmingly ASCII. Index complete non-space\n\t\t// runs at once instead of segmenting and allocating one mapping per cell.\n\t\tif (PRINTABLE_ASCII.test(line)) {\n\t\t\tlet index = 0;\n\t\t\twhile (index < line.length) {\n\t\t\t\tif (line.charCodeAt(index) === 0x20) {\n\t\t\t\t\tif (textLength > 0) pendingSeparator = true;\n\t\t\t\t\tcolumn += 1;\n\t\t\t\t\tindex += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet end = index + 1;\n\t\t\t\twhile (end < line.length && line.charCodeAt(end) !== 0x20) end += 1;\n\t\t\t\tappendSeparator();\n\t\t\t\tconst text = line.slice(index, end);\n\t\t\t\tchunks.push(text);\n\t\t\t\tspans.push({\n\t\t\t\t\ttextStart: textLength,\n\t\t\t\t\ttextEnd: textLength + text.length,\n\t\t\t\t\trow,\n\t\t\t\t\tstartCol: column,\n\t\t\t\t\tendCol: column + text.length,\n\t\t\t\t\tlinearColumns: true,\n\t\t\t\t});\n\t\t\t\ttextLength += text.length;\n\t\t\t\tcolumn += text.length;\n\t\t\t\tindex = end;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const grapheme of segmenter.segment(line)) {\n\t\t\t\tconst text = grapheme.segment;\n\t\t\t\tconst width = visibleWidth(text);\n\t\t\t\tif (/^\\s+$/u.test(text)) {\n\t\t\t\t\tif (textLength > 0) pendingSeparator = true;\n\t\t\t\t\tcolumn += width;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tappendSeparator();\n\t\t\t\tchunks.push(text);\n\t\t\t\tspans.push({\n\t\t\t\t\ttextStart: textLength,\n\t\t\t\t\ttextEnd: textLength + text.length,\n\t\t\t\t\trow,\n\t\t\t\t\tstartCol: column,\n\t\t\t\t\tendCol: column + width,\n\t\t\t\t\tlinearColumns: false,\n\t\t\t\t});\n\t\t\t\ttextLength += text.length;\n\t\t\t\tcolumn += width;\n\t\t\t}\n\t\t}\n\t\tif (textLength > 0) pendingSeparator = true;\n\t}\n\n\treturn { text: chunks.join(\"\"), spans };\n}\n\nfunction normalizeQuery(query: string): string {\n\treturn query.replace(/\\s+/gu, \" \").trim();\n}\n\nfunction escapeRegExp(text: string): string {\n\treturn text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction findSearchCorpusMatches(corpus: SearchCorpus, normalizedQuery: string): AltScreenSearchMatch[] {\n\tif (!normalizedQuery) return [];\n\tconst expression = new RegExp(escapeRegExp(normalizedQuery), \"giu\");\n\tconst matches: AltScreenSearchMatch[] = [];\n\tlet spanIndex = 0;\n\n\tfor (const match of corpus.text.matchAll(expression)) {\n\t\tconst start = match.index;\n\t\tconst end = start + match[0].length;\n\t\twhile (spanIndex < corpus.spans.length && corpus.spans[spanIndex]!.textEnd <= start) spanIndex += 1;\n\n\t\tconst segments: AltScreenSearchSegment[] = [];\n\t\tfor (let index = spanIndex; index < corpus.spans.length; index++) {\n\t\t\tconst span = corpus.spans[index]!;\n\t\t\tif (span.textStart >= end) break;\n\t\t\tif (span.textEnd <= start) continue;\n\t\t\tconst startCol = span.linearColumns\n\t\t\t\t? span.startCol + Math.max(start, span.textStart) - span.textStart\n\t\t\t\t: span.startCol;\n\t\t\tconst endCol = span.linearColumns ? span.startCol + Math.min(end, span.textEnd) - span.textStart : span.endCol;\n\t\t\tconst previous = segments[segments.length - 1];\n\t\t\tif (previous && previous.row === span.row && startCol <= previous.endCol) {\n\t\t\t\tprevious.endCol = Math.max(previous.endCol, endCol);\n\t\t\t} else {\n\t\t\t\tsegments.push({ row: span.row, startCol, endCol });\n\t\t\t}\n\t\t}\n\t\twhile (spanIndex < corpus.spans.length && corpus.spans[spanIndex]!.textEnd <= end) spanIndex += 1;\n\t\tif (segments.length > 0) matches.push({ segments });\n\t}\n\n\treturn matches;\n}\n\nexport interface AltScreenSearchResult {\n\tmatches: AltScreenSearchMatch[];\n\tchanged: boolean;\n}\n\n/** Cache the searchable corpus and matches while rendered transcript lines remain unchanged. */\nexport class AltScreenSearchIndex {\n\tprivate sourceLines: string[] | undefined;\n\tprivate corpus: SearchCorpus | undefined;\n\tprivate normalizedQuery: string | undefined;\n\tprivate matches: AltScreenSearchMatch[] = [];\n\n\tsearch(lines: readonly string[], query: string): AltScreenSearchResult {\n\t\tlet sourceChanged = this.sourceLines?.length !== lines.length;\n\t\tif (!sourceChanged && this.sourceLines) {\n\t\t\tfor (let index = 0; index < lines.length; index++) {\n\t\t\t\tif (this.sourceLines[index] === lines[index]) continue;\n\t\t\t\tsourceChanged = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (sourceChanged || !this.corpus) {\n\t\t\tthis.sourceLines = Array.from(lines);\n\t\t\tthis.corpus = buildSearchCorpus(lines);\n\t\t}\n\n\t\tconst normalizedQuery = normalizeQuery(query);\n\t\tconst changed = sourceChanged || normalizedQuery !== this.normalizedQuery;\n\t\tif (changed) {\n\t\t\tthis.normalizedQuery = normalizedQuery;\n\t\t\tthis.matches = findSearchCorpusMatches(this.corpus, normalizedQuery);\n\t\t}\n\t\treturn { matches: this.matches, changed };\n\t}\n}\n\nexport function findAltScreenSearchMatches(lines: readonly string[], query: string): AltScreenSearchMatch[] {\n\tconst normalizedQuery = normalizeQuery(query);\n\treturn normalizedQuery ? findSearchCorpusMatches(buildSearchCorpus(lines), normalizedQuery) : [];\n}\n\nexport function getAltScreenSearchMatchKey(match: AltScreenSearchMatch): string {\n\tconst first = match.segments[0];\n\tconst last = match.segments[match.segments.length - 1];\n\treturn first && last ? `${first.row}:${first.startCol}:${last.row}:${last.endCol}` : \"\";\n}\n\nexport class AltScreenSearchComponent implements Component, Focusable {\n\tprivate readonly input = new Input({\n\t\tprompt: \" \",\n\t\tplaceholder: \"Find in transcript\",\n\t\tplaceholderStyle: (text) => `\\x1b[2m${text}\\x1b[22m`,\n\t});\n\tprivate readonly onQueryChange: (query: string) => void;\n\tprivate readonly navigationButtonStyle: (text: string, hovered: boolean) => string;\n\tprivate resultCount = 0;\n\tprivate resultIndex = -1;\n\tprivate previousButtonStart = -1;\n\tprivate previousButtonEnd = -1;\n\tprivate nextButtonStart = -1;\n\tprivate nextButtonEnd = -1;\n\tprivate hoveredNavigationDirection: -1 | 1 | undefined;\n\tprivate _focused = false;\n\n\tconstructor(\n\t\tonQueryChange: (query: string) => void,\n\t\tnavigationButtonStyle: (text: string, hovered: boolean) => string = (text) => text,\n\t) {\n\t\tthis.onQueryChange = onQueryChange;\n\t\tthis.navigationButtonStyle = navigationButtonStyle;\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tsetResult(index: number, count: number): void {\n\t\tthis.resultIndex = index;\n\t\tthis.resultCount = count;\n\t}\n\n\tgetNavigationDirectionAt(row: number, column: number): -1 | 1 | undefined {\n\t\tif (row !== 2) return undefined;\n\t\tif (column >= this.previousButtonStart && column < this.previousButtonEnd) return -1;\n\t\tif (column >= this.nextButtonStart && column < this.nextButtonEnd) return 1;\n\t\treturn undefined;\n\t}\n\n\tsetHoveredNavigationDirection(direction: -1 | 1 | undefined): boolean {\n\t\tif (direction === this.hoveredNavigationDirection) return false;\n\t\tthis.hoveredNavigationDirection = direction;\n\t\treturn true;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst previous = this.input.getValue();\n\t\tthis.input.handleInput(data);\n\t\tconst query = this.input.getValue();\n\t\tif (query !== previous) this.onQueryChange(query);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.input.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst innerWidth = Math.max(0, safeWidth - 2);\n\t\tconst formatKey = (key: string | undefined): string =>\n\t\t\tkey\n\t\t\t\t? key\n\t\t\t\t\t\t.split(\"+\")\n\t\t\t\t\t\t.map((part) => {\n\t\t\t\t\t\t\tif (process.platform === \"darwin\" && part.toLowerCase() === \"alt\") return \"Option\";\n\t\t\t\t\t\t\treturn part.charAt(0).toUpperCase() + part.slice(1);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"+\")\n\t\t\t\t: \"Unbound\";\n\t\tconst keybindings = getKeybindings();\n\t\tconst previousKey = formatKey(keybindings.getKeys(\"tui.altScreen.searchPrevious\")[0]);\n\t\tconst nextKey = formatKey(keybindings.getKeys(\"tui.altScreen.searchNext\")[0]);\n\t\tconst query = this.input.getValue();\n\t\tconst result = !query\n\t\t\t? \"\"\n\t\t\t: this.resultCount === 0\n\t\t\t\t? \"No matches\"\n\t\t\t\t: `${this.resultIndex + 1}/${this.resultCount}`;\n\t\tconst resultSpace = Math.max(0, innerWidth - 3);\n\t\tconst visibleResult = truncateToWidth(result, resultSpace, \"\");\n\t\tconst resultText = visibleResult ? `\\x1b[2m ${visibleResult} \\x1b[22m` : \"\";\n\t\tconst inputWidth = Math.max(0, innerWidth - visibleWidth(resultText));\n\t\tconst inputLine = truncateToWidth(this.input.render(Math.max(1, inputWidth))[0] ?? \"\", inputWidth, \"\");\n\t\tconst inputPadding = \" \".repeat(Math.max(0, inputWidth - visibleWidth(inputLine)));\n\t\tconst content = `${inputLine}${inputPadding}${resultText}`;\n\n\t\tlet previousButton = `↑ ${previousKey}`;\n\t\tlet nextButton = `↓ ${nextKey}`;\n\t\tlet separator = \" · \";\n\t\tconst outerGapWidth = 1;\n\t\tconst availableControlsWidth = Math.max(0, innerWidth - outerGapWidth * 2 - 1);\n\t\tlet controlsWidth = visibleWidth(previousButton) + visibleWidth(separator) + visibleWidth(nextButton);\n\t\tif (controlsWidth > availableControlsWidth) {\n\t\t\tpreviousButton = \"↑\";\n\t\t\tnextButton = \"↓\";\n\t\t\tseparator = \" \";\n\t\t\tcontrolsWidth = visibleWidth(previousButton) + visibleWidth(separator) + visibleWidth(nextButton);\n\t\t}\n\t\tconst showButtons = controlsWidth <= availableControlsWidth;\n\t\tconst renderedButtons = showButtons\n\t\t\t? this.navigationButtonStyle(previousButton, this.hoveredNavigationDirection === -1) +\n\t\t\t\tseparator +\n\t\t\t\tthis.navigationButtonStyle(nextButton, this.hoveredNavigationDirection === 1)\n\t\t\t: \"\";\n\t\tconst outerGapsWidth = showButtons ? outerGapWidth * 2 : 0;\n\t\tconst rightRuleWidth = renderedButtons && innerWidth > controlsWidth + outerGapsWidth ? 1 : 0;\n\t\tconst leftRuleWidth = Math.max(\n\t\t\t0,\n\t\t\tinnerWidth - (showButtons ? controlsWidth : 0) - outerGapsWidth - rightRuleWidth,\n\t\t);\n\t\tconst previousStart = 1 + leftRuleWidth + outerGapWidth;\n\t\tthis.previousButtonStart = showButtons ? previousStart : -1;\n\t\tthis.previousButtonEnd = showButtons ? previousStart + visibleWidth(previousButton) : -1;\n\t\tthis.nextButtonStart = showButtons ? this.previousButtonEnd + visibleWidth(separator) : -1;\n\t\tthis.nextButtonEnd = showButtons ? this.nextButtonStart + visibleWidth(nextButton) : -1;\n\n\t\tif (safeWidth === 1) return [\"┌\", \"│\", \"└\"];\n\t\treturn [\n\t\t\t`┌${\"─\".repeat(innerWidth)}┐`,\n\t\t\t`│${content}│`,\n\t\t\t`└${\"─\".repeat(leftRuleWidth)}${renderedButtons ? \" \" : \"\"}${renderedButtons}${renderedButtons ? \" \" : \"\"}${\"─\".repeat(rightRuleWidth)}┘`,\n\t\t];\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../src/autocomplete.ts"],"names":[],"mappings":"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"}
|