@earendil-works/pi-tui 0.83.0 → 0.84.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.
Files changed (90) hide show
  1. package/README.md +77 -14
  2. package/dist/components/alt-screen-flash.d.ts +13 -0
  3. package/dist/components/alt-screen-flash.d.ts.map +1 -0
  4. package/dist/components/alt-screen-flash.js +37 -0
  5. package/dist/components/alt-screen-flash.js.map +1 -0
  6. package/dist/components/editor.d.ts.map +1 -1
  7. package/dist/components/editor.js +11 -0
  8. package/dist/components/editor.js.map +1 -1
  9. package/dist/components/h-stack.d.ts +7 -0
  10. package/dist/components/h-stack.d.ts.map +1 -0
  11. package/dist/components/h-stack.js +43 -0
  12. package/dist/components/h-stack.js.map +1 -0
  13. package/dist/components/markdown.d.ts +4 -0
  14. package/dist/components/markdown.d.ts.map +1 -1
  15. package/dist/components/markdown.js +128 -2
  16. package/dist/components/markdown.js.map +1 -1
  17. package/dist/components/scroll-view.d.ts +51 -0
  18. package/dist/components/scroll-view.d.ts.map +1 -0
  19. package/dist/components/scroll-view.js +173 -0
  20. package/dist/components/scroll-view.js.map +1 -0
  21. package/dist/components/settings-list.d.ts.map +1 -1
  22. package/dist/components/settings-list.js +3 -6
  23. package/dist/components/settings-list.js.map +1 -1
  24. package/dist/components/stack.d.ts +32 -0
  25. package/dist/components/stack.d.ts.map +1 -0
  26. package/dist/components/stack.js +111 -0
  27. package/dist/components/stack.js.map +1 -0
  28. package/dist/components/v-stack.d.ts +8 -0
  29. package/dist/components/v-stack.d.ts.map +1 -0
  30. package/dist/components/v-stack.js +26 -0
  31. package/dist/components/v-stack.js.map +1 -0
  32. package/dist/index.d.ts +9 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +10 -2
  35. package/dist/index.js.map +1 -1
  36. package/dist/keybindings.d.ts +44 -4
  37. package/dist/keybindings.d.ts.map +1 -1
  38. package/dist/keybindings.js +31 -4
  39. package/dist/keybindings.js.map +1 -1
  40. package/dist/latex.d.ts +10 -0
  41. package/dist/latex.d.ts.map +1 -0
  42. package/dist/latex.js +1124 -0
  43. package/dist/latex.js.map +1 -0
  44. package/dist/layout-node.d.ts +40 -0
  45. package/dist/layout-node.d.ts.map +1 -0
  46. package/dist/layout-node.js +6 -0
  47. package/dist/layout-node.js.map +1 -0
  48. package/dist/layout.d.ts +40 -0
  49. package/dist/layout.d.ts.map +1 -0
  50. package/dist/layout.js +314 -0
  51. package/dist/layout.js.map +1 -0
  52. package/dist/native-modifiers.d.ts.map +1 -1
  53. package/dist/native-modifiers.js +10 -3
  54. package/dist/native-modifiers.js.map +1 -1
  55. package/dist/terminal-colors.d.ts.map +1 -1
  56. package/dist/terminal-colors.js +1 -1
  57. package/dist/terminal-colors.js.map +1 -1
  58. package/dist/terminal-image.d.ts +21 -0
  59. package/dist/terminal-image.d.ts.map +1 -1
  60. package/dist/terminal-image.js +128 -3
  61. package/dist/terminal-image.js.map +1 -1
  62. package/dist/terminal.d.ts +1 -0
  63. package/dist/terminal.d.ts.map +1 -1
  64. package/dist/terminal.js +10 -7
  65. package/dist/terminal.js.map +1 -1
  66. package/dist/tui-alt-screen.d.ts +85 -0
  67. package/dist/tui-alt-screen.d.ts.map +1 -0
  68. package/dist/tui-alt-screen.js +768 -0
  69. package/dist/tui-alt-screen.js.map +1 -0
  70. package/dist/tui-main-screen.d.ts +39 -0
  71. package/dist/tui-main-screen.d.ts.map +1 -0
  72. package/dist/tui-main-screen.js +533 -0
  73. package/dist/tui-main-screen.js.map +1 -0
  74. package/dist/tui.d.ts +75 -38
  75. package/dist/tui.d.ts.map +1 -1
  76. package/dist/tui.js +98 -573
  77. package/dist/tui.js.map +1 -1
  78. package/dist/utils.d.ts +11 -0
  79. package/dist/utils.d.ts.map +1 -1
  80. package/dist/utils.js +119 -6
  81. package/dist/utils.js.map +1 -1
  82. package/native/darwin/README.md +20 -0
  83. package/native/darwin/build.sh +62 -0
  84. package/native/darwin/src/darwin-modifiers.c +70 -0
  85. package/native/win32/README.md +22 -0
  86. package/native/win32/build.mjs +229 -0
  87. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  88. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  89. package/native/win32/src/win32-console-mode.c +119 -0
  90. package/package.json +9 -1
package/dist/latex.js ADDED
@@ -0,0 +1,1124 @@
1
+ import { visibleWidth } from "./utils.js";
2
+ const SYMBOLS = {
3
+ alpha: "α",
4
+ beta: "β",
5
+ gamma: "γ",
6
+ delta: "δ",
7
+ epsilon: "ϵ",
8
+ varepsilon: "ε",
9
+ zeta: "ζ",
10
+ eta: "η",
11
+ theta: "θ",
12
+ vartheta: "ϑ",
13
+ iota: "ι",
14
+ kappa: "κ",
15
+ varkappa: "ϰ",
16
+ lambda: "λ",
17
+ mu: "μ",
18
+ nu: "ν",
19
+ xi: "ξ",
20
+ pi: "π",
21
+ varpi: "ϖ",
22
+ rho: "ρ",
23
+ varrho: "ϱ",
24
+ sigma: "σ",
25
+ varsigma: "ς",
26
+ tau: "τ",
27
+ upsilon: "υ",
28
+ phi: "ϕ",
29
+ varphi: "φ",
30
+ chi: "χ",
31
+ psi: "ψ",
32
+ omega: "ω",
33
+ Gamma: "Γ",
34
+ Delta: "Δ",
35
+ Theta: "Θ",
36
+ Lambda: "Λ",
37
+ Xi: "Ξ",
38
+ Pi: "Π",
39
+ Sigma: "Σ",
40
+ Upsilon: "Υ",
41
+ Phi: "Φ",
42
+ Psi: "Ψ",
43
+ Omega: "Ω",
44
+ pm: "±",
45
+ mp: "∓",
46
+ times: "×",
47
+ div: "÷",
48
+ cdot: "·",
49
+ ast: "∗",
50
+ star: "⋆",
51
+ circ: "∘",
52
+ bullet: "•",
53
+ oplus: "⊕",
54
+ ominus: "⊖",
55
+ otimes: "⊗",
56
+ oslash: "⊘",
57
+ odot: "⊙",
58
+ bigcirc: "○",
59
+ dagger: "†",
60
+ ddagger: "‡",
61
+ amalg: "⨿",
62
+ uplus: "⊎",
63
+ sqcap: "⊓",
64
+ sqcup: "⊔",
65
+ triangleleft: "◁",
66
+ triangleright: "▷",
67
+ wr: "≀",
68
+ cap: "∩",
69
+ cup: "∪",
70
+ bigcap: "⋂",
71
+ bigcup: "⋃",
72
+ bigwedge: "⋀",
73
+ bigvee: "⋁",
74
+ bigsqcup: "⨆",
75
+ biguplus: "⨄",
76
+ bigoplus: "⨁",
77
+ bigotimes: "⨂",
78
+ bigodot: "⨀",
79
+ setminus: "∖",
80
+ in: "∈",
81
+ notin: "∉",
82
+ ni: "∋",
83
+ subset: "⊂",
84
+ supset: "⊃",
85
+ subseteq: "⊆",
86
+ supseteq: "⊇",
87
+ sqsubset: "⊏",
88
+ sqsupset: "⊐",
89
+ sqsubseteq: "⊑",
90
+ sqsupseteq: "⊒",
91
+ prec: "≺",
92
+ preceq: "≼",
93
+ succ: "≻",
94
+ succeq: "≽",
95
+ ll: "≪",
96
+ gg: "≫",
97
+ le: "≤",
98
+ leq: "≤",
99
+ leqslant: "≤",
100
+ ge: "≥",
101
+ geq: "≥",
102
+ geqslant: "≥",
103
+ ne: "≠",
104
+ neq: "≠",
105
+ equiv: "≡",
106
+ approx: "≈",
107
+ sim: "∼",
108
+ simeq: "≃",
109
+ cong: "≅",
110
+ asymp: "≍",
111
+ doteq: "≐",
112
+ propto: "∝",
113
+ parallel: "∥",
114
+ perp: "⊥",
115
+ mid: "∣",
116
+ vdash: "⊢",
117
+ dashv: "⊣",
118
+ models: "⊨",
119
+ Vdash: "⊩",
120
+ Vvdash: "⊪",
121
+ nvdash: "⊬",
122
+ nvDash: "⊭",
123
+ forall: "∀",
124
+ exists: "∃",
125
+ nexists: "∄",
126
+ neg: "¬",
127
+ land: "∧",
128
+ wedge: "∧",
129
+ lor: "∨",
130
+ vee: "∨",
131
+ to: "→",
132
+ rightarrow: "→",
133
+ longrightarrow: "→",
134
+ leftarrow: "←",
135
+ longleftarrow: "←",
136
+ gets: "←",
137
+ leftrightarrow: "↔",
138
+ longleftrightarrow: "↔",
139
+ hookleftarrow: "↩",
140
+ hookrightarrow: "↪",
141
+ twoheadleftarrow: "↞",
142
+ twoheadrightarrow: "↠",
143
+ leftharpoonup: "↼",
144
+ leftharpoondown: "↽",
145
+ rightharpoonup: "⇀",
146
+ rightharpoondown: "⇁",
147
+ rightleftharpoons: "⇌",
148
+ leftrightharpoons: "⇋",
149
+ nearrow: "↗",
150
+ searrow: "↘",
151
+ swarrow: "↙",
152
+ nwarrow: "↖",
153
+ rightsquigarrow: "⇝",
154
+ leadsto: "⇝",
155
+ Rightarrow: "⇒",
156
+ Longrightarrow: "⇒",
157
+ Leftarrow: "⇐",
158
+ Longleftarrow: "⇐",
159
+ Leftrightarrow: "⇔",
160
+ Longleftrightarrow: "⇔",
161
+ implies: "⇒",
162
+ iff: "⇔",
163
+ mapsto: "↦",
164
+ longmapsto: "↦",
165
+ uparrow: "↑",
166
+ downarrow: "↓",
167
+ partial: "∂",
168
+ nabla: "∇",
169
+ int: "∫",
170
+ iint: "∬",
171
+ iiint: "∭",
172
+ oint: "∮",
173
+ sum: "∑",
174
+ prod: "∏",
175
+ coprod: "∐",
176
+ infty: "∞",
177
+ emptyset: "∅",
178
+ varnothing: "∅",
179
+ angle: "∠",
180
+ therefore: "∴",
181
+ because: "∵",
182
+ aleph: "ℵ",
183
+ beth: "ℶ",
184
+ gimel: "ℷ",
185
+ daleth: "ℸ",
186
+ top: "⊤",
187
+ bot: "⊥",
188
+ triangle: "△",
189
+ square: "□",
190
+ lozenge: "◊",
191
+ checkmark: "✓",
192
+ complement: "∁",
193
+ wp: "℘",
194
+ prime: "′",
195
+ ldots: "…",
196
+ dots: "…",
197
+ cdots: "⋯",
198
+ vdots: "⋮",
199
+ ddots: "⋱",
200
+ ell: "ℓ",
201
+ hbar: "ℏ",
202
+ Im: "ℑ",
203
+ Re: "ℜ",
204
+ langle: "⟨",
205
+ rangle: "⟩",
206
+ vert: "|",
207
+ lvert: "|",
208
+ rvert: "|",
209
+ Vert: "‖",
210
+ lVert: "‖",
211
+ rVert: "‖",
212
+ lbrace: "{",
213
+ rbrace: "}",
214
+ backslash: "\\",
215
+ lfloor: "⌊",
216
+ rfloor: "⌋",
217
+ lceil: "⌈",
218
+ rceil: "⌉",
219
+ colon: ":",
220
+ };
221
+ const NAMED_OPERATORS = new Set([
222
+ "arccos",
223
+ "arcsin",
224
+ "arctan",
225
+ "arg",
226
+ "cos",
227
+ "cosh",
228
+ "cot",
229
+ "coth",
230
+ "csc",
231
+ "deg",
232
+ "det",
233
+ "dim",
234
+ "exp",
235
+ "gcd",
236
+ "hom",
237
+ "inf",
238
+ "ker",
239
+ "lg",
240
+ "lim",
241
+ "liminf",
242
+ "limsup",
243
+ "ln",
244
+ "log",
245
+ "max",
246
+ "min",
247
+ "Pr",
248
+ "sec",
249
+ "sin",
250
+ "sinh",
251
+ "sup",
252
+ "tan",
253
+ "tanh",
254
+ ]);
255
+ const LIMIT_OPERATORS = new Set([
256
+ "argmax",
257
+ "argmin",
258
+ "inf",
259
+ "injlim",
260
+ "lim",
261
+ "liminf",
262
+ "limsup",
263
+ "max",
264
+ "min",
265
+ "projlim",
266
+ "sup",
267
+ ]);
268
+ const DISPLAY_LIMIT_SYMBOLS = new Set([
269
+ "bigcap",
270
+ "bigcup",
271
+ "bigodot",
272
+ "bigoplus",
273
+ "bigotimes",
274
+ "bigsqcup",
275
+ "biguplus",
276
+ "bigvee",
277
+ "bigwedge",
278
+ "coprod",
279
+ "int",
280
+ "iint",
281
+ "iiint",
282
+ "oint",
283
+ "prod",
284
+ "sum",
285
+ ]);
286
+ const NEGATED_SYMBOLS = {
287
+ "<": "≮",
288
+ ">": "≯",
289
+ "=": "≠",
290
+ "∈": "∉",
291
+ "∋": "∌",
292
+ "∣": "∤",
293
+ "∥": "∦",
294
+ "∼": "≁",
295
+ "≃": "≄",
296
+ "≅": "≇",
297
+ "≈": "≉",
298
+ "≡": "≢",
299
+ "≤": "≰",
300
+ "≥": "≱",
301
+ "≺": "⊀",
302
+ "≻": "⊁",
303
+ "⊂": "⊄",
304
+ "⊃": "⊅",
305
+ "⊆": "⊈",
306
+ "⊇": "⊉",
307
+ "⊢": "⊬",
308
+ "⊨": "⊭",
309
+ "↔": "↮",
310
+ "←": "↚",
311
+ "→": "↛",
312
+ "⇒": "⇏",
313
+ "⇐": "⇍",
314
+ "⇔": "⇎",
315
+ "≼": "⋠",
316
+ "≽": "⋡",
317
+ };
318
+ const BLACKBOARD = {
319
+ C: "ℂ",
320
+ H: "ℍ",
321
+ N: "ℕ",
322
+ P: "ℙ",
323
+ Q: "ℚ",
324
+ R: "ℝ",
325
+ Z: "ℤ",
326
+ };
327
+ const SUPERSCRIPTS = {
328
+ "0": "⁰",
329
+ "1": "¹",
330
+ "2": "²",
331
+ "3": "³",
332
+ "4": "⁴",
333
+ "5": "⁵",
334
+ "6": "⁶",
335
+ "7": "⁷",
336
+ "8": "⁸",
337
+ "9": "⁹",
338
+ "+": "⁺",
339
+ "-": "⁻",
340
+ "=": "⁼",
341
+ "(": "⁽",
342
+ ")": "⁾",
343
+ a: "ᵃ",
344
+ b: "ᵇ",
345
+ c: "ᶜ",
346
+ d: "ᵈ",
347
+ e: "ᵉ",
348
+ f: "ᶠ",
349
+ g: "ᵍ",
350
+ h: "ʰ",
351
+ i: "ⁱ",
352
+ j: "ʲ",
353
+ k: "ᵏ",
354
+ l: "ˡ",
355
+ m: "ᵐ",
356
+ n: "ⁿ",
357
+ o: "ᵒ",
358
+ p: "ᵖ",
359
+ r: "ʳ",
360
+ s: "ˢ",
361
+ t: "ᵗ",
362
+ u: "ᵘ",
363
+ v: "ᵛ",
364
+ w: "ʷ",
365
+ x: "ˣ",
366
+ y: "ʸ",
367
+ z: "ᶻ",
368
+ };
369
+ const SUBSCRIPTS = {
370
+ "0": "₀",
371
+ "1": "₁",
372
+ "2": "₂",
373
+ "3": "₃",
374
+ "4": "₄",
375
+ "5": "₅",
376
+ "6": "₆",
377
+ "7": "₇",
378
+ "8": "₈",
379
+ "9": "₉",
380
+ "+": "₊",
381
+ "-": "₋",
382
+ "=": "₌",
383
+ "(": "₍",
384
+ ")": "₎",
385
+ a: "ₐ",
386
+ e: "ₑ",
387
+ h: "ₕ",
388
+ i: "ᵢ",
389
+ j: "ⱼ",
390
+ k: "ₖ",
391
+ l: "ₗ",
392
+ m: "ₘ",
393
+ n: "ₙ",
394
+ o: "ₒ",
395
+ p: "ₚ",
396
+ r: "ᵣ",
397
+ s: "ₛ",
398
+ t: "ₜ",
399
+ u: "ᵤ",
400
+ v: "ᵥ",
401
+ x: "ₓ",
402
+ };
403
+ const SPACING_COMMANDS = new Set([
404
+ ",",
405
+ ":",
406
+ ";",
407
+ " ",
408
+ ">",
409
+ "enspace",
410
+ "enskip",
411
+ "medspace",
412
+ "quad",
413
+ "qquad",
414
+ "thickspace",
415
+ "thinspace",
416
+ ]);
417
+ const NEGATIVE_SPACING_COMMANDS = new Set(["!", "negmedspace", "negthickspace", "negthinspace"]);
418
+ const NEGATIVE_SPACE = "\u0000";
419
+ const IGNORED_COMMANDS = new Set([
420
+ "displaystyle",
421
+ "limits",
422
+ "nolimits",
423
+ "scriptstyle",
424
+ "scriptscriptstyle",
425
+ "textstyle",
426
+ ]);
427
+ const SIZE_COMMANDS = new Set([
428
+ "big",
429
+ "Big",
430
+ "bigg",
431
+ "Bigg",
432
+ "bigl",
433
+ "Bigl",
434
+ "biggl",
435
+ "Biggl",
436
+ "bigr",
437
+ "Bigr",
438
+ "biggr",
439
+ "Biggr",
440
+ ]);
441
+ const PLAIN_WRAPPERS = new Set([
442
+ "emph",
443
+ "mathcal",
444
+ "mathbf",
445
+ "mathfrak",
446
+ "mathit",
447
+ "mathrm",
448
+ "mathnormal",
449
+ "mathscr",
450
+ "mathsf",
451
+ "mathtt",
452
+ "mathup",
453
+ "mbox",
454
+ "overbrace",
455
+ "pmb",
456
+ "smash",
457
+ "substack",
458
+ "text",
459
+ "textbf",
460
+ "textit",
461
+ "textmd",
462
+ "textnormal",
463
+ "textrm",
464
+ "textsc",
465
+ "textsf",
466
+ "textsl",
467
+ "texttt",
468
+ "textup",
469
+ "underbrace",
470
+ "bm",
471
+ "boldsymbol",
472
+ ]);
473
+ const ACCENTS = {
474
+ acute: "\u0301",
475
+ bar: "\u0305",
476
+ breve: "\u0306",
477
+ check: "\u030c",
478
+ ddot: "\u0308",
479
+ dot: "\u0307",
480
+ grave: "\u0300",
481
+ hat: "\u0302",
482
+ mathring: "\u030a",
483
+ overleftarrow: "\u20d6",
484
+ overleftrightarrow: "\u20e1",
485
+ overline: "\u0305",
486
+ overrightarrow: "\u20d7",
487
+ tilde: "\u0303",
488
+ underline: "\u0332",
489
+ vec: "\u20d7",
490
+ widehat: "\u0302",
491
+ widetilde: "\u0303",
492
+ };
493
+ function replaceCharacters(value, replacements) {
494
+ let result = "";
495
+ for (const character of value) {
496
+ const replacement = replacements[character];
497
+ if (replacement === undefined) {
498
+ return undefined;
499
+ }
500
+ result += replacement;
501
+ }
502
+ return result;
503
+ }
504
+ function formatScript(value, kind) {
505
+ value = value.trim();
506
+ const replacements = kind === "sub" ? SUBSCRIPTS : SUPERSCRIPTS;
507
+ const unicode = replaceCharacters(value, replacements);
508
+ if (unicode !== undefined) {
509
+ return unicode;
510
+ }
511
+ const prefix = kind === "sub" ? "_" : "^";
512
+ if (Array.from(value).length === 1 || (kind === "sub" && /^[A-Za-z]+$/.test(value))) {
513
+ return `${prefix}${value}`;
514
+ }
515
+ return `${prefix}(${value})`;
516
+ }
517
+ function formatFraction(numerator, denominator) {
518
+ numerator = numerator.trim();
519
+ denominator = denominator.trim();
520
+ const simpleNumerator = /^[\p{L}\p{N}.]+$/u.test(numerator);
521
+ const simpleDenominator = /^[\p{N}.]+$/u.test(denominator) || Array.from(denominator).length === 1;
522
+ return `${simpleNumerator ? numerator : `(${numerator})`}/${simpleDenominator ? denominator : `(${denominator})`}`;
523
+ }
524
+ function formatRoot(value, symbol = "√") {
525
+ value = value.trim();
526
+ return /^[\p{L}\p{N}.]+$/u.test(value) ? `${symbol}${value}` : `${symbol}(${value})`;
527
+ }
528
+ function normalizeOutput(value) {
529
+ return value
530
+ .split("\n")
531
+ .map((line) => line.replace(/[ \t]+/g, " ").trim())
532
+ .filter((line, index, lines) => line.length > 0 || (index > 0 && index < lines.length - 1))
533
+ .join("\n")
534
+ .trim();
535
+ }
536
+ const LAYOUT_MARKER_START = "\u{f0000}";
537
+ const LAYOUT_MARKER_END = "\u{f0001}";
538
+ const LAYOUT_MARKER_PATTERN = /\u{f0000}(\d+)\u{f0001}/gu;
539
+ const PROTECTED_SPACE = "\u00a0";
540
+ function padLayoutLine(line, width, centered = false) {
541
+ const padding = Math.max(0, width - visibleWidth(line));
542
+ const left = centered ? Math.floor(padding / 2) : 0;
543
+ return `${" ".repeat(left)}${line}${" ".repeat(padding - left)}`;
544
+ }
545
+ function joinLayouts(layouts) {
546
+ if (layouts.length === 0) {
547
+ return { lines: [""], width: 0, baseline: 0 };
548
+ }
549
+ const baseline = Math.max(...layouts.map((layout) => layout.baseline));
550
+ const below = Math.max(...layouts.map((layout) => layout.lines.length - layout.baseline - 1));
551
+ const lines = [];
552
+ for (let row = 0; row <= baseline + below; row++) {
553
+ let line = "";
554
+ for (const layout of layouts) {
555
+ const sourceRow = row - baseline + layout.baseline;
556
+ line +=
557
+ sourceRow >= 0 && sourceRow < layout.lines.length
558
+ ? padLayoutLine(layout.lines[sourceRow] ?? "", layout.width)
559
+ : " ".repeat(layout.width);
560
+ }
561
+ lines.push(line.trimEnd());
562
+ }
563
+ return {
564
+ lines,
565
+ width: layouts.reduce((width, layout) => width + layout.width, 0),
566
+ baseline,
567
+ };
568
+ }
569
+ function renderLayout(source, nodes) {
570
+ const renderedLines = [];
571
+ let firstBaseline = 0;
572
+ for (const sourceLine of source.split("\n")) {
573
+ const layouts = [];
574
+ let position = 0;
575
+ let previousWasNode = false;
576
+ for (const match of sourceLine.matchAll(LAYOUT_MARKER_PATTERN)) {
577
+ const index = match.index;
578
+ if (index > position) {
579
+ const sliced = sourceLine.slice(position, index);
580
+ const text = (previousWasNode ? sliced.trimStart() : sliced).trimEnd();
581
+ layouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });
582
+ }
583
+ const node = nodes[Number(match[1])];
584
+ if (!node) {
585
+ continue;
586
+ }
587
+ if (node.type === "fraction") {
588
+ const numerator = renderLayout(node.numerator, nodes);
589
+ const denominator = renderLayout(node.denominator, nodes);
590
+ const contentWidth = Math.max(numerator.width, denominator.width, 1);
591
+ const width = contentWidth + 2;
592
+ layouts.push({
593
+ lines: [
594
+ ...numerator.lines.map((line) => padLayoutLine(line, width, true)),
595
+ ` ${"─".repeat(contentWidth)} `,
596
+ ...denominator.lines.map((line) => padLayoutLine(line, width, true)),
597
+ ],
598
+ width,
599
+ baseline: numerator.lines.length,
600
+ });
601
+ }
602
+ else {
603
+ const contentWidth = Math.max(visibleWidth(node.operator), node.lower === undefined ? 0 : visibleWidth(node.lower), node.upper === undefined ? 0 : visibleWidth(node.upper));
604
+ const lines = [];
605
+ if (node.upper !== undefined) {
606
+ lines.push(`${padLayoutLine(node.upper, contentWidth, true)} `);
607
+ }
608
+ lines.push(`${padLayoutLine(node.operator, contentWidth, true)} `);
609
+ if (node.lower !== undefined) {
610
+ lines.push(`${padLayoutLine(node.lower, contentWidth, true)} `);
611
+ }
612
+ layouts.push({
613
+ lines,
614
+ width: contentWidth + 1,
615
+ baseline: node.upper === undefined ? 0 : 1,
616
+ });
617
+ }
618
+ position = index + match[0].length;
619
+ previousWasNode = true;
620
+ }
621
+ if (position < sourceLine.length) {
622
+ const sliced = sourceLine.slice(position);
623
+ const text = previousWasNode ? sliced.trimStart() : sliced;
624
+ layouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });
625
+ }
626
+ const lineLayout = joinLayouts(layouts);
627
+ if (renderedLines.length === 0) {
628
+ firstBaseline = lineLayout.baseline;
629
+ }
630
+ renderedLines.push(...lineLayout.lines);
631
+ }
632
+ return {
633
+ lines: renderedLines,
634
+ width: Math.max(0, ...renderedLines.map((line) => visibleWidth(line))),
635
+ baseline: firstBaseline,
636
+ };
637
+ }
638
+ class LatexParser {
639
+ source;
640
+ layoutNodes;
641
+ position = 0;
642
+ supported = true;
643
+ stackFractions = true;
644
+ constructor(source, layoutNodes) {
645
+ this.source = source;
646
+ this.layoutNodes = layoutNodes;
647
+ }
648
+ render() {
649
+ const rendered = this.parseSequence();
650
+ if (!this.supported || this.position !== this.source.length) {
651
+ return undefined;
652
+ }
653
+ return normalizeOutput(rendered);
654
+ }
655
+ parseSequence(endCharacter) {
656
+ let result = "";
657
+ while (this.position < this.source.length) {
658
+ const character = this.source[this.position];
659
+ if (endCharacter && character === endCharacter) {
660
+ this.position++;
661
+ return result;
662
+ }
663
+ if (character === "}") {
664
+ this.supported = false;
665
+ return result;
666
+ }
667
+ if (character === "{") {
668
+ this.position++;
669
+ result += this.parseSequence("}");
670
+ continue;
671
+ }
672
+ if (character === "\\") {
673
+ const command = this.parseCommand();
674
+ if (command === NEGATIVE_SPACE) {
675
+ result = result.trimEnd();
676
+ }
677
+ else {
678
+ result += command;
679
+ }
680
+ continue;
681
+ }
682
+ if (character === "^" || character === "_") {
683
+ this.position++;
684
+ result = result.trimEnd();
685
+ result += formatScript(this.parseRequiredArgument(false), character === "_" ? "sub" : "sup");
686
+ continue;
687
+ }
688
+ if (/\s/.test(character)) {
689
+ result += this.parseWhitespace();
690
+ continue;
691
+ }
692
+ if (character === "&") {
693
+ this.position++;
694
+ continue;
695
+ }
696
+ if (character === "~") {
697
+ this.position++;
698
+ result += " ";
699
+ continue;
700
+ }
701
+ result += character;
702
+ this.position++;
703
+ }
704
+ if (endCharacter) {
705
+ this.supported = false;
706
+ }
707
+ return result;
708
+ }
709
+ parseWhitespace() {
710
+ while (this.position < this.source.length && /\s/.test(this.source[this.position] ?? "")) {
711
+ this.position++;
712
+ }
713
+ return " ";
714
+ }
715
+ parseCommand() {
716
+ this.position++;
717
+ if (this.position >= this.source.length) {
718
+ this.supported = false;
719
+ return "";
720
+ }
721
+ let command = "";
722
+ const first = this.source[this.position] ?? "";
723
+ if (/[A-Za-z]/.test(first)) {
724
+ const start = this.position;
725
+ while (this.position < this.source.length && /[A-Za-z]/.test(this.source[this.position] ?? "")) {
726
+ this.position++;
727
+ }
728
+ command = this.source.slice(start, this.position);
729
+ }
730
+ else {
731
+ command = first;
732
+ this.position++;
733
+ }
734
+ if (command === "\\") {
735
+ return "\n";
736
+ }
737
+ if (SPACING_COMMANDS.has(command)) {
738
+ return " ";
739
+ }
740
+ if (NEGATIVE_SPACING_COMMANDS.has(command)) {
741
+ return NEGATIVE_SPACE;
742
+ }
743
+ if (IGNORED_COMMANDS.has(command)) {
744
+ return "";
745
+ }
746
+ if (command === "{" ||
747
+ command === "}" ||
748
+ command === "$" ||
749
+ command === "%" ||
750
+ command === "#" ||
751
+ command === "_" ||
752
+ command === "&") {
753
+ return command;
754
+ }
755
+ if (command === "|") {
756
+ return "‖";
757
+ }
758
+ if (command === "not") {
759
+ const value = this.parseRequiredArgument(false).trim();
760
+ const negated = NEGATED_SYMBOLS[value];
761
+ if (negated !== undefined) {
762
+ return negated;
763
+ }
764
+ const characters = Array.from(value);
765
+ if (characters.length === 0) {
766
+ this.supported = false;
767
+ return "";
768
+ }
769
+ return `${characters[0]}\u0338${characters.slice(1).join("")}`;
770
+ }
771
+ if (LIMIT_OPERATORS.has(command)) {
772
+ return this.parseOperator(command, "bracket", true, true);
773
+ }
774
+ const symbol = SYMBOLS[command];
775
+ if (symbol !== undefined) {
776
+ return DISPLAY_LIMIT_SYMBOLS.has(command) ? this.parseOperator(symbol, "script", true) : symbol;
777
+ }
778
+ if (NAMED_OPERATORS.has(command)) {
779
+ return ` ${command} `;
780
+ }
781
+ if (SIZE_COMMANDS.has(command)) {
782
+ return "";
783
+ }
784
+ if (command === "left" || command === "middle" || command === "right") {
785
+ if (this.source[this.position] === ".") {
786
+ this.position++;
787
+ }
788
+ return "";
789
+ }
790
+ if (command === "frac" || command === "dfrac" || command === "tfrac") {
791
+ const shouldStack = this.layoutNodes !== undefined && this.stackFractions && command !== "tfrac";
792
+ const numerator = this.parseRequiredArgument(!shouldStack);
793
+ const denominator = this.parseRequiredArgument(!shouldStack);
794
+ if (shouldStack && this.layoutNodes) {
795
+ const index = this.layoutNodes.push({
796
+ type: "fraction",
797
+ numerator: normalizeOutput(numerator),
798
+ denominator: normalizeOutput(denominator),
799
+ }) - 1;
800
+ return `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;
801
+ }
802
+ return formatFraction(numerator, denominator);
803
+ }
804
+ if (command === "sqrt") {
805
+ const degree = this.parseOptionalArgument()?.trim();
806
+ const value = this.parseRequiredArgument();
807
+ if (degree === undefined || degree === "2") {
808
+ return formatRoot(value);
809
+ }
810
+ if (degree === "3") {
811
+ return formatRoot(value, "∛");
812
+ }
813
+ if (degree === "4") {
814
+ return formatRoot(value, "∜");
815
+ }
816
+ return `${formatScript(degree, "sup")}${formatRoot(value)}`;
817
+ }
818
+ if (command === "boxed" || command === "fbox") {
819
+ return `[${this.parseRequiredArgument().trim()}]`;
820
+ }
821
+ if (command === "binom" || command === "dbinom" || command === "tbinom") {
822
+ return `(${this.parseRequiredArgument()} choose ${this.parseRequiredArgument()})`;
823
+ }
824
+ const accent = ACCENTS[command];
825
+ if (accent !== undefined) {
826
+ const value = this.parseRequiredArgument();
827
+ return Array.from(value).length === 1 ? `${value}${accent}` : `${command}(${value})`;
828
+ }
829
+ if (command === "mathbb") {
830
+ const value = this.parseRequiredArgument();
831
+ return Array.from(value, (character) => BLACKBOARD[character] ?? character).join("");
832
+ }
833
+ if (command === "operatorname") {
834
+ const starred = this.source[this.position] === "*";
835
+ if (starred) {
836
+ this.position++;
837
+ }
838
+ const operator = normalizeOutput(this.parseRequiredArgument()).trim();
839
+ return this.parseOperator(operator, "bracket", starred, true);
840
+ }
841
+ if (command === "mod" || command === "bmod") {
842
+ return " mod ";
843
+ }
844
+ if (command === "pmod" || command === "pod") {
845
+ const value = this.parseRequiredArgument().trim();
846
+ return command === "pmod" ? ` (mod ${value})` : ` (${value})`;
847
+ }
848
+ if (command === "overset" || command === "stackrel") {
849
+ const upper = this.parseRequiredArgument();
850
+ const value = this.parseRequiredArgument().trim();
851
+ return `${value}${formatScript(upper, "sup")}`;
852
+ }
853
+ if (command === "underset") {
854
+ const lower = this.parseRequiredArgument();
855
+ const value = this.parseRequiredArgument().trim();
856
+ return `${value}${formatScript(lower, "sub")}`;
857
+ }
858
+ if (PLAIN_WRAPPERS.has(command)) {
859
+ const value = this.parseRequiredArgument();
860
+ return command.startsWith("text") || command === "mbox" ? value : value.trim();
861
+ }
862
+ if (command === "begin") {
863
+ return this.parseEnvironment();
864
+ }
865
+ if (command === "end") {
866
+ this.supported = false;
867
+ return "";
868
+ }
869
+ this.supported = false;
870
+ return `\\${command}`;
871
+ }
872
+ parseOperator(operator, inlineLowerStyle, displayLimits, spaced = false) {
873
+ let useDisplayLimits = displayLimits;
874
+ let modifierPosition = this.position;
875
+ while (modifierPosition < this.source.length && /[ \t]/.test(this.source[modifierPosition] ?? "")) {
876
+ modifierPosition++;
877
+ }
878
+ const modifier = /^\\(limits|nolimits)(?![A-Za-z])/.exec(this.source.slice(modifierPosition));
879
+ if (modifier) {
880
+ useDisplayLimits = modifier[1] === "limits";
881
+ this.position = modifierPosition + modifier[0].length;
882
+ }
883
+ let lower;
884
+ let upper;
885
+ while (true) {
886
+ let scriptPosition = this.position;
887
+ while (scriptPosition < this.source.length && /[ \t]/.test(this.source[scriptPosition] ?? "")) {
888
+ scriptPosition++;
889
+ }
890
+ const kind = this.source[scriptPosition];
891
+ if (kind !== "_" && kind !== "^") {
892
+ break;
893
+ }
894
+ this.position = scriptPosition + 1;
895
+ const value = normalizeOutput(this.parseRequiredArgument(false)).replaceAll(" ", "");
896
+ if (kind === "_") {
897
+ if (lower !== undefined) {
898
+ this.supported = false;
899
+ }
900
+ lower = value;
901
+ }
902
+ else {
903
+ if (upper !== undefined) {
904
+ this.supported = false;
905
+ }
906
+ upper = value;
907
+ }
908
+ }
909
+ if (this.layoutNodes && useDisplayLimits && (lower !== undefined || upper !== undefined)) {
910
+ const index = this.layoutNodes.push({ type: "operator", operator, lower, upper }) - 1;
911
+ return `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;
912
+ }
913
+ let rendered = operator;
914
+ if (lower !== undefined) {
915
+ rendered += inlineLowerStyle === "bracket" ? `[${lower}]` : formatScript(lower, "sub");
916
+ }
917
+ if (upper !== undefined) {
918
+ rendered += formatScript(upper, "sup");
919
+ }
920
+ return spaced ? ` ${rendered} ` : rendered;
921
+ }
922
+ parseRequiredArgument(stackFractions = true) {
923
+ const previousStackFractions = this.stackFractions;
924
+ this.stackFractions = previousStackFractions && stackFractions;
925
+ const value = this.parseRequiredArgumentValue();
926
+ this.stackFractions = previousStackFractions;
927
+ return value;
928
+ }
929
+ parseRequiredArgumentValue() {
930
+ while (this.position < this.source.length && /[ \t]/.test(this.source[this.position] ?? "")) {
931
+ this.position++;
932
+ }
933
+ if (this.position >= this.source.length) {
934
+ this.supported = false;
935
+ return "";
936
+ }
937
+ if (this.source[this.position] === "{") {
938
+ this.position++;
939
+ return this.parseSequence("}");
940
+ }
941
+ if (this.source[this.position] === "\\") {
942
+ return this.parseCommand();
943
+ }
944
+ const value = this.source[this.position] ?? "";
945
+ this.position++;
946
+ return value;
947
+ }
948
+ parseOptionalArgument() {
949
+ while (this.position < this.source.length && /[ \t]/.test(this.source[this.position] ?? "")) {
950
+ this.position++;
951
+ }
952
+ if (this.source[this.position] !== "[") {
953
+ return undefined;
954
+ }
955
+ const end = this.source.indexOf("]", this.position + 1);
956
+ if (end < 0) {
957
+ this.supported = false;
958
+ return undefined;
959
+ }
960
+ const value = this.source.slice(this.position + 1, end);
961
+ this.position = end + 1;
962
+ return this.renderNested(value);
963
+ }
964
+ readRawGroup() {
965
+ while (this.position < this.source.length && /[ \t]/.test(this.source[this.position] ?? "")) {
966
+ this.position++;
967
+ }
968
+ if (this.source[this.position] !== "{") {
969
+ this.supported = false;
970
+ return undefined;
971
+ }
972
+ const start = ++this.position;
973
+ let depth = 1;
974
+ while (this.position < this.source.length) {
975
+ const character = this.source[this.position];
976
+ if (character === "\\") {
977
+ this.position += 2;
978
+ continue;
979
+ }
980
+ if (character === "{")
981
+ depth++;
982
+ if (character === "}")
983
+ depth--;
984
+ if (depth === 0) {
985
+ const value = this.source.slice(start, this.position);
986
+ this.position++;
987
+ return value;
988
+ }
989
+ this.position++;
990
+ }
991
+ this.supported = false;
992
+ return undefined;
993
+ }
994
+ splitEnvironmentRows(body) {
995
+ return body.split(/\\\\(?:\[[^\]\n]*\])?/);
996
+ }
997
+ parseEnvironment() {
998
+ const environment = this.readRawGroup();
999
+ if (!environment) {
1000
+ return "";
1001
+ }
1002
+ const endMarker = `\\end{${environment}}`;
1003
+ const end = this.source.indexOf(endMarker, this.position);
1004
+ if (end < 0) {
1005
+ this.supported = false;
1006
+ return "";
1007
+ }
1008
+ const body = this.source.slice(this.position, end);
1009
+ this.position = end + endMarker.length;
1010
+ if (environment === "equation" || environment === "equation*" || environment === "displaymath") {
1011
+ return this.renderNested(body).trim();
1012
+ }
1013
+ if (environment === "aligned" ||
1014
+ environment === "align" ||
1015
+ environment === "align*" ||
1016
+ environment === "alignedat" ||
1017
+ environment === "alignat" ||
1018
+ environment === "alignat*" ||
1019
+ environment === "gather" ||
1020
+ environment === "gathered" ||
1021
+ environment === "multline" ||
1022
+ environment === "multline*" ||
1023
+ environment === "split") {
1024
+ const alignedAt = ["alignedat", "alignat", "alignat*"].includes(environment);
1025
+ const alignedBody = alignedAt ? body.replace(/^\s*\{[^}]*\}/, "") : body;
1026
+ return this.splitEnvironmentRows(alignedBody)
1027
+ .map((row) => {
1028
+ const cells = row.split("&");
1029
+ const source = alignedAt
1030
+ ? Array.from({ length: Math.ceil(cells.length / 2) }, (_, index) => cells.slice(index * 2, index * 2 + 2).join("")).join(" ")
1031
+ : cells.join("");
1032
+ return this.renderNested(source).trim();
1033
+ })
1034
+ .filter(Boolean)
1035
+ .join("\n");
1036
+ }
1037
+ if (environment === "cases" || environment === "cases*") {
1038
+ const rows = this.splitEnvironmentRows(body)
1039
+ .map((row) => row.split("&").map((cell) => this.renderNested(cell, false).trim()))
1040
+ .filter((row) => row.some(Boolean));
1041
+ return rows
1042
+ .map((row, index) => {
1043
+ const value = (row[0] ?? "").replace(/,\s*$/, "");
1044
+ const condition = row[1] ?? "";
1045
+ const delimiter = index === 0 ? "⎧" : index === rows.length - 1 ? "⎩" : "⎨";
1046
+ const conditionPrefix = /^(?:if|when|for|otherwise)\b/i.test(condition) ? " " : " if ";
1047
+ return `${delimiter} ${value}${condition ? `${conditionPrefix}${condition}` : ""}`;
1048
+ })
1049
+ .join("\n");
1050
+ }
1051
+ if (["array", "matrix", "smallmatrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix"].includes(environment)) {
1052
+ const matrixBody = environment === "array" ? body.replace(/^\s*\{[^}]*\}/, "") : body;
1053
+ return this.renderMatrix(environment, matrixBody);
1054
+ }
1055
+ this.supported = false;
1056
+ return body;
1057
+ }
1058
+ renderMatrix(environment, body) {
1059
+ const matrix = this.splitEnvironmentRows(body)
1060
+ .map((row) => row.split("&").map((cell) => this.renderNested(cell, false).trim()))
1061
+ .filter((row) => row.some(Boolean));
1062
+ const columnCount = Math.max(0, ...matrix.map((row) => row.length));
1063
+ const columnWidths = Array.from({ length: columnCount }, (_, column) => Math.max(0, ...matrix.map((row) => visibleWidth(row[column] ?? ""))));
1064
+ const rows = matrix.map((row) => Array.from({ length: columnCount }, (_, column) => {
1065
+ const cell = row[column] ?? "";
1066
+ return `${cell}${PROTECTED_SPACE.repeat(Math.max(0, (columnWidths[column] ?? 0) - visibleWidth(cell)))}`;
1067
+ }).join(" │ "));
1068
+ if (environment === "array" || environment === "matrix" || environment === "smallmatrix") {
1069
+ return rows.join("\n");
1070
+ }
1071
+ const delimiters = {
1072
+ pmatrix: ["⎛", "⎞", "⎜", "⎟", "⎝", "⎠"],
1073
+ bmatrix: ["⎡", "⎤", "⎢", "⎥", "⎣", "⎦"],
1074
+ Bmatrix: ["⎧", "⎫", "⎨", "⎬", "⎩", "⎭"],
1075
+ vmatrix: ["│", "│", "│", "│", "│", "│"],
1076
+ Vmatrix: ["║", "║", "║", "║", "║", "║"],
1077
+ };
1078
+ const delimiter = delimiters[environment];
1079
+ if (!delimiter) {
1080
+ this.supported = false;
1081
+ return rows.join("\n");
1082
+ }
1083
+ if (rows.length === 1) {
1084
+ return `${delimiter[0]} ${rows[0]} ${delimiter[1]}`;
1085
+ }
1086
+ return rows
1087
+ .map((row, index) => {
1088
+ const left = index === 0 ? delimiter[0] : index === rows.length - 1 ? delimiter[4] : delimiter[2];
1089
+ const right = index === 0 ? delimiter[1] : index === rows.length - 1 ? delimiter[5] : delimiter[3];
1090
+ return `${left} ${row} ${right}`;
1091
+ })
1092
+ .join("\n");
1093
+ }
1094
+ renderNested(source, stackFractions = true) {
1095
+ const rendered = new LatexParser(source, stackFractions ? this.layoutNodes : undefined).render();
1096
+ if (rendered === undefined) {
1097
+ this.supported = false;
1098
+ return source;
1099
+ }
1100
+ return rendered;
1101
+ }
1102
+ }
1103
+ /**
1104
+ * Render a basic LaTeX math expression as terminal-friendly Unicode text.
1105
+ * Returns undefined when the expression contains unsupported or malformed syntax.
1106
+ */
1107
+ export function renderLatex(source, options = {}) {
1108
+ const layoutNodes = options.display ? [] : undefined;
1109
+ const rendered = new LatexParser(source, layoutNodes).render();
1110
+ if (rendered === undefined) {
1111
+ return undefined;
1112
+ }
1113
+ if (!layoutNodes || layoutNodes.length === 0) {
1114
+ return rendered.replaceAll(PROTECTED_SPACE, " ");
1115
+ }
1116
+ const lines = renderLayout(rendered, layoutNodes).lines;
1117
+ const indentation = Math.min(...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length));
1118
+ return lines
1119
+ .map((line) => line.slice(indentation).trimEnd())
1120
+ .join("\n")
1121
+ .trimEnd()
1122
+ .replaceAll(PROTECTED_SPACE, " ");
1123
+ }
1124
+ //# sourceMappingURL=latex.js.map