@nghyane/arcane-tui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/utils.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { sliceWithWidth } from "@nghyane/arcane-natives";
2
+
3
+ export { Ellipsis, extractSegments, sliceWithWidth, truncateToWidth, wrapTextWithAnsi } from "@nghyane/arcane-natives";
4
+
5
+ // Pre-allocated space buffer for padding
6
+ const SPACE_BUFFER = " ".repeat(512);
7
+
8
+ /*
9
+ * Replace tabs with 3 spaces for consistent rendering.
10
+ */
11
+ export function replaceTabs(text: string): string {
12
+ return text.replaceAll("\t", " ");
13
+ }
14
+
15
+ /**
16
+ * Returns a string of n spaces. Uses a pre-allocated buffer for efficiency.
17
+ */
18
+ export function padding(n: number): string {
19
+ if (n <= 0) return "";
20
+ if (n <= 512) return SPACE_BUFFER.slice(0, n);
21
+ return " ".repeat(n);
22
+ }
23
+
24
+ // Grapheme segmenter (shared instance)
25
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
26
+
27
+ /**
28
+ * Get the shared grapheme segmenter instance.
29
+ */
30
+ export function getSegmenter(): Intl.Segmenter {
31
+ return segmenter;
32
+ }
33
+
34
+ // Cache for non-ASCII strings
35
+ //const WIDTH_CACHE_SIZE = 512;
36
+ //const widthCache = new Map<string, number>();
37
+
38
+ /**
39
+ * Calculate the visible width of a string in terminal columns.
40
+ */
41
+ export function visibleWidthRaw(str: string): number {
42
+ if (str.length === 0) {
43
+ return 0;
44
+ }
45
+
46
+ // Fast path: pure ASCII printable
47
+ let isPureAscii = true;
48
+ let tabLength = 0;
49
+ for (let i = 0; i < str.length; i++) {
50
+ const code = str.charCodeAt(i);
51
+ if (code === 9) {
52
+ tabLength += 3;
53
+ } else if (code < 0x20 || code > 0x7e) {
54
+ isPureAscii = false;
55
+ }
56
+ }
57
+ if (isPureAscii) {
58
+ return str.length + tabLength;
59
+ }
60
+ return Bun.stringWidth(str) + tabLength;
61
+ }
62
+
63
+ /**
64
+ * Calculate the visible width of a string in terminal columns.
65
+ */
66
+ export function visibleWidth(str: string): number {
67
+ if (str.length === 0) {
68
+ return 0;
69
+ }
70
+ return visibleWidthRaw(str);
71
+
72
+ // === Disabled cache ===
73
+
74
+ /*
75
+ // Check cache
76
+ const cached = widthCache.get(str);
77
+ if (cached !== undefined) {
78
+ return cached;
79
+ }
80
+
81
+ const width = visibleWidthRaw(str);
82
+ if (widthCache.size >= WIDTH_CACHE_SIZE) {
83
+ const firstKey = widthCache.keys().next().value;
84
+ if (firstKey !== undefined) {
85
+ widthCache.delete(firstKey);
86
+ }
87
+ }
88
+ widthCache.set(str, width);
89
+
90
+ return width;
91
+ */
92
+ }
93
+
94
+ const makeBoolArray = (chars: string): ReadonlyArray<boolean> => {
95
+ const table = Array.from({ length: 128 }, () => false);
96
+ for (let i = 0; i < chars.length; i++) {
97
+ const code = chars.charCodeAt(i);
98
+ if (code < table.length) {
99
+ table[code] = true;
100
+ }
101
+ }
102
+ return table;
103
+ };
104
+
105
+ const ASCII_WHITESPACE = makeBoolArray("\x09\x0a\x0b\x0c\x0d\x20");
106
+
107
+ /**
108
+ * Check if a character is whitespace.
109
+ */
110
+ export function isWhitespaceChar(char: string): boolean {
111
+ const code = char.codePointAt(0) || 0;
112
+ return ASCII_WHITESPACE[code] ?? false;
113
+ }
114
+
115
+ const ASCII_PUNCTUATION = makeBoolArray("(){}[]<>.,;:'\"!?+-=*/\\|&%^$#@~`");
116
+
117
+ /**
118
+ * Check if a character is punctuation.
119
+ */
120
+ export function isPunctuationChar(char: string): boolean {
121
+ const code = char.codePointAt(0) || 0;
122
+ return ASCII_PUNCTUATION[code] ?? false;
123
+ }
124
+
125
+ /**
126
+ * Apply background color to a line, padding to full width.
127
+ *
128
+ * @param line - Line of text (may contain ANSI codes)
129
+ * @param width - Total width to pad to
130
+ * @param bgFn - Background color function
131
+ * @returns Line with background applied and padded to width
132
+ */
133
+ export function applyBackgroundToLine(line: string, width: number, bgFn: (text: string) => string): string {
134
+ // Calculate padding needed
135
+ const visibleLen = visibleWidth(line);
136
+ const paddingNeeded = Math.max(0, width - visibleLen);
137
+
138
+ // Apply background to content + padding
139
+ const withPadding = line + padding(paddingNeeded);
140
+ return bgFn(withPadding);
141
+ }
142
+
143
+ /**
144
+ * Extract a range of visible columns from a line. Handles ANSI codes and wide chars.
145
+ * @param strict - If true, exclude wide chars at boundary that would extend past the range
146
+ */
147
+ export function sliceByColumn(line: string, startCol: number, length: number, strict = false): string {
148
+ return sliceWithWidth(line, startCol, length, strict).text;
149
+ }