@sayknow-cli/tui 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/animation-scheduler.d.ts +13 -0
- package/dist/types/autocomplete.d.ts +83 -0
- package/dist/types/bracketed-paste.d.ts +26 -0
- package/dist/types/components/box.d.ts +20 -0
- package/dist/types/components/cancellable-loader.d.ts +21 -0
- package/dist/types/components/editor.d.ts +126 -0
- package/dist/types/components/image.d.ts +16 -0
- package/dist/types/components/input.d.ts +16 -0
- package/dist/types/components/loader.d.ts +23 -0
- package/dist/types/components/markdown.d.ts +77 -0
- package/dist/types/components/select-list.d.ts +46 -0
- package/dist/types/components/settings-list.d.ts +39 -0
- package/dist/types/components/spacer.d.ts +11 -0
- package/dist/types/components/tab-bar.d.ts +56 -0
- package/dist/types/components/text.d.ts +13 -0
- package/dist/types/components/truncated-text.d.ts +10 -0
- package/dist/types/editor-component.d.ts +36 -0
- package/dist/types/fuzzy.d.ts +15 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/keybindings.d.ts +201 -0
- package/dist/types/keys.d.ts +208 -0
- package/dist/types/kill-ring.d.ts +27 -0
- package/dist/types/metrics.d.ts +85 -0
- package/dist/types/stdin-buffer.d.ts +50 -0
- package/dist/types/symbols.d.ts +23 -0
- package/dist/types/terminal-capabilities.d.ts +75 -0
- package/dist/types/terminal.d.ts +88 -0
- package/dist/types/ttyid.d.ts +9 -0
- package/dist/types/tui.d.ts +206 -0
- package/dist/types/utils.d.ts +87 -0
- package/package.json +10 -9
- package/src/animation-scheduler.ts +99 -0
- package/src/autocomplete.ts +119 -96
- package/src/components/editor.ts +310 -128
- package/src/components/input.ts +2 -1
- package/src/components/loader.ts +36 -37
- package/src/components/markdown.ts +79 -2
- package/src/components/select-list.ts +8 -1
- package/src/index.ts +1 -0
- package/src/stdin-buffer.ts +89 -11
- package/src/terminal.ts +44 -8
- package/src/tui.ts +362 -64
- package/src/utils.ts +77 -11
package/src/utils.ts
CHANGED
|
@@ -3,16 +3,31 @@ import {
|
|
|
3
3
|
type ExtractSegmentsResult,
|
|
4
4
|
extractSegments as nativeExtractSegments,
|
|
5
5
|
sliceWithWidth as nativeSliceWithWidth,
|
|
6
|
+
truncateLinesToWidth as nativeTruncateLinesToWidth,
|
|
6
7
|
truncateToWidth as nativeTruncateToWidth,
|
|
8
|
+
visibleWidths as nativeVisibleWidths,
|
|
7
9
|
wrapTextWithAnsi as nativeWrapTextWithAnsi,
|
|
8
10
|
type SliceResult,
|
|
9
11
|
} from "@sayknow-cli/natives";
|
|
10
|
-
import { getDefaultTabWidth, getIndentation } from "@sayknow-cli/utils";
|
|
12
|
+
import { getDefaultTabWidth, getIndentation, onDefaultTabWidthChange } from "@sayknow-cli/utils";
|
|
11
13
|
import { renderMetrics } from "./metrics";
|
|
12
14
|
|
|
13
15
|
export { Ellipsis } from "@sayknow-cli/natives";
|
|
14
16
|
|
|
15
17
|
export { getDefaultTabWidth, getIndentation } from "@sayknow-cli/utils";
|
|
18
|
+
/** Test-only performance counters for advisory baseline tests. */
|
|
19
|
+
export const __textHelperPerfCounters = {
|
|
20
|
+
truncateToWidthCalls: 0,
|
|
21
|
+
wrapTextWithAnsiCalls: 0,
|
|
22
|
+
truncateLinesToWidthCalls: 0,
|
|
23
|
+
visibleWidthsCalls: 0,
|
|
24
|
+
reset(): void {
|
|
25
|
+
this.truncateToWidthCalls = 0;
|
|
26
|
+
this.wrapTextWithAnsiCalls = 0;
|
|
27
|
+
this.truncateLinesToWidthCalls = 0;
|
|
28
|
+
this.visibleWidthsCalls = 0;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
16
31
|
|
|
17
32
|
function recordTextHelper<T>(name: string, fn: () => T): T {
|
|
18
33
|
if (!renderMetrics.enabled) return fn();
|
|
@@ -24,6 +39,18 @@ function recordTextHelper<T>(name: string, fn: () => T): T {
|
|
|
24
39
|
}
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
let cachedTabWidth: number | undefined;
|
|
43
|
+
|
|
44
|
+
function getCachedTabWidth(): number {
|
|
45
|
+
cachedTabWidth ??= getDefaultTabWidth();
|
|
46
|
+
return cachedTabWidth;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function invalidateTabWidthCache(): void {
|
|
50
|
+
cachedTabWidth = undefined;
|
|
51
|
+
}
|
|
52
|
+
onDefaultTabWidthChange(invalidateTabWidthCache);
|
|
53
|
+
|
|
27
54
|
export function isPrintableAscii(text: string): boolean {
|
|
28
55
|
for (let i = 0; i < text.length; i++) {
|
|
29
56
|
const code = text.charCodeAt(i);
|
|
@@ -33,7 +60,7 @@ export function isPrintableAscii(text: string): boolean {
|
|
|
33
60
|
}
|
|
34
61
|
|
|
35
62
|
export function sliceWithWidth(line: string, startCol: number, length: number, strict?: boolean | null): SliceResult {
|
|
36
|
-
return nativeSliceWithWidth(line, startCol, length, strict ?? null,
|
|
63
|
+
return nativeSliceWithWidth(line, startCol, length, strict ?? null, getCachedTabWidth());
|
|
37
64
|
}
|
|
38
65
|
|
|
39
66
|
export function truncateToWidth(
|
|
@@ -42,26 +69,53 @@ export function truncateToWidth(
|
|
|
42
69
|
ellipsisKind?: Ellipsis | null,
|
|
43
70
|
pad?: boolean | null,
|
|
44
71
|
): string {
|
|
72
|
+
__textHelperPerfCounters.truncateToWidthCalls += 1;
|
|
45
73
|
// Guard nullish napi inputs: napi-rs 3 on the Windows prebuilt rejects
|
|
46
74
|
// `null` for `Option<u8>` (Ellipsis) / `Option<bool>` (pad) (issue #848),
|
|
47
75
|
// and `maxWidth` is a required `u32` that throws on `null`/`undefined`
|
|
48
|
-
// everywhere.
|
|
76
|
+
// everywhere. The `text` arg is a required `String` that likewise throws on
|
|
77
|
+
// `null`/`undefined` on every platform, which crashed renderers that passed
|
|
78
|
+
// an optional/possibly-undefined field. Pass concrete defaults that mirror
|
|
79
|
+
// the Rust `unwrap_or`s.
|
|
80
|
+
const safeText = typeof text === "string" ? text : String(text ?? "");
|
|
49
81
|
const safeWidth = Number.isFinite(maxWidth) ? Math.max(0, Math.trunc(maxWidth)) : 0;
|
|
50
82
|
let resolvedEllipsis: Ellipsis | null | undefined | string = ellipsisKind;
|
|
51
83
|
if (typeof resolvedEllipsis === "string") {
|
|
52
84
|
resolvedEllipsis = resolvedEllipsis === "" ? Ellipsis.Omit : Ellipsis.Unicode;
|
|
53
85
|
}
|
|
54
86
|
return nativeTruncateToWidth(
|
|
55
|
-
|
|
87
|
+
safeText,
|
|
88
|
+
safeWidth,
|
|
89
|
+
resolvedEllipsis ?? Ellipsis.Unicode,
|
|
90
|
+
pad ?? false,
|
|
91
|
+
getCachedTabWidth(),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function truncateLinesToWidth(
|
|
96
|
+
lines: readonly string[],
|
|
97
|
+
maxWidth: number,
|
|
98
|
+
ellipsisKind?: Ellipsis | null,
|
|
99
|
+
pad?: boolean | null,
|
|
100
|
+
): string[] {
|
|
101
|
+
__textHelperPerfCounters.truncateLinesToWidthCalls += 1;
|
|
102
|
+
const safeWidth = Number.isFinite(maxWidth) ? Math.max(0, Math.trunc(maxWidth)) : 0;
|
|
103
|
+
let resolvedEllipsis: Ellipsis | null | undefined | string = ellipsisKind;
|
|
104
|
+
if (typeof resolvedEllipsis === "string") {
|
|
105
|
+
resolvedEllipsis = resolvedEllipsis === "" ? Ellipsis.Omit : Ellipsis.Unicode;
|
|
106
|
+
}
|
|
107
|
+
return nativeTruncateLinesToWidth(
|
|
108
|
+
lines.map(line => (typeof line === "string" ? line : String(line ?? ""))),
|
|
56
109
|
safeWidth,
|
|
57
110
|
resolvedEllipsis ?? Ellipsis.Unicode,
|
|
58
111
|
pad ?? false,
|
|
59
|
-
|
|
112
|
+
getCachedTabWidth(),
|
|
60
113
|
);
|
|
61
114
|
}
|
|
62
115
|
|
|
63
116
|
export function wrapTextWithAnsi(text: string, width: number): string[] {
|
|
64
|
-
|
|
117
|
+
__textHelperPerfCounters.wrapTextWithAnsiCalls += 1;
|
|
118
|
+
return nativeWrapTextWithAnsi(text, width, getCachedTabWidth());
|
|
65
119
|
}
|
|
66
120
|
|
|
67
121
|
export function extractSegments(
|
|
@@ -71,7 +125,7 @@ export function extractSegments(
|
|
|
71
125
|
afterLen: number,
|
|
72
126
|
strictAfter: boolean,
|
|
73
127
|
): ExtractSegmentsResult {
|
|
74
|
-
return nativeExtractSegments(line, beforeEnd, afterStart, afterLen, strictAfter,
|
|
128
|
+
return nativeExtractSegments(line, beforeEnd, afterStart, afterLen, strictAfter, getCachedTabWidth());
|
|
75
129
|
}
|
|
76
130
|
|
|
77
131
|
// Pre-allocated space buffer for padding
|
|
@@ -120,7 +174,7 @@ export function visibleWidthRaw(str: string): number {
|
|
|
120
174
|
if (str.length === 1) {
|
|
121
175
|
const code = str.charCodeAt(0);
|
|
122
176
|
if (code >= 0x20 && code <= 0x7e) return 1;
|
|
123
|
-
if (code === 9) return
|
|
177
|
+
if (code === 9) return getCachedTabWidth();
|
|
124
178
|
}
|
|
125
179
|
|
|
126
180
|
let tabCount = 0;
|
|
@@ -134,12 +188,11 @@ export function visibleWidthRaw(str: string): number {
|
|
|
134
188
|
}
|
|
135
189
|
}
|
|
136
190
|
if (isPureAscii) {
|
|
137
|
-
return str.length + tabCount * (
|
|
191
|
+
return str.length + tabCount * (getCachedTabWidth() - 1);
|
|
138
192
|
}
|
|
139
|
-
|
|
140
193
|
const normalized = normalizeForWidth(str);
|
|
141
194
|
if (tabCount === 0) return Bun.stringWidth(normalized);
|
|
142
|
-
return Bun.stringWidth(normalized.replaceAll("\t", " ".repeat(
|
|
195
|
+
return Bun.stringWidth(normalized.replaceAll("\t", " ".repeat(getCachedTabWidth())));
|
|
143
196
|
}
|
|
144
197
|
|
|
145
198
|
/**
|
|
@@ -150,6 +203,19 @@ export function visibleWidth(str: string): number {
|
|
|
150
203
|
return recordTextHelper("text.visibleWidth", () => visibleWidthRaw(str));
|
|
151
204
|
}
|
|
152
205
|
|
|
206
|
+
export function visibleWidthsNative(lines: readonly string[]): number[] {
|
|
207
|
+
__textHelperPerfCounters.visibleWidthsCalls += 1;
|
|
208
|
+
return nativeVisibleWidths(
|
|
209
|
+
lines.map(line => (typeof line === "string" ? line : String(line ?? ""))),
|
|
210
|
+
getCachedTabWidth(),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function visibleWidths(lines: readonly string[]): number[] {
|
|
215
|
+
void visibleWidthsNative(lines);
|
|
216
|
+
return lines.map(line => visibleWidth(typeof line === "string" ? line : String(line ?? "")));
|
|
217
|
+
}
|
|
218
|
+
|
|
153
219
|
const THAI_LAO_AM_REGEX = /[\u0e33\u0eb3]/;
|
|
154
220
|
const THAI_LAO_AM_GLOBAL_REGEX = /[\u0e33\u0eb3]/g;
|
|
155
221
|
const HANGUL_JAMO_REGEX = /[\u1100-\u11ff\ua960-\ua97f\ud7b0-\ud7ff]/;
|