cvc-tui 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.js +71148 -61
- package/package.json +2 -2
- package/dist/app/completion.js +0 -102
- package/dist/app/createGatewayEventHandler.js +0 -508
- package/dist/app/createSlashHandler.js +0 -101
- package/dist/app/delegationStore.js +0 -51
- package/dist/app/gatewayContext.js +0 -17
- package/dist/app/historyStore.js +0 -123
- package/dist/app/inputBuffer.js +0 -120
- package/dist/app/inputSelectionStore.js +0 -8
- package/dist/app/inputStore.js +0 -28
- package/dist/app/interfaces.js +0 -6
- package/dist/app/overlayStore.js +0 -40
- package/dist/app/promptStore.js +0 -44
- package/dist/app/queueStore.js +0 -25
- package/dist/app/scroll.js +0 -44
- package/dist/app/setupHandoff.js +0 -28
- package/dist/app/slash/commands/core.js +0 -479
- package/dist/app/slash/commands/debug.js +0 -44
- package/dist/app/slash/commands/ops.js +0 -498
- package/dist/app/slash/commands/session.js +0 -431
- package/dist/app/slash/commands/setup.js +0 -20
- package/dist/app/slash/commands/toggles.js +0 -40
- package/dist/app/slash/registry.js +0 -18
- package/dist/app/slash/types.js +0 -1
- package/dist/app/spawnHistoryStore.js +0 -105
- package/dist/app/turnController.js +0 -650
- package/dist/app/turnStore.js +0 -48
- package/dist/app/uiStore.js +0 -36
- package/dist/app/useComposerState.js +0 -265
- package/dist/app/useConfigSync.js +0 -144
- package/dist/app/useInputHandlers.js +0 -403
- package/dist/app/useLongRunToolCharms.js +0 -50
- package/dist/app/useMainApp.js +0 -629
- package/dist/app/useSessionLifecycle.js +0 -175
- package/dist/app/useSubmission.js +0 -287
- package/dist/app.js +0 -15
- package/dist/banner.js +0 -57
- package/dist/components/agentsOverlay.js +0 -474
- package/dist/components/appChrome.js +0 -252
- package/dist/components/appLayout.js +0 -121
- package/dist/components/appOverlays.js +0 -65
- package/dist/components/branding.js +0 -97
- package/dist/components/fpsOverlay.js +0 -22
- package/dist/components/helpHint.js +0 -21
- package/dist/components/markdown.js +0 -501
- package/dist/components/maskedPrompt.js +0 -12
- package/dist/components/messageLine.js +0 -82
- package/dist/components/modelPicker.js +0 -254
- package/dist/components/overlayControls.js +0 -30
- package/dist/components/overlays/confirmPrompt.js +0 -25
- package/dist/components/overlays/helpOverlay.js +0 -76
- package/dist/components/overlays/historySearch.js +0 -49
- package/dist/components/overlays/modelPicker.js +0 -60
- package/dist/components/overlays/overlayUtils.js +0 -19
- package/dist/components/overlays/secretPrompt.js +0 -36
- package/dist/components/overlays/sessionPicker.js +0 -93
- package/dist/components/overlays/skillsHub.js +0 -71
- package/dist/components/prompts.js +0 -95
- package/dist/components/queuedMessages.js +0 -24
- package/dist/components/sessionPicker.js +0 -130
- package/dist/components/skillsHub.js +0 -165
- package/dist/components/streamingAssistant.js +0 -35
- package/dist/components/streamingMarkdown.js +0 -144
- package/dist/components/textInput.js +0 -794
- package/dist/components/themed.js +0 -12
- package/dist/components/thinking.js +0 -496
- package/dist/components/todoPanel.js +0 -40
- package/dist/components/transcript.js +0 -22
- package/dist/config/env.js +0 -18
- package/dist/config/limits.js +0 -22
- package/dist/config/timing.js +0 -18
- package/dist/content/charms.js +0 -5
- package/dist/content/faces.js +0 -21
- package/dist/content/fortunes.js +0 -29
- package/dist/content/hotkeys.js +0 -38
- package/dist/content/placeholders.js +0 -15
- package/dist/content/setup.js +0 -14
- package/dist/content/verbs.js +0 -41
- package/dist/domain/details.js +0 -53
- package/dist/domain/messages.js +0 -63
- package/dist/domain/paths.js +0 -16
- package/dist/domain/providers.js +0 -11
- package/dist/domain/roles.js +0 -6
- package/dist/domain/slash.js +0 -11
- package/dist/domain/usage.js +0 -1
- package/dist/domain/viewport.js +0 -33
- package/dist/gateway/client.js +0 -312
- package/dist/gatewayClient.js +0 -574
- package/dist/gatewayTypes.js +0 -1
- package/dist/hooks/useCompletion.js +0 -86
- package/dist/hooks/useGitBranch.js +0 -58
- package/dist/hooks/useInputHistory.js +0 -12
- package/dist/hooks/useQueue.js +0 -57
- package/dist/hooks/useVirtualHistory.js +0 -401
- package/dist/lib/circularBuffer.js +0 -43
- package/dist/lib/clipboard.js +0 -126
- package/dist/lib/editor.js +0 -41
- package/dist/lib/editor.test.js +0 -58
- package/dist/lib/emoji.js +0 -49
- package/dist/lib/externalCli.js +0 -11
- package/dist/lib/forceTruecolor.js +0 -26
- package/dist/lib/fpsStore.js +0 -36
- package/dist/lib/gracefulExit.js +0 -29
- package/dist/lib/history.js +0 -69
- package/dist/lib/inputMetrics.js +0 -143
- package/dist/lib/liveProgress.js +0 -51
- package/dist/lib/liveProgress.test.js +0 -89
- package/dist/lib/mathUnicode.js +0 -685
- package/dist/lib/memory.js +0 -123
- package/dist/lib/memoryMonitor.js +0 -76
- package/dist/lib/messages.js +0 -3
- package/dist/lib/messages.test.js +0 -25
- package/dist/lib/osc52.js +0 -53
- package/dist/lib/perfPane.js +0 -94
- package/dist/lib/platform.js +0 -312
- package/dist/lib/precisionWheel.js +0 -25
- package/dist/lib/reasoning.js +0 -39
- package/dist/lib/rpc.js +0 -26
- package/dist/lib/subagentTree.js +0 -287
- package/dist/lib/syntax.js +0 -89
- package/dist/lib/terminalModes.js +0 -46
- package/dist/lib/terminalParity.js +0 -48
- package/dist/lib/terminalSetup.js +0 -321
- package/dist/lib/text.js +0 -203
- package/dist/lib/text.test.js +0 -18
- package/dist/lib/todo.js +0 -2
- package/dist/lib/todo.test.js +0 -22
- package/dist/lib/viewportStore.js +0 -82
- package/dist/lib/virtualHeights.js +0 -61
- package/dist/lib/wheelAccel.js +0 -143
- package/dist/theme.js +0 -398
- package/dist/types.js +0 -1
package/dist/lib/mathUnicode.js
DELETED
|
@@ -1,685 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
// Best-effort LaTeX → Unicode for inline / display math captured by the
|
|
6
|
-
// markdown renderer. The terminal can't typeset LaTeX, but Unicode covers
|
|
7
|
-
// most of what models actually emit: Greek letters, blackboard / fraktur /
|
|
8
|
-
// calligraphic capitals, set theory + logic operators, common arrows,
|
|
9
|
-
// sub/superscripts, and `\frac{a}{b}` collapsed to `a/b`.
|
|
10
|
-
//
|
|
11
|
-
// Design rules:
|
|
12
|
-
// • Pure regex pipeline. Anything we don't recognise is preserved
|
|
13
|
-
// verbatim (so a `\foo{bar}` we've never heard of still survives).
|
|
14
|
-
// A real LaTeX parser would be more correct but throws on partial
|
|
15
|
-
// input — terminal users would rather see the raw command than a
|
|
16
|
-
// parse-error placeholder.
|
|
17
|
-
// • Longest-match-first ordering on commands so `\le` doesn't shadow
|
|
18
|
-
// `\leq`, `\sub` doesn't shadow `\subseteq`, etc.
|
|
19
|
-
// • Word-boundary lookahead `(?![A-Za-z])` after each command so
|
|
20
|
-
// `\pix` (made-up command) doesn't get partially substituted as `π`.
|
|
21
|
-
// • `\mathbb{X}`, `\mathcal{X}`, `\mathfrak{X}` only handle a single
|
|
22
|
-
// letter argument — multi-letter `\mathbb{NN}` is rare and would
|
|
23
|
-
// need a real parser to do correctly.
|
|
24
|
-
// • Sub/super scripts only convert if EVERY character has a Unicode
|
|
25
|
-
// equivalent. Mixed content like `^{n+1}` falls back to the raw
|
|
26
|
-
// LaTeX so we don't emit `ⁿ+¹` (which has no `+` superscript glyph
|
|
27
|
-
// in some fonts and reads worse than the source).
|
|
28
|
-
const SYMBOLS = {
|
|
29
|
-
// Greek lowercase
|
|
30
|
-
'\\alpha': 'α',
|
|
31
|
-
'\\beta': 'β',
|
|
32
|
-
'\\gamma': 'γ',
|
|
33
|
-
'\\delta': 'δ',
|
|
34
|
-
'\\epsilon': 'ε',
|
|
35
|
-
'\\varepsilon': 'ε',
|
|
36
|
-
'\\zeta': 'ζ',
|
|
37
|
-
'\\eta': 'η',
|
|
38
|
-
'\\theta': 'θ',
|
|
39
|
-
'\\vartheta': 'ϑ',
|
|
40
|
-
'\\iota': 'ι',
|
|
41
|
-
'\\kappa': 'κ',
|
|
42
|
-
'\\lambda': 'λ',
|
|
43
|
-
'\\mu': 'μ',
|
|
44
|
-
'\\nu': 'ν',
|
|
45
|
-
'\\xi': 'ξ',
|
|
46
|
-
'\\pi': 'π',
|
|
47
|
-
'\\varpi': 'ϖ',
|
|
48
|
-
'\\rho': 'ρ',
|
|
49
|
-
'\\varrho': 'ϱ',
|
|
50
|
-
'\\sigma': 'σ',
|
|
51
|
-
'\\varsigma': 'ς',
|
|
52
|
-
'\\tau': 'τ',
|
|
53
|
-
'\\upsilon': 'υ',
|
|
54
|
-
'\\phi': 'φ',
|
|
55
|
-
'\\varphi': 'φ',
|
|
56
|
-
'\\chi': 'χ',
|
|
57
|
-
'\\psi': 'ψ',
|
|
58
|
-
'\\omega': 'ω',
|
|
59
|
-
// Greek uppercase
|
|
60
|
-
'\\Gamma': 'Γ',
|
|
61
|
-
'\\Delta': 'Δ',
|
|
62
|
-
'\\Theta': 'Θ',
|
|
63
|
-
'\\Lambda': 'Λ',
|
|
64
|
-
'\\Xi': 'Ξ',
|
|
65
|
-
'\\Pi': 'Π',
|
|
66
|
-
'\\Sigma': 'Σ',
|
|
67
|
-
'\\Upsilon': 'Υ',
|
|
68
|
-
'\\Phi': 'Φ',
|
|
69
|
-
'\\Psi': 'Ψ',
|
|
70
|
-
'\\Omega': 'Ω',
|
|
71
|
-
// Big operators
|
|
72
|
-
'\\sum': '∑',
|
|
73
|
-
'\\prod': '∏',
|
|
74
|
-
'\\coprod': '∐',
|
|
75
|
-
'\\int': '∫',
|
|
76
|
-
'\\iint': '∬',
|
|
77
|
-
'\\iiint': '∭',
|
|
78
|
-
'\\oint': '∮',
|
|
79
|
-
'\\bigcup': '⋃',
|
|
80
|
-
'\\bigcap': '⋂',
|
|
81
|
-
'\\bigvee': '⋁',
|
|
82
|
-
'\\bigwedge': '⋀',
|
|
83
|
-
'\\bigoplus': '⨁',
|
|
84
|
-
'\\bigotimes': '⨂',
|
|
85
|
-
// Calculus
|
|
86
|
-
'\\partial': '∂',
|
|
87
|
-
'\\nabla': '∇',
|
|
88
|
-
'\\sqrt': '√',
|
|
89
|
-
// Sets
|
|
90
|
-
'\\emptyset': '∅',
|
|
91
|
-
'\\varnothing': '∅',
|
|
92
|
-
'\\infty': '∞',
|
|
93
|
-
'\\in': '∈',
|
|
94
|
-
'\\notin': '∉',
|
|
95
|
-
'\\ni': '∋',
|
|
96
|
-
'\\subset': '⊂',
|
|
97
|
-
'\\supset': '⊃',
|
|
98
|
-
'\\subseteq': '⊆',
|
|
99
|
-
'\\supseteq': '⊇',
|
|
100
|
-
'\\subsetneq': '⊊',
|
|
101
|
-
'\\supsetneq': '⊋',
|
|
102
|
-
'\\cup': '∪',
|
|
103
|
-
'\\cap': '∩',
|
|
104
|
-
'\\setminus': '∖',
|
|
105
|
-
'\\complement': '∁',
|
|
106
|
-
// Logic
|
|
107
|
-
'\\forall': '∀',
|
|
108
|
-
'\\exists': '∃',
|
|
109
|
-
'\\nexists': '∄',
|
|
110
|
-
'\\land': '∧',
|
|
111
|
-
'\\lor': '∨',
|
|
112
|
-
'\\lnot': '¬',
|
|
113
|
-
'\\neg': '¬',
|
|
114
|
-
'\\therefore': '∴',
|
|
115
|
-
'\\because': '∵',
|
|
116
|
-
// Relations
|
|
117
|
-
'\\le': '≤',
|
|
118
|
-
'\\leq': '≤',
|
|
119
|
-
'\\ge': '≥',
|
|
120
|
-
'\\geq': '≥',
|
|
121
|
-
'\\ne': '≠',
|
|
122
|
-
'\\neq': '≠',
|
|
123
|
-
'\\ll': '≪',
|
|
124
|
-
'\\gg': '≫',
|
|
125
|
-
'\\approx': '≈',
|
|
126
|
-
'\\equiv': '≡',
|
|
127
|
-
'\\cong': '≅',
|
|
128
|
-
'\\sim': '∼',
|
|
129
|
-
'\\simeq': '≃',
|
|
130
|
-
'\\propto': '∝',
|
|
131
|
-
'\\perp': '⊥',
|
|
132
|
-
'\\parallel': '∥',
|
|
133
|
-
'\\models': '⊨',
|
|
134
|
-
'\\vdash': '⊢',
|
|
135
|
-
'\\mid': '∣',
|
|
136
|
-
'\\nmid': '∤',
|
|
137
|
-
'\\divides': '∣',
|
|
138
|
-
// Common standalone glyphs
|
|
139
|
-
'\\blacksquare': '■',
|
|
140
|
-
'\\square': '□',
|
|
141
|
-
'\\Box': '□',
|
|
142
|
-
'\\qed': '∎',
|
|
143
|
-
'\\bigstar': '★',
|
|
144
|
-
// Modular arithmetic — the `\pmod{p}` form (with arg) is handled below;
|
|
145
|
-
// the bare `\bmod` / `\mod` commands are simple text substitutions.
|
|
146
|
-
'\\bmod': 'mod',
|
|
147
|
-
'\\mod': 'mod',
|
|
148
|
-
// Brackets / fences (named delimiter commands; the `\left\X` / `\right\X`
|
|
149
|
-
// unwrapping below leaves these behind for the symbol pass to resolve).
|
|
150
|
-
'\\langle': '⟨',
|
|
151
|
-
'\\rangle': '⟩',
|
|
152
|
-
'\\lceil': '⌈',
|
|
153
|
-
'\\rceil': '⌉',
|
|
154
|
-
'\\lfloor': '⌊',
|
|
155
|
-
'\\rfloor': '⌋',
|
|
156
|
-
'\\|': '‖',
|
|
157
|
-
// Arrows
|
|
158
|
-
'\\to': '→',
|
|
159
|
-
'\\rightarrow': '→',
|
|
160
|
-
'\\leftarrow': '←',
|
|
161
|
-
'\\leftrightarrow': '↔',
|
|
162
|
-
'\\Rightarrow': '⇒',
|
|
163
|
-
'\\Leftarrow': '⇐',
|
|
164
|
-
'\\Leftrightarrow': '⇔',
|
|
165
|
-
'\\implies': '⟹',
|
|
166
|
-
'\\impliedby': '⟸',
|
|
167
|
-
'\\iff': '⟺',
|
|
168
|
-
'\\mapsto': '↦',
|
|
169
|
-
'\\hookrightarrow': '↪',
|
|
170
|
-
'\\hookleftarrow': '↩',
|
|
171
|
-
'\\uparrow': '↑',
|
|
172
|
-
'\\downarrow': '↓',
|
|
173
|
-
'\\updownarrow': '↕',
|
|
174
|
-
// Binary operators
|
|
175
|
-
'\\cdot': '⋅',
|
|
176
|
-
'\\cdots': '⋯',
|
|
177
|
-
'\\ldots': '…',
|
|
178
|
-
'\\dots': '…',
|
|
179
|
-
'\\dotsb': '…',
|
|
180
|
-
'\\dotsc': '…',
|
|
181
|
-
'\\vdots': '⋮',
|
|
182
|
-
'\\ddots': '⋱',
|
|
183
|
-
'\\times': '×',
|
|
184
|
-
'\\div': '÷',
|
|
185
|
-
'\\pm': '±',
|
|
186
|
-
'\\mp': '∓',
|
|
187
|
-
'\\circ': '∘',
|
|
188
|
-
'\\bullet': '•',
|
|
189
|
-
'\\star': '⋆',
|
|
190
|
-
'\\ast': '∗',
|
|
191
|
-
'\\oplus': '⊕',
|
|
192
|
-
'\\ominus': '⊖',
|
|
193
|
-
'\\otimes': '⊗',
|
|
194
|
-
'\\odot': '⊙',
|
|
195
|
-
'\\diamond': '⋄',
|
|
196
|
-
'\\angle': '∠',
|
|
197
|
-
'\\triangle': '△',
|
|
198
|
-
// Spacing — collapse to varying widths of regular space
|
|
199
|
-
'\\,': ' ',
|
|
200
|
-
'\\;': ' ',
|
|
201
|
-
'\\:': ' ',
|
|
202
|
-
'\\!': '',
|
|
203
|
-
'\\ ': ' ',
|
|
204
|
-
'\\quad': ' ',
|
|
205
|
-
'\\qquad': ' ',
|
|
206
|
-
// Functions (LaTeX renders these in roman; we just keep the name)
|
|
207
|
-
'\\sin': 'sin',
|
|
208
|
-
'\\cos': 'cos',
|
|
209
|
-
'\\tan': 'tan',
|
|
210
|
-
'\\cot': 'cot',
|
|
211
|
-
'\\sec': 'sec',
|
|
212
|
-
'\\csc': 'csc',
|
|
213
|
-
'\\arcsin': 'arcsin',
|
|
214
|
-
'\\arccos': 'arccos',
|
|
215
|
-
'\\arctan': 'arctan',
|
|
216
|
-
'\\sinh': 'sinh',
|
|
217
|
-
'\\cosh': 'cosh',
|
|
218
|
-
'\\tanh': 'tanh',
|
|
219
|
-
'\\log': 'log',
|
|
220
|
-
'\\ln': 'ln',
|
|
221
|
-
'\\exp': 'exp',
|
|
222
|
-
'\\det': 'det',
|
|
223
|
-
'\\dim': 'dim',
|
|
224
|
-
'\\ker': 'ker',
|
|
225
|
-
'\\lim': 'lim',
|
|
226
|
-
'\\liminf': 'liminf',
|
|
227
|
-
'\\limsup': 'limsup',
|
|
228
|
-
'\\sup': 'sup',
|
|
229
|
-
'\\inf': 'inf',
|
|
230
|
-
'\\max': 'max',
|
|
231
|
-
'\\min': 'min',
|
|
232
|
-
'\\arg': 'arg',
|
|
233
|
-
'\\gcd': 'gcd',
|
|
234
|
-
// Escaped literals — model occasionally emits these for display
|
|
235
|
-
'\\&': '&',
|
|
236
|
-
'\\%': '%',
|
|
237
|
-
'\\$': '$',
|
|
238
|
-
'\\#': '#',
|
|
239
|
-
'\\_': '_',
|
|
240
|
-
'\\{': '{',
|
|
241
|
-
'\\}': '}'
|
|
242
|
-
};
|
|
243
|
-
const BB = {
|
|
244
|
-
A: '𝔸',
|
|
245
|
-
B: '𝔹',
|
|
246
|
-
C: 'ℂ',
|
|
247
|
-
D: '𝔻',
|
|
248
|
-
E: '𝔼',
|
|
249
|
-
F: '𝔽',
|
|
250
|
-
G: '𝔾',
|
|
251
|
-
H: 'ℍ',
|
|
252
|
-
I: '𝕀',
|
|
253
|
-
J: '𝕁',
|
|
254
|
-
K: '𝕂',
|
|
255
|
-
L: '𝕃',
|
|
256
|
-
M: '𝕄',
|
|
257
|
-
N: 'ℕ',
|
|
258
|
-
O: '𝕆',
|
|
259
|
-
P: 'ℙ',
|
|
260
|
-
Q: 'ℚ',
|
|
261
|
-
R: 'ℝ',
|
|
262
|
-
S: '𝕊',
|
|
263
|
-
T: '𝕋',
|
|
264
|
-
U: '𝕌',
|
|
265
|
-
V: '𝕍',
|
|
266
|
-
W: '𝕎',
|
|
267
|
-
X: '𝕏',
|
|
268
|
-
Y: '𝕐',
|
|
269
|
-
Z: 'ℤ'
|
|
270
|
-
};
|
|
271
|
-
const CAL = {
|
|
272
|
-
A: '𝒜',
|
|
273
|
-
B: 'ℬ',
|
|
274
|
-
C: '𝒞',
|
|
275
|
-
D: '𝒟',
|
|
276
|
-
E: 'ℰ',
|
|
277
|
-
F: 'ℱ',
|
|
278
|
-
G: '𝒢',
|
|
279
|
-
H: 'ℋ',
|
|
280
|
-
I: 'ℐ',
|
|
281
|
-
J: '𝒥',
|
|
282
|
-
K: '𝒦',
|
|
283
|
-
L: 'ℒ',
|
|
284
|
-
M: 'ℳ',
|
|
285
|
-
N: '𝒩',
|
|
286
|
-
O: '𝒪',
|
|
287
|
-
P: '𝒫',
|
|
288
|
-
Q: '𝒬',
|
|
289
|
-
R: 'ℛ',
|
|
290
|
-
S: '𝒮',
|
|
291
|
-
T: '𝒯',
|
|
292
|
-
U: '𝒰',
|
|
293
|
-
V: '𝒱',
|
|
294
|
-
W: '𝒲',
|
|
295
|
-
X: '𝒳',
|
|
296
|
-
Y: '𝒴',
|
|
297
|
-
Z: '𝒵'
|
|
298
|
-
};
|
|
299
|
-
const FRAK = {
|
|
300
|
-
A: '𝔄',
|
|
301
|
-
B: '𝔅',
|
|
302
|
-
C: 'ℭ',
|
|
303
|
-
D: '𝔇',
|
|
304
|
-
E: '𝔈',
|
|
305
|
-
F: '𝔉',
|
|
306
|
-
G: '𝔊',
|
|
307
|
-
H: 'ℌ',
|
|
308
|
-
I: 'ℑ',
|
|
309
|
-
J: '𝔍',
|
|
310
|
-
K: '𝔎',
|
|
311
|
-
L: '𝔏',
|
|
312
|
-
M: '𝔐',
|
|
313
|
-
N: '𝔑',
|
|
314
|
-
O: '𝔒',
|
|
315
|
-
P: '𝔓',
|
|
316
|
-
Q: '𝔔',
|
|
317
|
-
R: 'ℜ',
|
|
318
|
-
S: '𝔖',
|
|
319
|
-
T: '𝔗',
|
|
320
|
-
U: '𝔘',
|
|
321
|
-
V: '𝔙',
|
|
322
|
-
W: '𝔚',
|
|
323
|
-
X: '𝔛',
|
|
324
|
-
Y: '𝔜',
|
|
325
|
-
Z: 'ℨ'
|
|
326
|
-
};
|
|
327
|
-
const SUPERSCRIPT = {
|
|
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 SUBSCRIPT = {
|
|
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
|
-
// Sentinel control characters used to mark `\boxed` / `\fbox` regions in
|
|
404
|
-
// the converted output. The renderer splits on these to apply a highlight
|
|
405
|
-
// style; consumers that don't want highlighting can strip them with the
|
|
406
|
-
// exported `BOX_RE` below.
|
|
407
|
-
export const BOX_OPEN = '\u0001';
|
|
408
|
-
export const BOX_CLOSE = '\u0002';
|
|
409
|
-
export const BOX_RE = /\u0001([^\u0001\u0002]*)\u0002/g;
|
|
410
|
-
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
411
|
-
// Pre-compile two symbol regexes: one for letter-ending commands (`\pi`,
|
|
412
|
-
// `\sum`) which need a `(?![A-Za-z])` lookahead so they don't partially
|
|
413
|
-
// match `\pix` or `\summa`, and one for punctuation-ending commands
|
|
414
|
-
// (`\{`, `\,`, `\|`) which must NOT have the lookahead — otherwise
|
|
415
|
-
// `\{p` would refuse to substitute because `p` is a letter.
|
|
416
|
-
//
|
|
417
|
-
// Longest commands first inside each group so `\leq` beats `\le`.
|
|
418
|
-
const splitByEnding = (keys) => {
|
|
419
|
-
const letter = [];
|
|
420
|
-
const punct = [];
|
|
421
|
-
for (const k of keys) {
|
|
422
|
-
if (/[A-Za-z]$/.test(k)) {
|
|
423
|
-
letter.push(k);
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
punct.push(k);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return { letter, punct };
|
|
430
|
-
};
|
|
431
|
-
const buildAlt = (cmds) => cmds
|
|
432
|
-
.sort((a, b) => b.length - a.length)
|
|
433
|
-
.map(escapeRe)
|
|
434
|
-
.join('|');
|
|
435
|
-
const { letter: LETTER_CMDS, punct: PUNCT_CMDS } = splitByEnding(Object.keys(SYMBOLS));
|
|
436
|
-
const SYMBOL_LETTER_RE = new RegExp('(?:' + buildAlt(LETTER_CMDS) + ')(?![A-Za-z])', 'g');
|
|
437
|
-
const SYMBOL_PUNCT_RE = new RegExp('(?:' + buildAlt(PUNCT_CMDS) + ')', 'g');
|
|
438
|
-
const convertScript = (input, table, sigil) => {
|
|
439
|
-
let out = '';
|
|
440
|
-
let allMapped = true;
|
|
441
|
-
for (const ch of input) {
|
|
442
|
-
const mapped = table[ch];
|
|
443
|
-
if (!mapped) {
|
|
444
|
-
allMapped = false;
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
out += mapped;
|
|
448
|
-
}
|
|
449
|
-
if (allMapped) {
|
|
450
|
-
return out;
|
|
451
|
-
}
|
|
452
|
-
// Fallback: if the body is a single visible character (e.g. `∞` after
|
|
453
|
-
// earlier symbol substitution), render it without braces — `^∞` reads
|
|
454
|
-
// far better than `^{∞}` in a terminal. Multi-char bodies that don't
|
|
455
|
-
// fully convert use parens (`e^(iπ)`) instead of braces (`e^{iπ}`)
|
|
456
|
-
// because parens are normal punctuation while braces look like
|
|
457
|
-
// unrendered LaTeX.
|
|
458
|
-
const trimmed = input.trim();
|
|
459
|
-
if ([...trimmed].length === 1) {
|
|
460
|
-
return `${sigil}${trimmed}`;
|
|
461
|
-
}
|
|
462
|
-
return `${sigil}(${trimmed})`;
|
|
463
|
-
};
|
|
464
|
-
// Walk the string and parse `{...}` honouring nested braces. Unlike a
|
|
465
|
-
// `\{[^{}]*\}` regex this survives `\frac{|t|^{p-1}|P(t)|^p}{...}` where
|
|
466
|
-
// the numerator contains its own braces from a superscript. Returns the
|
|
467
|
-
// inner content (without the outer braces) and the offset just past the
|
|
468
|
-
// closing `}`. Returns null if there is no balanced brace at `start`.
|
|
469
|
-
const readBraced = (s, start) => {
|
|
470
|
-
if (s[start] !== '{') {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
let depth = 1;
|
|
474
|
-
let i = start + 1;
|
|
475
|
-
while (i < s.length && depth > 0) {
|
|
476
|
-
const c = s[i];
|
|
477
|
-
// Skip escapes — `\{` and `\}` inside a body are literal braces and
|
|
478
|
-
// should not change the brace counter.
|
|
479
|
-
if (c === '\\' && i + 1 < s.length) {
|
|
480
|
-
i += 2;
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (c === '{') {
|
|
484
|
-
depth++;
|
|
485
|
-
}
|
|
486
|
-
else if (c === '}') {
|
|
487
|
-
depth--;
|
|
488
|
-
}
|
|
489
|
-
if (depth > 0) {
|
|
490
|
-
i++;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
if (depth !== 0) {
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
return { content: s.slice(start + 1, i), end: i + 1 };
|
|
497
|
-
};
|
|
498
|
-
// Replace every occurrence of `\command{arg}` using balanced-brace parsing
|
|
499
|
-
// (so `\boxed{x^{n+1}}` works where a `[^{}]*` regex would fail). The
|
|
500
|
-
// `render` callback receives the inner content already recursed-into, so
|
|
501
|
-
// `\boxed{\boxed{x}}` resolves outside-in cleanly. Unmatched `\command`
|
|
502
|
-
// (no following `{...}`) is preserved verbatim.
|
|
503
|
-
const replaceBracedCommand = (input, command, render) => {
|
|
504
|
-
const cmdLen = command.length;
|
|
505
|
-
let out = '';
|
|
506
|
-
let i = 0;
|
|
507
|
-
while (i < input.length) {
|
|
508
|
-
const idx = input.indexOf(command, i);
|
|
509
|
-
if (idx < 0) {
|
|
510
|
-
out += input.slice(i);
|
|
511
|
-
return out;
|
|
512
|
-
}
|
|
513
|
-
const after = input[idx + cmdLen];
|
|
514
|
-
if (after && /[A-Za-z]/.test(after)) {
|
|
515
|
-
out += input.slice(i, idx + cmdLen);
|
|
516
|
-
i = idx + cmdLen;
|
|
517
|
-
continue;
|
|
518
|
-
}
|
|
519
|
-
out += input.slice(i, idx);
|
|
520
|
-
let p = idx + cmdLen;
|
|
521
|
-
while (input[p] === ' ' || input[p] === '\t')
|
|
522
|
-
p++;
|
|
523
|
-
const arg = readBraced(input, p);
|
|
524
|
-
if (!arg) {
|
|
525
|
-
out += input.slice(idx, p + 1);
|
|
526
|
-
i = p + 1;
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
out += render(replaceBracedCommand(arg.content, command, render));
|
|
530
|
-
i = arg.end;
|
|
531
|
-
}
|
|
532
|
-
return out;
|
|
533
|
-
};
|
|
534
|
-
// Replace every `\frac{num}{den}` with `num/den` (parens around either
|
|
535
|
-
// side when its precedence demands it). The recursion handles nested
|
|
536
|
-
// fractions naturally: `\frac{1}{\frac{1}{x}}` collapses to `1/(1/x)`
|
|
537
|
-
// because we recurse into `den` before deciding whether to parenthesise.
|
|
538
|
-
const replaceFracs = (input) => {
|
|
539
|
-
let out = '';
|
|
540
|
-
let i = 0;
|
|
541
|
-
while (i < input.length) {
|
|
542
|
-
const idx = input.indexOf('\\frac', i);
|
|
543
|
-
if (idx < 0) {
|
|
544
|
-
out += input.slice(i);
|
|
545
|
-
return out;
|
|
546
|
-
}
|
|
547
|
-
const after = input[idx + 5];
|
|
548
|
-
// `(?![A-Za-z])` — protect hypothetical commands like `\fraction`.
|
|
549
|
-
if (after && /[A-Za-z]/.test(after)) {
|
|
550
|
-
out += input.slice(i, idx + 5);
|
|
551
|
-
i = idx + 5;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
out += input.slice(i, idx);
|
|
555
|
-
let p = idx + 5;
|
|
556
|
-
while (input[p] === ' ' || input[p] === '\t')
|
|
557
|
-
p++;
|
|
558
|
-
const num = readBraced(input, p);
|
|
559
|
-
if (!num) {
|
|
560
|
-
out += input.slice(idx, p + 1);
|
|
561
|
-
i = p + 1;
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
p = num.end;
|
|
565
|
-
while (input[p] === ' ' || input[p] === '\t')
|
|
566
|
-
p++;
|
|
567
|
-
const den = readBraced(input, p);
|
|
568
|
-
if (!den) {
|
|
569
|
-
out += input.slice(idx, p + 1);
|
|
570
|
-
i = p + 1;
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
out += `${wrapForFrac(replaceFracs(num.content))}/${wrapForFrac(replaceFracs(den.content))}`;
|
|
574
|
-
i = den.end;
|
|
575
|
-
}
|
|
576
|
-
return out;
|
|
577
|
-
};
|
|
578
|
-
// Wrap multi-token expressions in parens so `\frac{a+b}{c}` becomes
|
|
579
|
-
// `(a+b)/c` rather than `a+b/c`. We wrap whenever inline `/` would
|
|
580
|
-
// change the meaning — that's any binary operator (`+`, `-`, `*`, `/`)
|
|
581
|
-
// or whitespace separating tokens. `*` and `/` matter because nested
|
|
582
|
-
// fractions and products like `\frac{a*b}{c}` and `\frac{1/x}{y}` would
|
|
583
|
-
// otherwise read as `a*b/c` (right-associative ambiguity) and `1/x/y`.
|
|
584
|
-
// Atomic factors like `n!`, `x^2`, `\sin x` don't trigger any of these
|
|
585
|
-
// and stay un-parenthesised — wrapping them just clutters the output.
|
|
586
|
-
const wrapForFrac = (expr) => {
|
|
587
|
-
const trimmed = expr.trim();
|
|
588
|
-
if (!trimmed) {
|
|
589
|
-
return trimmed;
|
|
590
|
-
}
|
|
591
|
-
if (/^\(.*\)$/.test(trimmed)) {
|
|
592
|
-
return trimmed;
|
|
593
|
-
}
|
|
594
|
-
if (/[+\-/*]|\s/.test(trimmed)) {
|
|
595
|
-
return `(${trimmed})`;
|
|
596
|
-
}
|
|
597
|
-
return trimmed;
|
|
598
|
-
};
|
|
599
|
-
export function texToUnicode(input) {
|
|
600
|
-
let s = input;
|
|
601
|
-
s = s.replace(/\\mathbb\s*\{([A-Za-z])\}/g, (raw, c) => BB[c] ?? raw);
|
|
602
|
-
s = s.replace(/\\mathcal\s*\{([A-Za-z])\}/g, (raw, c) => CAL[c] ?? raw);
|
|
603
|
-
s = s.replace(/\\mathfrak\s*\{([A-Za-z])\}/g, (raw, c) => FRAK[c] ?? raw);
|
|
604
|
-
s = s.replace(/\\mathbf\s*\{([^{}]+)\}/g, (_, c) => c);
|
|
605
|
-
s = s.replace(/\\mathit\s*\{([^{}]+)\}/g, (_, c) => c);
|
|
606
|
-
s = s.replace(/\\mathrm\s*\{([^{}]+)\}/g, (_, c) => c);
|
|
607
|
-
s = s.replace(/\\text\s*\{([^{}]+)\}/g, (_, c) => c);
|
|
608
|
-
s = s.replace(/\\operatorname\s*\{([^{}]+)\}/g, (_, c) => c);
|
|
609
|
-
s = s.replace(/\\overline\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0305`);
|
|
610
|
-
s = s.replace(/\\hat\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0302`);
|
|
611
|
-
s = s.replace(/\\bar\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0304`);
|
|
612
|
-
s = s.replace(/\\tilde\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0303`);
|
|
613
|
-
s = s.replace(/\\vec\s*\{([^{}]+)\}/g, (_, c) => `${c}\u20D7`);
|
|
614
|
-
s = s.replace(/\\dot\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0307`);
|
|
615
|
-
s = s.replace(/\\ddot\s*\{([^{}]+)\}/g, (_, c) => `${c}\u0308`);
|
|
616
|
-
s = replaceFracs(s);
|
|
617
|
-
// `\boxed{X}` / `\fbox{X}` highlight a final answer. Terminals can't
|
|
618
|
-
// draw a real box, so we wrap the content in U+0001 / U+0002 control
|
|
619
|
-
// characters — non-printable, never present in real text — and let the
|
|
620
|
-
// markdown renderer split on them and apply a highlight style (inverse
|
|
621
|
-
// video) to the bracketed region. This keeps `texToUnicode` pure-string
|
|
622
|
-
// while letting the React layer do the actual visual emphasis.
|
|
623
|
-
// Argument is parsed with balanced braces so nested `{...}` from
|
|
624
|
-
// superscripts / fractions inside the box survive.
|
|
625
|
-
s = replaceBracedCommand(s, '\\boxed', body => `${BOX_OPEN}${body.trim()}${BOX_CLOSE}`);
|
|
626
|
-
s = replaceBracedCommand(s, '\\fbox', body => `${BOX_OPEN}${body.trim()}${BOX_CLOSE}`);
|
|
627
|
-
// `\xrightarrow{label}` / `\xleftarrow{label}` collapse to an arrow with
|
|
628
|
-
// the label inline. LaTeX renders the label above the arrow; in monospace
|
|
629
|
-
// we put it adjacent — `─label→` is the closest readable approximation.
|
|
630
|
-
// Run before the symbol pass so the label can still pick up Greek and
|
|
631
|
-
// operator substitutions afterwards.
|
|
632
|
-
s = s.replace(/\\xrightarrow\s*\{([^{}]*)\}/g, (_, label) => `─${label.trim()}→`);
|
|
633
|
-
s = s.replace(/\\xleftarrow\s*\{([^{}]*)\}/g, (_, label) => `←${label.trim()}─`);
|
|
634
|
-
s = s.replace(/\\Longrightarrow/g, '⟹');
|
|
635
|
-
s = s.replace(/\\Longleftarrow/g, '⟸');
|
|
636
|
-
s = s.replace(/\\Longleftrightarrow/g, '⟺');
|
|
637
|
-
// `\pmod{p}` → ` (mod p)` (LaTeX adds parens automatically); `\pod{p}`
|
|
638
|
-
// is a paren-less variant; `\tag{n}` is the equation-number annotation
|
|
639
|
-
// shown to the right of an equation. Collapse to a single-space-prefixed
|
|
640
|
-
// bracketed form. The leading `\s*` in the pattern absorbs any whitespace
|
|
641
|
-
// already in the source so we don't end up with `b (mod p)` (double
|
|
642
|
-
// space) when the user wrote `b \pmod{p}`.
|
|
643
|
-
s = s.replace(/\s*\\pmod\s*\{([^{}]*)\}/g, (_, p) => ` (mod ${p.trim()})`);
|
|
644
|
-
s = s.replace(/\s*\\pod\s*\{([^{}]*)\}/g, (_, p) => ` (${p.trim()})`);
|
|
645
|
-
s = s.replace(/\s*\\tag\s*\{([^{}]*)\}/g, (_, n) => ` (${n.trim()})`);
|
|
646
|
-
// `\big`, `\Big`, `\bigg`, `\Bigg` (with optional `l`/`r`/`m` suffix)
|
|
647
|
-
// are sizing wrappers analogous to `\left`/`\right` but without the
|
|
648
|
-
// automatic-pairing semantics. Strip them and leave whatever delimiter
|
|
649
|
-
// follows. The trailing `(?![A-Za-z])` protects `\bigtriangleup` and
|
|
650
|
-
// any other letter-continuation command from being shaved.
|
|
651
|
-
s = s.replace(/\\(?:Bigg|bigg|Big|big)[lrm]?(?![A-Za-z])/g, '');
|
|
652
|
-
// Style / size hints that don't typeset any glyph and only affect how
|
|
653
|
-
// things would be sized in a real LaTeX engine. In a terminal every
|
|
654
|
-
// glyph is one monospace cell, so there's nothing to do — drop them
|
|
655
|
-
// (with any trailing whitespace) so they don't leak through as raw
|
|
656
|
-
// `\displaystyle` in the output.
|
|
657
|
-
s = s.replace(/\\(?:scriptscriptstyle|displaystyle|scriptstyle|textstyle|nolimits|limits)(?![A-Za-z])\s*/g, '');
|
|
658
|
-
// `\left` and `\right` are sizing wrappers around any delimiter — bare
|
|
659
|
-
// (`\left(`), escaped (`\left\{`), or named (`\left\langle`). Strip the
|
|
660
|
-
// wrapper unconditionally and let the rest of the pipeline (or the
|
|
661
|
-
// upcoming symbol pass) handle whatever delimiter follows. The optional
|
|
662
|
-
// `.?` consumes `\left.` / `\right.` which mean "no delimiter".
|
|
663
|
-
// Lookahead `(?![A-Za-z])` keeps `\leftarrow` / `\leftrightarrow` safe.
|
|
664
|
-
s = s.replace(/\\left(?![A-Za-z])\.?/g, '');
|
|
665
|
-
s = s.replace(/\\right(?![A-Za-z])\.?/g, '');
|
|
666
|
-
// Run symbol substitution BEFORE scripts so a body like `^{\infty}`
|
|
667
|
-
// becomes `^{∞}` first; convertScript can then either map ∞ to a
|
|
668
|
-
// superscript (it can't — Unicode lacks one) or fall back to `^∞`
|
|
669
|
-
// by stripping braces around the now-single-character body.
|
|
670
|
-
//
|
|
671
|
-
// Punctuation pass first — these can be followed by letters (`\{p`
|
|
672
|
-
// is "open-brace then p"), so the letter pass's `(?![A-Za-z])` rule
|
|
673
|
-
// would wrongly block them.
|
|
674
|
-
s = s.replace(SYMBOL_PUNCT_RE, m => SYMBOLS[m] ?? m);
|
|
675
|
-
s = s.replace(SYMBOL_LETTER_RE, m => SYMBOLS[m] ?? m);
|
|
676
|
-
// Bare `^c` / `_c` handles ONLY alphanumerics and `+`/`-`/`=`. Parens
|
|
677
|
-
// are intentionally excluded because the braced-fallback above can
|
|
678
|
-
// emit `(...)` and we don't want a second pass to greedily convert
|
|
679
|
-
// its opening paren into `⁽` and orphan the closing one.
|
|
680
|
-
s = s.replace(/\^\s*\{([^{}]+)\}/g, (_, body) => convertScript(body, SUPERSCRIPT, '^'));
|
|
681
|
-
s = s.replace(/\^([A-Za-z0-9+\-=])/g, (raw, ch) => SUPERSCRIPT[ch] ?? raw);
|
|
682
|
-
s = s.replace(/_\s*\{([^{}]+)\}/g, (_, body) => convertScript(body, SUBSCRIPT, '_'));
|
|
683
|
-
s = s.replace(/_([A-Za-z0-9+\-=])/g, (raw, ch) => SUBSCRIPT[ch] ?? raw);
|
|
684
|
-
return s;
|
|
685
|
-
}
|