pi2dsh 0.2.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/LICENSE +21 -0
- package/README.md +122 -0
- package/README.zh.md +122 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.mjs +128 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/compat/pi-ai.d.mts +2597 -0
- package/dist/compat/pi-ai.d.mts.map +1 -0
- package/dist/compat/pi-ai.mjs +4669 -0
- package/dist/compat/pi-ai.mjs.map +1 -0
- package/dist/compat/pi-coding-agent.d.mts +745 -0
- package/dist/compat/pi-coding-agent.d.mts.map +1 -0
- package/dist/compat/pi-coding-agent.mjs +4 -0
- package/dist/compat/pi-tui.d.mts +3 -0
- package/dist/compat/pi-tui.mjs +3622 -0
- package/dist/compat/pi-tui.mjs.map +1 -0
- package/dist/host.d.mts +35 -0
- package/dist/host.d.mts.map +1 -0
- package/dist/host.mjs +197 -0
- package/dist/host.mjs.map +1 -0
- package/dist/index.d.mts +69 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/mcp-config-jL9w70It.mjs +1535 -0
- package/dist/mcp-config-jL9w70It.mjs.map +1 -0
- package/dist/pi-coding-agent-Dsg6_0ua.mjs +2060 -0
- package/dist/pi-coding-agent-Dsg6_0ua.mjs.map +1 -0
- package/dist/pi-config-shim-CZ1wFzqM.mjs +27 -0
- package/dist/pi-config-shim-CZ1wFzqM.mjs.map +1 -0
- package/dist/pi-tui-iHoF2tFc.d.mts +1043 -0
- package/dist/pi-tui-iHoF2tFc.d.mts.map +1 -0
- package/dist/pi-tui-utils-CcaVtm-3.mjs +895 -0
- package/dist/pi-tui-utils-CcaVtm-3.mjs.map +1 -0
- package/dist/pi-types-KazmR2O5.d.mts +62 -0
- package/dist/pi-types-KazmR2O5.d.mts.map +1 -0
- package/dist/pi-uuid-Db8ShZsK.mjs +47 -0
- package/dist/pi-uuid-Db8ShZsK.mjs.map +1 -0
- package/dist/rolldown-runtime-C2Q2p085.mjs +15 -0
- package/dist/runtime-D84Hv_3m.mjs +1499 -0
- package/dist/runtime-D84Hv_3m.mjs.map +1 -0
- package/dist/runtime.d.mts +31 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +3 -0
- package/dist/source-D7Ir-rPT.mjs +154 -0
- package/dist/source-D7Ir-rPT.mjs.map +1 -0
- package/dist/types-7IWJPPvS.d.mts +59 -0
- package/dist/types-7IWJPPvS.d.mts.map +1 -0
- package/package.json +135 -0
|
@@ -0,0 +1,3622 @@
|
|
|
1
|
+
|
|
2
|
+
import { _ as wrapTextWithAnsi, a as getGraphemeCellRange, c as getWordSegmenter, d as normalizeTerminalOutput, f as sliceByColumn, g as visibleWidth, h as truncateToWidth, i as extractAnsiCode, l as isPunctuationChar, m as stripTerminalSequences, n as applyBackgroundToLine, o as getGraphemeSegmenter, p as sliceWithWidth, r as cjkBreakRegex, s as getOsc8LinkAtColumn, t as PUNCTUATION_REGEX, u as isWhitespaceChar } from "../pi-tui-utils-CcaVtm-3.mjs";
|
|
3
|
+
import { isAbsolute } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { readdirSync, statSync } from "fs";
|
|
7
|
+
import { basename as basename$1, dirname as dirname$1, join as join$1 } from "path";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import { homedir as homedir$1 } from "os";
|
|
11
|
+
import { Marked } from "marked";
|
|
12
|
+
//#region src/compat/vendor/pi-tui-fuzzy.ts
|
|
13
|
+
function fuzzyMatch(query, text) {
|
|
14
|
+
const queryLower = query.toLowerCase();
|
|
15
|
+
const textLower = text.toLowerCase();
|
|
16
|
+
const matchQuery = (normalizedQuery) => {
|
|
17
|
+
if (normalizedQuery.length === 0) return {
|
|
18
|
+
matches: true,
|
|
19
|
+
score: 0
|
|
20
|
+
};
|
|
21
|
+
if (normalizedQuery.length > textLower.length) return {
|
|
22
|
+
matches: false,
|
|
23
|
+
score: 0
|
|
24
|
+
};
|
|
25
|
+
let queryIndex = 0;
|
|
26
|
+
let score = 0;
|
|
27
|
+
let lastMatchIndex = -1;
|
|
28
|
+
let consecutiveMatches = 0;
|
|
29
|
+
for (let i = 0; i < textLower.length && queryIndex < normalizedQuery.length; i++) if (textLower[i] === normalizedQuery[queryIndex]) {
|
|
30
|
+
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]);
|
|
31
|
+
if (lastMatchIndex === i - 1) {
|
|
32
|
+
consecutiveMatches++;
|
|
33
|
+
score -= consecutiveMatches * 5;
|
|
34
|
+
} else {
|
|
35
|
+
consecutiveMatches = 0;
|
|
36
|
+
if (lastMatchIndex >= 0) score += (i - lastMatchIndex - 1) * 2;
|
|
37
|
+
}
|
|
38
|
+
if (isWordBoundary) score -= 10;
|
|
39
|
+
score += i * .1;
|
|
40
|
+
lastMatchIndex = i;
|
|
41
|
+
queryIndex++;
|
|
42
|
+
}
|
|
43
|
+
if (queryIndex < normalizedQuery.length) return {
|
|
44
|
+
matches: false,
|
|
45
|
+
score: 0
|
|
46
|
+
};
|
|
47
|
+
if (normalizedQuery === textLower) score -= 100;
|
|
48
|
+
return {
|
|
49
|
+
matches: true,
|
|
50
|
+
score
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const primaryMatch = matchQuery(queryLower);
|
|
54
|
+
if (primaryMatch.matches) return primaryMatch;
|
|
55
|
+
const alphaNumericMatch = queryLower.match(/^(?<letters>[a-z]+)(?<digits>[0-9]+)$/);
|
|
56
|
+
const numericAlphaMatch = queryLower.match(/^(?<digits>[0-9]+)(?<letters>[a-z]+)$/);
|
|
57
|
+
const swappedQuery = alphaNumericMatch ? `${alphaNumericMatch.groups?.digits ?? ""}${alphaNumericMatch.groups?.letters ?? ""}` : numericAlphaMatch ? `${numericAlphaMatch.groups?.letters ?? ""}${numericAlphaMatch.groups?.digits ?? ""}` : "";
|
|
58
|
+
if (!swappedQuery) return primaryMatch;
|
|
59
|
+
const swappedMatch = matchQuery(swappedQuery);
|
|
60
|
+
if (!swappedMatch.matches) return primaryMatch;
|
|
61
|
+
return {
|
|
62
|
+
matches: true,
|
|
63
|
+
score: swappedMatch.score + 5
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Filter and sort items by fuzzy match quality (best matches first).
|
|
68
|
+
* Supports whitespace- and slash-separated tokens: all tokens must match.
|
|
69
|
+
*/
|
|
70
|
+
function fuzzyFilter(items, query, getText) {
|
|
71
|
+
if (!query.trim()) return items;
|
|
72
|
+
const tokens = query.trim().split(/[\s/]+/).filter((t) => t.length > 0);
|
|
73
|
+
if (tokens.length === 0) return items;
|
|
74
|
+
const results = [];
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
const text = getText(item);
|
|
77
|
+
let totalScore = 0;
|
|
78
|
+
let allMatch = true;
|
|
79
|
+
for (const token of tokens) {
|
|
80
|
+
const match = fuzzyMatch(token, text);
|
|
81
|
+
if (match.matches) totalScore += match.score;
|
|
82
|
+
else {
|
|
83
|
+
allMatch = false;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (allMatch) results.push({
|
|
88
|
+
item,
|
|
89
|
+
totalScore
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
results.sort((a, b) => a.totalScore - b.totalScore);
|
|
93
|
+
return results.map((r) => r.item);
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/compat/vendor/pi-tui-keys.ts
|
|
97
|
+
/**
|
|
98
|
+
* Keyboard input handling for terminal applications.
|
|
99
|
+
*
|
|
100
|
+
* Supports both legacy terminal sequences and Kitty keyboard protocol.
|
|
101
|
+
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
102
|
+
* Reference: https://github.com/sst/opentui/blob/7da92b4088aebfe27b9f691c04163a48821e49fd/packages/core/src/lib/parse.keypress.ts
|
|
103
|
+
*
|
|
104
|
+
* Symbol keys are also supported, however some ctrl+symbol combos
|
|
105
|
+
* overlap with ASCII codes, e.g. ctrl+[ = ESC.
|
|
106
|
+
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-ctrl-mapping-of-ascii-keys
|
|
107
|
+
* Those can still be * used for ctrl+shift combos
|
|
108
|
+
*
|
|
109
|
+
* API:
|
|
110
|
+
* - matchesKey(data, keyId) - Check if input matches a key identifier
|
|
111
|
+
* - parseKey(data) - Parse input and return the key identifier
|
|
112
|
+
* - Key - Helper object for creating typed key identifiers
|
|
113
|
+
* - setKittyProtocolActive(active) - Set global Kitty protocol state
|
|
114
|
+
* - isKittyProtocolActive() - Query global Kitty protocol state
|
|
115
|
+
*/
|
|
116
|
+
let _kittyProtocolActive = false;
|
|
117
|
+
/**
|
|
118
|
+
* Set the global Kitty keyboard protocol state.
|
|
119
|
+
* Called by ProcessTerminal after detecting protocol support.
|
|
120
|
+
*/
|
|
121
|
+
function setKittyProtocolActive(active) {
|
|
122
|
+
_kittyProtocolActive = active;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Query whether Kitty keyboard protocol is currently active.
|
|
126
|
+
*/
|
|
127
|
+
function isKittyProtocolActive() {
|
|
128
|
+
return _kittyProtocolActive;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Helper object for creating typed key identifiers with autocomplete.
|
|
132
|
+
*
|
|
133
|
+
* Usage:
|
|
134
|
+
* - Key.escape, Key.enter, Key.tab, etc. for special keys
|
|
135
|
+
* - Key.backtick, Key.comma, Key.period, etc. for symbol keys
|
|
136
|
+
* - Key.ctrl("c"), Key.alt("x"), Key.super("k") for single modifiers
|
|
137
|
+
* - Key.ctrlShift("p"), Key.ctrlAlt("x"), Key.ctrlSuper("k") for combined modifiers
|
|
138
|
+
*/
|
|
139
|
+
const Key = {
|
|
140
|
+
escape: "escape",
|
|
141
|
+
esc: "esc",
|
|
142
|
+
enter: "enter",
|
|
143
|
+
return: "return",
|
|
144
|
+
tab: "tab",
|
|
145
|
+
space: "space",
|
|
146
|
+
backspace: "backspace",
|
|
147
|
+
delete: "delete",
|
|
148
|
+
insert: "insert",
|
|
149
|
+
clear: "clear",
|
|
150
|
+
home: "home",
|
|
151
|
+
end: "end",
|
|
152
|
+
pageUp: "pageUp",
|
|
153
|
+
pageDown: "pageDown",
|
|
154
|
+
up: "up",
|
|
155
|
+
down: "down",
|
|
156
|
+
left: "left",
|
|
157
|
+
right: "right",
|
|
158
|
+
f1: "f1",
|
|
159
|
+
f2: "f2",
|
|
160
|
+
f3: "f3",
|
|
161
|
+
f4: "f4",
|
|
162
|
+
f5: "f5",
|
|
163
|
+
f6: "f6",
|
|
164
|
+
f7: "f7",
|
|
165
|
+
f8: "f8",
|
|
166
|
+
f9: "f9",
|
|
167
|
+
f10: "f10",
|
|
168
|
+
f11: "f11",
|
|
169
|
+
f12: "f12",
|
|
170
|
+
backtick: "`",
|
|
171
|
+
hyphen: "-",
|
|
172
|
+
equals: "=",
|
|
173
|
+
leftbracket: "[",
|
|
174
|
+
rightbracket: "]",
|
|
175
|
+
backslash: "\\",
|
|
176
|
+
semicolon: ";",
|
|
177
|
+
quote: "'",
|
|
178
|
+
comma: ",",
|
|
179
|
+
period: ".",
|
|
180
|
+
slash: "/",
|
|
181
|
+
exclamation: "!",
|
|
182
|
+
at: "@",
|
|
183
|
+
hash: "#",
|
|
184
|
+
dollar: "$",
|
|
185
|
+
percent: "%",
|
|
186
|
+
caret: "^",
|
|
187
|
+
ampersand: "&",
|
|
188
|
+
asterisk: "*",
|
|
189
|
+
leftparen: "(",
|
|
190
|
+
rightparen: ")",
|
|
191
|
+
underscore: "_",
|
|
192
|
+
plus: "+",
|
|
193
|
+
pipe: "|",
|
|
194
|
+
tilde: "~",
|
|
195
|
+
leftbrace: "{",
|
|
196
|
+
rightbrace: "}",
|
|
197
|
+
colon: ":",
|
|
198
|
+
lessthan: "<",
|
|
199
|
+
greaterthan: ">",
|
|
200
|
+
question: "?",
|
|
201
|
+
ctrl: (key) => `ctrl+${key}`,
|
|
202
|
+
shift: (key) => `shift+${key}`,
|
|
203
|
+
alt: (key) => `alt+${key}`,
|
|
204
|
+
super: (key) => `super+${key}`,
|
|
205
|
+
ctrlShift: (key) => `ctrl+shift+${key}`,
|
|
206
|
+
shiftCtrl: (key) => `shift+ctrl+${key}`,
|
|
207
|
+
ctrlAlt: (key) => `ctrl+alt+${key}`,
|
|
208
|
+
altCtrl: (key) => `alt+ctrl+${key}`,
|
|
209
|
+
shiftAlt: (key) => `shift+alt+${key}`,
|
|
210
|
+
altShift: (key) => `alt+shift+${key}`,
|
|
211
|
+
ctrlSuper: (key) => `ctrl+super+${key}`,
|
|
212
|
+
superCtrl: (key) => `super+ctrl+${key}`,
|
|
213
|
+
shiftSuper: (key) => `shift+super+${key}`,
|
|
214
|
+
superShift: (key) => `super+shift+${key}`,
|
|
215
|
+
altSuper: (key) => `alt+super+${key}`,
|
|
216
|
+
superAlt: (key) => `super+alt+${key}`,
|
|
217
|
+
ctrlShiftAlt: (key) => `ctrl+shift+alt+${key}`,
|
|
218
|
+
ctrlShiftSuper: (key) => `ctrl+shift+super+${key}`
|
|
219
|
+
};
|
|
220
|
+
const SYMBOL_KEYS = /* @__PURE__ */ new Set([
|
|
221
|
+
"`",
|
|
222
|
+
"-",
|
|
223
|
+
"=",
|
|
224
|
+
"[",
|
|
225
|
+
"]",
|
|
226
|
+
"\\",
|
|
227
|
+
";",
|
|
228
|
+
"'",
|
|
229
|
+
",",
|
|
230
|
+
".",
|
|
231
|
+
"/",
|
|
232
|
+
"!",
|
|
233
|
+
"@",
|
|
234
|
+
"#",
|
|
235
|
+
"$",
|
|
236
|
+
"%",
|
|
237
|
+
"^",
|
|
238
|
+
"&",
|
|
239
|
+
"*",
|
|
240
|
+
"(",
|
|
241
|
+
")",
|
|
242
|
+
"_",
|
|
243
|
+
"+",
|
|
244
|
+
"|",
|
|
245
|
+
"~",
|
|
246
|
+
"{",
|
|
247
|
+
"}",
|
|
248
|
+
":",
|
|
249
|
+
"<",
|
|
250
|
+
">",
|
|
251
|
+
"?"
|
|
252
|
+
]);
|
|
253
|
+
const MODIFIERS = {
|
|
254
|
+
shift: 1,
|
|
255
|
+
alt: 2,
|
|
256
|
+
ctrl: 4,
|
|
257
|
+
super: 8
|
|
258
|
+
};
|
|
259
|
+
const LOCK_MASK = 192;
|
|
260
|
+
const CODEPOINTS = {
|
|
261
|
+
escape: 27,
|
|
262
|
+
tab: 9,
|
|
263
|
+
enter: 13,
|
|
264
|
+
space: 32,
|
|
265
|
+
backspace: 127,
|
|
266
|
+
kpEnter: 57414
|
|
267
|
+
};
|
|
268
|
+
const ARROW_CODEPOINTS = {
|
|
269
|
+
up: -1,
|
|
270
|
+
down: -2,
|
|
271
|
+
right: -3,
|
|
272
|
+
left: -4
|
|
273
|
+
};
|
|
274
|
+
const FUNCTIONAL_CODEPOINTS = {
|
|
275
|
+
delete: -10,
|
|
276
|
+
insert: -11,
|
|
277
|
+
pageUp: -12,
|
|
278
|
+
pageDown: -13,
|
|
279
|
+
home: -14,
|
|
280
|
+
end: -15
|
|
281
|
+
};
|
|
282
|
+
const KITTY_FUNCTIONAL_KEY_EQUIVALENTS = /* @__PURE__ */ new Map([
|
|
283
|
+
[57399, 48],
|
|
284
|
+
[57400, 49],
|
|
285
|
+
[57401, 50],
|
|
286
|
+
[57402, 51],
|
|
287
|
+
[57403, 52],
|
|
288
|
+
[57404, 53],
|
|
289
|
+
[57405, 54],
|
|
290
|
+
[57406, 55],
|
|
291
|
+
[57407, 56],
|
|
292
|
+
[57408, 57],
|
|
293
|
+
[57409, 46],
|
|
294
|
+
[57410, 47],
|
|
295
|
+
[57411, 42],
|
|
296
|
+
[57412, 45],
|
|
297
|
+
[57413, 43],
|
|
298
|
+
[57415, 61],
|
|
299
|
+
[57416, 44],
|
|
300
|
+
[57417, ARROW_CODEPOINTS.left],
|
|
301
|
+
[57418, ARROW_CODEPOINTS.right],
|
|
302
|
+
[57419, ARROW_CODEPOINTS.up],
|
|
303
|
+
[57420, ARROW_CODEPOINTS.down],
|
|
304
|
+
[57421, FUNCTIONAL_CODEPOINTS.pageUp],
|
|
305
|
+
[57422, FUNCTIONAL_CODEPOINTS.pageDown],
|
|
306
|
+
[57423, FUNCTIONAL_CODEPOINTS.home],
|
|
307
|
+
[57424, FUNCTIONAL_CODEPOINTS.end],
|
|
308
|
+
[57425, FUNCTIONAL_CODEPOINTS.insert],
|
|
309
|
+
[57426, FUNCTIONAL_CODEPOINTS.delete]
|
|
310
|
+
]);
|
|
311
|
+
function normalizeKittyFunctionalCodepoint(codepoint) {
|
|
312
|
+
return KITTY_FUNCTIONAL_KEY_EQUIVALENTS.get(codepoint) ?? codepoint;
|
|
313
|
+
}
|
|
314
|
+
function normalizeShiftedLetterIdentityCodepoint(codepoint, modifier) {
|
|
315
|
+
if ((modifier & -193 & MODIFIERS.shift) !== 0 && codepoint >= 65 && codepoint <= 90) return codepoint + 32;
|
|
316
|
+
return codepoint;
|
|
317
|
+
}
|
|
318
|
+
const LEGACY_KEY_SEQUENCES = {
|
|
319
|
+
up: ["\x1B[A", "\x1BOA"],
|
|
320
|
+
down: ["\x1B[B", "\x1BOB"],
|
|
321
|
+
right: ["\x1B[C", "\x1BOC"],
|
|
322
|
+
left: ["\x1B[D", "\x1BOD"],
|
|
323
|
+
home: [
|
|
324
|
+
"\x1B[H",
|
|
325
|
+
"\x1BOH",
|
|
326
|
+
"\x1B[1~",
|
|
327
|
+
"\x1B[7~"
|
|
328
|
+
],
|
|
329
|
+
end: [
|
|
330
|
+
"\x1B[F",
|
|
331
|
+
"\x1BOF",
|
|
332
|
+
"\x1B[4~",
|
|
333
|
+
"\x1B[8~"
|
|
334
|
+
],
|
|
335
|
+
insert: ["\x1B[2~"],
|
|
336
|
+
delete: ["\x1B[3~"],
|
|
337
|
+
pageUp: ["\x1B[5~", "\x1B[[5~"],
|
|
338
|
+
pageDown: ["\x1B[6~", "\x1B[[6~"],
|
|
339
|
+
clear: ["\x1B[E", "\x1BOE"],
|
|
340
|
+
f1: [
|
|
341
|
+
"\x1BOP",
|
|
342
|
+
"\x1B[11~",
|
|
343
|
+
"\x1B[[A"
|
|
344
|
+
],
|
|
345
|
+
f2: [
|
|
346
|
+
"\x1BOQ",
|
|
347
|
+
"\x1B[12~",
|
|
348
|
+
"\x1B[[B"
|
|
349
|
+
],
|
|
350
|
+
f3: [
|
|
351
|
+
"\x1BOR",
|
|
352
|
+
"\x1B[13~",
|
|
353
|
+
"\x1B[[C"
|
|
354
|
+
],
|
|
355
|
+
f4: [
|
|
356
|
+
"\x1BOS",
|
|
357
|
+
"\x1B[14~",
|
|
358
|
+
"\x1B[[D"
|
|
359
|
+
],
|
|
360
|
+
f5: ["\x1B[15~", "\x1B[[E"],
|
|
361
|
+
f6: ["\x1B[17~"],
|
|
362
|
+
f7: ["\x1B[18~"],
|
|
363
|
+
f8: ["\x1B[19~"],
|
|
364
|
+
f9: ["\x1B[20~"],
|
|
365
|
+
f10: ["\x1B[21~"],
|
|
366
|
+
f11: ["\x1B[23~"],
|
|
367
|
+
f12: ["\x1B[24~"]
|
|
368
|
+
};
|
|
369
|
+
const LEGACY_SHIFT_SEQUENCES = {
|
|
370
|
+
up: ["\x1B[a"],
|
|
371
|
+
down: ["\x1B[b"],
|
|
372
|
+
right: ["\x1B[c"],
|
|
373
|
+
left: ["\x1B[d"],
|
|
374
|
+
clear: ["\x1B[e"],
|
|
375
|
+
insert: ["\x1B[2$"],
|
|
376
|
+
delete: ["\x1B[3$"],
|
|
377
|
+
pageUp: ["\x1B[5$"],
|
|
378
|
+
pageDown: ["\x1B[6$"],
|
|
379
|
+
home: ["\x1B[7$"],
|
|
380
|
+
end: ["\x1B[8$"]
|
|
381
|
+
};
|
|
382
|
+
const LEGACY_CTRL_SEQUENCES = {
|
|
383
|
+
up: ["\x1BOa"],
|
|
384
|
+
down: ["\x1BOb"],
|
|
385
|
+
right: ["\x1BOc"],
|
|
386
|
+
left: ["\x1BOd"],
|
|
387
|
+
clear: ["\x1BOe"],
|
|
388
|
+
insert: ["\x1B[2^"],
|
|
389
|
+
delete: ["\x1B[3^"],
|
|
390
|
+
pageUp: ["\x1B[5^"],
|
|
391
|
+
pageDown: ["\x1B[6^"],
|
|
392
|
+
home: ["\x1B[7^"],
|
|
393
|
+
end: ["\x1B[8^"]
|
|
394
|
+
};
|
|
395
|
+
const LEGACY_SEQUENCE_KEY_IDS = {
|
|
396
|
+
"\x1BOA": "up",
|
|
397
|
+
"\x1BOB": "down",
|
|
398
|
+
"\x1BOC": "right",
|
|
399
|
+
"\x1BOD": "left",
|
|
400
|
+
"\x1BOH": "home",
|
|
401
|
+
"\x1BOF": "end",
|
|
402
|
+
"\x1B[E": "clear",
|
|
403
|
+
"\x1BOE": "clear",
|
|
404
|
+
"\x1BOe": "ctrl+clear",
|
|
405
|
+
"\x1B[e": "shift+clear",
|
|
406
|
+
"\x1B[2~": "insert",
|
|
407
|
+
"\x1B[2$": "shift+insert",
|
|
408
|
+
"\x1B[2^": "ctrl+insert",
|
|
409
|
+
"\x1B[3$": "shift+delete",
|
|
410
|
+
"\x1B[3^": "ctrl+delete",
|
|
411
|
+
"\x1B[[5~": "pageUp",
|
|
412
|
+
"\x1B[[6~": "pageDown",
|
|
413
|
+
"\x1B[a": "shift+up",
|
|
414
|
+
"\x1B[b": "shift+down",
|
|
415
|
+
"\x1B[c": "shift+right",
|
|
416
|
+
"\x1B[d": "shift+left",
|
|
417
|
+
"\x1BOa": "ctrl+up",
|
|
418
|
+
"\x1BOb": "ctrl+down",
|
|
419
|
+
"\x1BOc": "ctrl+right",
|
|
420
|
+
"\x1BOd": "ctrl+left",
|
|
421
|
+
"\x1B[5$": "shift+pageUp",
|
|
422
|
+
"\x1B[6$": "shift+pageDown",
|
|
423
|
+
"\x1B[7$": "shift+home",
|
|
424
|
+
"\x1B[8$": "shift+end",
|
|
425
|
+
"\x1B[5^": "ctrl+pageUp",
|
|
426
|
+
"\x1B[6^": "ctrl+pageDown",
|
|
427
|
+
"\x1B[7^": "ctrl+home",
|
|
428
|
+
"\x1B[8^": "ctrl+end",
|
|
429
|
+
"\x1BOP": "f1",
|
|
430
|
+
"\x1BOQ": "f2",
|
|
431
|
+
"\x1BOR": "f3",
|
|
432
|
+
"\x1BOS": "f4",
|
|
433
|
+
"\x1B[11~": "f1",
|
|
434
|
+
"\x1B[12~": "f2",
|
|
435
|
+
"\x1B[13~": "f3",
|
|
436
|
+
"\x1B[14~": "f4",
|
|
437
|
+
"\x1B[[A": "f1",
|
|
438
|
+
"\x1B[[B": "f2",
|
|
439
|
+
"\x1B[[C": "f3",
|
|
440
|
+
"\x1B[[D": "f4",
|
|
441
|
+
"\x1B[[E": "f5",
|
|
442
|
+
"\x1B[15~": "f5",
|
|
443
|
+
"\x1B[17~": "f6",
|
|
444
|
+
"\x1B[18~": "f7",
|
|
445
|
+
"\x1B[19~": "f8",
|
|
446
|
+
"\x1B[20~": "f9",
|
|
447
|
+
"\x1B[21~": "f10",
|
|
448
|
+
"\x1B[23~": "f11",
|
|
449
|
+
"\x1B[24~": "f12",
|
|
450
|
+
"\x1Bb": "alt+left",
|
|
451
|
+
"\x1Bf": "alt+right",
|
|
452
|
+
"\x1Bp": "alt+up",
|
|
453
|
+
"\x1Bn": "alt+down"
|
|
454
|
+
};
|
|
455
|
+
const matchesLegacySequence = (data, sequences) => sequences.includes(data);
|
|
456
|
+
const matchesLegacyModifierSequence = (data, key, modifier) => {
|
|
457
|
+
if (modifier === MODIFIERS.shift) return matchesLegacySequence(data, LEGACY_SHIFT_SEQUENCES[key]);
|
|
458
|
+
if (modifier === MODIFIERS.ctrl) return matchesLegacySequence(data, LEGACY_CTRL_SEQUENCES[key]);
|
|
459
|
+
return false;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Check if the last parsed key event was a key release.
|
|
463
|
+
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
464
|
+
*/
|
|
465
|
+
function isKeyRelease(data) {
|
|
466
|
+
if (data.includes("\x1B[200~")) return false;
|
|
467
|
+
if (data.includes(":3u") || data.includes(":3~") || data.includes(":3A") || data.includes(":3B") || data.includes(":3C") || data.includes(":3D") || data.includes(":3H") || data.includes(":3F")) return true;
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Check if the last parsed key event was a key repeat.
|
|
472
|
+
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
473
|
+
*/
|
|
474
|
+
function isKeyRepeat(data) {
|
|
475
|
+
if (data.includes("\x1B[200~")) return false;
|
|
476
|
+
if (data.includes(":2u") || data.includes(":2~") || data.includes(":2A") || data.includes(":2B") || data.includes(":2C") || data.includes(":2D") || data.includes(":2H") || data.includes(":2F")) return true;
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
function parseEventType(eventTypeStr) {
|
|
480
|
+
if (!eventTypeStr) return "press";
|
|
481
|
+
const eventType = parseInt(eventTypeStr, 10);
|
|
482
|
+
if (eventType === 2) return "repeat";
|
|
483
|
+
if (eventType === 3) return "release";
|
|
484
|
+
return "press";
|
|
485
|
+
}
|
|
486
|
+
function parseKittySequence(data) {
|
|
487
|
+
const csiUMatch = data.match(/^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/);
|
|
488
|
+
if (csiUMatch) {
|
|
489
|
+
const codepoint = parseInt(csiUMatch[1], 10);
|
|
490
|
+
const shiftedKey = csiUMatch[2] && csiUMatch[2].length > 0 ? parseInt(csiUMatch[2], 10) : void 0;
|
|
491
|
+
const baseLayoutKey = csiUMatch[3] ? parseInt(csiUMatch[3], 10) : void 0;
|
|
492
|
+
const modValue = csiUMatch[4] ? parseInt(csiUMatch[4], 10) : 1;
|
|
493
|
+
const eventType = parseEventType(csiUMatch[5]);
|
|
494
|
+
return {
|
|
495
|
+
codepoint,
|
|
496
|
+
shiftedKey,
|
|
497
|
+
baseLayoutKey,
|
|
498
|
+
modifier: modValue - 1,
|
|
499
|
+
eventType
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
const arrowMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([ABCD])$/);
|
|
503
|
+
if (arrowMatch) {
|
|
504
|
+
const modValue = parseInt(arrowMatch[1], 10);
|
|
505
|
+
const eventType = parseEventType(arrowMatch[2]);
|
|
506
|
+
return {
|
|
507
|
+
codepoint: {
|
|
508
|
+
A: -1,
|
|
509
|
+
B: -2,
|
|
510
|
+
C: -3,
|
|
511
|
+
D: -4
|
|
512
|
+
}[arrowMatch[3]],
|
|
513
|
+
modifier: modValue - 1,
|
|
514
|
+
eventType
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
const funcMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?(?::(\d+))?~$/);
|
|
518
|
+
if (funcMatch) {
|
|
519
|
+
const keyNum = parseInt(funcMatch[1], 10);
|
|
520
|
+
const modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1;
|
|
521
|
+
const eventType = parseEventType(funcMatch[3]);
|
|
522
|
+
const codepoint = {
|
|
523
|
+
2: FUNCTIONAL_CODEPOINTS.insert,
|
|
524
|
+
3: FUNCTIONAL_CODEPOINTS.delete,
|
|
525
|
+
5: FUNCTIONAL_CODEPOINTS.pageUp,
|
|
526
|
+
6: FUNCTIONAL_CODEPOINTS.pageDown,
|
|
527
|
+
7: FUNCTIONAL_CODEPOINTS.home,
|
|
528
|
+
8: FUNCTIONAL_CODEPOINTS.end
|
|
529
|
+
}[keyNum];
|
|
530
|
+
if (codepoint !== void 0) return {
|
|
531
|
+
codepoint,
|
|
532
|
+
modifier: modValue - 1,
|
|
533
|
+
eventType
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const homeEndMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([HF])$/);
|
|
537
|
+
if (homeEndMatch) {
|
|
538
|
+
const modValue = parseInt(homeEndMatch[1], 10);
|
|
539
|
+
const eventType = parseEventType(homeEndMatch[2]);
|
|
540
|
+
return {
|
|
541
|
+
codepoint: homeEndMatch[3] === "H" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end,
|
|
542
|
+
modifier: modValue - 1,
|
|
543
|
+
eventType
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
549
|
+
const parsed = parseKittySequence(data);
|
|
550
|
+
if (!parsed) return false;
|
|
551
|
+
if ((parsed.modifier & -193) !== (expectedModifier & -193)) return false;
|
|
552
|
+
const normalizedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(parsed.codepoint), parsed.modifier);
|
|
553
|
+
if (normalizedCodepoint === normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(expectedCodepoint), expectedModifier)) return true;
|
|
554
|
+
if (parsed.baseLayoutKey !== void 0 && parsed.baseLayoutKey === expectedCodepoint) {
|
|
555
|
+
const cp = normalizedCodepoint;
|
|
556
|
+
const isLatinLetter = cp >= 97 && cp <= 122;
|
|
557
|
+
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(cp));
|
|
558
|
+
if (!isLatinLetter && !isKnownSymbol) return true;
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
function parseModifyOtherKeysSequence(data) {
|
|
563
|
+
const match = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
|
|
564
|
+
if (!match) return null;
|
|
565
|
+
const modValue = parseInt(match[1], 10);
|
|
566
|
+
return {
|
|
567
|
+
codepoint: parseInt(match[2], 10),
|
|
568
|
+
modifier: modValue - 1
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Match xterm modifyOtherKeys format: CSI 27 ; modifiers ; keycode ~
|
|
573
|
+
* This is used by terminals when Kitty protocol is not enabled.
|
|
574
|
+
* Modifier values are 1-indexed: 2=shift, 3=alt, 5=ctrl, etc.
|
|
575
|
+
*/
|
|
576
|
+
function matchesModifyOtherKeys(data, expectedKeycode, expectedModifier) {
|
|
577
|
+
const parsed = parseModifyOtherKeysSequence(data);
|
|
578
|
+
if (!parsed) return false;
|
|
579
|
+
return parsed.codepoint === expectedKeycode && parsed.modifier === expectedModifier;
|
|
580
|
+
}
|
|
581
|
+
function isWindowsTerminalSession() {
|
|
582
|
+
return Boolean(process.env.WT_SESSION) && !process.env.SSH_CONNECTION && !process.env.SSH_CLIENT && !process.env.SSH_TTY;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Raw 0x08 (BS) is ambiguous in legacy terminals.
|
|
586
|
+
*
|
|
587
|
+
* - Windows Terminal uses it for Ctrl+Backspace.
|
|
588
|
+
* - Some legacy terminals and tmux setups send it for plain Backspace.
|
|
589
|
+
*
|
|
590
|
+
* Prefer explicit Kitty / CSI-u / modifyOtherKeys sequences whenever they are
|
|
591
|
+
* available. Fall back to a Windows Terminal heuristic only for raw BS bytes.
|
|
592
|
+
*/
|
|
593
|
+
function matchesRawBackspace(data, expectedModifier) {
|
|
594
|
+
if (data === "") return expectedModifier === 0;
|
|
595
|
+
if (data !== "\b") return false;
|
|
596
|
+
return isWindowsTerminalSession() ? expectedModifier === MODIFIERS.ctrl : expectedModifier === 0;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Get the control character for a key.
|
|
600
|
+
* Uses the universal formula: code & 0x1f (mask to lower 5 bits)
|
|
601
|
+
*
|
|
602
|
+
* Works for:
|
|
603
|
+
* - Letters a-z → 1-26
|
|
604
|
+
* - Symbols [\]_ → 27, 28, 29, 31
|
|
605
|
+
* - Also maps - to same as _ (same physical key on US keyboards)
|
|
606
|
+
*/
|
|
607
|
+
function rawCtrlChar(key) {
|
|
608
|
+
const char = key.toLowerCase();
|
|
609
|
+
const code = char.charCodeAt(0);
|
|
610
|
+
if (code >= 97 && code <= 122 || char === "[" || char === "\\" || char === "]" || char === "_") return String.fromCharCode(code & 31);
|
|
611
|
+
if (char === "-") return String.fromCharCode(31);
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
function isDigitKey(key) {
|
|
615
|
+
return key >= "0" && key <= "9";
|
|
616
|
+
}
|
|
617
|
+
function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier) {
|
|
618
|
+
if (expectedModifier === 0) return false;
|
|
619
|
+
const parsed = parseModifyOtherKeysSequence(data);
|
|
620
|
+
if (!parsed || parsed.modifier !== expectedModifier) return false;
|
|
621
|
+
return normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
|
|
622
|
+
}
|
|
623
|
+
function formatKeyNameWithModifiers(keyName, modifier) {
|
|
624
|
+
const mods = [];
|
|
625
|
+
const effectiveMod = modifier & -193;
|
|
626
|
+
if ((effectiveMod & ~(MODIFIERS.shift | MODIFIERS.ctrl | MODIFIERS.alt | MODIFIERS.super)) !== 0) return void 0;
|
|
627
|
+
if (effectiveMod & MODIFIERS.shift) mods.push("shift");
|
|
628
|
+
if (effectiveMod & MODIFIERS.ctrl) mods.push("ctrl");
|
|
629
|
+
if (effectiveMod & MODIFIERS.alt) mods.push("alt");
|
|
630
|
+
if (effectiveMod & MODIFIERS.super) mods.push("super");
|
|
631
|
+
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
632
|
+
}
|
|
633
|
+
function parseKeyId(keyId) {
|
|
634
|
+
const parts = keyId.toLowerCase().split("+");
|
|
635
|
+
const key = parts[parts.length - 1];
|
|
636
|
+
if (!key) return null;
|
|
637
|
+
return {
|
|
638
|
+
key,
|
|
639
|
+
ctrl: parts.includes("ctrl"),
|
|
640
|
+
shift: parts.includes("shift"),
|
|
641
|
+
alt: parts.includes("alt"),
|
|
642
|
+
super: parts.includes("super")
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Match input data against a key identifier string.
|
|
647
|
+
*
|
|
648
|
+
* Supported key identifiers:
|
|
649
|
+
* - Single keys: "escape", "tab", "enter", "backspace", "delete", "home", "end", "space"
|
|
650
|
+
* - Arrow keys: "up", "down", "left", "right"
|
|
651
|
+
* - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
|
|
652
|
+
* - Shift combinations: "shift+tab", "shift+enter"
|
|
653
|
+
* - Alt combinations: "alt+enter", "alt+backspace"
|
|
654
|
+
* - Super combinations: "super+k", "super+enter"
|
|
655
|
+
* - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x", "ctrl+super+k"
|
|
656
|
+
*
|
|
657
|
+
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p"), Key.super("k")
|
|
658
|
+
*
|
|
659
|
+
* @param data - Raw input data from terminal
|
|
660
|
+
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
661
|
+
*/
|
|
662
|
+
function matchesKey(data, keyId) {
|
|
663
|
+
const parsed = parseKeyId(keyId);
|
|
664
|
+
if (!parsed) return false;
|
|
665
|
+
const { key, ctrl, shift, alt, super: superModifier } = parsed;
|
|
666
|
+
let modifier = 0;
|
|
667
|
+
if (shift) modifier |= MODIFIERS.shift;
|
|
668
|
+
if (alt) modifier |= MODIFIERS.alt;
|
|
669
|
+
if (ctrl) modifier |= MODIFIERS.ctrl;
|
|
670
|
+
if (superModifier) modifier |= MODIFIERS.super;
|
|
671
|
+
switch (key) {
|
|
672
|
+
case "escape":
|
|
673
|
+
case "esc":
|
|
674
|
+
if (modifier !== 0) return false;
|
|
675
|
+
return data === "\x1B" || matchesKittySequence(data, CODEPOINTS.escape, 0) || matchesModifyOtherKeys(data, CODEPOINTS.escape, 0);
|
|
676
|
+
case "space":
|
|
677
|
+
if (!_kittyProtocolActive) {
|
|
678
|
+
if (modifier === MODIFIERS.ctrl && data === "\0") return true;
|
|
679
|
+
if (modifier === MODIFIERS.alt && data === "\x1B ") return true;
|
|
680
|
+
}
|
|
681
|
+
if (modifier === 0) return data === " " || matchesKittySequence(data, CODEPOINTS.space, 0) || matchesModifyOtherKeys(data, CODEPOINTS.space, 0);
|
|
682
|
+
return matchesKittySequence(data, CODEPOINTS.space, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.space, modifier);
|
|
683
|
+
case "tab":
|
|
684
|
+
if (modifier === MODIFIERS.shift) return data === "\x1B[Z" || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift) || matchesModifyOtherKeys(data, CODEPOINTS.tab, MODIFIERS.shift);
|
|
685
|
+
if (modifier === 0) return data === " " || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
686
|
+
return matchesKittySequence(data, CODEPOINTS.tab, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.tab, modifier);
|
|
687
|
+
case "enter":
|
|
688
|
+
case "return":
|
|
689
|
+
if (modifier === MODIFIERS.shift) {
|
|
690
|
+
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) || matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)) return true;
|
|
691
|
+
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.shift)) return true;
|
|
692
|
+
if (_kittyProtocolActive) return data === "\x1B\r" || data === "\n";
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
if (modifier === MODIFIERS.alt) {
|
|
696
|
+
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) || matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)) return true;
|
|
697
|
+
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.alt)) return true;
|
|
698
|
+
if (!_kittyProtocolActive) return data === "\x1B\r";
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
if (modifier === 0) return data === "\r" || !_kittyProtocolActive && data === "\n" || data === "\x1BOM" || matchesKittySequence(data, CODEPOINTS.enter, 0) || matchesKittySequence(data, CODEPOINTS.kpEnter, 0);
|
|
702
|
+
return matchesKittySequence(data, CODEPOINTS.enter, modifier) || matchesKittySequence(data, CODEPOINTS.kpEnter, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier);
|
|
703
|
+
case "backspace":
|
|
704
|
+
if (modifier === MODIFIERS.alt) {
|
|
705
|
+
if (data === "\x1B" || data === "\x1B\b") return true;
|
|
706
|
+
return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt) || matchesModifyOtherKeys(data, CODEPOINTS.backspace, MODIFIERS.alt);
|
|
707
|
+
}
|
|
708
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
709
|
+
if (matchesRawBackspace(data, MODIFIERS.ctrl)) return true;
|
|
710
|
+
return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.ctrl) || matchesModifyOtherKeys(data, CODEPOINTS.backspace, MODIFIERS.ctrl);
|
|
711
|
+
}
|
|
712
|
+
if (modifier === 0) return matchesRawBackspace(data, 0) || matchesKittySequence(data, CODEPOINTS.backspace, 0) || matchesModifyOtherKeys(data, CODEPOINTS.backspace, 0);
|
|
713
|
+
return matchesKittySequence(data, CODEPOINTS.backspace, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.backspace, modifier);
|
|
714
|
+
case "insert":
|
|
715
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.insert) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, 0);
|
|
716
|
+
if (matchesLegacyModifierSequence(data, "insert", modifier)) return true;
|
|
717
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, modifier);
|
|
718
|
+
case "delete":
|
|
719
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.delete) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0);
|
|
720
|
+
if (matchesLegacyModifierSequence(data, "delete", modifier)) return true;
|
|
721
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);
|
|
722
|
+
case "clear":
|
|
723
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.clear);
|
|
724
|
+
return matchesLegacyModifierSequence(data, "clear", modifier);
|
|
725
|
+
case "home":
|
|
726
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.home) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0);
|
|
727
|
+
if (matchesLegacyModifierSequence(data, "home", modifier)) return true;
|
|
728
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);
|
|
729
|
+
case "end":
|
|
730
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.end) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0);
|
|
731
|
+
if (matchesLegacyModifierSequence(data, "end", modifier)) return true;
|
|
732
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);
|
|
733
|
+
case "pageup":
|
|
734
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageUp) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, 0);
|
|
735
|
+
if (matchesLegacyModifierSequence(data, "pageUp", modifier)) return true;
|
|
736
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, modifier);
|
|
737
|
+
case "pagedown":
|
|
738
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageDown) || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, 0);
|
|
739
|
+
if (matchesLegacyModifierSequence(data, "pageDown", modifier)) return true;
|
|
740
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, modifier);
|
|
741
|
+
case "up":
|
|
742
|
+
if (modifier === MODIFIERS.alt) return data === "\x1Bp" || matchesKittySequence(data, ARROW_CODEPOINTS.up, MODIFIERS.alt);
|
|
743
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.up) || matchesKittySequence(data, ARROW_CODEPOINTS.up, 0);
|
|
744
|
+
if (matchesLegacyModifierSequence(data, "up", modifier)) return true;
|
|
745
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
|
|
746
|
+
case "down":
|
|
747
|
+
if (modifier === MODIFIERS.alt) return data === "\x1Bn" || matchesKittySequence(data, ARROW_CODEPOINTS.down, MODIFIERS.alt);
|
|
748
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.down) || matchesKittySequence(data, ARROW_CODEPOINTS.down, 0);
|
|
749
|
+
if (matchesLegacyModifierSequence(data, "down", modifier)) return true;
|
|
750
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
|
|
751
|
+
case "left":
|
|
752
|
+
if (modifier === MODIFIERS.alt) return data === "\x1B[1;3D" || !_kittyProtocolActive && data === "\x1BB" || data === "\x1Bb" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt);
|
|
753
|
+
if (modifier === MODIFIERS.ctrl) return data === "\x1B[1;5D" || matchesLegacyModifierSequence(data, "left", MODIFIERS.ctrl) || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl);
|
|
754
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.left) || matchesKittySequence(data, ARROW_CODEPOINTS.left, 0);
|
|
755
|
+
if (matchesLegacyModifierSequence(data, "left", modifier)) return true;
|
|
756
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
|
|
757
|
+
case "right":
|
|
758
|
+
if (modifier === MODIFIERS.alt) return data === "\x1B[1;3C" || !_kittyProtocolActive && data === "\x1BF" || data === "\x1Bf" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt);
|
|
759
|
+
if (modifier === MODIFIERS.ctrl) return data === "\x1B[1;5C" || matchesLegacyModifierSequence(data, "right", MODIFIERS.ctrl) || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl);
|
|
760
|
+
if (modifier === 0) return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.right) || matchesKittySequence(data, ARROW_CODEPOINTS.right, 0);
|
|
761
|
+
if (matchesLegacyModifierSequence(data, "right", modifier)) return true;
|
|
762
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);
|
|
763
|
+
case "f1":
|
|
764
|
+
case "f2":
|
|
765
|
+
case "f3":
|
|
766
|
+
case "f4":
|
|
767
|
+
case "f5":
|
|
768
|
+
case "f6":
|
|
769
|
+
case "f7":
|
|
770
|
+
case "f8":
|
|
771
|
+
case "f9":
|
|
772
|
+
case "f10":
|
|
773
|
+
case "f11":
|
|
774
|
+
case "f12":
|
|
775
|
+
if (modifier !== 0) return false;
|
|
776
|
+
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[key]);
|
|
777
|
+
}
|
|
778
|
+
if (key.length === 1 && (key >= "a" && key <= "z" || isDigitKey(key) || SYMBOL_KEYS.has(key))) {
|
|
779
|
+
const codepoint = key.charCodeAt(0);
|
|
780
|
+
const rawCtrl = rawCtrlChar(key);
|
|
781
|
+
const isLetter = key >= "a" && key <= "z";
|
|
782
|
+
const isDigit = isDigitKey(key);
|
|
783
|
+
if (modifier === MODIFIERS.ctrl + MODIFIERS.alt && !_kittyProtocolActive && rawCtrl) {
|
|
784
|
+
if (data === `\x1b${rawCtrl}`) return true;
|
|
785
|
+
}
|
|
786
|
+
if (modifier === MODIFIERS.alt && !_kittyProtocolActive && (isLetter || isDigit || SYMBOL_KEYS.has(key))) {
|
|
787
|
+
if (data === `\x1b${key}`) return true;
|
|
788
|
+
}
|
|
789
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
790
|
+
if (rawCtrl && data === rawCtrl) return true;
|
|
791
|
+
return matchesKittySequence(data, codepoint, MODIFIERS.ctrl) || matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.ctrl);
|
|
792
|
+
}
|
|
793
|
+
if (modifier === MODIFIERS.shift + MODIFIERS.ctrl) return matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl) || matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl);
|
|
794
|
+
if (modifier === MODIFIERS.shift) {
|
|
795
|
+
if (isLetter && data === key.toUpperCase()) return true;
|
|
796
|
+
return matchesKittySequence(data, codepoint, MODIFIERS.shift) || matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift);
|
|
797
|
+
}
|
|
798
|
+
if (modifier !== 0) return matchesKittySequence(data, codepoint, modifier) || matchesPrintableModifyOtherKeys(data, codepoint, modifier);
|
|
799
|
+
return data === key || matchesKittySequence(data, codepoint, 0);
|
|
800
|
+
}
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Parse input data and return the key identifier if recognized.
|
|
805
|
+
*
|
|
806
|
+
* @param data - Raw input data from terminal
|
|
807
|
+
* @returns Key identifier string (e.g., "ctrl+c") or undefined
|
|
808
|
+
*/
|
|
809
|
+
function formatParsedKey(codepoint, modifier, baseLayoutKey) {
|
|
810
|
+
const identityCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(codepoint), modifier);
|
|
811
|
+
const isLatinLetter = identityCodepoint >= 97 && identityCodepoint <= 122;
|
|
812
|
+
const isDigit = identityCodepoint >= 48 && identityCodepoint <= 57;
|
|
813
|
+
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(identityCodepoint));
|
|
814
|
+
const effectiveCodepoint = isLatinLetter || isDigit || isKnownSymbol ? identityCodepoint : baseLayoutKey ?? identityCodepoint;
|
|
815
|
+
let keyName;
|
|
816
|
+
if (effectiveCodepoint === CODEPOINTS.escape) keyName = "escape";
|
|
817
|
+
else if (effectiveCodepoint === CODEPOINTS.tab) keyName = "tab";
|
|
818
|
+
else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter) keyName = "enter";
|
|
819
|
+
else if (effectiveCodepoint === CODEPOINTS.space) keyName = "space";
|
|
820
|
+
else if (effectiveCodepoint === CODEPOINTS.backspace) keyName = "backspace";
|
|
821
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete) keyName = "delete";
|
|
822
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert) keyName = "insert";
|
|
823
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home) keyName = "home";
|
|
824
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end) keyName = "end";
|
|
825
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp) keyName = "pageUp";
|
|
826
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown) keyName = "pageDown";
|
|
827
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.up) keyName = "up";
|
|
828
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.down) keyName = "down";
|
|
829
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.left) keyName = "left";
|
|
830
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.right) keyName = "right";
|
|
831
|
+
else if (effectiveCodepoint >= 48 && effectiveCodepoint <= 57) keyName = String.fromCharCode(effectiveCodepoint);
|
|
832
|
+
else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122) keyName = String.fromCharCode(effectiveCodepoint);
|
|
833
|
+
else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint))) keyName = String.fromCharCode(effectiveCodepoint);
|
|
834
|
+
if (!keyName) return void 0;
|
|
835
|
+
return formatKeyNameWithModifiers(keyName, modifier);
|
|
836
|
+
}
|
|
837
|
+
function parseKey(data) {
|
|
838
|
+
const kitty = parseKittySequence(data);
|
|
839
|
+
if (kitty) return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
|
|
840
|
+
const modifyOtherKeys = parseModifyOtherKeysSequence(data);
|
|
841
|
+
if (modifyOtherKeys) return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
|
|
842
|
+
if (_kittyProtocolActive) {
|
|
843
|
+
if (data === "\x1B\r" || data === "\n") return "shift+enter";
|
|
844
|
+
}
|
|
845
|
+
const legacySequenceKeyId = LEGACY_SEQUENCE_KEY_IDS[data];
|
|
846
|
+
if (legacySequenceKeyId) return legacySequenceKeyId;
|
|
847
|
+
if (data === "\x1B") return "escape";
|
|
848
|
+
if (data === "") return "ctrl+\\";
|
|
849
|
+
if (data === "") return "ctrl+]";
|
|
850
|
+
if (data === "") return "ctrl+-";
|
|
851
|
+
if (data === "\x1B\x1B") return "ctrl+alt+[";
|
|
852
|
+
if (data === "\x1B") return "ctrl+alt+\\";
|
|
853
|
+
if (data === "\x1B") return "ctrl+alt+]";
|
|
854
|
+
if (data === "\x1B") return "ctrl+alt+-";
|
|
855
|
+
if (data === " ") return "tab";
|
|
856
|
+
if (data === "\r" || !_kittyProtocolActive && data === "\n" || data === "\x1BOM") return "enter";
|
|
857
|
+
if (data === "\0") return "ctrl+space";
|
|
858
|
+
if (data === " ") return "space";
|
|
859
|
+
if (data === "") return "backspace";
|
|
860
|
+
if (data === "\b") return isWindowsTerminalSession() ? "ctrl+backspace" : "backspace";
|
|
861
|
+
if (data === "\x1B[Z") return "shift+tab";
|
|
862
|
+
if (!_kittyProtocolActive && data === "\x1B\r") return "alt+enter";
|
|
863
|
+
if (!_kittyProtocolActive && data === "\x1B ") return "alt+space";
|
|
864
|
+
if (data === "\x1B" || data === "\x1B\b") return "alt+backspace";
|
|
865
|
+
if (!_kittyProtocolActive && data === "\x1BB") return "alt+left";
|
|
866
|
+
if (!_kittyProtocolActive && data === "\x1BF") return "alt+right";
|
|
867
|
+
if (!_kittyProtocolActive && data.length === 2 && data[0] === "\x1B") {
|
|
868
|
+
const code = data.charCodeAt(1);
|
|
869
|
+
if (code >= 1 && code <= 26) return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
870
|
+
const key = String.fromCharCode(code);
|
|
871
|
+
if (code >= 97 && code <= 122 || code >= 48 && code <= 57 || SYMBOL_KEYS.has(key)) return `alt+${key}`;
|
|
872
|
+
}
|
|
873
|
+
if (data === "\x1B[A") return "up";
|
|
874
|
+
if (data === "\x1B[B") return "down";
|
|
875
|
+
if (data === "\x1B[C") return "right";
|
|
876
|
+
if (data === "\x1B[D") return "left";
|
|
877
|
+
if (data === "\x1B[H" || data === "\x1BOH") return "home";
|
|
878
|
+
if (data === "\x1B[F" || data === "\x1BOF") return "end";
|
|
879
|
+
if (data === "\x1B[3~") return "delete";
|
|
880
|
+
if (data === "\x1B[5~") return "pageUp";
|
|
881
|
+
if (data === "\x1B[6~") return "pageDown";
|
|
882
|
+
if (data.length === 1) {
|
|
883
|
+
const code = data.charCodeAt(0);
|
|
884
|
+
if (code >= 1 && code <= 26) return `ctrl+${String.fromCharCode(code + 96)}`;
|
|
885
|
+
if (code >= 32 && code <= 126) return data;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const KITTY_CSI_U_REGEX = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/;
|
|
889
|
+
const KITTY_PRINTABLE_ALLOWED_MODIFIERS = MODIFIERS.shift | LOCK_MASK;
|
|
890
|
+
/**
|
|
891
|
+
* Decode a Kitty CSI-u sequence into a printable character, if applicable.
|
|
892
|
+
*
|
|
893
|
+
* When Kitty keyboard protocol flag 1 (disambiguate) is active, terminals send
|
|
894
|
+
* CSI-u sequences for all keys, including plain printable characters. This
|
|
895
|
+
* function extracts the printable character from such sequences.
|
|
896
|
+
*
|
|
897
|
+
* Only accepts plain or Shift-modified keys. Rejects Ctrl, Alt, and unsupported
|
|
898
|
+
* modifier combinations (those are handled by keybinding matching instead).
|
|
899
|
+
* Prefers the shifted keycode when Shift is held and a shifted key is reported.
|
|
900
|
+
*
|
|
901
|
+
* @param data - Raw input data from terminal
|
|
902
|
+
* @returns The printable character, or undefined if not a printable CSI-u sequence
|
|
903
|
+
*/
|
|
904
|
+
function decodeKittyPrintable(data) {
|
|
905
|
+
const match = data.match(KITTY_CSI_U_REGEX);
|
|
906
|
+
if (!match) return void 0;
|
|
907
|
+
const codepoint = Number.parseInt(match[1] ?? "", 10);
|
|
908
|
+
if (!Number.isFinite(codepoint)) return void 0;
|
|
909
|
+
const shiftedKey = match[2] && match[2].length > 0 ? Number.parseInt(match[2], 10) : void 0;
|
|
910
|
+
const modValue = match[4] ? Number.parseInt(match[4], 10) : 1;
|
|
911
|
+
const modifier = Number.isFinite(modValue) ? modValue - 1 : 0;
|
|
912
|
+
if ((modifier & ~KITTY_PRINTABLE_ALLOWED_MODIFIERS) !== 0) return void 0;
|
|
913
|
+
if (modifier & (MODIFIERS.alt | MODIFIERS.ctrl)) return void 0;
|
|
914
|
+
let effectiveCodepoint = codepoint;
|
|
915
|
+
if (modifier & MODIFIERS.shift && typeof shiftedKey === "number") effectiveCodepoint = shiftedKey;
|
|
916
|
+
effectiveCodepoint = normalizeKittyFunctionalCodepoint(effectiveCodepoint);
|
|
917
|
+
if (!Number.isFinite(effectiveCodepoint) || effectiveCodepoint < 32) return void 0;
|
|
918
|
+
try {
|
|
919
|
+
return String.fromCodePoint(effectiveCodepoint);
|
|
920
|
+
} catch {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region src/compat/vendor/pi-tui-terminal-colors.ts
|
|
926
|
+
function hexToRgb(hex) {
|
|
927
|
+
const normalized = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
928
|
+
return {
|
|
929
|
+
r: parseInt(normalized.slice(0, 2), 16),
|
|
930
|
+
g: parseInt(normalized.slice(2, 4), 16),
|
|
931
|
+
b: parseInt(normalized.slice(4, 6), 16)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function parseOscHexChannel(channel) {
|
|
935
|
+
if (!/^[0-9a-f]+$/i.test(channel)) return;
|
|
936
|
+
const max = 16 ** channel.length - 1;
|
|
937
|
+
if (max <= 0) return;
|
|
938
|
+
return Math.round(parseInt(channel, 16) / max * 255);
|
|
939
|
+
}
|
|
940
|
+
const OSC11_BACKGROUND_COLOR_RESPONSE_PATTERN = /^\x1b\]11;([^\x07\x1b]*)(?:\x07|\x1b\\)$/i;
|
|
941
|
+
const COLOR_SCHEME_REPORT_PATTERN = /^(?:\x1b\[\?997;(1|2)n)+$/;
|
|
942
|
+
function parseOsc11BackgroundColor(data) {
|
|
943
|
+
const match = data.match(OSC11_BACKGROUND_COLOR_RESPONSE_PATTERN);
|
|
944
|
+
if (!match) return;
|
|
945
|
+
const value = match[1].trim();
|
|
946
|
+
if (value.startsWith("#")) {
|
|
947
|
+
const hex = value.slice(1);
|
|
948
|
+
if (/^[0-9a-f]{6}$/i.test(hex)) return hexToRgb(value);
|
|
949
|
+
if (/^[0-9a-f]{12}$/i.test(hex)) {
|
|
950
|
+
const r = parseOscHexChannel(hex.slice(0, 4));
|
|
951
|
+
const g = parseOscHexChannel(hex.slice(4, 8));
|
|
952
|
+
const b = parseOscHexChannel(hex.slice(8, 12));
|
|
953
|
+
return r !== void 0 && g !== void 0 && b !== void 0 ? {
|
|
954
|
+
r,
|
|
955
|
+
g,
|
|
956
|
+
b
|
|
957
|
+
} : void 0;
|
|
958
|
+
}
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
const [red, green, blue] = value.replace(/^rgba?:/i, "").split("/");
|
|
962
|
+
if (red === void 0 || green === void 0 || blue === void 0) return;
|
|
963
|
+
const r = parseOscHexChannel(red);
|
|
964
|
+
const g = parseOscHexChannel(green);
|
|
965
|
+
const b = parseOscHexChannel(blue);
|
|
966
|
+
return r !== void 0 && g !== void 0 && b !== void 0 ? {
|
|
967
|
+
r,
|
|
968
|
+
g,
|
|
969
|
+
b
|
|
970
|
+
} : void 0;
|
|
971
|
+
}
|
|
972
|
+
function parseTerminalColorSchemeReport(data) {
|
|
973
|
+
const match = data.match(COLOR_SCHEME_REPORT_PATTERN);
|
|
974
|
+
if (!match) return;
|
|
975
|
+
return match[1] === "2" ? "light" : "dark";
|
|
976
|
+
}
|
|
977
|
+
//#endregion
|
|
978
|
+
//#region src/compat/vendor/pi-tui-keybindings.ts
|
|
979
|
+
const TUI_KEYBINDINGS = {
|
|
980
|
+
"tui.editor.cursorUp": {
|
|
981
|
+
defaultKeys: "up",
|
|
982
|
+
description: "Move cursor up"
|
|
983
|
+
},
|
|
984
|
+
"tui.editor.cursorDown": {
|
|
985
|
+
defaultKeys: "down",
|
|
986
|
+
description: "Move cursor down"
|
|
987
|
+
},
|
|
988
|
+
"tui.editor.historyPrevious": {
|
|
989
|
+
defaultKeys: [],
|
|
990
|
+
description: "Select previous prompt history entry"
|
|
991
|
+
},
|
|
992
|
+
"tui.editor.historyNext": {
|
|
993
|
+
defaultKeys: [],
|
|
994
|
+
description: "Select next prompt history entry"
|
|
995
|
+
},
|
|
996
|
+
"tui.editor.cursorLeft": {
|
|
997
|
+
defaultKeys: ["left", "ctrl+b"],
|
|
998
|
+
description: "Move cursor left"
|
|
999
|
+
},
|
|
1000
|
+
"tui.editor.cursorRight": {
|
|
1001
|
+
defaultKeys: ["right", "ctrl+f"],
|
|
1002
|
+
description: "Move cursor right"
|
|
1003
|
+
},
|
|
1004
|
+
"tui.editor.cursorWordLeft": {
|
|
1005
|
+
defaultKeys: [
|
|
1006
|
+
"alt+left",
|
|
1007
|
+
"ctrl+left",
|
|
1008
|
+
"alt+b"
|
|
1009
|
+
],
|
|
1010
|
+
description: "Move cursor word left"
|
|
1011
|
+
},
|
|
1012
|
+
"tui.editor.cursorWordRight": {
|
|
1013
|
+
defaultKeys: [
|
|
1014
|
+
"alt+right",
|
|
1015
|
+
"ctrl+right",
|
|
1016
|
+
"alt+f"
|
|
1017
|
+
],
|
|
1018
|
+
description: "Move cursor word right"
|
|
1019
|
+
},
|
|
1020
|
+
"tui.editor.cursorLineStart": {
|
|
1021
|
+
defaultKeys: [
|
|
1022
|
+
"home",
|
|
1023
|
+
"ctrl+home",
|
|
1024
|
+
"ctrl+a"
|
|
1025
|
+
],
|
|
1026
|
+
description: "Move to line start"
|
|
1027
|
+
},
|
|
1028
|
+
"tui.editor.cursorLineEnd": {
|
|
1029
|
+
defaultKeys: [
|
|
1030
|
+
"end",
|
|
1031
|
+
"ctrl+end",
|
|
1032
|
+
"ctrl+e"
|
|
1033
|
+
],
|
|
1034
|
+
description: "Move to line end"
|
|
1035
|
+
},
|
|
1036
|
+
"tui.editor.jumpForward": {
|
|
1037
|
+
defaultKeys: "ctrl+]",
|
|
1038
|
+
description: "Jump forward to character"
|
|
1039
|
+
},
|
|
1040
|
+
"tui.editor.jumpBackward": {
|
|
1041
|
+
defaultKeys: "ctrl+alt+]",
|
|
1042
|
+
description: "Jump backward to character"
|
|
1043
|
+
},
|
|
1044
|
+
"tui.editor.pageUp": {
|
|
1045
|
+
defaultKeys: ["pageUp", "ctrl+pageUp"],
|
|
1046
|
+
description: "Page up"
|
|
1047
|
+
},
|
|
1048
|
+
"tui.editor.pageDown": {
|
|
1049
|
+
defaultKeys: ["pageDown", "ctrl+pageDown"],
|
|
1050
|
+
description: "Page down"
|
|
1051
|
+
},
|
|
1052
|
+
"tui.editor.deleteCharBackward": {
|
|
1053
|
+
defaultKeys: "backspace",
|
|
1054
|
+
description: "Delete character backward"
|
|
1055
|
+
},
|
|
1056
|
+
"tui.editor.deleteCharForward": {
|
|
1057
|
+
defaultKeys: ["delete", "ctrl+d"],
|
|
1058
|
+
description: "Delete character forward"
|
|
1059
|
+
},
|
|
1060
|
+
"tui.editor.deleteWordBackward": {
|
|
1061
|
+
defaultKeys: ["ctrl+w", "alt+backspace"],
|
|
1062
|
+
description: "Delete word backward"
|
|
1063
|
+
},
|
|
1064
|
+
"tui.editor.deleteWordForward": {
|
|
1065
|
+
defaultKeys: ["alt+d", "alt+delete"],
|
|
1066
|
+
description: "Delete word forward"
|
|
1067
|
+
},
|
|
1068
|
+
"tui.editor.deleteToLineStart": {
|
|
1069
|
+
defaultKeys: "ctrl+u",
|
|
1070
|
+
description: "Delete to line start"
|
|
1071
|
+
},
|
|
1072
|
+
"tui.editor.deleteToLineEnd": {
|
|
1073
|
+
defaultKeys: "ctrl+k",
|
|
1074
|
+
description: "Delete to line end"
|
|
1075
|
+
},
|
|
1076
|
+
"tui.editor.yank": {
|
|
1077
|
+
defaultKeys: "ctrl+y",
|
|
1078
|
+
description: "Yank"
|
|
1079
|
+
},
|
|
1080
|
+
"tui.editor.yankPop": {
|
|
1081
|
+
defaultKeys: "alt+y",
|
|
1082
|
+
description: "Yank pop"
|
|
1083
|
+
},
|
|
1084
|
+
"tui.editor.undo": {
|
|
1085
|
+
defaultKeys: "ctrl+-",
|
|
1086
|
+
description: "Undo"
|
|
1087
|
+
},
|
|
1088
|
+
"tui.input.newLine": {
|
|
1089
|
+
defaultKeys: ["shift+enter", "ctrl+j"],
|
|
1090
|
+
description: "Insert newline"
|
|
1091
|
+
},
|
|
1092
|
+
"tui.input.submit": {
|
|
1093
|
+
defaultKeys: "enter",
|
|
1094
|
+
description: "Submit input"
|
|
1095
|
+
},
|
|
1096
|
+
"tui.input.tab": {
|
|
1097
|
+
defaultKeys: "tab",
|
|
1098
|
+
description: "Tab / autocomplete"
|
|
1099
|
+
},
|
|
1100
|
+
"tui.input.copy": {
|
|
1101
|
+
defaultKeys: "ctrl+c",
|
|
1102
|
+
description: "Copy selection"
|
|
1103
|
+
},
|
|
1104
|
+
"tui.select.up": {
|
|
1105
|
+
defaultKeys: "up",
|
|
1106
|
+
description: "Move selection up"
|
|
1107
|
+
},
|
|
1108
|
+
"tui.select.down": {
|
|
1109
|
+
defaultKeys: "down",
|
|
1110
|
+
description: "Move selection down"
|
|
1111
|
+
},
|
|
1112
|
+
"tui.select.pageUp": {
|
|
1113
|
+
defaultKeys: "pageUp",
|
|
1114
|
+
description: "Selection page up"
|
|
1115
|
+
},
|
|
1116
|
+
"tui.select.pageDown": {
|
|
1117
|
+
defaultKeys: "pageDown",
|
|
1118
|
+
description: "Selection page down"
|
|
1119
|
+
},
|
|
1120
|
+
"tui.select.confirm": {
|
|
1121
|
+
defaultKeys: "enter",
|
|
1122
|
+
description: "Confirm selection"
|
|
1123
|
+
},
|
|
1124
|
+
"tui.select.cancel": {
|
|
1125
|
+
defaultKeys: ["escape", "ctrl+c"],
|
|
1126
|
+
description: "Cancel selection"
|
|
1127
|
+
},
|
|
1128
|
+
"tui.altScreen.pageUp": {
|
|
1129
|
+
defaultKeys: "pageUp",
|
|
1130
|
+
description: "Scroll viewport up one page"
|
|
1131
|
+
},
|
|
1132
|
+
"tui.altScreen.pageDown": {
|
|
1133
|
+
defaultKeys: "pageDown",
|
|
1134
|
+
description: "Scroll viewport down one page"
|
|
1135
|
+
},
|
|
1136
|
+
"tui.altScreen.halfPageUp": {
|
|
1137
|
+
defaultKeys: [],
|
|
1138
|
+
description: "Scroll viewport up half a page"
|
|
1139
|
+
},
|
|
1140
|
+
"tui.altScreen.halfPageDown": {
|
|
1141
|
+
defaultKeys: [],
|
|
1142
|
+
description: "Scroll viewport down half a page"
|
|
1143
|
+
},
|
|
1144
|
+
"tui.altScreen.lineUp": {
|
|
1145
|
+
defaultKeys: [],
|
|
1146
|
+
description: "Scroll viewport up one line"
|
|
1147
|
+
},
|
|
1148
|
+
"tui.altScreen.lineDown": {
|
|
1149
|
+
defaultKeys: [],
|
|
1150
|
+
description: "Scroll viewport down one line"
|
|
1151
|
+
},
|
|
1152
|
+
"tui.altScreen.previousPrompt": {
|
|
1153
|
+
defaultKeys: "ctrl+shift+up",
|
|
1154
|
+
description: "Jump to previous semantic prompt"
|
|
1155
|
+
},
|
|
1156
|
+
"tui.altScreen.nextPrompt": {
|
|
1157
|
+
defaultKeys: "ctrl+shift+down",
|
|
1158
|
+
description: "Jump to next semantic prompt"
|
|
1159
|
+
},
|
|
1160
|
+
"tui.altScreen.search": {
|
|
1161
|
+
defaultKeys: "ctrl+shift+f",
|
|
1162
|
+
description: "Search the primary scroll view"
|
|
1163
|
+
},
|
|
1164
|
+
"tui.altScreen.searchNext": {
|
|
1165
|
+
defaultKeys: ["enter", "ctrl+g"],
|
|
1166
|
+
description: "Select the next search match"
|
|
1167
|
+
},
|
|
1168
|
+
"tui.altScreen.searchPrevious": {
|
|
1169
|
+
defaultKeys: ["shift+enter", "ctrl+shift+g"],
|
|
1170
|
+
description: "Select the previous search match"
|
|
1171
|
+
},
|
|
1172
|
+
"tui.altScreen.searchClose": {
|
|
1173
|
+
defaultKeys: "escape",
|
|
1174
|
+
description: "Close transcript search"
|
|
1175
|
+
},
|
|
1176
|
+
"tui.altScreen.top": {
|
|
1177
|
+
defaultKeys: "home",
|
|
1178
|
+
description: "Scroll viewport to top"
|
|
1179
|
+
},
|
|
1180
|
+
"tui.altScreen.bottom": {
|
|
1181
|
+
defaultKeys: "end",
|
|
1182
|
+
description: "Scroll viewport to bottom"
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
function normalizeKeys(keys) {
|
|
1186
|
+
if (keys === void 0) return [];
|
|
1187
|
+
const keyList = Array.isArray(keys) ? keys : [keys];
|
|
1188
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1189
|
+
const result = [];
|
|
1190
|
+
for (const key of keyList) if (!seen.has(key)) {
|
|
1191
|
+
seen.add(key);
|
|
1192
|
+
result.push(key);
|
|
1193
|
+
}
|
|
1194
|
+
return result;
|
|
1195
|
+
}
|
|
1196
|
+
var KeybindingsManager = class {
|
|
1197
|
+
definitions;
|
|
1198
|
+
userBindings;
|
|
1199
|
+
keysById = /* @__PURE__ */ new Map();
|
|
1200
|
+
conflicts = [];
|
|
1201
|
+
constructor(definitions, userBindings = {}) {
|
|
1202
|
+
this.definitions = definitions;
|
|
1203
|
+
this.userBindings = userBindings;
|
|
1204
|
+
this.rebuild();
|
|
1205
|
+
}
|
|
1206
|
+
rebuild() {
|
|
1207
|
+
this.keysById.clear();
|
|
1208
|
+
this.conflicts = [];
|
|
1209
|
+
const userClaims = /* @__PURE__ */ new Map();
|
|
1210
|
+
for (const [keybinding, keys] of Object.entries(this.userBindings)) {
|
|
1211
|
+
if (!(keybinding in this.definitions)) continue;
|
|
1212
|
+
for (const key of normalizeKeys(keys)) {
|
|
1213
|
+
const claimants = userClaims.get(key) ?? /* @__PURE__ */ new Set();
|
|
1214
|
+
claimants.add(keybinding);
|
|
1215
|
+
userClaims.set(key, claimants);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
for (const [key, keybindings] of userClaims) if (keybindings.size > 1) this.conflicts.push({
|
|
1219
|
+
key,
|
|
1220
|
+
keybindings: [...keybindings]
|
|
1221
|
+
});
|
|
1222
|
+
for (const [id, definition] of Object.entries(this.definitions)) {
|
|
1223
|
+
const userKeys = this.userBindings[id];
|
|
1224
|
+
const keys = userKeys === void 0 ? normalizeKeys(definition.defaultKeys) : normalizeKeys(userKeys);
|
|
1225
|
+
this.keysById.set(id, keys);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
matches(data, keybinding) {
|
|
1229
|
+
const keys = this.keysById.get(keybinding) ?? [];
|
|
1230
|
+
for (const key of keys) if (matchesKey(data, key)) return true;
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
getKeys(keybinding) {
|
|
1234
|
+
return [...this.keysById.get(keybinding) ?? []];
|
|
1235
|
+
}
|
|
1236
|
+
getDefinition(keybinding) {
|
|
1237
|
+
return this.definitions[keybinding];
|
|
1238
|
+
}
|
|
1239
|
+
getConflicts() {
|
|
1240
|
+
return this.conflicts.map((conflict) => ({
|
|
1241
|
+
...conflict,
|
|
1242
|
+
keybindings: [...conflict.keybindings]
|
|
1243
|
+
}));
|
|
1244
|
+
}
|
|
1245
|
+
setUserBindings(userBindings) {
|
|
1246
|
+
this.userBindings = userBindings;
|
|
1247
|
+
this.rebuild();
|
|
1248
|
+
}
|
|
1249
|
+
getUserBindings() {
|
|
1250
|
+
return { ...this.userBindings };
|
|
1251
|
+
}
|
|
1252
|
+
getResolvedBindings() {
|
|
1253
|
+
const resolved = {};
|
|
1254
|
+
for (const id of Object.keys(this.definitions)) {
|
|
1255
|
+
const keys = this.keysById.get(id) ?? [];
|
|
1256
|
+
resolved[id] = keys.length === 1 ? keys[0] : [...keys];
|
|
1257
|
+
}
|
|
1258
|
+
return resolved;
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
let globalKeybindings = null;
|
|
1262
|
+
function setKeybindings(keybindings) {
|
|
1263
|
+
globalKeybindings = keybindings;
|
|
1264
|
+
}
|
|
1265
|
+
function getKeybindings() {
|
|
1266
|
+
if (!globalKeybindings) globalKeybindings = new KeybindingsManager(TUI_KEYBINDINGS);
|
|
1267
|
+
return globalKeybindings;
|
|
1268
|
+
}
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region src/compat/vendor/pi-tui-latex.ts
|
|
1271
|
+
const SYMBOLS = {
|
|
1272
|
+
alpha: "α",
|
|
1273
|
+
beta: "β",
|
|
1274
|
+
gamma: "γ",
|
|
1275
|
+
delta: "δ",
|
|
1276
|
+
epsilon: "ϵ",
|
|
1277
|
+
varepsilon: "ε",
|
|
1278
|
+
zeta: "ζ",
|
|
1279
|
+
eta: "η",
|
|
1280
|
+
theta: "θ",
|
|
1281
|
+
vartheta: "ϑ",
|
|
1282
|
+
iota: "ι",
|
|
1283
|
+
kappa: "κ",
|
|
1284
|
+
varkappa: "ϰ",
|
|
1285
|
+
lambda: "λ",
|
|
1286
|
+
mu: "μ",
|
|
1287
|
+
nu: "ν",
|
|
1288
|
+
xi: "ξ",
|
|
1289
|
+
pi: "π",
|
|
1290
|
+
varpi: "ϖ",
|
|
1291
|
+
rho: "ρ",
|
|
1292
|
+
varrho: "ϱ",
|
|
1293
|
+
sigma: "σ",
|
|
1294
|
+
varsigma: "ς",
|
|
1295
|
+
tau: "τ",
|
|
1296
|
+
upsilon: "υ",
|
|
1297
|
+
phi: "ϕ",
|
|
1298
|
+
varphi: "φ",
|
|
1299
|
+
chi: "χ",
|
|
1300
|
+
psi: "ψ",
|
|
1301
|
+
omega: "ω",
|
|
1302
|
+
Gamma: "Γ",
|
|
1303
|
+
Delta: "Δ",
|
|
1304
|
+
Theta: "Θ",
|
|
1305
|
+
Lambda: "Λ",
|
|
1306
|
+
Xi: "Ξ",
|
|
1307
|
+
Pi: "Π",
|
|
1308
|
+
Sigma: "Σ",
|
|
1309
|
+
Upsilon: "Υ",
|
|
1310
|
+
Phi: "Φ",
|
|
1311
|
+
Psi: "Ψ",
|
|
1312
|
+
Omega: "Ω",
|
|
1313
|
+
pm: "±",
|
|
1314
|
+
mp: "∓",
|
|
1315
|
+
times: "×",
|
|
1316
|
+
div: "÷",
|
|
1317
|
+
cdot: "·",
|
|
1318
|
+
ast: "∗",
|
|
1319
|
+
star: "⋆",
|
|
1320
|
+
circ: "∘",
|
|
1321
|
+
bullet: "•",
|
|
1322
|
+
oplus: "⊕",
|
|
1323
|
+
ominus: "⊖",
|
|
1324
|
+
otimes: "⊗",
|
|
1325
|
+
oslash: "⊘",
|
|
1326
|
+
odot: "⊙",
|
|
1327
|
+
bigcirc: "○",
|
|
1328
|
+
dagger: "†",
|
|
1329
|
+
ddagger: "‡",
|
|
1330
|
+
amalg: "⨿",
|
|
1331
|
+
uplus: "⊎",
|
|
1332
|
+
sqcap: "⊓",
|
|
1333
|
+
sqcup: "⊔",
|
|
1334
|
+
triangleleft: "◁",
|
|
1335
|
+
triangleright: "▷",
|
|
1336
|
+
wr: "≀",
|
|
1337
|
+
cap: "∩",
|
|
1338
|
+
cup: "∪",
|
|
1339
|
+
bigcap: "⋂",
|
|
1340
|
+
bigcup: "⋃",
|
|
1341
|
+
bigwedge: "⋀",
|
|
1342
|
+
bigvee: "⋁",
|
|
1343
|
+
bigsqcup: "⨆",
|
|
1344
|
+
biguplus: "⨄",
|
|
1345
|
+
bigoplus: "⨁",
|
|
1346
|
+
bigotimes: "⨂",
|
|
1347
|
+
bigodot: "⨀",
|
|
1348
|
+
setminus: "∖",
|
|
1349
|
+
in: "∈",
|
|
1350
|
+
notin: "∉",
|
|
1351
|
+
ni: "∋",
|
|
1352
|
+
subset: "⊂",
|
|
1353
|
+
supset: "⊃",
|
|
1354
|
+
subseteq: "⊆",
|
|
1355
|
+
supseteq: "⊇",
|
|
1356
|
+
sqsubset: "⊏",
|
|
1357
|
+
sqsupset: "⊐",
|
|
1358
|
+
sqsubseteq: "⊑",
|
|
1359
|
+
sqsupseteq: "⊒",
|
|
1360
|
+
prec: "≺",
|
|
1361
|
+
preceq: "≼",
|
|
1362
|
+
succ: "≻",
|
|
1363
|
+
succeq: "≽",
|
|
1364
|
+
ll: "≪",
|
|
1365
|
+
gg: "≫",
|
|
1366
|
+
le: "≤",
|
|
1367
|
+
leq: "≤",
|
|
1368
|
+
leqslant: "≤",
|
|
1369
|
+
ge: "≥",
|
|
1370
|
+
geq: "≥",
|
|
1371
|
+
geqslant: "≥",
|
|
1372
|
+
ne: "≠",
|
|
1373
|
+
neq: "≠",
|
|
1374
|
+
equiv: "≡",
|
|
1375
|
+
approx: "≈",
|
|
1376
|
+
sim: "∼",
|
|
1377
|
+
simeq: "≃",
|
|
1378
|
+
cong: "≅",
|
|
1379
|
+
asymp: "≍",
|
|
1380
|
+
doteq: "≐",
|
|
1381
|
+
propto: "∝",
|
|
1382
|
+
parallel: "∥",
|
|
1383
|
+
perp: "⊥",
|
|
1384
|
+
mid: "∣",
|
|
1385
|
+
vdash: "⊢",
|
|
1386
|
+
dashv: "⊣",
|
|
1387
|
+
models: "⊨",
|
|
1388
|
+
Vdash: "⊩",
|
|
1389
|
+
Vvdash: "⊪",
|
|
1390
|
+
nvdash: "⊬",
|
|
1391
|
+
nvDash: "⊭",
|
|
1392
|
+
forall: "∀",
|
|
1393
|
+
exists: "∃",
|
|
1394
|
+
nexists: "∄",
|
|
1395
|
+
neg: "¬",
|
|
1396
|
+
land: "∧",
|
|
1397
|
+
wedge: "∧",
|
|
1398
|
+
lor: "∨",
|
|
1399
|
+
vee: "∨",
|
|
1400
|
+
to: "→",
|
|
1401
|
+
rightarrow: "→",
|
|
1402
|
+
longrightarrow: "→",
|
|
1403
|
+
leftarrow: "←",
|
|
1404
|
+
longleftarrow: "←",
|
|
1405
|
+
gets: "←",
|
|
1406
|
+
leftrightarrow: "↔",
|
|
1407
|
+
longleftrightarrow: "↔",
|
|
1408
|
+
hookleftarrow: "↩",
|
|
1409
|
+
hookrightarrow: "↪",
|
|
1410
|
+
twoheadleftarrow: "↞",
|
|
1411
|
+
twoheadrightarrow: "↠",
|
|
1412
|
+
leftharpoonup: "↼",
|
|
1413
|
+
leftharpoondown: "↽",
|
|
1414
|
+
rightharpoonup: "⇀",
|
|
1415
|
+
rightharpoondown: "⇁",
|
|
1416
|
+
rightleftharpoons: "⇌",
|
|
1417
|
+
leftrightharpoons: "⇋",
|
|
1418
|
+
nearrow: "↗",
|
|
1419
|
+
searrow: "↘",
|
|
1420
|
+
swarrow: "↙",
|
|
1421
|
+
nwarrow: "↖",
|
|
1422
|
+
rightsquigarrow: "⇝",
|
|
1423
|
+
leadsto: "⇝",
|
|
1424
|
+
Rightarrow: "⇒",
|
|
1425
|
+
Longrightarrow: "⇒",
|
|
1426
|
+
Leftarrow: "⇐",
|
|
1427
|
+
Longleftarrow: "⇐",
|
|
1428
|
+
Leftrightarrow: "⇔",
|
|
1429
|
+
Longleftrightarrow: "⇔",
|
|
1430
|
+
implies: "⇒",
|
|
1431
|
+
iff: "⇔",
|
|
1432
|
+
mapsto: "↦",
|
|
1433
|
+
longmapsto: "↦",
|
|
1434
|
+
uparrow: "↑",
|
|
1435
|
+
downarrow: "↓",
|
|
1436
|
+
partial: "∂",
|
|
1437
|
+
nabla: "∇",
|
|
1438
|
+
int: "∫",
|
|
1439
|
+
iint: "∬",
|
|
1440
|
+
iiint: "∭",
|
|
1441
|
+
oint: "∮",
|
|
1442
|
+
sum: "∑",
|
|
1443
|
+
prod: "∏",
|
|
1444
|
+
coprod: "∐",
|
|
1445
|
+
infty: "∞",
|
|
1446
|
+
emptyset: "∅",
|
|
1447
|
+
varnothing: "∅",
|
|
1448
|
+
angle: "∠",
|
|
1449
|
+
therefore: "∴",
|
|
1450
|
+
because: "∵",
|
|
1451
|
+
aleph: "ℵ",
|
|
1452
|
+
beth: "ℶ",
|
|
1453
|
+
gimel: "ℷ",
|
|
1454
|
+
daleth: "ℸ",
|
|
1455
|
+
top: "⊤",
|
|
1456
|
+
bot: "⊥",
|
|
1457
|
+
triangle: "△",
|
|
1458
|
+
square: "□",
|
|
1459
|
+
lozenge: "◊",
|
|
1460
|
+
checkmark: "✓",
|
|
1461
|
+
complement: "∁",
|
|
1462
|
+
wp: "℘",
|
|
1463
|
+
prime: "′",
|
|
1464
|
+
ldots: "…",
|
|
1465
|
+
dots: "…",
|
|
1466
|
+
cdots: "⋯",
|
|
1467
|
+
vdots: "⋮",
|
|
1468
|
+
ddots: "⋱",
|
|
1469
|
+
ell: "ℓ",
|
|
1470
|
+
hbar: "ℏ",
|
|
1471
|
+
Im: "ℑ",
|
|
1472
|
+
Re: "ℜ",
|
|
1473
|
+
langle: "⟨",
|
|
1474
|
+
rangle: "⟩",
|
|
1475
|
+
vert: "|",
|
|
1476
|
+
lvert: "|",
|
|
1477
|
+
rvert: "|",
|
|
1478
|
+
Vert: "‖",
|
|
1479
|
+
lVert: "‖",
|
|
1480
|
+
rVert: "‖",
|
|
1481
|
+
lbrace: "{",
|
|
1482
|
+
rbrace: "}",
|
|
1483
|
+
backslash: "\\",
|
|
1484
|
+
lfloor: "⌊",
|
|
1485
|
+
rfloor: "⌋",
|
|
1486
|
+
lceil: "⌈",
|
|
1487
|
+
rceil: "⌉",
|
|
1488
|
+
colon: ":"
|
|
1489
|
+
};
|
|
1490
|
+
const NAMED_OPERATORS = /* @__PURE__ */ new Set([
|
|
1491
|
+
"arccos",
|
|
1492
|
+
"arcsin",
|
|
1493
|
+
"arctan",
|
|
1494
|
+
"arg",
|
|
1495
|
+
"cos",
|
|
1496
|
+
"cosh",
|
|
1497
|
+
"cot",
|
|
1498
|
+
"coth",
|
|
1499
|
+
"csc",
|
|
1500
|
+
"deg",
|
|
1501
|
+
"det",
|
|
1502
|
+
"dim",
|
|
1503
|
+
"exp",
|
|
1504
|
+
"gcd",
|
|
1505
|
+
"hom",
|
|
1506
|
+
"inf",
|
|
1507
|
+
"ker",
|
|
1508
|
+
"lg",
|
|
1509
|
+
"lim",
|
|
1510
|
+
"liminf",
|
|
1511
|
+
"limsup",
|
|
1512
|
+
"ln",
|
|
1513
|
+
"log",
|
|
1514
|
+
"max",
|
|
1515
|
+
"min",
|
|
1516
|
+
"Pr",
|
|
1517
|
+
"sec",
|
|
1518
|
+
"sin",
|
|
1519
|
+
"sinh",
|
|
1520
|
+
"sup",
|
|
1521
|
+
"tan",
|
|
1522
|
+
"tanh"
|
|
1523
|
+
]);
|
|
1524
|
+
const LIMIT_OPERATORS = /* @__PURE__ */ new Set([
|
|
1525
|
+
"argmax",
|
|
1526
|
+
"argmin",
|
|
1527
|
+
"inf",
|
|
1528
|
+
"injlim",
|
|
1529
|
+
"lim",
|
|
1530
|
+
"liminf",
|
|
1531
|
+
"limsup",
|
|
1532
|
+
"max",
|
|
1533
|
+
"min",
|
|
1534
|
+
"projlim",
|
|
1535
|
+
"sup"
|
|
1536
|
+
]);
|
|
1537
|
+
const DISPLAY_LIMIT_SYMBOLS = /* @__PURE__ */ new Set([
|
|
1538
|
+
"bigcap",
|
|
1539
|
+
"bigcup",
|
|
1540
|
+
"bigodot",
|
|
1541
|
+
"bigoplus",
|
|
1542
|
+
"bigotimes",
|
|
1543
|
+
"bigsqcup",
|
|
1544
|
+
"biguplus",
|
|
1545
|
+
"bigvee",
|
|
1546
|
+
"bigwedge",
|
|
1547
|
+
"coprod",
|
|
1548
|
+
"int",
|
|
1549
|
+
"iint",
|
|
1550
|
+
"iiint",
|
|
1551
|
+
"oint",
|
|
1552
|
+
"prod",
|
|
1553
|
+
"sum"
|
|
1554
|
+
]);
|
|
1555
|
+
const RELATION_COMMANDS = /* @__PURE__ */ new Set([
|
|
1556
|
+
"Leftarrow",
|
|
1557
|
+
"Leftrightarrow",
|
|
1558
|
+
"Longleftarrow",
|
|
1559
|
+
"Longleftrightarrow",
|
|
1560
|
+
"Longrightarrow",
|
|
1561
|
+
"Rightarrow",
|
|
1562
|
+
"Vdash",
|
|
1563
|
+
"Vvdash",
|
|
1564
|
+
"approx",
|
|
1565
|
+
"asymp",
|
|
1566
|
+
"cong",
|
|
1567
|
+
"dashv",
|
|
1568
|
+
"doteq",
|
|
1569
|
+
"downarrow",
|
|
1570
|
+
"equiv",
|
|
1571
|
+
"ge",
|
|
1572
|
+
"geq",
|
|
1573
|
+
"geqslant",
|
|
1574
|
+
"gets",
|
|
1575
|
+
"gg",
|
|
1576
|
+
"hookleftarrow",
|
|
1577
|
+
"hookrightarrow",
|
|
1578
|
+
"iff",
|
|
1579
|
+
"implies",
|
|
1580
|
+
"in",
|
|
1581
|
+
"leadsto",
|
|
1582
|
+
"le",
|
|
1583
|
+
"leftarrow",
|
|
1584
|
+
"leftharpoondown",
|
|
1585
|
+
"leftharpoonup",
|
|
1586
|
+
"leftrightarrow",
|
|
1587
|
+
"leftrightharpoons",
|
|
1588
|
+
"leq",
|
|
1589
|
+
"leqslant",
|
|
1590
|
+
"ll",
|
|
1591
|
+
"longleftarrow",
|
|
1592
|
+
"longleftrightarrow",
|
|
1593
|
+
"longmapsto",
|
|
1594
|
+
"longrightarrow",
|
|
1595
|
+
"mapsto",
|
|
1596
|
+
"mid",
|
|
1597
|
+
"models",
|
|
1598
|
+
"ne",
|
|
1599
|
+
"nearrow",
|
|
1600
|
+
"neq",
|
|
1601
|
+
"ni",
|
|
1602
|
+
"notin",
|
|
1603
|
+
"nvdash",
|
|
1604
|
+
"nvDash",
|
|
1605
|
+
"nwarrow",
|
|
1606
|
+
"parallel",
|
|
1607
|
+
"perp",
|
|
1608
|
+
"prec",
|
|
1609
|
+
"preceq",
|
|
1610
|
+
"propto",
|
|
1611
|
+
"rightharpoondown",
|
|
1612
|
+
"rightharpoonup",
|
|
1613
|
+
"rightleftharpoons",
|
|
1614
|
+
"rightarrow",
|
|
1615
|
+
"rightsquigarrow",
|
|
1616
|
+
"searrow",
|
|
1617
|
+
"sim",
|
|
1618
|
+
"simeq",
|
|
1619
|
+
"sqsubset",
|
|
1620
|
+
"sqsubseteq",
|
|
1621
|
+
"sqsupset",
|
|
1622
|
+
"sqsupseteq",
|
|
1623
|
+
"subset",
|
|
1624
|
+
"subseteq",
|
|
1625
|
+
"succ",
|
|
1626
|
+
"succeq",
|
|
1627
|
+
"supset",
|
|
1628
|
+
"supseteq",
|
|
1629
|
+
"swarrow",
|
|
1630
|
+
"to",
|
|
1631
|
+
"triangleleft",
|
|
1632
|
+
"triangleright",
|
|
1633
|
+
"twoheadleftarrow",
|
|
1634
|
+
"twoheadrightarrow",
|
|
1635
|
+
"uparrow",
|
|
1636
|
+
"vdash"
|
|
1637
|
+
]);
|
|
1638
|
+
const NEGATED_SYMBOLS = {
|
|
1639
|
+
"<": "≮",
|
|
1640
|
+
">": "≯",
|
|
1641
|
+
"=": "≠",
|
|
1642
|
+
"∈": "∉",
|
|
1643
|
+
"∋": "∌",
|
|
1644
|
+
"∣": "∤",
|
|
1645
|
+
"∥": "∦",
|
|
1646
|
+
"∼": "≁",
|
|
1647
|
+
"≃": "≄",
|
|
1648
|
+
"≅": "≇",
|
|
1649
|
+
"≈": "≉",
|
|
1650
|
+
"≡": "≢",
|
|
1651
|
+
"≤": "≰",
|
|
1652
|
+
"≥": "≱",
|
|
1653
|
+
"≺": "⊀",
|
|
1654
|
+
"≻": "⊁",
|
|
1655
|
+
"⊂": "⊄",
|
|
1656
|
+
"⊃": "⊅",
|
|
1657
|
+
"⊆": "⊈",
|
|
1658
|
+
"⊇": "⊉",
|
|
1659
|
+
"⊢": "⊬",
|
|
1660
|
+
"⊨": "⊭",
|
|
1661
|
+
"↔": "↮",
|
|
1662
|
+
"←": "↚",
|
|
1663
|
+
"→": "↛",
|
|
1664
|
+
"⇒": "⇏",
|
|
1665
|
+
"⇐": "⇍",
|
|
1666
|
+
"⇔": "⇎",
|
|
1667
|
+
"≼": "⋠",
|
|
1668
|
+
"≽": "⋡"
|
|
1669
|
+
};
|
|
1670
|
+
const BLACKBOARD = {
|
|
1671
|
+
C: "ℂ",
|
|
1672
|
+
H: "ℍ",
|
|
1673
|
+
N: "ℕ",
|
|
1674
|
+
P: "ℙ",
|
|
1675
|
+
Q: "ℚ",
|
|
1676
|
+
R: "ℝ",
|
|
1677
|
+
Z: "ℤ"
|
|
1678
|
+
};
|
|
1679
|
+
const SUPERSCRIPTS = {
|
|
1680
|
+
"0": "⁰",
|
|
1681
|
+
"1": "¹",
|
|
1682
|
+
"2": "²",
|
|
1683
|
+
"3": "³",
|
|
1684
|
+
"4": "⁴",
|
|
1685
|
+
"5": "⁵",
|
|
1686
|
+
"6": "⁶",
|
|
1687
|
+
"7": "⁷",
|
|
1688
|
+
"8": "⁸",
|
|
1689
|
+
"9": "⁹",
|
|
1690
|
+
"+": "⁺",
|
|
1691
|
+
"-": "⁻",
|
|
1692
|
+
"=": "⁼",
|
|
1693
|
+
"(": "⁽",
|
|
1694
|
+
")": "⁾",
|
|
1695
|
+
a: "ᵃ",
|
|
1696
|
+
b: "ᵇ",
|
|
1697
|
+
c: "ᶜ",
|
|
1698
|
+
d: "ᵈ",
|
|
1699
|
+
e: "ᵉ",
|
|
1700
|
+
f: "ᶠ",
|
|
1701
|
+
g: "ᵍ",
|
|
1702
|
+
h: "ʰ",
|
|
1703
|
+
i: "ⁱ",
|
|
1704
|
+
j: "ʲ",
|
|
1705
|
+
k: "ᵏ",
|
|
1706
|
+
l: "ˡ",
|
|
1707
|
+
m: "ᵐ",
|
|
1708
|
+
n: "ⁿ",
|
|
1709
|
+
o: "ᵒ",
|
|
1710
|
+
p: "ᵖ",
|
|
1711
|
+
r: "ʳ",
|
|
1712
|
+
s: "ˢ",
|
|
1713
|
+
t: "ᵗ",
|
|
1714
|
+
u: "ᵘ",
|
|
1715
|
+
v: "ᵛ",
|
|
1716
|
+
w: "ʷ",
|
|
1717
|
+
x: "ˣ",
|
|
1718
|
+
y: "ʸ",
|
|
1719
|
+
z: "ᶻ"
|
|
1720
|
+
};
|
|
1721
|
+
const SUBSCRIPTS = {
|
|
1722
|
+
"0": "₀",
|
|
1723
|
+
"1": "₁",
|
|
1724
|
+
"2": "₂",
|
|
1725
|
+
"3": "₃",
|
|
1726
|
+
"4": "₄",
|
|
1727
|
+
"5": "₅",
|
|
1728
|
+
"6": "₆",
|
|
1729
|
+
"7": "₇",
|
|
1730
|
+
"8": "₈",
|
|
1731
|
+
"9": "₉",
|
|
1732
|
+
"+": "₊",
|
|
1733
|
+
"-": "₋",
|
|
1734
|
+
"=": "₌",
|
|
1735
|
+
"(": "₍",
|
|
1736
|
+
")": "₎",
|
|
1737
|
+
a: "ₐ",
|
|
1738
|
+
e: "ₑ",
|
|
1739
|
+
h: "ₕ",
|
|
1740
|
+
i: "ᵢ",
|
|
1741
|
+
j: "ⱼ",
|
|
1742
|
+
k: "ₖ",
|
|
1743
|
+
l: "ₗ",
|
|
1744
|
+
m: "ₘ",
|
|
1745
|
+
n: "ₙ",
|
|
1746
|
+
o: "ₒ",
|
|
1747
|
+
p: "ₚ",
|
|
1748
|
+
r: "ᵣ",
|
|
1749
|
+
s: "ₛ",
|
|
1750
|
+
t: "ₜ",
|
|
1751
|
+
u: "ᵤ",
|
|
1752
|
+
v: "ᵥ",
|
|
1753
|
+
x: "ₓ"
|
|
1754
|
+
};
|
|
1755
|
+
const SPACING_COMMANDS = /* @__PURE__ */ new Set([
|
|
1756
|
+
",",
|
|
1757
|
+
":",
|
|
1758
|
+
";",
|
|
1759
|
+
" ",
|
|
1760
|
+
">",
|
|
1761
|
+
"enspace",
|
|
1762
|
+
"enskip",
|
|
1763
|
+
"medspace",
|
|
1764
|
+
"quad",
|
|
1765
|
+
"qquad",
|
|
1766
|
+
"thickspace",
|
|
1767
|
+
"thinspace"
|
|
1768
|
+
]);
|
|
1769
|
+
const NEGATIVE_SPACING_COMMANDS = /* @__PURE__ */ new Set([
|
|
1770
|
+
"!",
|
|
1771
|
+
"negmedspace",
|
|
1772
|
+
"negthickspace",
|
|
1773
|
+
"negthinspace"
|
|
1774
|
+
]);
|
|
1775
|
+
const NEGATIVE_SPACE = "\0";
|
|
1776
|
+
const IGNORED_COMMANDS = /* @__PURE__ */ new Set([
|
|
1777
|
+
"displaystyle",
|
|
1778
|
+
"limits",
|
|
1779
|
+
"nolimits",
|
|
1780
|
+
"scriptstyle",
|
|
1781
|
+
"scriptscriptstyle",
|
|
1782
|
+
"textstyle"
|
|
1783
|
+
]);
|
|
1784
|
+
const SIZE_COMMANDS = /* @__PURE__ */ new Set([
|
|
1785
|
+
"big",
|
|
1786
|
+
"Big",
|
|
1787
|
+
"bigg",
|
|
1788
|
+
"Bigg",
|
|
1789
|
+
"bigl",
|
|
1790
|
+
"Bigl",
|
|
1791
|
+
"biggl",
|
|
1792
|
+
"Biggl",
|
|
1793
|
+
"bigr",
|
|
1794
|
+
"Bigr",
|
|
1795
|
+
"biggr",
|
|
1796
|
+
"Biggr"
|
|
1797
|
+
]);
|
|
1798
|
+
const PLAIN_WRAPPERS = /* @__PURE__ */ new Set([
|
|
1799
|
+
"emph",
|
|
1800
|
+
"mathcal",
|
|
1801
|
+
"mathbf",
|
|
1802
|
+
"mathfrak",
|
|
1803
|
+
"mathit",
|
|
1804
|
+
"mathrm",
|
|
1805
|
+
"mathnormal",
|
|
1806
|
+
"mathscr",
|
|
1807
|
+
"mathsf",
|
|
1808
|
+
"mathtt",
|
|
1809
|
+
"mathup",
|
|
1810
|
+
"mbox",
|
|
1811
|
+
"overbrace",
|
|
1812
|
+
"pmb",
|
|
1813
|
+
"smash",
|
|
1814
|
+
"substack",
|
|
1815
|
+
"text",
|
|
1816
|
+
"textbf",
|
|
1817
|
+
"textit",
|
|
1818
|
+
"textmd",
|
|
1819
|
+
"textnormal",
|
|
1820
|
+
"textrm",
|
|
1821
|
+
"textsc",
|
|
1822
|
+
"textsf",
|
|
1823
|
+
"textsl",
|
|
1824
|
+
"texttt",
|
|
1825
|
+
"textup",
|
|
1826
|
+
"underbrace",
|
|
1827
|
+
"bm",
|
|
1828
|
+
"boldsymbol"
|
|
1829
|
+
]);
|
|
1830
|
+
const ACCENTS = {
|
|
1831
|
+
acute: "́",
|
|
1832
|
+
bar: "̅",
|
|
1833
|
+
breve: "̆",
|
|
1834
|
+
check: "̌",
|
|
1835
|
+
ddot: "̈",
|
|
1836
|
+
dot: "̇",
|
|
1837
|
+
grave: "̀",
|
|
1838
|
+
hat: "̂",
|
|
1839
|
+
mathring: "̊",
|
|
1840
|
+
overleftarrow: "⃖",
|
|
1841
|
+
overleftrightarrow: "⃡",
|
|
1842
|
+
overline: "̅",
|
|
1843
|
+
overrightarrow: "⃗",
|
|
1844
|
+
tilde: "̃",
|
|
1845
|
+
underline: "̲",
|
|
1846
|
+
vec: "⃗",
|
|
1847
|
+
widehat: "̂",
|
|
1848
|
+
widetilde: "̃"
|
|
1849
|
+
};
|
|
1850
|
+
function replaceCharacters(value, replacements) {
|
|
1851
|
+
let result = "";
|
|
1852
|
+
for (const character of value) {
|
|
1853
|
+
const replacement = replacements[character];
|
|
1854
|
+
if (replacement === void 0) return;
|
|
1855
|
+
result += replacement;
|
|
1856
|
+
}
|
|
1857
|
+
return result;
|
|
1858
|
+
}
|
|
1859
|
+
function formatScript(value, kind) {
|
|
1860
|
+
value = value.trim();
|
|
1861
|
+
const replacements = kind === "sub" ? SUBSCRIPTS : SUPERSCRIPTS;
|
|
1862
|
+
const unicode = replaceCharacters(value.replace(/\s*([=+-])\s*/g, "$1"), replacements);
|
|
1863
|
+
if (unicode !== void 0) return unicode;
|
|
1864
|
+
const prefix = kind === "sub" ? "_" : "^";
|
|
1865
|
+
if (Array.from(value).length === 1 || kind === "sub" && /^[A-Za-z]+$/.test(value)) return `${prefix}${value}`;
|
|
1866
|
+
return `${prefix}(${value})`;
|
|
1867
|
+
}
|
|
1868
|
+
function formatFraction(numerator, denominator) {
|
|
1869
|
+
numerator = numerator.trim();
|
|
1870
|
+
denominator = denominator.trim();
|
|
1871
|
+
const simpleNumerator = /^[\p{L}\p{N}.]+$/u.test(numerator);
|
|
1872
|
+
const simpleDenominator = /^[\p{N}.]+$/u.test(denominator) || Array.from(denominator).length === 1;
|
|
1873
|
+
return `${simpleNumerator ? numerator : `(${numerator})`}/${simpleDenominator ? denominator : `(${denominator})`}`;
|
|
1874
|
+
}
|
|
1875
|
+
function formatRoot(value, symbol = "√") {
|
|
1876
|
+
value = value.trim();
|
|
1877
|
+
return /^[\p{L}\p{N}.]+$/u.test(value) ? `${symbol}${value}` : `${symbol}(${value})`;
|
|
1878
|
+
}
|
|
1879
|
+
const NAMED_OPERATOR_START = "";
|
|
1880
|
+
const NAMED_OPERATOR_END = "";
|
|
1881
|
+
const NAMED_OPERATOR_LEFT_SPACING_PATTERN = /(?<=[\p{L}\p{N})\]}\u{f0001}])\u{f0004}/gu;
|
|
1882
|
+
const NAMED_OPERATOR_RIGHT_SPACING_PATTERN = /\u{f0005}(?=[\p{L}\p{N}√\u{f0000}])/gu;
|
|
1883
|
+
function normalizeOutput(value) {
|
|
1884
|
+
return value.replace(NAMED_OPERATOR_LEFT_SPACING_PATTERN, " ").replaceAll(NAMED_OPERATOR_START, "").replace(NAMED_OPERATOR_RIGHT_SPACING_PATTERN, " ").replaceAll(NAMED_OPERATOR_END, "").split("\n").map((line) => line.replace(/[ \t]+/g, " ").trim()).filter((line, index, lines) => line.length > 0 || index > 0 && index < lines.length - 1).join("\n").trim();
|
|
1885
|
+
}
|
|
1886
|
+
const LAYOUT_MARKER_START = "";
|
|
1887
|
+
const LAYOUT_MARKER_END = "";
|
|
1888
|
+
const LAYOUT_MARKER_PATTERN = /\u{f0000}(\d+)\u{f0001}/gu;
|
|
1889
|
+
const TRAILING_LAYOUT_MARKER_PATTERN = /\u{f0000}(\d+)\u{f0001}$/u;
|
|
1890
|
+
const PROTECTED_SPACE = "";
|
|
1891
|
+
function padLayoutLine(line, width, centered = false) {
|
|
1892
|
+
const padding = Math.max(0, width - visibleWidth(line));
|
|
1893
|
+
const left = centered ? Math.floor(padding / 2) : 0;
|
|
1894
|
+
return `${" ".repeat(left)}${line}${" ".repeat(padding - left)}`;
|
|
1895
|
+
}
|
|
1896
|
+
function joinLayouts(layouts) {
|
|
1897
|
+
if (layouts.length === 0) return {
|
|
1898
|
+
lines: [""],
|
|
1899
|
+
width: 0,
|
|
1900
|
+
baseline: 0
|
|
1901
|
+
};
|
|
1902
|
+
const baseline = Math.max(...layouts.map((layout) => layout.baseline));
|
|
1903
|
+
const below = Math.max(...layouts.map((layout) => layout.lines.length - layout.baseline - 1));
|
|
1904
|
+
const lines = [];
|
|
1905
|
+
for (let row = 0; row <= baseline + below; row++) {
|
|
1906
|
+
let line = "";
|
|
1907
|
+
for (const layout of layouts) {
|
|
1908
|
+
const sourceRow = row - baseline + layout.baseline;
|
|
1909
|
+
line += sourceRow >= 0 && sourceRow < layout.lines.length ? padLayoutLine(layout.lines[sourceRow] ?? "", layout.width) : " ".repeat(layout.width);
|
|
1910
|
+
}
|
|
1911
|
+
lines.push(line.trimEnd());
|
|
1912
|
+
}
|
|
1913
|
+
return {
|
|
1914
|
+
lines,
|
|
1915
|
+
width: layouts.reduce((width, layout) => width + layout.width, 0),
|
|
1916
|
+
baseline
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function renderLayout(source, nodes) {
|
|
1920
|
+
const renderedLines = [];
|
|
1921
|
+
let firstBaseline = 0;
|
|
1922
|
+
for (const sourceLine of source.split("\n")) {
|
|
1923
|
+
const layouts = [];
|
|
1924
|
+
let position = 0;
|
|
1925
|
+
let previousNode;
|
|
1926
|
+
for (const match of sourceLine.matchAll(LAYOUT_MARKER_PATTERN)) {
|
|
1927
|
+
const index = match.index;
|
|
1928
|
+
const node = nodes[Number(match[1])];
|
|
1929
|
+
if (!node) continue;
|
|
1930
|
+
if (index > position) {
|
|
1931
|
+
const sliced = sourceLine.slice(position, index);
|
|
1932
|
+
const trimmed = (previousNode ? sliced.trimStart() : sliced).trimEnd();
|
|
1933
|
+
const preserveLeadingSpace = previousNode?.type === "matrix" && /^\s/.test(sliced);
|
|
1934
|
+
const preserveTrailingSpace = node.type === "matrix" && /\s$/.test(sliced);
|
|
1935
|
+
const text = trimmed ? `${preserveLeadingSpace ? " " : ""}${trimmed}${preserveTrailingSpace ? " " : ""}` : preserveLeadingSpace || preserveTrailingSpace ? " " : "";
|
|
1936
|
+
layouts.push({
|
|
1937
|
+
lines: [text],
|
|
1938
|
+
width: visibleWidth(text),
|
|
1939
|
+
baseline: 0
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
if (node.type === "fraction") {
|
|
1943
|
+
const numerator = renderLayout(node.numerator, nodes);
|
|
1944
|
+
const denominator = renderLayout(node.denominator, nodes);
|
|
1945
|
+
const contentWidth = Math.max(numerator.width, denominator.width, 1);
|
|
1946
|
+
const width = contentWidth + 2;
|
|
1947
|
+
layouts.push({
|
|
1948
|
+
lines: [
|
|
1949
|
+
...numerator.lines.map((line) => padLayoutLine(line, width, true)),
|
|
1950
|
+
` ${"─".repeat(contentWidth)} `,
|
|
1951
|
+
...denominator.lines.map((line) => padLayoutLine(line, width, true))
|
|
1952
|
+
],
|
|
1953
|
+
width,
|
|
1954
|
+
baseline: numerator.lines.length
|
|
1955
|
+
});
|
|
1956
|
+
} else if (node.type === "operator") {
|
|
1957
|
+
const contentWidth = Math.max(visibleWidth(node.operator), node.lower === void 0 ? 0 : visibleWidth(node.lower), node.upper === void 0 ? 0 : visibleWidth(node.upper));
|
|
1958
|
+
const lines = [];
|
|
1959
|
+
if (node.upper !== void 0) lines.push(`${padLayoutLine(node.upper, contentWidth, true)} `);
|
|
1960
|
+
lines.push(`${padLayoutLine(node.operator, contentWidth, true)} `);
|
|
1961
|
+
if (node.lower !== void 0) lines.push(`${padLayoutLine(node.lower, contentWidth, true)} `);
|
|
1962
|
+
layouts.push({
|
|
1963
|
+
lines,
|
|
1964
|
+
width: contentWidth + 1,
|
|
1965
|
+
baseline: node.upper === void 0 ? 0 : 1
|
|
1966
|
+
});
|
|
1967
|
+
} else {
|
|
1968
|
+
const width = Math.max(0, ...node.lines.map((line) => visibleWidth(line)));
|
|
1969
|
+
layouts.push({
|
|
1970
|
+
lines: node.lines.map((line) => padLayoutLine(line, width)),
|
|
1971
|
+
width,
|
|
1972
|
+
baseline: node.baseline
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
position = index + match[0].length;
|
|
1976
|
+
previousNode = node;
|
|
1977
|
+
}
|
|
1978
|
+
if (position < sourceLine.length) {
|
|
1979
|
+
const sliced = sourceLine.slice(position);
|
|
1980
|
+
const trimmed = previousNode ? sliced.trimStart() : sliced;
|
|
1981
|
+
const text = previousNode?.type === "matrix" && /^\s/.test(sliced) ? ` ${trimmed}` : trimmed;
|
|
1982
|
+
layouts.push({
|
|
1983
|
+
lines: [text],
|
|
1984
|
+
width: visibleWidth(text),
|
|
1985
|
+
baseline: 0
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
const lineLayout = joinLayouts(layouts);
|
|
1989
|
+
if (renderedLines.length === 0) firstBaseline = lineLayout.baseline;
|
|
1990
|
+
renderedLines.push(...lineLayout.lines);
|
|
1991
|
+
}
|
|
1992
|
+
return {
|
|
1993
|
+
lines: renderedLines,
|
|
1994
|
+
width: Math.max(0, ...renderedLines.map((line) => visibleWidth(line))),
|
|
1995
|
+
baseline: firstBaseline
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
var LatexParser = class LatexParser {
|
|
1999
|
+
source;
|
|
2000
|
+
layoutNodes;
|
|
2001
|
+
display;
|
|
2002
|
+
position = 0;
|
|
2003
|
+
supported = true;
|
|
2004
|
+
stackFractions = true;
|
|
2005
|
+
constructor(source, layoutNodes, display) {
|
|
2006
|
+
this.source = source;
|
|
2007
|
+
this.layoutNodes = layoutNodes;
|
|
2008
|
+
this.display = display;
|
|
2009
|
+
}
|
|
2010
|
+
render() {
|
|
2011
|
+
const rendered = this.parseSequence();
|
|
2012
|
+
if (!this.supported || this.position !== this.source.length) return;
|
|
2013
|
+
return normalizeOutput(rendered);
|
|
2014
|
+
}
|
|
2015
|
+
parseSequence(endCharacter) {
|
|
2016
|
+
let result = "";
|
|
2017
|
+
while (this.position < this.source.length) {
|
|
2018
|
+
const character = this.source[this.position];
|
|
2019
|
+
if (endCharacter && character === endCharacter) {
|
|
2020
|
+
this.position++;
|
|
2021
|
+
return result;
|
|
2022
|
+
}
|
|
2023
|
+
if (character === "}") {
|
|
2024
|
+
this.supported = false;
|
|
2025
|
+
return result;
|
|
2026
|
+
}
|
|
2027
|
+
if (character === "{") {
|
|
2028
|
+
this.position++;
|
|
2029
|
+
result += this.parseSequence("}");
|
|
2030
|
+
continue;
|
|
2031
|
+
}
|
|
2032
|
+
if (character === "\\") {
|
|
2033
|
+
const command = this.parseCommand();
|
|
2034
|
+
if (command === NEGATIVE_SPACE) {
|
|
2035
|
+
result = result.trimEnd();
|
|
2036
|
+
if (result.endsWith(NAMED_OPERATOR_END)) result = result.slice(0, -2);
|
|
2037
|
+
} else result += command;
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
if (character === "^" || character === "_") {
|
|
2041
|
+
this.position++;
|
|
2042
|
+
result = result.trimEnd();
|
|
2043
|
+
const script = formatScript(this.parseRequiredArgument(false), character === "_" ? "sub" : "sup");
|
|
2044
|
+
if (result.endsWith(NAMED_OPERATOR_END)) result = `${result.slice(0, -2)}${script}${NAMED_OPERATOR_END}`;
|
|
2045
|
+
else result += script;
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
if (/\s/.test(character)) {
|
|
2049
|
+
result += this.parseWhitespace();
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
if (character === "=" || character === "<" || character === ">") {
|
|
2053
|
+
result = `${result.trimEnd()} ${character} `;
|
|
2054
|
+
this.position++;
|
|
2055
|
+
continue;
|
|
2056
|
+
}
|
|
2057
|
+
if (character === "&") {
|
|
2058
|
+
this.position++;
|
|
2059
|
+
continue;
|
|
2060
|
+
}
|
|
2061
|
+
if (character === "~") {
|
|
2062
|
+
this.position++;
|
|
2063
|
+
result += " ";
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
if (character === ".") {
|
|
2067
|
+
const marker = TRAILING_LAYOUT_MARKER_PATTERN.exec(result);
|
|
2068
|
+
const node = marker ? this.layoutNodes[Number(marker[1])] : void 0;
|
|
2069
|
+
if (node?.type === "matrix") {
|
|
2070
|
+
const lastLine = node.lines.length - 1;
|
|
2071
|
+
node.lines[lastLine] = `${node.lines[lastLine] ?? ""}${character}`;
|
|
2072
|
+
this.position++;
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
result += character;
|
|
2077
|
+
this.position++;
|
|
2078
|
+
}
|
|
2079
|
+
if (endCharacter) this.supported = false;
|
|
2080
|
+
return result;
|
|
2081
|
+
}
|
|
2082
|
+
parseWhitespace() {
|
|
2083
|
+
while (this.position < this.source.length && /\s/.test(this.source[this.position] ?? "")) this.position++;
|
|
2084
|
+
return " ";
|
|
2085
|
+
}
|
|
2086
|
+
parseCommand() {
|
|
2087
|
+
this.position++;
|
|
2088
|
+
if (this.position >= this.source.length) {
|
|
2089
|
+
this.supported = false;
|
|
2090
|
+
return "";
|
|
2091
|
+
}
|
|
2092
|
+
let command = "";
|
|
2093
|
+
const first = this.source[this.position] ?? "";
|
|
2094
|
+
if (first === "\n" || first === "\r") {
|
|
2095
|
+
this.position++;
|
|
2096
|
+
if (first === "\r" && this.source[this.position] === "\n") this.position++;
|
|
2097
|
+
return " ";
|
|
2098
|
+
}
|
|
2099
|
+
if (/[A-Za-z]/.test(first)) {
|
|
2100
|
+
const start = this.position;
|
|
2101
|
+
while (this.position < this.source.length && /[A-Za-z]/.test(this.source[this.position] ?? "")) this.position++;
|
|
2102
|
+
command = this.source.slice(start, this.position);
|
|
2103
|
+
} else {
|
|
2104
|
+
command = first;
|
|
2105
|
+
this.position++;
|
|
2106
|
+
}
|
|
2107
|
+
if (command === "\\") return "\n";
|
|
2108
|
+
if (SPACING_COMMANDS.has(command)) return " ";
|
|
2109
|
+
if (NEGATIVE_SPACING_COMMANDS.has(command)) return NEGATIVE_SPACE;
|
|
2110
|
+
if (IGNORED_COMMANDS.has(command)) return "";
|
|
2111
|
+
if (command === "{" || command === "}" || command === "$" || command === "%" || command === "#" || command === "_" || command === "&") return command;
|
|
2112
|
+
if (command === "|") return "‖";
|
|
2113
|
+
if (command === "not") {
|
|
2114
|
+
const value = this.parseRequiredArgument(false).trim();
|
|
2115
|
+
const negated = NEGATED_SYMBOLS[value];
|
|
2116
|
+
if (negated !== void 0) return ` ${negated} `;
|
|
2117
|
+
const characters = Array.from(value);
|
|
2118
|
+
if (characters.length === 0) {
|
|
2119
|
+
this.supported = false;
|
|
2120
|
+
return "";
|
|
2121
|
+
}
|
|
2122
|
+
return ` ${characters[0]}\u0338${characters.slice(1).join("")} `;
|
|
2123
|
+
}
|
|
2124
|
+
if (LIMIT_OPERATORS.has(command)) return this.parseOperator(command, "bracket", true, true);
|
|
2125
|
+
const symbol = SYMBOLS[command];
|
|
2126
|
+
if (symbol !== void 0) {
|
|
2127
|
+
if (DISPLAY_LIMIT_SYMBOLS.has(command)) return this.parseOperator(symbol, "script", true);
|
|
2128
|
+
return command === "cdot" || command === "times" || RELATION_COMMANDS.has(command) ? ` ${symbol} ` : symbol;
|
|
2129
|
+
}
|
|
2130
|
+
if (NAMED_OPERATORS.has(command)) return `${NAMED_OPERATOR_START}${command}${NAMED_OPERATOR_END}`;
|
|
2131
|
+
if (SIZE_COMMANDS.has(command)) return "";
|
|
2132
|
+
if (command === "left" || command === "middle" || command === "right") {
|
|
2133
|
+
if (this.source[this.position] === ".") this.position++;
|
|
2134
|
+
return "";
|
|
2135
|
+
}
|
|
2136
|
+
if (command === "frac" || command === "dfrac" || command === "tfrac") {
|
|
2137
|
+
const shouldStack = this.display && this.stackFractions && command !== "tfrac";
|
|
2138
|
+
const numerator = this.parseRequiredArgument(!shouldStack);
|
|
2139
|
+
const denominator = this.parseRequiredArgument(!shouldStack);
|
|
2140
|
+
if (shouldStack) {
|
|
2141
|
+
const index = this.layoutNodes.push({
|
|
2142
|
+
type: "fraction",
|
|
2143
|
+
numerator: normalizeOutput(numerator),
|
|
2144
|
+
denominator: normalizeOutput(denominator)
|
|
2145
|
+
}) - 1;
|
|
2146
|
+
return `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;
|
|
2147
|
+
}
|
|
2148
|
+
return formatFraction(numerator, denominator);
|
|
2149
|
+
}
|
|
2150
|
+
if (command === "sqrt") {
|
|
2151
|
+
const degree = this.parseOptionalArgument()?.trim();
|
|
2152
|
+
const value = this.parseRequiredArgument();
|
|
2153
|
+
if (degree === void 0 || degree === "2") return formatRoot(value);
|
|
2154
|
+
if (degree === "3") return formatRoot(value, "∛");
|
|
2155
|
+
if (degree === "4") return formatRoot(value, "∜");
|
|
2156
|
+
return `${formatScript(degree, "sup")}${formatRoot(value)}`;
|
|
2157
|
+
}
|
|
2158
|
+
if (command === "boxed" || command === "fbox") return `[${this.parseRequiredArgument().trim()}]`;
|
|
2159
|
+
if (command === "binom" || command === "dbinom" || command === "tbinom") return `(${this.parseRequiredArgument()} choose ${this.parseRequiredArgument()})`;
|
|
2160
|
+
const accent = ACCENTS[command];
|
|
2161
|
+
if (accent !== void 0) {
|
|
2162
|
+
const value = this.parseRequiredArgument();
|
|
2163
|
+
return Array.from(value).length === 1 ? `${value}${accent}` : `${command}(${value})`;
|
|
2164
|
+
}
|
|
2165
|
+
if (command === "mathbb") {
|
|
2166
|
+
const value = this.parseRequiredArgument();
|
|
2167
|
+
return Array.from(value, (character) => BLACKBOARD[character] ?? character).join("");
|
|
2168
|
+
}
|
|
2169
|
+
if (command === "operatorname") {
|
|
2170
|
+
const starred = this.source[this.position] === "*";
|
|
2171
|
+
if (starred) this.position++;
|
|
2172
|
+
const operator = normalizeOutput(this.parseRequiredArgument()).trim();
|
|
2173
|
+
return this.parseOperator(operator, "bracket", starred, true);
|
|
2174
|
+
}
|
|
2175
|
+
if (command === "mod" || command === "bmod") return " mod ";
|
|
2176
|
+
if (command === "pmod" || command === "pod") {
|
|
2177
|
+
const value = this.parseRequiredArgument().trim();
|
|
2178
|
+
return command === "pmod" ? ` (mod ${value})` : ` (${value})`;
|
|
2179
|
+
}
|
|
2180
|
+
if (command === "overset" || command === "stackrel") {
|
|
2181
|
+
const upper = this.parseRequiredArgument();
|
|
2182
|
+
return `${this.parseRequiredArgument().trim()}${formatScript(upper, "sup")}`;
|
|
2183
|
+
}
|
|
2184
|
+
if (command === "underset") {
|
|
2185
|
+
const lower = this.parseRequiredArgument();
|
|
2186
|
+
return `${this.parseRequiredArgument().trim()}${formatScript(lower, "sub")}`;
|
|
2187
|
+
}
|
|
2188
|
+
if (PLAIN_WRAPPERS.has(command)) {
|
|
2189
|
+
const value = this.parseRequiredArgument();
|
|
2190
|
+
return command.startsWith("text") || command === "mbox" ? value : value.trim();
|
|
2191
|
+
}
|
|
2192
|
+
if (command === "begin") return this.parseEnvironment();
|
|
2193
|
+
if (command === "end") {
|
|
2194
|
+
this.supported = false;
|
|
2195
|
+
return "";
|
|
2196
|
+
}
|
|
2197
|
+
this.supported = false;
|
|
2198
|
+
return `\\${command}`;
|
|
2199
|
+
}
|
|
2200
|
+
parseOperator(operator, inlineLowerStyle, displayLimits, spaced = false) {
|
|
2201
|
+
let useDisplayLimits = displayLimits;
|
|
2202
|
+
let modifierPosition = this.position;
|
|
2203
|
+
while (modifierPosition < this.source.length && /[ \t]/.test(this.source[modifierPosition] ?? "")) modifierPosition++;
|
|
2204
|
+
const modifier = /^\\(limits|nolimits)(?![A-Za-z])/.exec(this.source.slice(modifierPosition));
|
|
2205
|
+
if (modifier) {
|
|
2206
|
+
useDisplayLimits = modifier[1] === "limits";
|
|
2207
|
+
this.position = modifierPosition + modifier[0].length;
|
|
2208
|
+
}
|
|
2209
|
+
let lower;
|
|
2210
|
+
let upper;
|
|
2211
|
+
while (true) {
|
|
2212
|
+
let scriptPosition = this.position;
|
|
2213
|
+
while (scriptPosition < this.source.length && /[ \t]/.test(this.source[scriptPosition] ?? "")) scriptPosition++;
|
|
2214
|
+
const kind = this.source[scriptPosition];
|
|
2215
|
+
if (kind !== "_" && kind !== "^") break;
|
|
2216
|
+
this.position = scriptPosition + 1;
|
|
2217
|
+
const value = normalizeOutput(this.parseRequiredArgument(false)).replaceAll(" ", "");
|
|
2218
|
+
if (kind === "_") {
|
|
2219
|
+
if (lower !== void 0) this.supported = false;
|
|
2220
|
+
lower = value;
|
|
2221
|
+
} else {
|
|
2222
|
+
if (upper !== void 0) this.supported = false;
|
|
2223
|
+
upper = value;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
if (this.display && useDisplayLimits && (lower !== void 0 || upper !== void 0)) {
|
|
2227
|
+
const index = this.layoutNodes.push({
|
|
2228
|
+
type: "operator",
|
|
2229
|
+
operator,
|
|
2230
|
+
lower,
|
|
2231
|
+
upper
|
|
2232
|
+
}) - 1;
|
|
2233
|
+
return `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;
|
|
2234
|
+
}
|
|
2235
|
+
let rendered = operator;
|
|
2236
|
+
if (lower !== void 0) rendered += inlineLowerStyle === "bracket" ? `[${lower}]` : formatScript(lower, "sub");
|
|
2237
|
+
if (upper !== void 0) rendered += formatScript(upper, "sup");
|
|
2238
|
+
return spaced ? ` ${rendered} ` : rendered;
|
|
2239
|
+
}
|
|
2240
|
+
parseRequiredArgument(stackFractions = true) {
|
|
2241
|
+
const previousStackFractions = this.stackFractions;
|
|
2242
|
+
this.stackFractions = previousStackFractions && stackFractions;
|
|
2243
|
+
const value = this.parseRequiredArgumentValue();
|
|
2244
|
+
this.stackFractions = previousStackFractions;
|
|
2245
|
+
return value;
|
|
2246
|
+
}
|
|
2247
|
+
parseRequiredArgumentValue() {
|
|
2248
|
+
while (this.position < this.source.length && /\s/.test(this.source[this.position] ?? "")) this.position++;
|
|
2249
|
+
if (this.position >= this.source.length) {
|
|
2250
|
+
this.supported = false;
|
|
2251
|
+
return "";
|
|
2252
|
+
}
|
|
2253
|
+
if (this.source[this.position] === "{") {
|
|
2254
|
+
this.position++;
|
|
2255
|
+
return this.parseSequence("}");
|
|
2256
|
+
}
|
|
2257
|
+
if (this.source[this.position] === "\\") return this.parseCommand();
|
|
2258
|
+
const value = this.source[this.position] ?? "";
|
|
2259
|
+
this.position++;
|
|
2260
|
+
return value;
|
|
2261
|
+
}
|
|
2262
|
+
parseOptionalArgument() {
|
|
2263
|
+
while (this.position < this.source.length && /[ \t]/.test(this.source[this.position] ?? "")) this.position++;
|
|
2264
|
+
if (this.source[this.position] !== "[") return;
|
|
2265
|
+
const end = this.source.indexOf("]", this.position + 1);
|
|
2266
|
+
if (end < 0) {
|
|
2267
|
+
this.supported = false;
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
const value = this.source.slice(this.position + 1, end);
|
|
2271
|
+
this.position = end + 1;
|
|
2272
|
+
return this.renderNested(value);
|
|
2273
|
+
}
|
|
2274
|
+
readRawGroup() {
|
|
2275
|
+
while (this.position < this.source.length && /[ \t]/.test(this.source[this.position] ?? "")) this.position++;
|
|
2276
|
+
if (this.source[this.position] !== "{") {
|
|
2277
|
+
this.supported = false;
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
const start = ++this.position;
|
|
2281
|
+
let depth = 1;
|
|
2282
|
+
while (this.position < this.source.length) {
|
|
2283
|
+
const character = this.source[this.position];
|
|
2284
|
+
if (character === "\\") {
|
|
2285
|
+
this.position += 2;
|
|
2286
|
+
continue;
|
|
2287
|
+
}
|
|
2288
|
+
if (character === "{") depth++;
|
|
2289
|
+
if (character === "}") depth--;
|
|
2290
|
+
if (depth === 0) {
|
|
2291
|
+
const value = this.source.slice(start, this.position);
|
|
2292
|
+
this.position++;
|
|
2293
|
+
return value;
|
|
2294
|
+
}
|
|
2295
|
+
this.position++;
|
|
2296
|
+
}
|
|
2297
|
+
this.supported = false;
|
|
2298
|
+
}
|
|
2299
|
+
splitEnvironmentRows(body) {
|
|
2300
|
+
return body.split(/\\\\(?:\[[^\]\n]*\])?/);
|
|
2301
|
+
}
|
|
2302
|
+
parseEnvironment() {
|
|
2303
|
+
const environment = this.readRawGroup();
|
|
2304
|
+
if (!environment) return "";
|
|
2305
|
+
const endMarker = `\\end{${environment}}`;
|
|
2306
|
+
const end = this.source.indexOf(endMarker, this.position);
|
|
2307
|
+
if (end < 0) {
|
|
2308
|
+
this.supported = false;
|
|
2309
|
+
return "";
|
|
2310
|
+
}
|
|
2311
|
+
const body = this.source.slice(this.position, end);
|
|
2312
|
+
this.position = end + endMarker.length;
|
|
2313
|
+
if (environment === "equation" || environment === "equation*" || environment === "displaymath") return this.renderNested(body).trim();
|
|
2314
|
+
if (environment === "aligned" || environment === "align" || environment === "align*" || environment === "alignedat" || environment === "alignat" || environment === "alignat*" || environment === "gather" || environment === "gathered" || environment === "multline" || environment === "multline*" || environment === "split") {
|
|
2315
|
+
const alignedAt = [
|
|
2316
|
+
"alignedat",
|
|
2317
|
+
"alignat",
|
|
2318
|
+
"alignat*"
|
|
2319
|
+
].includes(environment);
|
|
2320
|
+
const alignedBody = alignedAt ? body.replace(/^\s*\{[^}]*\}/, "") : body;
|
|
2321
|
+
return this.splitEnvironmentRows(alignedBody).map((row) => {
|
|
2322
|
+
const cells = row.split("&");
|
|
2323
|
+
const source = alignedAt ? Array.from({ length: Math.ceil(cells.length / 2) }, (_, index) => cells.slice(index * 2, index * 2 + 2).join("")).join(" ") : cells.join("");
|
|
2324
|
+
return this.renderNested(source).trim();
|
|
2325
|
+
}).filter(Boolean).join("\n");
|
|
2326
|
+
}
|
|
2327
|
+
if (environment === "cases" || environment === "cases*") {
|
|
2328
|
+
const rows = this.splitEnvironmentRows(body).map((row) => row.split("&").map((cell) => this.renderNested(cell, false).trim())).filter((row) => row.some(Boolean));
|
|
2329
|
+
return rows.map((row, index) => {
|
|
2330
|
+
const value = (row[0] ?? "").replace(/,\s*$/, "");
|
|
2331
|
+
const condition = row[1] ?? "";
|
|
2332
|
+
const delimiter = index === 0 ? "⎧" : index === rows.length - 1 ? "⎩" : "⎨";
|
|
2333
|
+
const conditionPrefix = /^(?:if|when|for|otherwise)\b/i.test(condition) ? " " : " if ";
|
|
2334
|
+
return `${delimiter} ${value}${condition ? `${conditionPrefix}${condition}` : ""}`;
|
|
2335
|
+
}).join("\n");
|
|
2336
|
+
}
|
|
2337
|
+
if ([
|
|
2338
|
+
"array",
|
|
2339
|
+
"matrix",
|
|
2340
|
+
"smallmatrix",
|
|
2341
|
+
"pmatrix",
|
|
2342
|
+
"bmatrix",
|
|
2343
|
+
"Bmatrix",
|
|
2344
|
+
"vmatrix",
|
|
2345
|
+
"Vmatrix"
|
|
2346
|
+
].includes(environment)) {
|
|
2347
|
+
const matrixBody = environment === "array" ? body.replace(/^\s*\{[^}]*\}/, "") : body;
|
|
2348
|
+
return this.renderMatrix(environment, matrixBody);
|
|
2349
|
+
}
|
|
2350
|
+
this.supported = false;
|
|
2351
|
+
return body;
|
|
2352
|
+
}
|
|
2353
|
+
renderMatrix(environment, body) {
|
|
2354
|
+
const matrix = this.splitEnvironmentRows(body).map((row) => row.split("&").map((cell) => this.renderNested(cell, false).trim())).filter((row) => row.some(Boolean));
|
|
2355
|
+
const columnCount = Math.max(0, ...matrix.map((row) => row.length));
|
|
2356
|
+
const columnWidths = Array.from({ length: columnCount }, (_, column) => Math.max(0, ...matrix.map((row) => visibleWidth(row[column] ?? ""))));
|
|
2357
|
+
const rows = matrix.map((row) => Array.from({ length: columnCount }, (_, column) => {
|
|
2358
|
+
const cell = row[column] ?? "";
|
|
2359
|
+
return `${cell}${PROTECTED_SPACE.repeat(Math.max(0, (columnWidths[column] ?? 0) - visibleWidth(cell)))}`;
|
|
2360
|
+
}).join(" │ "));
|
|
2361
|
+
let lines;
|
|
2362
|
+
if (environment === "array" || environment === "matrix" || environment === "smallmatrix") lines = rows;
|
|
2363
|
+
else {
|
|
2364
|
+
const delimiter = {
|
|
2365
|
+
pmatrix: [
|
|
2366
|
+
"⎛",
|
|
2367
|
+
"⎞",
|
|
2368
|
+
"⎜",
|
|
2369
|
+
"⎟",
|
|
2370
|
+
"⎝",
|
|
2371
|
+
"⎠"
|
|
2372
|
+
],
|
|
2373
|
+
bmatrix: [
|
|
2374
|
+
"⎡",
|
|
2375
|
+
"⎤",
|
|
2376
|
+
"⎢",
|
|
2377
|
+
"⎥",
|
|
2378
|
+
"⎣",
|
|
2379
|
+
"⎦"
|
|
2380
|
+
],
|
|
2381
|
+
Bmatrix: [
|
|
2382
|
+
"⎧",
|
|
2383
|
+
"⎫",
|
|
2384
|
+
"⎨",
|
|
2385
|
+
"⎬",
|
|
2386
|
+
"⎩",
|
|
2387
|
+
"⎭"
|
|
2388
|
+
],
|
|
2389
|
+
vmatrix: [
|
|
2390
|
+
"│",
|
|
2391
|
+
"│",
|
|
2392
|
+
"│",
|
|
2393
|
+
"│",
|
|
2394
|
+
"│",
|
|
2395
|
+
"│"
|
|
2396
|
+
],
|
|
2397
|
+
Vmatrix: [
|
|
2398
|
+
"║",
|
|
2399
|
+
"║",
|
|
2400
|
+
"║",
|
|
2401
|
+
"║",
|
|
2402
|
+
"║",
|
|
2403
|
+
"║"
|
|
2404
|
+
]
|
|
2405
|
+
}[environment];
|
|
2406
|
+
if (!delimiter) {
|
|
2407
|
+
this.supported = false;
|
|
2408
|
+
return rows.join("\n");
|
|
2409
|
+
}
|
|
2410
|
+
lines = rows.map((row, index) => {
|
|
2411
|
+
return `${index === 0 ? delimiter[0] : index === rows.length - 1 ? delimiter[4] : delimiter[2]} ${row} ${index === 0 ? delimiter[1] : index === rows.length - 1 ? delimiter[5] : delimiter[3]}`;
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
if (lines.length <= 1) return lines[0] ?? "";
|
|
2415
|
+
const index = this.layoutNodes.push({
|
|
2416
|
+
type: "matrix",
|
|
2417
|
+
lines,
|
|
2418
|
+
baseline: 0
|
|
2419
|
+
}) - 1;
|
|
2420
|
+
return `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;
|
|
2421
|
+
}
|
|
2422
|
+
renderNested(source, stackFractions = true) {
|
|
2423
|
+
const rendered = new LatexParser(source, this.layoutNodes, this.display && stackFractions).render();
|
|
2424
|
+
if (rendered === void 0) {
|
|
2425
|
+
this.supported = false;
|
|
2426
|
+
return source;
|
|
2427
|
+
}
|
|
2428
|
+
return rendered;
|
|
2429
|
+
}
|
|
2430
|
+
};
|
|
2431
|
+
/**
|
|
2432
|
+
* Render a basic LaTeX math expression as terminal-friendly Unicode text.
|
|
2433
|
+
* Returns undefined when the expression contains unsupported or malformed syntax.
|
|
2434
|
+
*/
|
|
2435
|
+
function renderLatex(source, options = {}) {
|
|
2436
|
+
const layoutNodes = [];
|
|
2437
|
+
const rendered = new LatexParser(source, layoutNodes, options.display === true).render();
|
|
2438
|
+
if (rendered === void 0) return;
|
|
2439
|
+
if (layoutNodes.length === 0) return rendered.replaceAll(PROTECTED_SPACE, " ");
|
|
2440
|
+
const lines = renderLayout(rendered, layoutNodes).lines;
|
|
2441
|
+
const indentation = Math.min(...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length));
|
|
2442
|
+
return lines.map((line) => line.slice(indentation).trimEnd()).join("\n").trimEnd().replaceAll(PROTECTED_SPACE, " ");
|
|
2443
|
+
}
|
|
2444
|
+
//#endregion
|
|
2445
|
+
//#region src/compat/vendor/pi-tui-terminal-image.ts
|
|
2446
|
+
let cachedCapabilities = null;
|
|
2447
|
+
let cellDimensions = {
|
|
2448
|
+
widthPx: 9,
|
|
2449
|
+
heightPx: 18
|
|
2450
|
+
};
|
|
2451
|
+
function getCellDimensions() {
|
|
2452
|
+
return cellDimensions;
|
|
2453
|
+
}
|
|
2454
|
+
function setCellDimensions(dims) {
|
|
2455
|
+
cellDimensions = dims;
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Checks whether the attached tmux client forwards OSC 8 hyperlinks to the
|
|
2459
|
+
* outer terminal. tmux only re-emits them when its `client_termfeatures` lists
|
|
2460
|
+
* `hyperlinks`, and strips them otherwise. On any error fallbacks `false`.
|
|
2461
|
+
*/
|
|
2462
|
+
function probeTmuxHyperlinks() {
|
|
2463
|
+
try {
|
|
2464
|
+
return execSync("tmux display-message -p '#{client_termfeatures}'", {
|
|
2465
|
+
encoding: "utf8",
|
|
2466
|
+
timeout: 250,
|
|
2467
|
+
stdio: [
|
|
2468
|
+
"ignore",
|
|
2469
|
+
"pipe",
|
|
2470
|
+
"ignore"
|
|
2471
|
+
]
|
|
2472
|
+
}).split(",").map((feature) => feature.trim()).includes("hyperlinks");
|
|
2473
|
+
} catch {
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
function detectCapabilities(tmuxForwardsHyperlink = probeTmuxHyperlinks) {
|
|
2478
|
+
const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || "";
|
|
2479
|
+
const terminalEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || "";
|
|
2480
|
+
const term = process.env.TERM?.toLowerCase() || "";
|
|
2481
|
+
const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
|
|
2482
|
+
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
2483
|
+
const isWindowsConsole = process.platform === "win32";
|
|
2484
|
+
if (process.env.TMUX || term.startsWith("tmux")) return {
|
|
2485
|
+
images: null,
|
|
2486
|
+
trueColor: hasTrueColorHint,
|
|
2487
|
+
hyperlinks: tmuxForwardsHyperlink()
|
|
2488
|
+
};
|
|
2489
|
+
if (term.startsWith("screen")) return {
|
|
2490
|
+
images: null,
|
|
2491
|
+
trueColor: hasTrueColorHint,
|
|
2492
|
+
hyperlinks: false
|
|
2493
|
+
};
|
|
2494
|
+
if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") return {
|
|
2495
|
+
images: "kitty",
|
|
2496
|
+
trueColor: true,
|
|
2497
|
+
hyperlinks: true
|
|
2498
|
+
};
|
|
2499
|
+
if (termProgram === "ghostty" || term.includes("ghostty") || process.env.GHOSTTY_RESOURCES_DIR) return {
|
|
2500
|
+
images: "kitty",
|
|
2501
|
+
trueColor: true,
|
|
2502
|
+
hyperlinks: true
|
|
2503
|
+
};
|
|
2504
|
+
if (process.env.WEZTERM_PANE || termProgram === "wezterm") return {
|
|
2505
|
+
images: "kitty",
|
|
2506
|
+
trueColor: true,
|
|
2507
|
+
hyperlinks: true
|
|
2508
|
+
};
|
|
2509
|
+
if (termProgram === "warpterminal" || process.env.WARP_SESSION_ID || process.env.WARP_TERMINAL_SESSION_UUID) return {
|
|
2510
|
+
images: "kitty",
|
|
2511
|
+
trueColor: true,
|
|
2512
|
+
hyperlinks: true
|
|
2513
|
+
};
|
|
2514
|
+
if (process.env.ITERM_SESSION_ID || termProgram === "iterm.app") return {
|
|
2515
|
+
images: "iterm2",
|
|
2516
|
+
trueColor: true,
|
|
2517
|
+
hyperlinks: true
|
|
2518
|
+
};
|
|
2519
|
+
if (process.env.WT_SESSION) return {
|
|
2520
|
+
images: null,
|
|
2521
|
+
trueColor: true,
|
|
2522
|
+
hyperlinks: true
|
|
2523
|
+
};
|
|
2524
|
+
if (termProgram === "vscode") return {
|
|
2525
|
+
images: null,
|
|
2526
|
+
trueColor: true,
|
|
2527
|
+
hyperlinks: true
|
|
2528
|
+
};
|
|
2529
|
+
if (termProgram === "alacritty") return {
|
|
2530
|
+
images: null,
|
|
2531
|
+
trueColor: true,
|
|
2532
|
+
hyperlinks: true
|
|
2533
|
+
};
|
|
2534
|
+
if (terminalEmulator === "jetbrains-jediterm") return {
|
|
2535
|
+
images: null,
|
|
2536
|
+
trueColor: true,
|
|
2537
|
+
hyperlinks: false
|
|
2538
|
+
};
|
|
2539
|
+
if (isWindowsConsole) return {
|
|
2540
|
+
images: null,
|
|
2541
|
+
trueColor: true,
|
|
2542
|
+
hyperlinks: false
|
|
2543
|
+
};
|
|
2544
|
+
return {
|
|
2545
|
+
images: null,
|
|
2546
|
+
trueColor: hasTrueColorHint,
|
|
2547
|
+
hyperlinks: false
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
function getCapabilities() {
|
|
2551
|
+
if (!cachedCapabilities) cachedCapabilities = detectCapabilities();
|
|
2552
|
+
return cachedCapabilities;
|
|
2553
|
+
}
|
|
2554
|
+
function resetCapabilitiesCache() {
|
|
2555
|
+
cachedCapabilities = null;
|
|
2556
|
+
}
|
|
2557
|
+
/** Override the cached capabilities. Useful in tests to exercise both code paths. */
|
|
2558
|
+
function setCapabilities(caps) {
|
|
2559
|
+
cachedCapabilities = caps;
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Generate a random image ID for Kitty graphics protocol.
|
|
2563
|
+
* Uses random IDs to avoid collisions between different module instances
|
|
2564
|
+
* (e.g., main app vs extensions).
|
|
2565
|
+
*/
|
|
2566
|
+
function allocateImageId() {
|
|
2567
|
+
return Math.floor(Math.random() * 4294967294) + 1;
|
|
2568
|
+
}
|
|
2569
|
+
function encodeKitty(base64Data, options = {}) {
|
|
2570
|
+
const CHUNK_SIZE = 4096;
|
|
2571
|
+
const params = [
|
|
2572
|
+
"a=T",
|
|
2573
|
+
"f=100",
|
|
2574
|
+
"q=2"
|
|
2575
|
+
];
|
|
2576
|
+
if (options.moveCursor === false) params.push("C=1");
|
|
2577
|
+
if (options.columns) params.push(`c=${options.columns}`);
|
|
2578
|
+
if (options.rows) params.push(`r=${options.rows}`);
|
|
2579
|
+
if (options.imageId) params.push(`i=${options.imageId}`);
|
|
2580
|
+
if (base64Data.length <= CHUNK_SIZE) return `\x1b_G${params.join(",")};${base64Data}\x1b\\`;
|
|
2581
|
+
const chunks = [];
|
|
2582
|
+
let offset = 0;
|
|
2583
|
+
let isFirst = true;
|
|
2584
|
+
while (offset < base64Data.length) {
|
|
2585
|
+
const chunk = base64Data.slice(offset, offset + CHUNK_SIZE);
|
|
2586
|
+
const isLast = offset + CHUNK_SIZE >= base64Data.length;
|
|
2587
|
+
if (isFirst) {
|
|
2588
|
+
chunks.push(`\x1b_G${params.join(",")},m=1;${chunk}\x1b\\`);
|
|
2589
|
+
isFirst = false;
|
|
2590
|
+
} else if (isLast) chunks.push(`\x1b_Gm=0;${chunk}\x1b\\`);
|
|
2591
|
+
else chunks.push(`\x1b_Gm=1;${chunk}\x1b\\`);
|
|
2592
|
+
offset += CHUNK_SIZE;
|
|
2593
|
+
}
|
|
2594
|
+
return chunks.join("");
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* Delete a Kitty graphics image by ID.
|
|
2598
|
+
* Uses uppercase 'I' to also free the image data.
|
|
2599
|
+
*/
|
|
2600
|
+
function deleteKittyImage(imageId) {
|
|
2601
|
+
return `\x1b_Ga=d,d=I,i=${imageId},q=2\x1b\\`;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Delete all visible Kitty graphics images.
|
|
2605
|
+
* Uses uppercase 'A' to also free the image data.
|
|
2606
|
+
*/
|
|
2607
|
+
function deleteAllKittyImages() {
|
|
2608
|
+
return "\x1B_Ga=d,d=A,q=2\x1B\\";
|
|
2609
|
+
}
|
|
2610
|
+
function encodeITerm2(base64Data, options = {}) {
|
|
2611
|
+
const params = [`inline=${options.inline !== false ? 1 : 0}`, `size=${Buffer.byteLength(base64Data, "base64")}`];
|
|
2612
|
+
if (options.width !== void 0) params.push(`width=${options.width}`);
|
|
2613
|
+
if (options.height !== void 0) params.push(`height=${options.height}`);
|
|
2614
|
+
if (options.name) {
|
|
2615
|
+
const nameBase64 = Buffer.from(options.name).toString("base64");
|
|
2616
|
+
params.push(`name=${nameBase64}`);
|
|
2617
|
+
}
|
|
2618
|
+
if (options.preserveAspectRatio === false) params.push("preserveAspectRatio=0");
|
|
2619
|
+
return `\x1b]1337;File=${params.join(";")}:${base64Data}\x07`;
|
|
2620
|
+
}
|
|
2621
|
+
function calculateImageCellSize(imageDimensions, maxWidthCells, maxHeightCells, cellDimensions = {
|
|
2622
|
+
widthPx: 9,
|
|
2623
|
+
heightPx: 18
|
|
2624
|
+
}) {
|
|
2625
|
+
const maxWidth = Math.max(1, Math.floor(maxWidthCells));
|
|
2626
|
+
const maxHeight = maxHeightCells === void 0 ? void 0 : Math.max(1, Math.floor(maxHeightCells));
|
|
2627
|
+
const imageWidth = Math.max(1, imageDimensions.widthPx);
|
|
2628
|
+
const imageHeight = Math.max(1, imageDimensions.heightPx);
|
|
2629
|
+
const widthScale = maxWidth * cellDimensions.widthPx / imageWidth;
|
|
2630
|
+
const heightScale = maxHeight === void 0 ? widthScale : maxHeight * cellDimensions.heightPx / imageHeight;
|
|
2631
|
+
const scale = Math.min(widthScale, heightScale);
|
|
2632
|
+
const scaledWidthPx = imageWidth * scale;
|
|
2633
|
+
const scaledHeightPx = imageHeight * scale;
|
|
2634
|
+
const columns = Math.ceil(scaledWidthPx / cellDimensions.widthPx);
|
|
2635
|
+
const rows = Math.ceil(scaledHeightPx / cellDimensions.heightPx);
|
|
2636
|
+
return {
|
|
2637
|
+
columns: Math.max(1, Math.min(maxWidth, columns)),
|
|
2638
|
+
rows: Math.max(1, maxHeight === void 0 ? rows : Math.min(maxHeight, rows))
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
function calculateImageRows(imageDimensions, targetWidthCells, cellDimensions = {
|
|
2642
|
+
widthPx: 9,
|
|
2643
|
+
heightPx: 18
|
|
2644
|
+
}) {
|
|
2645
|
+
return calculateImageCellSize(imageDimensions, targetWidthCells, void 0, cellDimensions).rows;
|
|
2646
|
+
}
|
|
2647
|
+
function getPngDimensions(base64Data) {
|
|
2648
|
+
try {
|
|
2649
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
2650
|
+
if (buffer.length < 24) return null;
|
|
2651
|
+
if (buffer[0] !== 137 || buffer[1] !== 80 || buffer[2] !== 78 || buffer[3] !== 71) return null;
|
|
2652
|
+
return {
|
|
2653
|
+
widthPx: buffer.readUInt32BE(16),
|
|
2654
|
+
heightPx: buffer.readUInt32BE(20)
|
|
2655
|
+
};
|
|
2656
|
+
} catch {
|
|
2657
|
+
return null;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
function getJpegDimensions(base64Data) {
|
|
2661
|
+
try {
|
|
2662
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
2663
|
+
if (buffer.length < 2) return null;
|
|
2664
|
+
if (buffer[0] !== 255 || buffer[1] !== 216) return null;
|
|
2665
|
+
let offset = 2;
|
|
2666
|
+
while (offset < buffer.length - 9) {
|
|
2667
|
+
if (buffer[offset] !== 255) {
|
|
2668
|
+
offset++;
|
|
2669
|
+
continue;
|
|
2670
|
+
}
|
|
2671
|
+
const marker = buffer[offset + 1];
|
|
2672
|
+
if (marker >= 192 && marker <= 194) {
|
|
2673
|
+
const height = buffer.readUInt16BE(offset + 5);
|
|
2674
|
+
return {
|
|
2675
|
+
widthPx: buffer.readUInt16BE(offset + 7),
|
|
2676
|
+
heightPx: height
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
if (offset + 3 >= buffer.length) return null;
|
|
2680
|
+
const length = buffer.readUInt16BE(offset + 2);
|
|
2681
|
+
if (length < 2) return null;
|
|
2682
|
+
offset += 2 + length;
|
|
2683
|
+
}
|
|
2684
|
+
return null;
|
|
2685
|
+
} catch {
|
|
2686
|
+
return null;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
function getGifDimensions(base64Data) {
|
|
2690
|
+
try {
|
|
2691
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
2692
|
+
if (buffer.length < 10) return null;
|
|
2693
|
+
const sig = buffer.slice(0, 6).toString("ascii");
|
|
2694
|
+
if (sig !== "GIF87a" && sig !== "GIF89a") return null;
|
|
2695
|
+
return {
|
|
2696
|
+
widthPx: buffer.readUInt16LE(6),
|
|
2697
|
+
heightPx: buffer.readUInt16LE(8)
|
|
2698
|
+
};
|
|
2699
|
+
} catch {
|
|
2700
|
+
return null;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
function getWebpDimensions(base64Data) {
|
|
2704
|
+
try {
|
|
2705
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
2706
|
+
if (buffer.length < 30) return null;
|
|
2707
|
+
const riff = buffer.slice(0, 4).toString("ascii");
|
|
2708
|
+
const webp = buffer.slice(8, 12).toString("ascii");
|
|
2709
|
+
if (riff !== "RIFF" || webp !== "WEBP") return null;
|
|
2710
|
+
const chunk = buffer.slice(12, 16).toString("ascii");
|
|
2711
|
+
if (chunk === "VP8 ") {
|
|
2712
|
+
if (buffer.length < 30) return null;
|
|
2713
|
+
return {
|
|
2714
|
+
widthPx: buffer.readUInt16LE(26) & 16383,
|
|
2715
|
+
heightPx: buffer.readUInt16LE(28) & 16383
|
|
2716
|
+
};
|
|
2717
|
+
} else if (chunk === "VP8L") {
|
|
2718
|
+
if (buffer.length < 25) return null;
|
|
2719
|
+
const bits = buffer.readUInt32LE(21);
|
|
2720
|
+
return {
|
|
2721
|
+
widthPx: (bits & 16383) + 1,
|
|
2722
|
+
heightPx: (bits >> 14 & 16383) + 1
|
|
2723
|
+
};
|
|
2724
|
+
} else if (chunk === "VP8X") {
|
|
2725
|
+
if (buffer.length < 30) return null;
|
|
2726
|
+
return {
|
|
2727
|
+
widthPx: (buffer[24] | buffer[25] << 8 | buffer[26] << 16) + 1,
|
|
2728
|
+
heightPx: (buffer[27] | buffer[28] << 8 | buffer[29] << 16) + 1
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
return null;
|
|
2732
|
+
} catch {
|
|
2733
|
+
return null;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
function getImageDimensions(base64Data, mimeType) {
|
|
2737
|
+
if (mimeType === "image/png") return getPngDimensions(base64Data);
|
|
2738
|
+
if (mimeType === "image/jpeg") return getJpegDimensions(base64Data);
|
|
2739
|
+
if (mimeType === "image/gif") return getGifDimensions(base64Data);
|
|
2740
|
+
if (mimeType === "image/webp") return getWebpDimensions(base64Data);
|
|
2741
|
+
return null;
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Wrap text in an OSC 8 hyperlink sequence.
|
|
2745
|
+
* The text is rendered as a clickable hyperlink in terminals that support OSC 8
|
|
2746
|
+
* (Ghostty, Kitty, WezTerm, iTerm2, VSCode, and others).
|
|
2747
|
+
* In terminals that do not support OSC 8, the escape sequences are ignored
|
|
2748
|
+
* and only the plain text is displayed.
|
|
2749
|
+
*
|
|
2750
|
+
* @param text - The visible text to display
|
|
2751
|
+
* @param url - The URL to link to
|
|
2752
|
+
*/
|
|
2753
|
+
function hyperlink(text, url) {
|
|
2754
|
+
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
2755
|
+
}
|
|
2756
|
+
/** Shorten home-prefixed absolute paths to ~/... for compact display. */
|
|
2757
|
+
function shortenImagePath(filename) {
|
|
2758
|
+
const home = homedir();
|
|
2759
|
+
if (home && (filename === home || filename.startsWith(`${home}/`) || filename.startsWith(`${home}\\`))) return `~${filename.slice(home.length)}`;
|
|
2760
|
+
return filename;
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* Text fallback when the terminal cannot render inline images.
|
|
2764
|
+
* Absolute paths are shown shortened (~/...) and, when OSC 8 hyperlinks are
|
|
2765
|
+
* available, linked to file:// so the full path remains openable.
|
|
2766
|
+
*/
|
|
2767
|
+
function imageFallback(mimeType, dimensions, filename) {
|
|
2768
|
+
const parts = [];
|
|
2769
|
+
if (filename) {
|
|
2770
|
+
const display = shortenImagePath(filename);
|
|
2771
|
+
if (getCapabilities().hyperlinks && isAbsolute(filename)) parts.push(hyperlink(display, pathToFileURL(filename).href));
|
|
2772
|
+
else parts.push(display);
|
|
2773
|
+
}
|
|
2774
|
+
parts.push(`[${mimeType}]`);
|
|
2775
|
+
if (dimensions) parts.push(`${dimensions.widthPx}x${dimensions.heightPx}`);
|
|
2776
|
+
return `[Image: ${parts.join(" ")}]`;
|
|
2777
|
+
}
|
|
2778
|
+
//#endregion
|
|
2779
|
+
//#region src/compat/vendor/pi-tui-autocomplete.ts
|
|
2780
|
+
const PATH_DELIMITERS = /* @__PURE__ */ new Set([
|
|
2781
|
+
" ",
|
|
2782
|
+
" ",
|
|
2783
|
+
"\"",
|
|
2784
|
+
"'",
|
|
2785
|
+
"="
|
|
2786
|
+
]);
|
|
2787
|
+
function toDisplayPath(value) {
|
|
2788
|
+
return value.replace(/\\/g, "/");
|
|
2789
|
+
}
|
|
2790
|
+
function escapeRegex(value) {
|
|
2791
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2792
|
+
}
|
|
2793
|
+
function buildFdPathQuery(query) {
|
|
2794
|
+
const normalized = toDisplayPath(query);
|
|
2795
|
+
if (!normalized.includes("/")) return normalized;
|
|
2796
|
+
const hasTrailingSeparator = normalized.endsWith("/");
|
|
2797
|
+
const trimmed = normalized.replace(/^\/+|\/+$/g, "");
|
|
2798
|
+
if (!trimmed) return normalized;
|
|
2799
|
+
const separatorPattern = "[\\\\/]";
|
|
2800
|
+
const segments = trimmed.split("/").filter(Boolean).map((segment) => escapeRegex(segment));
|
|
2801
|
+
if (segments.length === 0) return normalized;
|
|
2802
|
+
let pattern = segments.join(separatorPattern);
|
|
2803
|
+
if (hasTrailingSeparator) pattern += separatorPattern;
|
|
2804
|
+
return pattern;
|
|
2805
|
+
}
|
|
2806
|
+
function findLastDelimiter(text) {
|
|
2807
|
+
for (let i = text.length - 1; i >= 0; i -= 1) if (PATH_DELIMITERS.has(text[i] ?? "")) return i;
|
|
2808
|
+
return -1;
|
|
2809
|
+
}
|
|
2810
|
+
function findUnclosedQuoteStart(text) {
|
|
2811
|
+
let inQuotes = false;
|
|
2812
|
+
let quoteStart = -1;
|
|
2813
|
+
for (let i = 0; i < text.length; i += 1) if (text[i] === "\"") {
|
|
2814
|
+
inQuotes = !inQuotes;
|
|
2815
|
+
if (inQuotes) quoteStart = i;
|
|
2816
|
+
}
|
|
2817
|
+
return inQuotes ? quoteStart : null;
|
|
2818
|
+
}
|
|
2819
|
+
function isTokenStart(text, index) {
|
|
2820
|
+
return index === 0 || PATH_DELIMITERS.has(text[index - 1] ?? "");
|
|
2821
|
+
}
|
|
2822
|
+
function extractQuotedPrefix(text) {
|
|
2823
|
+
const quoteStart = findUnclosedQuoteStart(text);
|
|
2824
|
+
if (quoteStart === null) return null;
|
|
2825
|
+
if (quoteStart > 0 && text[quoteStart - 1] === "@") {
|
|
2826
|
+
if (!isTokenStart(text, quoteStart - 1)) return null;
|
|
2827
|
+
return text.slice(quoteStart - 1);
|
|
2828
|
+
}
|
|
2829
|
+
if (!isTokenStart(text, quoteStart)) return null;
|
|
2830
|
+
return text.slice(quoteStart);
|
|
2831
|
+
}
|
|
2832
|
+
function parsePathPrefix(prefix) {
|
|
2833
|
+
if (prefix.startsWith("@\"")) return {
|
|
2834
|
+
rawPrefix: prefix.slice(2),
|
|
2835
|
+
isAtPrefix: true,
|
|
2836
|
+
isQuotedPrefix: true
|
|
2837
|
+
};
|
|
2838
|
+
if (prefix.startsWith("\"")) return {
|
|
2839
|
+
rawPrefix: prefix.slice(1),
|
|
2840
|
+
isAtPrefix: false,
|
|
2841
|
+
isQuotedPrefix: true
|
|
2842
|
+
};
|
|
2843
|
+
if (prefix.startsWith("@")) return {
|
|
2844
|
+
rawPrefix: prefix.slice(1),
|
|
2845
|
+
isAtPrefix: true,
|
|
2846
|
+
isQuotedPrefix: false
|
|
2847
|
+
};
|
|
2848
|
+
return {
|
|
2849
|
+
rawPrefix: prefix,
|
|
2850
|
+
isAtPrefix: false,
|
|
2851
|
+
isQuotedPrefix: false
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
function buildCompletionValue(path, options) {
|
|
2855
|
+
const needsQuotes = options.isQuotedPrefix || path.includes(" ");
|
|
2856
|
+
const prefix = options.isAtPrefix ? "@" : "";
|
|
2857
|
+
if (!needsQuotes) return `${prefix}${path}`;
|
|
2858
|
+
return `${`${prefix}"`}${path}"`;
|
|
2859
|
+
}
|
|
2860
|
+
async function walkDirectoryWithFd(baseDir, fdPath, query, maxResults, signal) {
|
|
2861
|
+
const args = [
|
|
2862
|
+
"--base-directory",
|
|
2863
|
+
baseDir,
|
|
2864
|
+
"--max-results",
|
|
2865
|
+
String(maxResults),
|
|
2866
|
+
"--type",
|
|
2867
|
+
"f",
|
|
2868
|
+
"--type",
|
|
2869
|
+
"d",
|
|
2870
|
+
"--follow",
|
|
2871
|
+
"--hidden",
|
|
2872
|
+
"--exclude",
|
|
2873
|
+
".git",
|
|
2874
|
+
"--exclude",
|
|
2875
|
+
".git/*",
|
|
2876
|
+
"--exclude",
|
|
2877
|
+
".git/**"
|
|
2878
|
+
];
|
|
2879
|
+
if (toDisplayPath(query).includes("/")) args.push("--full-path");
|
|
2880
|
+
if (query) args.push(buildFdPathQuery(query));
|
|
2881
|
+
return await new Promise((resolve) => {
|
|
2882
|
+
if (signal.aborted) {
|
|
2883
|
+
resolve([]);
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
const child = spawn(fdPath, args, { stdio: [
|
|
2887
|
+
"ignore",
|
|
2888
|
+
"pipe",
|
|
2889
|
+
"pipe"
|
|
2890
|
+
] });
|
|
2891
|
+
let stdout = "";
|
|
2892
|
+
let resolved = false;
|
|
2893
|
+
const finish = (results) => {
|
|
2894
|
+
if (resolved) return;
|
|
2895
|
+
resolved = true;
|
|
2896
|
+
signal.removeEventListener("abort", onAbort);
|
|
2897
|
+
resolve(results);
|
|
2898
|
+
};
|
|
2899
|
+
const onAbort = () => {
|
|
2900
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
2901
|
+
};
|
|
2902
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2903
|
+
child.stdout.setEncoding("utf-8");
|
|
2904
|
+
child.stdout.on("data", (chunk) => {
|
|
2905
|
+
stdout += chunk;
|
|
2906
|
+
});
|
|
2907
|
+
child.on("error", () => {
|
|
2908
|
+
finish([]);
|
|
2909
|
+
});
|
|
2910
|
+
child.on("close", (code) => {
|
|
2911
|
+
if (signal.aborted || code !== 0 || !stdout) {
|
|
2912
|
+
finish([]);
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
2916
|
+
const results = [];
|
|
2917
|
+
for (const line of lines) {
|
|
2918
|
+
const displayLine = toDisplayPath(line);
|
|
2919
|
+
const hasTrailingSeparator = displayLine.endsWith("/");
|
|
2920
|
+
const normalizedPath = hasTrailingSeparator ? displayLine.slice(0, -1) : displayLine;
|
|
2921
|
+
if (normalizedPath === ".git" || normalizedPath.startsWith(".git/") || normalizedPath.includes("/.git/")) continue;
|
|
2922
|
+
results.push({
|
|
2923
|
+
path: displayLine,
|
|
2924
|
+
isDirectory: hasTrailingSeparator
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
finish(results);
|
|
2928
|
+
});
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
var CombinedAutocompleteProvider = class {
|
|
2932
|
+
commands;
|
|
2933
|
+
basePath;
|
|
2934
|
+
fdPath;
|
|
2935
|
+
constructor(commands = [], basePath, fdPath = null) {
|
|
2936
|
+
this.commands = commands;
|
|
2937
|
+
this.basePath = basePath;
|
|
2938
|
+
this.fdPath = fdPath;
|
|
2939
|
+
}
|
|
2940
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
2941
|
+
const textBeforeCursor = (lines[cursorLine] || "").slice(0, cursorCol);
|
|
2942
|
+
const atPrefix = this.extractAtPrefix(textBeforeCursor);
|
|
2943
|
+
if (atPrefix) {
|
|
2944
|
+
const { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
2945
|
+
const suggestions = await this.getFuzzyFileSuggestions(rawPrefix, {
|
|
2946
|
+
isQuotedPrefix,
|
|
2947
|
+
signal: options.signal
|
|
2948
|
+
});
|
|
2949
|
+
if (suggestions.length === 0) return null;
|
|
2950
|
+
return {
|
|
2951
|
+
items: suggestions,
|
|
2952
|
+
prefix: atPrefix
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
if (!options.force && textBeforeCursor.startsWith("/")) {
|
|
2956
|
+
const spaceIndex = textBeforeCursor.indexOf(" ");
|
|
2957
|
+
if (spaceIndex === -1) {
|
|
2958
|
+
const prefix = textBeforeCursor.slice(1);
|
|
2959
|
+
const filtered = fuzzyFilter(this.commands.map((cmd) => {
|
|
2960
|
+
const name = "name" in cmd ? cmd.name : cmd.value;
|
|
2961
|
+
const hint = "argumentHint" in cmd && cmd.argumentHint ? cmd.argumentHint : void 0;
|
|
2962
|
+
const desc = cmd.description ?? "";
|
|
2963
|
+
return {
|
|
2964
|
+
name,
|
|
2965
|
+
label: name,
|
|
2966
|
+
description: (hint ? desc ? `${hint} — ${desc}` : hint : desc) || void 0
|
|
2967
|
+
};
|
|
2968
|
+
}), prefix, (item) => item.name).map((item) => ({
|
|
2969
|
+
value: item.name,
|
|
2970
|
+
label: item.label,
|
|
2971
|
+
...item.description && { description: item.description }
|
|
2972
|
+
}));
|
|
2973
|
+
if (filtered.length === 0) return null;
|
|
2974
|
+
return {
|
|
2975
|
+
items: filtered,
|
|
2976
|
+
prefix: textBeforeCursor
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
const commandName = textBeforeCursor.slice(1, spaceIndex);
|
|
2980
|
+
const argumentText = textBeforeCursor.slice(spaceIndex + 1);
|
|
2981
|
+
const command = this.commands.find((cmd) => {
|
|
2982
|
+
return ("name" in cmd ? cmd.name : cmd.value) === commandName;
|
|
2983
|
+
});
|
|
2984
|
+
if (!command || !("getArgumentCompletions" in command) || !command.getArgumentCompletions) return null;
|
|
2985
|
+
const argumentSuggestions = await command.getArgumentCompletions(argumentText);
|
|
2986
|
+
if (!Array.isArray(argumentSuggestions) || argumentSuggestions.length === 0) return null;
|
|
2987
|
+
return {
|
|
2988
|
+
items: argumentSuggestions,
|
|
2989
|
+
prefix: argumentText
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
const pathMatch = this.extractPathPrefix(textBeforeCursor, options.force ?? false);
|
|
2993
|
+
if (pathMatch === null) return null;
|
|
2994
|
+
const suggestions = this.getFileSuggestions(pathMatch);
|
|
2995
|
+
if (suggestions.length === 0) return null;
|
|
2996
|
+
return {
|
|
2997
|
+
items: suggestions,
|
|
2998
|
+
prefix: pathMatch
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
3002
|
+
const currentLine = lines[cursorLine] || "";
|
|
3003
|
+
const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
|
|
3004
|
+
const afterCursor = currentLine.slice(cursorCol);
|
|
3005
|
+
const isQuotedPrefix = prefix.startsWith("\"") || prefix.startsWith("@\"");
|
|
3006
|
+
const hasLeadingQuoteAfterCursor = afterCursor.startsWith("\"");
|
|
3007
|
+
const hasTrailingQuoteInItem = item.value.endsWith("\"");
|
|
3008
|
+
const adjustedAfterCursor = isQuotedPrefix && hasTrailingQuoteInItem && hasLeadingQuoteAfterCursor ? afterCursor.slice(1) : afterCursor;
|
|
3009
|
+
if (prefix.startsWith("/") && beforePrefix.trim() === "" && !prefix.slice(1).includes("/")) {
|
|
3010
|
+
const newLine = `${beforePrefix}/${item.value} ${adjustedAfterCursor}`;
|
|
3011
|
+
const newLines = [...lines];
|
|
3012
|
+
newLines[cursorLine] = newLine;
|
|
3013
|
+
return {
|
|
3014
|
+
lines: newLines,
|
|
3015
|
+
cursorLine,
|
|
3016
|
+
cursorCol: beforePrefix.length + item.value.length + 2
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
if (prefix.startsWith("@")) {
|
|
3020
|
+
const isDirectory = item.label.endsWith("/");
|
|
3021
|
+
const suffix = isDirectory ? "" : " ";
|
|
3022
|
+
const newLine = `${beforePrefix + item.value}${suffix}${adjustedAfterCursor}`;
|
|
3023
|
+
const newLines = [...lines];
|
|
3024
|
+
newLines[cursorLine] = newLine;
|
|
3025
|
+
const hasTrailingQuote = item.value.endsWith("\"");
|
|
3026
|
+
const cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;
|
|
3027
|
+
return {
|
|
3028
|
+
lines: newLines,
|
|
3029
|
+
cursorLine,
|
|
3030
|
+
cursorCol: beforePrefix.length + cursorOffset + suffix.length
|
|
3031
|
+
};
|
|
3032
|
+
}
|
|
3033
|
+
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
3034
|
+
if (textBeforeCursor.includes("/") && textBeforeCursor.includes(" ")) {
|
|
3035
|
+
const newLine = beforePrefix + item.value + adjustedAfterCursor;
|
|
3036
|
+
const newLines = [...lines];
|
|
3037
|
+
newLines[cursorLine] = newLine;
|
|
3038
|
+
const isDirectory = item.label.endsWith("/");
|
|
3039
|
+
const hasTrailingQuote = item.value.endsWith("\"");
|
|
3040
|
+
const cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;
|
|
3041
|
+
return {
|
|
3042
|
+
lines: newLines,
|
|
3043
|
+
cursorLine,
|
|
3044
|
+
cursorCol: beforePrefix.length + cursorOffset
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
const newLine = beforePrefix + item.value + adjustedAfterCursor;
|
|
3048
|
+
const newLines = [...lines];
|
|
3049
|
+
newLines[cursorLine] = newLine;
|
|
3050
|
+
const isDirectory = item.label.endsWith("/");
|
|
3051
|
+
const hasTrailingQuote = item.value.endsWith("\"");
|
|
3052
|
+
const cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;
|
|
3053
|
+
return {
|
|
3054
|
+
lines: newLines,
|
|
3055
|
+
cursorLine,
|
|
3056
|
+
cursorCol: beforePrefix.length + cursorOffset
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
extractAtPrefix(text) {
|
|
3060
|
+
const quotedPrefix = extractQuotedPrefix(text);
|
|
3061
|
+
if (quotedPrefix?.startsWith("@\"")) return quotedPrefix;
|
|
3062
|
+
const lastDelimiterIndex = findLastDelimiter(text);
|
|
3063
|
+
const tokenStart = lastDelimiterIndex === -1 ? 0 : lastDelimiterIndex + 1;
|
|
3064
|
+
if (text[tokenStart] === "@") return text.slice(tokenStart);
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
extractPathPrefix(text, forceExtract = false) {
|
|
3068
|
+
const quotedPrefix = extractQuotedPrefix(text);
|
|
3069
|
+
if (quotedPrefix) return quotedPrefix;
|
|
3070
|
+
const lastDelimiterIndex = findLastDelimiter(text);
|
|
3071
|
+
const pathPrefix = lastDelimiterIndex === -1 ? text : text.slice(lastDelimiterIndex + 1);
|
|
3072
|
+
if (forceExtract) return pathPrefix;
|
|
3073
|
+
if (pathPrefix.includes("/") || pathPrefix.startsWith(".") || pathPrefix.startsWith("~/")) return pathPrefix;
|
|
3074
|
+
if (pathPrefix === "" && text.endsWith(" ")) return pathPrefix;
|
|
3075
|
+
return null;
|
|
3076
|
+
}
|
|
3077
|
+
expandHomePath(path) {
|
|
3078
|
+
if (path.startsWith("~/")) {
|
|
3079
|
+
const expandedPath = join$1(homedir$1(), path.slice(2));
|
|
3080
|
+
return path.endsWith("/") && !expandedPath.endsWith("/") ? `${expandedPath}/` : expandedPath;
|
|
3081
|
+
} else if (path === "~") return homedir$1();
|
|
3082
|
+
return path;
|
|
3083
|
+
}
|
|
3084
|
+
resolveScopedFuzzyQuery(rawQuery) {
|
|
3085
|
+
const normalizedQuery = toDisplayPath(rawQuery);
|
|
3086
|
+
const slashIndex = normalizedQuery.lastIndexOf("/");
|
|
3087
|
+
if (slashIndex === -1) return null;
|
|
3088
|
+
const displayBase = normalizedQuery.slice(0, slashIndex + 1);
|
|
3089
|
+
const query = normalizedQuery.slice(slashIndex + 1);
|
|
3090
|
+
let baseDir;
|
|
3091
|
+
if (displayBase.startsWith("~/")) baseDir = this.expandHomePath(displayBase);
|
|
3092
|
+
else if (displayBase.startsWith("/")) baseDir = displayBase;
|
|
3093
|
+
else baseDir = join$1(this.basePath, displayBase);
|
|
3094
|
+
try {
|
|
3095
|
+
if (!statSync(baseDir).isDirectory()) return null;
|
|
3096
|
+
} catch {
|
|
3097
|
+
return null;
|
|
3098
|
+
}
|
|
3099
|
+
return {
|
|
3100
|
+
baseDir,
|
|
3101
|
+
query,
|
|
3102
|
+
displayBase
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
scopedPathForDisplay(displayBase, relativePath) {
|
|
3106
|
+
const normalizedRelativePath = toDisplayPath(relativePath);
|
|
3107
|
+
if (displayBase === "/") return `/${normalizedRelativePath}`;
|
|
3108
|
+
return `${toDisplayPath(displayBase)}${normalizedRelativePath}`;
|
|
3109
|
+
}
|
|
3110
|
+
getFileSuggestions(prefix) {
|
|
3111
|
+
try {
|
|
3112
|
+
let searchDir;
|
|
3113
|
+
let searchPrefix;
|
|
3114
|
+
const { rawPrefix, isAtPrefix, isQuotedPrefix } = parsePathPrefix(prefix);
|
|
3115
|
+
let expandedPrefix = rawPrefix;
|
|
3116
|
+
if (expandedPrefix.startsWith("~")) expandedPrefix = this.expandHomePath(expandedPrefix);
|
|
3117
|
+
if (rawPrefix === "" || rawPrefix === "./" || rawPrefix === "../" || rawPrefix === "~" || rawPrefix === "~/" || rawPrefix === "/" || isAtPrefix && rawPrefix === "") {
|
|
3118
|
+
if (rawPrefix.startsWith("~") || expandedPrefix.startsWith("/")) searchDir = expandedPrefix;
|
|
3119
|
+
else searchDir = join$1(this.basePath, expandedPrefix);
|
|
3120
|
+
searchPrefix = "";
|
|
3121
|
+
} else if (rawPrefix.endsWith("/")) {
|
|
3122
|
+
if (rawPrefix.startsWith("~") || expandedPrefix.startsWith("/")) searchDir = expandedPrefix;
|
|
3123
|
+
else searchDir = join$1(this.basePath, expandedPrefix);
|
|
3124
|
+
searchPrefix = "";
|
|
3125
|
+
} else {
|
|
3126
|
+
const dir = dirname$1(expandedPrefix);
|
|
3127
|
+
const file = basename$1(expandedPrefix);
|
|
3128
|
+
if (rawPrefix.startsWith("~") || expandedPrefix.startsWith("/")) searchDir = dir;
|
|
3129
|
+
else searchDir = join$1(this.basePath, dir);
|
|
3130
|
+
searchPrefix = file;
|
|
3131
|
+
}
|
|
3132
|
+
const entries = readdirSync(searchDir, { withFileTypes: true });
|
|
3133
|
+
const suggestions = [];
|
|
3134
|
+
for (const entry of entries) {
|
|
3135
|
+
if (!entry.name.toLowerCase().startsWith(searchPrefix.toLowerCase())) continue;
|
|
3136
|
+
let isDirectory = entry.isDirectory();
|
|
3137
|
+
if (!isDirectory && entry.isSymbolicLink()) try {
|
|
3138
|
+
const fullPath = join$1(searchDir, entry.name);
|
|
3139
|
+
isDirectory = statSync(fullPath).isDirectory();
|
|
3140
|
+
} catch {}
|
|
3141
|
+
let relativePath;
|
|
3142
|
+
const name = entry.name;
|
|
3143
|
+
const displayPrefix = rawPrefix;
|
|
3144
|
+
if (displayPrefix.endsWith("/")) relativePath = displayPrefix + name;
|
|
3145
|
+
else if (displayPrefix.includes("/") || displayPrefix.includes("\\")) {
|
|
3146
|
+
if (displayPrefix.startsWith("~/")) {
|
|
3147
|
+
const homeRelativeDir = displayPrefix.slice(2);
|
|
3148
|
+
const dir = dirname$1(homeRelativeDir);
|
|
3149
|
+
relativePath = `~/${dir === "." ? name : join$1(dir, name)}`;
|
|
3150
|
+
} else if (displayPrefix.startsWith("/")) {
|
|
3151
|
+
const dir = dirname$1(displayPrefix);
|
|
3152
|
+
if (dir === "/") relativePath = `/${name}`;
|
|
3153
|
+
else relativePath = `${dir}/${name}`;
|
|
3154
|
+
} else {
|
|
3155
|
+
relativePath = join$1(dirname$1(displayPrefix), name);
|
|
3156
|
+
if (displayPrefix.startsWith("./") && !relativePath.startsWith("./")) relativePath = `./${relativePath}`;
|
|
3157
|
+
}
|
|
3158
|
+
} else if (displayPrefix.startsWith("~")) relativePath = `~/${name}`;
|
|
3159
|
+
else relativePath = name;
|
|
3160
|
+
relativePath = toDisplayPath(relativePath);
|
|
3161
|
+
const value = buildCompletionValue(isDirectory ? `${relativePath}/` : relativePath, {
|
|
3162
|
+
isDirectory,
|
|
3163
|
+
isAtPrefix,
|
|
3164
|
+
isQuotedPrefix
|
|
3165
|
+
});
|
|
3166
|
+
suggestions.push({
|
|
3167
|
+
value,
|
|
3168
|
+
label: name + (isDirectory ? "/" : "")
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
suggestions.sort((a, b) => {
|
|
3172
|
+
const aIsDir = a.value.endsWith("/");
|
|
3173
|
+
const bIsDir = b.value.endsWith("/");
|
|
3174
|
+
if (aIsDir && !bIsDir) return -1;
|
|
3175
|
+
if (!aIsDir && bIsDir) return 1;
|
|
3176
|
+
return a.label.localeCompare(b.label);
|
|
3177
|
+
});
|
|
3178
|
+
return suggestions;
|
|
3179
|
+
} catch (_e) {
|
|
3180
|
+
return [];
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
scoreEntry(filePath, query, isDirectory) {
|
|
3184
|
+
const lowerFileName = basename$1(filePath).toLowerCase();
|
|
3185
|
+
const lowerQuery = query.toLowerCase();
|
|
3186
|
+
let score = 0;
|
|
3187
|
+
if (lowerFileName === lowerQuery) score = 100;
|
|
3188
|
+
else if (lowerFileName.startsWith(lowerQuery)) score = 80;
|
|
3189
|
+
else if (lowerFileName.includes(lowerQuery)) score = 50;
|
|
3190
|
+
else if (filePath.toLowerCase().includes(lowerQuery)) score = 30;
|
|
3191
|
+
if (isDirectory && score > 0) score += 10;
|
|
3192
|
+
return score;
|
|
3193
|
+
}
|
|
3194
|
+
async getFuzzyFileSuggestions(query, options) {
|
|
3195
|
+
if (!this.fdPath || options.signal.aborted) return [];
|
|
3196
|
+
try {
|
|
3197
|
+
const scopedQuery = this.resolveScopedFuzzyQuery(query);
|
|
3198
|
+
const fdBaseDir = scopedQuery?.baseDir ?? this.basePath;
|
|
3199
|
+
const fdQuery = scopedQuery?.query ?? query;
|
|
3200
|
+
const entries = await walkDirectoryWithFd(fdBaseDir, this.fdPath, fdQuery, 100, options.signal);
|
|
3201
|
+
if (options.signal.aborted) return [];
|
|
3202
|
+
const scoredEntries = entries.map((entry) => ({
|
|
3203
|
+
...entry,
|
|
3204
|
+
score: fdQuery ? this.scoreEntry(entry.path, fdQuery, entry.isDirectory) : 1
|
|
3205
|
+
})).filter((entry) => entry.score > 0);
|
|
3206
|
+
scoredEntries.sort((a, b) => b.score - a.score);
|
|
3207
|
+
const topEntries = scoredEntries.slice(0, 20);
|
|
3208
|
+
const suggestions = [];
|
|
3209
|
+
for (const { path: entryPath, isDirectory } of topEntries) {
|
|
3210
|
+
const pathWithoutSlash = isDirectory ? entryPath.slice(0, -1) : entryPath;
|
|
3211
|
+
const displayPath = scopedQuery ? this.scopedPathForDisplay(scopedQuery.displayBase, pathWithoutSlash) : pathWithoutSlash;
|
|
3212
|
+
const entryName = basename$1(pathWithoutSlash);
|
|
3213
|
+
const value = buildCompletionValue(isDirectory ? `${displayPath}/` : displayPath, {
|
|
3214
|
+
isDirectory,
|
|
3215
|
+
isAtPrefix: true,
|
|
3216
|
+
isQuotedPrefix: options.isQuotedPrefix
|
|
3217
|
+
});
|
|
3218
|
+
suggestions.push({
|
|
3219
|
+
value,
|
|
3220
|
+
label: entryName + (isDirectory ? "/" : ""),
|
|
3221
|
+
description: displayPath
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
return suggestions;
|
|
3225
|
+
} catch {
|
|
3226
|
+
return [];
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
|
|
3230
|
+
const textBeforeCursor = (lines[cursorLine] || "").slice(0, cursorCol);
|
|
3231
|
+
if (textBeforeCursor.trim().startsWith("/") && !textBeforeCursor.trim().includes(" ")) return false;
|
|
3232
|
+
return true;
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
//#endregion
|
|
3236
|
+
//#region src/compat/pi-tui.ts
|
|
3237
|
+
const CURSOR_MARKER = "\x1B_pi:c\x07";
|
|
3238
|
+
function isFocusable(value) {
|
|
3239
|
+
return typeof value === "object" && value !== null && "focused" in value;
|
|
3240
|
+
}
|
|
3241
|
+
function blankLines(count) {
|
|
3242
|
+
return Array.from({ length: Math.max(0, count) }, () => "");
|
|
3243
|
+
}
|
|
3244
|
+
var Container = class {
|
|
3245
|
+
children = [];
|
|
3246
|
+
addChild(child) {
|
|
3247
|
+
this.children.push(child);
|
|
3248
|
+
}
|
|
3249
|
+
removeChild(child) {
|
|
3250
|
+
this.children = this.children.filter((candidate) => candidate !== child);
|
|
3251
|
+
}
|
|
3252
|
+
clear() {
|
|
3253
|
+
this.children = [];
|
|
3254
|
+
}
|
|
3255
|
+
invalidate() {
|
|
3256
|
+
for (const child of this.children) child.invalidate();
|
|
3257
|
+
}
|
|
3258
|
+
render(width) {
|
|
3259
|
+
return this.children.flatMap((child) => child.render(width));
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
var Text = class {
|
|
3263
|
+
text;
|
|
3264
|
+
paddingX;
|
|
3265
|
+
paddingY;
|
|
3266
|
+
customBgFn;
|
|
3267
|
+
constructor(text = "", paddingX = 1, paddingY = 1, customBgFn) {
|
|
3268
|
+
this.text = text;
|
|
3269
|
+
this.paddingX = paddingX;
|
|
3270
|
+
this.paddingY = paddingY;
|
|
3271
|
+
this.customBgFn = customBgFn;
|
|
3272
|
+
}
|
|
3273
|
+
setText(text) {
|
|
3274
|
+
this.text = text;
|
|
3275
|
+
}
|
|
3276
|
+
setCustomBgFn(customBgFn) {
|
|
3277
|
+
this.customBgFn = customBgFn;
|
|
3278
|
+
}
|
|
3279
|
+
invalidate() {}
|
|
3280
|
+
render(width) {
|
|
3281
|
+
const pad = " ".repeat(Math.max(0, this.paddingX));
|
|
3282
|
+
const inner = Math.max(1, width - this.paddingX * 2);
|
|
3283
|
+
const rendered = this.text.split("\n").flatMap((line) => wrapTextWithAnsi(line, inner)).map((line) => {
|
|
3284
|
+
const value = `${pad}${line}${pad}`;
|
|
3285
|
+
return this.customBgFn === void 0 ? value : this.customBgFn(value);
|
|
3286
|
+
});
|
|
3287
|
+
return [
|
|
3288
|
+
...blankLines(this.paddingY),
|
|
3289
|
+
...rendered,
|
|
3290
|
+
...blankLines(this.paddingY)
|
|
3291
|
+
];
|
|
3292
|
+
}
|
|
3293
|
+
};
|
|
3294
|
+
var TruncatedText = class {
|
|
3295
|
+
text;
|
|
3296
|
+
paddingX;
|
|
3297
|
+
paddingY;
|
|
3298
|
+
constructor(text, paddingX = 0, paddingY = 0) {
|
|
3299
|
+
this.text = text;
|
|
3300
|
+
this.paddingX = paddingX;
|
|
3301
|
+
this.paddingY = paddingY;
|
|
3302
|
+
}
|
|
3303
|
+
invalidate() {}
|
|
3304
|
+
render(width) {
|
|
3305
|
+
const pad = " ".repeat(Math.max(0, this.paddingX));
|
|
3306
|
+
const inner = Math.max(1, width - this.paddingX * 2);
|
|
3307
|
+
const lines = this.text.split("\n").map((line) => `${pad}${truncateToWidth(line, inner)}${pad}`);
|
|
3308
|
+
return [
|
|
3309
|
+
...blankLines(this.paddingY),
|
|
3310
|
+
...lines,
|
|
3311
|
+
...blankLines(this.paddingY)
|
|
3312
|
+
];
|
|
3313
|
+
}
|
|
3314
|
+
};
|
|
3315
|
+
var Spacer = class {
|
|
3316
|
+
lines;
|
|
3317
|
+
constructor(lines = 1) {
|
|
3318
|
+
this.lines = lines;
|
|
3319
|
+
}
|
|
3320
|
+
setLines(lines) {
|
|
3321
|
+
this.lines = lines;
|
|
3322
|
+
}
|
|
3323
|
+
invalidate() {}
|
|
3324
|
+
render(_width) {
|
|
3325
|
+
return blankLines(this.lines);
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
var Box = class {
|
|
3329
|
+
paddingX;
|
|
3330
|
+
paddingY;
|
|
3331
|
+
children = [];
|
|
3332
|
+
bgFn;
|
|
3333
|
+
constructor(paddingX = 1, paddingY = 1, bgFn) {
|
|
3334
|
+
this.paddingX = paddingX;
|
|
3335
|
+
this.paddingY = paddingY;
|
|
3336
|
+
this.bgFn = bgFn;
|
|
3337
|
+
}
|
|
3338
|
+
addChild(component) {
|
|
3339
|
+
this.children.push(component);
|
|
3340
|
+
}
|
|
3341
|
+
removeChild(component) {
|
|
3342
|
+
this.children = this.children.filter((candidate) => candidate !== component);
|
|
3343
|
+
}
|
|
3344
|
+
clear() {
|
|
3345
|
+
this.children = [];
|
|
3346
|
+
}
|
|
3347
|
+
setBgFn(bgFn) {
|
|
3348
|
+
this.bgFn = bgFn;
|
|
3349
|
+
}
|
|
3350
|
+
invalidate() {
|
|
3351
|
+
for (const child of this.children) child.invalidate();
|
|
3352
|
+
}
|
|
3353
|
+
render(width) {
|
|
3354
|
+
const pad = " ".repeat(Math.max(0, this.paddingX));
|
|
3355
|
+
const inner = Math.max(1, width - this.paddingX * 2);
|
|
3356
|
+
const lines = this.children.flatMap((child) => child.render(inner));
|
|
3357
|
+
const rendered = [
|
|
3358
|
+
...blankLines(this.paddingY),
|
|
3359
|
+
...lines.map((line) => `${pad}${line}${pad}`),
|
|
3360
|
+
...blankLines(this.paddingY)
|
|
3361
|
+
];
|
|
3362
|
+
return this.bgFn === void 0 ? rendered : rendered.map((line) => this.bgFn(line));
|
|
3363
|
+
}
|
|
3364
|
+
};
|
|
3365
|
+
var Markdown = class {
|
|
3366
|
+
text;
|
|
3367
|
+
paddingX;
|
|
3368
|
+
paddingY;
|
|
3369
|
+
theme;
|
|
3370
|
+
options;
|
|
3371
|
+
constructor(text = "", paddingX = 1, paddingY = 1, theme, options) {
|
|
3372
|
+
this.text = text;
|
|
3373
|
+
this.paddingX = paddingX;
|
|
3374
|
+
this.paddingY = paddingY;
|
|
3375
|
+
this.theme = theme;
|
|
3376
|
+
this.options = options;
|
|
3377
|
+
}
|
|
3378
|
+
setText(text) {
|
|
3379
|
+
this.text = text;
|
|
3380
|
+
}
|
|
3381
|
+
invalidate() {}
|
|
3382
|
+
render(width) {
|
|
3383
|
+
const pad = " ".repeat(Math.max(0, this.paddingX));
|
|
3384
|
+
const inner = Math.max(1, width - this.paddingX * 2);
|
|
3385
|
+
const lines = this.text.split("\n").flatMap((line) => wrapTextWithAnsi(line, inner));
|
|
3386
|
+
return [
|
|
3387
|
+
...blankLines(this.paddingY),
|
|
3388
|
+
...lines.map((line) => `${pad}${line}${pad}`),
|
|
3389
|
+
...blankLines(this.paddingY)
|
|
3390
|
+
];
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
var SelectList = class {
|
|
3394
|
+
items;
|
|
3395
|
+
maxVisible;
|
|
3396
|
+
theme;
|
|
3397
|
+
layout;
|
|
3398
|
+
onSelect;
|
|
3399
|
+
onCancel;
|
|
3400
|
+
onSelectionChange;
|
|
3401
|
+
filtered;
|
|
3402
|
+
selectedIndex = 0;
|
|
3403
|
+
constructor(items, maxVisible, theme, layout = {}) {
|
|
3404
|
+
this.items = items;
|
|
3405
|
+
this.maxVisible = maxVisible;
|
|
3406
|
+
this.theme = theme;
|
|
3407
|
+
this.layout = layout;
|
|
3408
|
+
this.filtered = [...items];
|
|
3409
|
+
}
|
|
3410
|
+
setFilter(filter) {
|
|
3411
|
+
const query = filter.toLowerCase();
|
|
3412
|
+
this.filtered = this.items.filter((item) => item.label.toLowerCase().includes(query) || item.value.toLowerCase().includes(query));
|
|
3413
|
+
this.selectedIndex = 0;
|
|
3414
|
+
}
|
|
3415
|
+
setSelectedIndex(index) {
|
|
3416
|
+
this.selectedIndex = Math.max(0, Math.min(index, this.filtered.length - 1));
|
|
3417
|
+
}
|
|
3418
|
+
getSelectedItem() {
|
|
3419
|
+
return this.filtered[this.selectedIndex] ?? null;
|
|
3420
|
+
}
|
|
3421
|
+
invalidate() {}
|
|
3422
|
+
handleInput(_keyData) {}
|
|
3423
|
+
render(width) {
|
|
3424
|
+
return this.filtered.slice(0, this.maxVisible).map((item, index) => truncateToWidth(`${index === this.selectedIndex ? "→ " : " "}${item.label}`, Math.max(1, width)));
|
|
3425
|
+
}
|
|
3426
|
+
};
|
|
3427
|
+
var SettingsList = class {
|
|
3428
|
+
items;
|
|
3429
|
+
maxVisible;
|
|
3430
|
+
theme;
|
|
3431
|
+
onChange;
|
|
3432
|
+
onCancel;
|
|
3433
|
+
options;
|
|
3434
|
+
constructor(items, maxVisible, theme, onChange, onCancel, options = {}) {
|
|
3435
|
+
this.items = items;
|
|
3436
|
+
this.maxVisible = maxVisible;
|
|
3437
|
+
this.theme = theme;
|
|
3438
|
+
this.onChange = onChange;
|
|
3439
|
+
this.onCancel = onCancel;
|
|
3440
|
+
this.options = options;
|
|
3441
|
+
}
|
|
3442
|
+
updateValue(id, newValue) {
|
|
3443
|
+
const item = this.items.find((candidate) => candidate.id === id);
|
|
3444
|
+
if (item !== void 0) item.currentValue = newValue;
|
|
3445
|
+
}
|
|
3446
|
+
invalidate() {}
|
|
3447
|
+
handleInput(_data) {}
|
|
3448
|
+
render(width) {
|
|
3449
|
+
return this.items.slice(0, this.maxVisible).map((item) => truncateToWidth(`${item.label}: ${item.currentValue}`, Math.max(1, width)));
|
|
3450
|
+
}
|
|
3451
|
+
};
|
|
3452
|
+
var Input = class {
|
|
3453
|
+
focused = false;
|
|
3454
|
+
onSubmit;
|
|
3455
|
+
onEscape;
|
|
3456
|
+
value = "";
|
|
3457
|
+
getValue() {
|
|
3458
|
+
return this.value;
|
|
3459
|
+
}
|
|
3460
|
+
setValue(value) {
|
|
3461
|
+
this.value = value;
|
|
3462
|
+
}
|
|
3463
|
+
handleInput(data) {
|
|
3464
|
+
if (data === "\r" || data === "\n") {
|
|
3465
|
+
this.onSubmit?.(this.value);
|
|
3466
|
+
return;
|
|
3467
|
+
}
|
|
3468
|
+
if (data === "\x1B") {
|
|
3469
|
+
this.onEscape?.();
|
|
3470
|
+
return;
|
|
3471
|
+
}
|
|
3472
|
+
if (data >= " ") this.value += data;
|
|
3473
|
+
}
|
|
3474
|
+
invalidate() {}
|
|
3475
|
+
render(width) {
|
|
3476
|
+
return [truncateToWidth(this.value, Math.max(1, width))];
|
|
3477
|
+
}
|
|
3478
|
+
};
|
|
3479
|
+
var Editor = class {
|
|
3480
|
+
tui;
|
|
3481
|
+
theme;
|
|
3482
|
+
options;
|
|
3483
|
+
focused = false;
|
|
3484
|
+
borderColor = (str) => str;
|
|
3485
|
+
onSubmit;
|
|
3486
|
+
onChange;
|
|
3487
|
+
disableSubmit = false;
|
|
3488
|
+
text = "";
|
|
3489
|
+
history = [];
|
|
3490
|
+
autocompleteProvider;
|
|
3491
|
+
constructor(tui, theme = {}, options = {}) {
|
|
3492
|
+
this.tui = tui;
|
|
3493
|
+
this.theme = theme;
|
|
3494
|
+
this.options = options;
|
|
3495
|
+
}
|
|
3496
|
+
getText() {
|
|
3497
|
+
return this.text;
|
|
3498
|
+
}
|
|
3499
|
+
setText(text) {
|
|
3500
|
+
this.text = text;
|
|
3501
|
+
this.onChange?.(text);
|
|
3502
|
+
}
|
|
3503
|
+
getExpandedText() {
|
|
3504
|
+
return this.text;
|
|
3505
|
+
}
|
|
3506
|
+
insertTextAtCursor(text) {
|
|
3507
|
+
this.setText(this.text + text);
|
|
3508
|
+
}
|
|
3509
|
+
addToHistory(text) {
|
|
3510
|
+
this.history.push(text);
|
|
3511
|
+
}
|
|
3512
|
+
setAutocompleteProvider(provider) {
|
|
3513
|
+
this.autocompleteProvider = provider;
|
|
3514
|
+
}
|
|
3515
|
+
getPaddingX() {
|
|
3516
|
+
return this.options.paddingX ?? 0;
|
|
3517
|
+
}
|
|
3518
|
+
setPaddingX(padding) {
|
|
3519
|
+
this.options.paddingX = padding;
|
|
3520
|
+
}
|
|
3521
|
+
getAutocompleteMaxVisible() {
|
|
3522
|
+
return this.options.autocompleteMaxVisible ?? 5;
|
|
3523
|
+
}
|
|
3524
|
+
setAutocompleteMaxVisible(maxVisible) {
|
|
3525
|
+
this.options.autocompleteMaxVisible = maxVisible;
|
|
3526
|
+
}
|
|
3527
|
+
handleInput(data) {
|
|
3528
|
+
if (data === "\r" || data === "\n") {
|
|
3529
|
+
if (!this.disableSubmit) this.onSubmit?.(this.text);
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
if (data >= " ") this.setText(this.text + data);
|
|
3533
|
+
}
|
|
3534
|
+
invalidate() {}
|
|
3535
|
+
render(width) {
|
|
3536
|
+
return this.text.split("\n").flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
var Stack = class extends Container {
|
|
3540
|
+
options;
|
|
3541
|
+
constructor(children = [], options = {}) {
|
|
3542
|
+
super();
|
|
3543
|
+
this.options = options;
|
|
3544
|
+
for (const child of children) this.addChild("component" in child ? child.component : child);
|
|
3545
|
+
}
|
|
3546
|
+
};
|
|
3547
|
+
var VStack = class extends Stack {};
|
|
3548
|
+
var HStack = class extends Stack {
|
|
3549
|
+
render(width) {
|
|
3550
|
+
const columns = this.children.map((child) => child.render(width));
|
|
3551
|
+
const height = Math.max(0, ...columns.map((column) => column.length));
|
|
3552
|
+
const lines = [];
|
|
3553
|
+
for (let row = 0; row < height; row += 1) lines.push(columns.map((column) => column[row] ?? "").join(" "));
|
|
3554
|
+
return lines;
|
|
3555
|
+
}
|
|
3556
|
+
};
|
|
3557
|
+
var Loader = class extends Text {
|
|
3558
|
+
constructor(message = "", indicator) {
|
|
3559
|
+
super(message, 0, 0);
|
|
3560
|
+
}
|
|
3561
|
+
start() {}
|
|
3562
|
+
stop() {}
|
|
3563
|
+
setMessage(message) {
|
|
3564
|
+
this.setText(message);
|
|
3565
|
+
}
|
|
3566
|
+
setIndicator(_indicator) {}
|
|
3567
|
+
};
|
|
3568
|
+
var CancellableLoader = class extends Loader {
|
|
3569
|
+
onCancel;
|
|
3570
|
+
handleInput(data) {
|
|
3571
|
+
if (data === "\x1B" || data === "") this.onCancel?.();
|
|
3572
|
+
}
|
|
3573
|
+
dispose() {}
|
|
3574
|
+
};
|
|
3575
|
+
var Image = class {
|
|
3576
|
+
base64Data;
|
|
3577
|
+
mimeType;
|
|
3578
|
+
options;
|
|
3579
|
+
constructor(base64Data = "", mimeType = "image/png", options = {}) {
|
|
3580
|
+
this.base64Data = base64Data;
|
|
3581
|
+
this.mimeType = mimeType;
|
|
3582
|
+
this.options = options;
|
|
3583
|
+
}
|
|
3584
|
+
getImageId() {}
|
|
3585
|
+
invalidate() {}
|
|
3586
|
+
render(width) {
|
|
3587
|
+
return [truncateToWidth(`[image ${this.mimeType}]`, Math.max(1, width))];
|
|
3588
|
+
}
|
|
3589
|
+
};
|
|
3590
|
+
var ScrollView = class extends Container {
|
|
3591
|
+
options;
|
|
3592
|
+
scrollTop = 0;
|
|
3593
|
+
constructor(component, options = {}) {
|
|
3594
|
+
super();
|
|
3595
|
+
this.options = options;
|
|
3596
|
+
this.addChild(component);
|
|
3597
|
+
}
|
|
3598
|
+
setScrollbar(_scrollbar) {}
|
|
3599
|
+
getContentWidth(width) {
|
|
3600
|
+
return Math.max(1, width - 1);
|
|
3601
|
+
}
|
|
3602
|
+
setScrollbarActive(_active) {}
|
|
3603
|
+
scrollTo(scrollTop, _options = {}) {
|
|
3604
|
+
this.scrollTop = Math.max(0, scrollTop);
|
|
3605
|
+
}
|
|
3606
|
+
scrollBy(lines) {
|
|
3607
|
+
this.scrollTop = Math.max(0, this.scrollTop + lines);
|
|
3608
|
+
return this.scrollTop;
|
|
3609
|
+
}
|
|
3610
|
+
scrollToStart() {
|
|
3611
|
+
this.scrollTop = 0;
|
|
3612
|
+
}
|
|
3613
|
+
scrollToEnd() {}
|
|
3614
|
+
updateLayout(_contentHeight, _viewportHeight, _requestRender) {}
|
|
3615
|
+
};
|
|
3616
|
+
function isViewportTUI(value) {
|
|
3617
|
+
return false;
|
|
3618
|
+
}
|
|
3619
|
+
//#endregion
|
|
3620
|
+
export { Box, CURSOR_MARKER, CancellableLoader, CombinedAutocompleteProvider, Container, Editor, HStack, Image, Input, Key, KeybindingsManager, Loader, Markdown, Marked, PUNCTUATION_REGEX, ScrollView, SelectList, SettingsList, Spacer, TUI_KEYBINDINGS, Text, TruncatedText, VStack, allocateImageId, applyBackgroundToLine, calculateImageRows, cjkBreakRegex, decodeKittyPrintable, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, extractAnsiCode, fuzzyFilter, fuzzyMatch, getCapabilities, getCellDimensions, getGifDimensions, getGraphemeCellRange, getGraphemeSegmenter, getImageDimensions, getJpegDimensions, getKeybindings, getOsc8LinkAtColumn, getPngDimensions, getWebpDimensions, getWordSegmenter, hyperlink, imageFallback, isFocusable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, isPunctuationChar, isViewportTUI, isWhitespaceChar, matchesKey, normalizeTerminalOutput, parseKey, parseOsc11BackgroundColor, parseTerminalColorSchemeReport, renderLatex, resetCapabilitiesCache, setCapabilities, setCellDimensions, setKeybindings, setKittyProtocolActive, sliceByColumn, sliceWithWidth, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi };
|
|
3621
|
+
|
|
3622
|
+
//# sourceMappingURL=pi-tui.mjs.map
|