@simon_he/vue-tui 1.1.2 → 1.1.4

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 (52) hide show
  1. package/dist/{TMermaidText-frVr8Ysb.js → TMermaidText-wLnMMvVS.js} +1 -1
  2. package/dist/{TSelect-tJ87rKPM.js → TSelect-CMBOOFwK.js} +4 -4
  3. package/dist/{TTree-CGdE-CYu.js → TTree-kk8ntrpg.js} +6 -5
  4. package/dist/agent/mermaid.d.cts +2 -2
  5. package/dist/agent/mermaid.d.ts +2 -2
  6. package/dist/agent.cjs +3896 -13616
  7. package/dist/agent.js +25 -82
  8. package/dist/{block-source-CrlmaBM5.js → block-source-D3YGTErb.js} +447 -326
  9. package/dist/cli.cjs +52 -15
  10. package/dist/cli.js +69 -14
  11. package/dist/core.js +3 -3
  12. package/dist/{hyperlink-_1FAj7Wq.js → create-terminal-Db_-UGVQ.js} +474 -102
  13. package/dist/{dom-renderer-DXx8fWzD.js → dom-renderer-BfwAEQMf.js} +3 -2
  14. package/dist/experimental.cjs +99 -66
  15. package/dist/experimental.js +20 -74
  16. package/dist/hyperlink-clbb_ha_.js +100 -0
  17. package/dist/index.js +8 -8
  18. package/dist/{linkify-D68T7rdW.js → linkify-CL9wwnZN.js} +2 -2
  19. package/dist/markdown.cjs +3376 -13056
  20. package/dist/markdown.d.cts +4 -0
  21. package/dist/markdown.d.ts +4 -0
  22. package/dist/markdown.js +79 -8
  23. package/dist/math-image-BZhmfUeu.js +308 -0
  24. package/dist/mermaid-uEfRItDe.js +1205 -0
  25. package/dist/mermaid.cjs +1089 -3
  26. package/dist/mermaid.d.cts +4 -0
  27. package/dist/mermaid.d.ts +4 -0
  28. package/dist/mermaid.js +7 -5
  29. package/dist/{rect-DUpeDfBg.js → rect-JzWMJxxc.js} +1 -1
  30. package/dist/renderer/cli/stdout-renderer.d.ts +44 -0
  31. package/dist/renderer/terminal-graphics.d.ts +44 -0
  32. package/dist/renderer-dom.js +4 -4
  33. package/dist/{terminal-graphics-Bbe83wSr.js → terminal-graphics-DWZrl3oh.js} +85 -2
  34. package/dist/{tlog-theme-Bul2Na3u.js → tlog-theme-DqnF_zNe.js} +5 -4
  35. package/dist/vue/components/TAgentTerminalGraphic.d.ts +1 -1
  36. package/dist/vue/components/TMarkdownText.d.ts +70 -2
  37. package/dist/vue/components/TMermaidImage.d.ts +733 -0
  38. package/dist/vue/components/TMermaidText.d.ts +2 -2
  39. package/dist/vue/components/TVirtualMarkdown.d.ts +70 -2
  40. package/dist/vue/markdown/ast.d.ts +5 -0
  41. package/dist/vue/markdown/document.d.ts +3 -0
  42. package/dist/vue/markdown/math-image.d.ts +68 -0
  43. package/dist/vue/markdown/math.d.ts +2 -0
  44. package/dist/vue/markdown/theme.d.ts +12 -0
  45. package/dist/vue/markdown/types.d.ts +12 -2
  46. package/dist/vue/mermaid/beautiful-mermaid.d.ts +4 -4
  47. package/dist/vue/mermaid/mermaid-image.d.ts +65 -0
  48. package/dist/vue.js +9 -9
  49. package/package.json +20 -3
  50. package/dist/mermaid-BNhM1jAZ.js +0 -123
  51. package/dist/width-GqllnV8C.js +0 -474
  52. /package/dist/{ansi-palette-CH6jc2Z7.js → ansi-palette-BHF1rN7R.js} +0 -0
@@ -1,11 +1,11 @@
1
- import { hasEncodedControl, sanitizeDomHref } from "./hyperlink-_1FAj7Wq.js";
2
- import { EventZIndexContextKey, currentTextWidthProvider, forEachTextCellSegment, intersectRect, normalizeCellRect, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCellsRange, spaces, textCellWidth$1 as textCellWidth, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-DUpeDfBg.js";
1
+ import { hasEncodedControl, sanitizeDomHref } from "./hyperlink-clbb_ha_.js";
2
+ import { EventZIndexContextKey, currentTextWidthProvider, forEachTextCellSegment, intersectRect, normalizeCellRect, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCellsRange, spaces, textCellWidth$1 as textCellWidth, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-JzWMJxxc.js";
3
3
  import { terminalSelectionRowSpans, terminalSelectionVisibleRowSpans } from "./terminal-selection-4W1SJj4c.js";
4
4
  import { createDebugLogger, isDebugEnabled } from "./debug-logger-CmsGI4of.js";
5
5
  import { applyWheelScroll, createWheelScrollState, resetWheelScrollState } from "./wheel-scroll-DASkEZr-.js";
6
- import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, isTerminalGraphicsProtocol, stableTerminalGraphicNumericId, subscribeTerminalGraphicsOutput } from "./terminal-graphics-Bbe83wSr.js";
6
+ import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, isTerminalGraphicsProtocol, stableTerminalGraphicNumericId, subscribeTerminalGraphicsOutput } from "./terminal-graphics-DWZrl3oh.js";
7
+ import { enqueueMarkdownMathImages, getCachedMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, resolveMarkdownMathColor, subscribeMarkdownMathImage } from "./math-image-BZhmfUeu.js";
7
8
  import { computed, defineComponent, getCurrentInstance, h, inject, markRaw, onBeforeUnmount, ref, shallowRef, watch, watchEffect } from "vue";
8
- import katex from "katex";
9
9
  import { getMarkdown, parseMarkdownToStructure } from "stream-markdown-parser";
10
10
 
11
11
  //#region src/vue/markdown/image.ts
@@ -114,182 +114,6 @@ function readMarkdownImageDimensions(base64) {
114
114
  return null;
115
115
  }
116
116
 
117
- //#endregion
118
- //#region src/vue/markdown/math.ts
119
- const KATEX_TEXT_OPTIONS = Object.freeze({
120
- output: "mathml",
121
- throwOnError: false,
122
- strict: "ignore",
123
- trust: false,
124
- maxSize: 20,
125
- maxExpand: 200
126
- });
127
- const KATEX_VALIDATE_OPTIONS = Object.freeze({
128
- ...KATEX_TEXT_OPTIONS,
129
- throwOnError: true
130
- });
131
- const TEX_SYMBOLS = Object.freeze({
132
- alpha: "α",
133
- beta: "β",
134
- gamma: "γ",
135
- delta: "δ",
136
- epsilon: "ε",
137
- theta: "θ",
138
- lambda: "λ",
139
- mu: "μ",
140
- pi: "π",
141
- rho: "ρ",
142
- sigma: "σ",
143
- tau: "τ",
144
- phi: "φ",
145
- omega: "ω",
146
- Gamma: "Γ",
147
- Delta: "Δ",
148
- Theta: "Θ",
149
- Lambda: "Λ",
150
- Pi: "Π",
151
- Sigma: "Σ",
152
- Phi: "Φ",
153
- Omega: "Ω",
154
- int: "∫",
155
- sum: "∑",
156
- prod: "∏",
157
- infty: "∞",
158
- infinity: "∞",
159
- pm: "±",
160
- times: "×",
161
- cdot: "·",
162
- div: "÷",
163
- le: "≤",
164
- leq: "≤",
165
- ge: "≥",
166
- geq: "≥",
167
- neq: "≠",
168
- ne: "≠",
169
- approx: "≈",
170
- equiv: "≡",
171
- to: "→",
172
- rightarrow: "→",
173
- leftarrow: "←",
174
- leftrightarrow: "↔",
175
- partial: "∂",
176
- nabla: "∇",
177
- sqrt: "√"
178
- });
179
- const SUPPORTED_COMMANDS = new Set([
180
- ...Object.keys(TEX_SYMBOLS),
181
- "frac",
182
- "sqrt"
183
- ]);
184
- function decodeXmlEntities(value) {
185
- return value.replace(/&(?:#(\d+)|#x([0-9a-f]+)|([a-z]+));/gi, (match, dec, hex, named) => {
186
- if (dec) return String.fromCodePoint(Number(dec));
187
- if (hex) return String.fromCodePoint(Number.parseInt(hex, 16));
188
- switch (String(named).toLowerCase()) {
189
- case "amp": return "&";
190
- case "lt": return "<";
191
- case "gt": return ">";
192
- case "quot": return "\"";
193
- case "apos": return "'";
194
- default: return match;
195
- }
196
- });
197
- }
198
- function mathmlTagName(tag) {
199
- const start = tag.startsWith("/") ? 1 : 0;
200
- let end = start;
201
- while (end < tag.length) {
202
- const ch = tag[end];
203
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "/") break;
204
- end++;
205
- }
206
- return tag.slice(start, end).toLowerCase();
207
- }
208
- function textFromKatexMathml(html) {
209
- let readable = "";
210
- let offset = 0;
211
- const lowerHtml = html.toLowerCase();
212
- while (offset < html.length) {
213
- const tagStart = html.indexOf("<", offset);
214
- if (tagStart < 0) {
215
- readable += html.slice(offset);
216
- break;
217
- }
218
- readable += html.slice(offset, tagStart);
219
- const tagEnd = html.indexOf(">", tagStart + 1);
220
- if (tagEnd < 0) break;
221
- const tag = html.slice(tagStart + 1, tagEnd).trim();
222
- const tagName = mathmlTagName(tag);
223
- const closing = tag.startsWith("/");
224
- if (!closing && tagName === "annotation") {
225
- const annotationEnd = lowerHtml.indexOf("</annotation>", tagEnd + 1);
226
- offset = annotationEnd < 0 ? tagEnd + 1 : annotationEnd + 13;
227
- continue;
228
- }
229
- if (!closing && tagName === "mfrac") readable += "(";
230
- else if (closing && tagName === "mfrac") readable += ")";
231
- else if (!closing && tagName === "mspace") readable += " ";
232
- offset = tagEnd + 1;
233
- }
234
- const text = sanitizeInlineText(decodeXmlEntities(readable).replace(/[<>]/g, "").replace(/\s+/g, " ").trim());
235
- return text;
236
- }
237
- function approximateTexToUnicode(tex) {
238
- let out = String(tex ?? "");
239
- out = out.replace(/\\frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g, "$1/$2");
240
- out = out.replace(/\\sqrt\s*\{([^{}]*)\}/g, "√($1)");
241
- out = out.replace(/\\([A-Za-z]+)\b/g, (_match, command) => TEX_SYMBOLS[command] ?? command);
242
- out = out.replace(/\\([^A-Za-z])/g, "$1");
243
- out = out.replace(/[{}]/g, "");
244
- out = out.replace(/\s+/g, " ").trim();
245
- return sanitizeInlineText(out.replace(/[<>]/g, ""));
246
- }
247
- function canRenderTerminalMath(tex) {
248
- const source = String(tex ?? "").trim();
249
- if (!source || source.length > 160) return false;
250
- if (/\\(?:begin|end|left|right|matrix|cases|array|align|color|href|html|includegraphics|class|style|tag|text|operatorname)\b/.test(source)) return false;
251
- const commands = source.matchAll(/\\([A-Za-z]+)\b/g);
252
- for (const match of commands) if (!SUPPORTED_COMMANDS.has(match[1] ?? "")) return false;
253
- try {
254
- katex.renderToString(source, KATEX_VALIDATE_OPTIONS);
255
- } catch {
256
- return false;
257
- }
258
- return true;
259
- }
260
- function renderMarkdownInlineMathSegment(tex) {
261
- const source = String(tex ?? "");
262
- if (!source.trim()) return {
263
- text: "",
264
- supported: false
265
- };
266
- if (!canRenderTerminalMath(source)) return {
267
- text: "",
268
- supported: false
269
- };
270
- try {
271
- const html = katex.renderToString(source, KATEX_TEXT_OPTIONS);
272
- const rendered = /\\frac\b/.test(source) ? approximateTexToUnicode(source) : textFromKatexMathml(html);
273
- if (rendered && !rendered.includes("\\")) return {
274
- text: rendered,
275
- supported: true
276
- };
277
- } catch {
278
- return {
279
- text: "",
280
- supported: false
281
- };
282
- }
283
- const approximate = approximateTexToUnicode(source);
284
- return approximate && !approximate.includes("\\") ? {
285
- text: approximate,
286
- supported: true
287
- } : {
288
- text: "",
289
- supported: false
290
- };
291
- }
292
-
293
117
  //#endregion
294
118
  //#region src/vue/markdown/parser.ts
295
119
  const markdownInstanceCache = new Map();
@@ -370,6 +194,15 @@ function stringProp(node, key) {
370
194
  const value = node[key];
371
195
  return typeof value === "string" ? value : "";
372
196
  }
197
+ /**
198
+ * Temporary fallback: stream-markdown-parser only recovers `![alt](file:///X:/...)`
199
+ * destinations (Windows drive letters, fixed in 1.1.8). Unix-style `file:///...`
200
+ * destinations at inline start are still emitted as `text("!") + link`. Remove this
201
+ * once the parser covers all `file://` destinations.
202
+ */
203
+ function isUnixFileUrlNeedingImageFallback(href) {
204
+ return /^file:\/\//i.test(href) && !/^file:\/\/\/[a-z]:\//i.test(href);
205
+ }
373
206
  function imageSourceFromRaw(raw) {
374
207
  if (!raw.startsWith("![")) return "";
375
208
  const marker = raw.indexOf("](");
@@ -466,132 +299,172 @@ function codeBlockLanguage(node) {
466
299
  }
467
300
  function inlineNodeSegments(nodes, theme, inheritedStyle, options = {}) {
468
301
  const out = [];
469
- for (const node of nodes) switch (node.type) {
470
- case "text":
471
- pushTextSegments(out, stringProp(node, "content"), inheritedStyle);
472
- break;
473
- case "inline":
474
- case "paragraph":
475
- case "heading":
476
- case "list_item":
477
- case "blockquote":
478
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
479
- break;
480
- case "strong":
481
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strong), options));
482
- break;
483
- case "emphasis":
484
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.emphasis), options));
485
- break;
486
- case "strikethrough":
487
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strikethrough), options));
488
- break;
489
- case "highlight":
490
- case "insert":
491
- case "subscript":
492
- case "superscript":
493
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
494
- break;
495
- case "inline_code":
496
- pushTextSegments(out, stringProp(node, "code"), mergeStyle$1(inheritedStyle, theme.inlineCode));
497
- break;
498
- case "hardbreak":
499
- out.push(HARD_BREAK_SEGMENT);
500
- break;
501
- case "link": {
502
- const href = stringProp(node, "href");
503
- const safeHref = href ? sanitizeMarkdownLink(href) : null;
504
- const linkStyle = safeHref ? mergeStyle$1(inheritedStyle, {
505
- ...theme.link,
506
- href: safeHref
507
- }) : inheritedStyle;
508
- const children = nodeChildren(node);
509
- if (children.length) out.push(...inlineNodeSegments(children, theme, linkStyle, options));
510
- else pushTextSegments(out, stringProp(node, "text"), linkStyle);
511
- break;
302
+ for (let index = 0; index < nodes.length; index++) {
303
+ let node = nodes[index];
304
+ const next = nodes[index + 1];
305
+ const text = node.type === "text" ? stringProp(node, "content") : "";
306
+ if (node.type === "text" && text.endsWith("!") && stringProp(node, "raw").endsWith("!") && next?.type === "link" && isUnixFileUrlNeedingImageFallback(stringProp(next, "href"))) {
307
+ const src = stringProp(next, "href");
308
+ if (sanitizeMarkdownImageSource(src)) {
309
+ pushTextSegments(out, text.slice(0, -1), inheritedStyle);
310
+ node = {
311
+ type: "image",
312
+ src,
313
+ alt: stringProp(next, "text"),
314
+ raw: `!${stringProp(next, "raw")}`
315
+ };
316
+ index++;
317
+ }
512
318
  }
513
- case "image": {
514
- const raw = stringProp(node, "raw");
515
- const rawSource = stringProp(node, "src") || imageSourceFromRaw(raw);
516
- const source = sanitizeMarkdownImageSource(rawSource);
517
- const alt = stringProp(node, "alt") || imageFallbackText(raw, rawSource, source?.src);
518
- if (source) {
519
- const size = options.imageSize;
520
- const imageLinkStyle = mergeStyle$1(inheritedStyle, {
319
+ switch (node.type) {
320
+ case "text":
321
+ pushTextSegments(out, stringProp(node, "content"), inheritedStyle);
322
+ break;
323
+ case "inline":
324
+ case "paragraph":
325
+ case "heading":
326
+ case "list_item":
327
+ case "blockquote":
328
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
329
+ break;
330
+ case "strong":
331
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strong), options));
332
+ break;
333
+ case "emphasis":
334
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.emphasis), options));
335
+ break;
336
+ case "strikethrough":
337
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strikethrough), options));
338
+ break;
339
+ case "highlight":
340
+ case "insert":
341
+ case "subscript":
342
+ case "superscript":
343
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
344
+ break;
345
+ case "inline_code":
346
+ pushTextSegments(out, stringProp(node, "code"), mergeStyle$1(inheritedStyle, theme.inlineCode));
347
+ break;
348
+ case "hardbreak":
349
+ out.push(HARD_BREAK_SEGMENT);
350
+ break;
351
+ case "link": {
352
+ const href = stringProp(node, "href");
353
+ const safeHref = href ? sanitizeMarkdownLink(href) : null;
354
+ const linkStyle = safeHref ? mergeStyle$1(inheritedStyle, {
521
355
  ...theme.link,
522
- href: source.src
523
- });
524
- const sourceDimensions = readMarkdownImageDimensions(source.base64);
525
- const graphicBase = {
526
- kind: "image",
527
- src: source.src,
528
- alt,
529
- ...source.mime ? { mime: source.mime } : {},
530
- ...source.base64 ? { base64: source.base64 } : {},
531
- ...sourceDimensions ? {
532
- naturalWidth: sourceDimensions.width,
533
- naturalHeight: sourceDimensions.height
534
- } : {},
535
- ...resolveImageDisplaySize(alt, size, sourceDimensions?.width, sourceDimensions?.height)
536
- };
537
- const resolved = normalizeImageResolverResult(options.imageResolver?.(graphicBase));
538
- const base64 = resolved.base64 || source.base64;
539
- if (base64) {
540
- const dimensions = readMarkdownImageDimensions(base64) ?? sourceDimensions;
541
- const graphic = {
542
- ...graphicBase,
543
- base64,
544
- ...resolved.originalBase64 ? { originalBase64: resolved.originalBase64 } : {},
545
- ...resolved.mime ? { mime: resolved.mime } : {},
546
- ...resolved.originalMime ? { originalMime: resolved.originalMime } : {},
547
- ...dimensions ? {
548
- naturalWidth: dimensions.width,
549
- naturalHeight: dimensions.height
356
+ href: safeHref
357
+ }) : inheritedStyle;
358
+ const children = nodeChildren(node);
359
+ if (children.length) out.push(...inlineNodeSegments(children, theme, linkStyle, options));
360
+ else pushTextSegments(out, stringProp(node, "text"), linkStyle);
361
+ break;
362
+ }
363
+ case "image": {
364
+ const raw = stringProp(node, "raw");
365
+ const rawSource = stringProp(node, "src") || imageSourceFromRaw(raw);
366
+ const source = sanitizeMarkdownImageSource(rawSource);
367
+ const alt = stringProp(node, "alt") || imageFallbackText(raw, rawSource, source?.src);
368
+ if (source) {
369
+ const size = options.imageSize;
370
+ const imageLinkStyle = mergeStyle$1(inheritedStyle, {
371
+ ...theme.link,
372
+ href: source.src
373
+ });
374
+ const sourceDimensions = readMarkdownImageDimensions(source.base64);
375
+ const graphicBase = {
376
+ kind: "image",
377
+ src: source.src,
378
+ alt,
379
+ ...source.mime ? { mime: source.mime } : {},
380
+ ...source.base64 ? { base64: source.base64 } : {},
381
+ ...sourceDimensions ? {
382
+ naturalWidth: sourceDimensions.width,
383
+ naturalHeight: sourceDimensions.height
550
384
  } : {},
551
- ...resolveImageDisplaySize(alt, size, dimensions?.width, dimensions?.height)
385
+ ...resolveImageDisplaySize(alt, size, sourceDimensions?.width, sourceDimensions?.height)
552
386
  };
553
- pushTextSegments(out, alt, imageLinkStyle, graphic);
554
- } else pushTextSegments(out, alt, imageLinkStyle);
555
- } else pushTextSegments(out, alt, mergeStyle$1(inheritedStyle, theme.link));
556
- break;
557
- }
558
- case "checkbox":
559
- case "checkbox_input":
560
- pushTextSegments(out, booleanProp(node, "checked") ? "[x]" : "[ ]", inheritedStyle);
561
- break;
562
- case "emoji":
563
- pushTextSegments(out, stringProp(node, "markup") || stringProp(node, "name"), inheritedStyle);
564
- break;
565
- case "math_inline":
566
- {
567
- const source = stringProp(node, "content");
568
- const raw = stringProp(node, "raw") || `$${source}$`;
569
- const rendered = renderMarkdownInlineMathSegment(source);
570
- pushTextSegments(out, rendered.supported ? rendered.text : raw, mergeStyle$1(inheritedStyle, theme.inlineCode), void 0, {
571
- source,
572
- raw,
573
- rendered: rendered.supported
574
- });
575
- }
576
- break;
577
- case "reference":
578
- case "footnote_reference":
579
- case "footnote_anchor":
580
- pushTextSegments(out, stringProp(node, "raw"), inheritedStyle);
581
- break;
582
- case "html_inline":
583
- pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
584
- break;
585
- case "html_block":
586
- pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
587
- break;
588
- default:
589
- if (nodeChildren(node).length) {
590
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
387
+ const resolved = normalizeImageResolverResult(options.imageResolver?.(graphicBase));
388
+ const base64 = resolved.base64 || source.base64;
389
+ if (base64) {
390
+ const dimensions = readMarkdownImageDimensions(base64) ?? sourceDimensions;
391
+ const graphic = {
392
+ ...graphicBase,
393
+ base64,
394
+ ...resolved.originalBase64 ? { originalBase64: resolved.originalBase64 } : {},
395
+ ...resolved.mime ? { mime: resolved.mime } : {},
396
+ ...resolved.originalMime ? { originalMime: resolved.originalMime } : {},
397
+ ...dimensions ? {
398
+ naturalWidth: dimensions.width,
399
+ naturalHeight: dimensions.height
400
+ } : {},
401
+ ...resolveImageDisplaySize(alt, size, dimensions?.width, dimensions?.height)
402
+ };
403
+ pushTextSegments(out, alt, imageLinkStyle, graphic);
404
+ } else pushTextSegments(out, alt, imageLinkStyle);
405
+ } else pushTextSegments(out, alt, mergeStyle$1(inheritedStyle, theme.link));
591
406
  break;
592
407
  }
593
- pushTextSegments(out, stringProp(node, "raw"), inheritedStyle);
594
- break;
408
+ case "checkbox":
409
+ case "checkbox_input":
410
+ pushTextSegments(out, booleanProp(node, "checked") ? "[x]" : "[ ]", inheritedStyle);
411
+ break;
412
+ case "emoji":
413
+ pushTextSegments(out, stringProp(node, "markup") || stringProp(node, "name"), inheritedStyle);
414
+ break;
415
+ case "math_inline":
416
+ {
417
+ const source = stringProp(node, "content");
418
+ const markup = stringProp(node, "markup") || "$";
419
+ const raw = stringProp(node, "raw") || `${markup}${source}${markup}`;
420
+ const mathStyle = mergeStyle$1(inheritedStyle, theme.inlineCode);
421
+ const tex = sanitizeInlineText(source);
422
+ const mathEnabled = options.math?.enabled !== false;
423
+ const cached = tex && mathEnabled ? getCachedMarkdownMathImage(tex, "inline", options.math) : null;
424
+ if (cached) pushTextSegments(out, raw, mathStyle, {
425
+ kind: "math",
426
+ tex,
427
+ raw,
428
+ base64: cached.base64,
429
+ displayWidth: cached.widthCells,
430
+ displayHeight: cached.heightCells
431
+ }, {
432
+ source: tex,
433
+ raw,
434
+ rendered: true
435
+ });
436
+ else pushTextSegments(out, raw, mathStyle, void 0, tex && mathEnabled ? {
437
+ source: tex,
438
+ raw,
439
+ rendered: false,
440
+ pendingImage: true,
441
+ mode: "inline"
442
+ } : {
443
+ source,
444
+ raw,
445
+ rendered: false
446
+ });
447
+ }
448
+ break;
449
+ case "reference":
450
+ case "footnote_reference":
451
+ case "footnote_anchor":
452
+ pushTextSegments(out, stringProp(node, "raw"), inheritedStyle);
453
+ break;
454
+ case "html_inline":
455
+ pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
456
+ break;
457
+ case "html_block":
458
+ pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
459
+ break;
460
+ default:
461
+ if (nodeChildren(node).length) {
462
+ out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle, options));
463
+ break;
464
+ }
465
+ pushTextSegments(out, stringProp(node, "raw"), inheritedStyle);
466
+ break;
467
+ }
595
468
  }
596
469
  return out;
597
470
  }
@@ -607,14 +480,16 @@ function inlineBlock(key, segments, context) {
607
480
  function blockFromParagraph(node, key, context, theme) {
608
481
  return inlineBlock(key, inlineNodeSegments(nodeChildren(node), theme, void 0, {
609
482
  imageResolver: context.imageResolver,
610
- imageSize: context.imageSize
483
+ imageSize: context.imageSize,
484
+ math: context.math
611
485
  }), context);
612
486
  }
613
487
  function blockFromHeading(node, key, context, theme) {
614
488
  const level = Math.min(6, Math.max(1, Math.floor(numberProp(node, "level") ?? 1)));
615
489
  return inlineBlock(key, inlineNodeSegments(nodeChildren(node), theme, theme.heading[level - 1], {
616
490
  imageResolver: context.imageResolver,
617
- imageSize: context.imageSize
491
+ imageSize: context.imageSize,
492
+ math: context.math
618
493
  }), context);
619
494
  }
620
495
  function blockFromCodeBlock(node, key, context, theme) {
@@ -633,7 +508,10 @@ function blockFromHtmlBlock(node, key, context, theme) {
633
508
  type: "html_block",
634
509
  raw: stringProp(node, "raw"),
635
510
  content: stringProp(node, "content")
636
- }], theme, void 0, { imageResolver: context.imageResolver }), context);
511
+ }], theme, void 0, {
512
+ imageResolver: context.imageResolver,
513
+ math: context.math
514
+ }), context);
637
515
  }
638
516
  function tableRows$1(node, key) {
639
517
  const value = node[key];
@@ -653,14 +531,16 @@ function blockFromTable(node, key, context, theme) {
653
531
  const header = tableCells(headerRow ?? { type: "table_row" }).map((cell) => ({
654
532
  segments: inlineNodeSegments(nodeChildren(cell), theme, theme.strong, {
655
533
  imageResolver: context.imageResolver,
656
- imageSize: context.imageSize
534
+ imageSize: context.imageSize,
535
+ math: context.math
657
536
  }),
658
537
  align: tableCellAlign(cell)
659
538
  }));
660
539
  const rows = tableRows$1(node, "rows").map((row) => tableCells(row).map((cell) => ({
661
540
  segments: inlineNodeSegments(nodeChildren(cell), theme, void 0, {
662
541
  imageResolver: context.imageResolver,
663
- imageSize: context.imageSize
542
+ imageSize: context.imageSize,
543
+ math: context.math
664
544
  }),
665
545
  align: tableCellAlign(cell)
666
546
  })));
@@ -698,11 +578,13 @@ function listItemBlocks(item, index, ordered, start, context, theme, keyPrefix)
698
578
  const indentSegment = { text: spaces(markerCells) };
699
579
  const firstContext = {
700
580
  prefixSegments: [...context.prefixSegments, markerSegment],
701
- continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment]
581
+ continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment],
582
+ math: context.math
702
583
  };
703
584
  const nestedContext = {
704
585
  prefixSegments: [...context.continuationPrefixSegments, indentSegment],
705
- continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment]
586
+ continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment],
587
+ math: context.math
706
588
  };
707
589
  const children = nodeChildren(item);
708
590
  if (!children.length) return [inlineBlock(`${keyPrefix}-empty`, [], firstContext)];
@@ -734,6 +616,61 @@ function listBlocks(node, context, theme, keyPrefix) {
734
616
  for (let i = 0; i < items.length; i++) out.push(...listItemBlocks(items[i], i, booleanProp(node, "ordered"), numberProp(node, "start") ?? 1, context, theme, `${keyPrefix}-${i}`));
735
617
  return out;
736
618
  }
619
+ /**
620
+ * Block-level display math (`$...$`). When a rasterized PNG is available this
621
+ * becomes a graphic segment rendered through the terminal graphics pipeline
622
+ * (Kitty/iTerm2/Sixel); otherwise it is wrapped in a box with the raw TeX text
623
+ * (also the permanent presentation when graphics/raster support is missing).
624
+ * Clicking either form emits `mathAction` so consumers can copy the raw content.
625
+ */
626
+ function blockMathSegments(source, raw, math, theme) {
627
+ const tex = sanitizeInlineText(source);
628
+ const canUseImage = Boolean(tex) && math.enabled !== false;
629
+ const cached = canUseImage ? getCachedMarkdownMathImage(tex, "display", math) : null;
630
+ if (canUseImage && cached) return [{
631
+ text: raw,
632
+ style: theme.math,
633
+ graphic: {
634
+ kind: "math",
635
+ tex,
636
+ raw,
637
+ base64: cached.base64,
638
+ displayWidth: cached.widthCells,
639
+ displayHeight: cached.heightCells
640
+ },
641
+ mathAction: {
642
+ source: tex,
643
+ raw,
644
+ rendered: true
645
+ }
646
+ }];
647
+ const maxWidth = Math.max(8, Math.floor(math.maxWidthCells ?? 0) || 40);
648
+ const rawCells = textCellWidth(raw);
649
+ const inner = Math.min(Math.max(1, rawCells + 2), maxWidth);
650
+ const pad = Math.max(0, inner - rawCells);
651
+ return [
652
+ {
653
+ text: `┌${"─".repeat(inner)}┐`,
654
+ style: theme.math
655
+ },
656
+ HARD_BREAK_SEGMENT,
657
+ {
658
+ text: `│ ${raw}${" ".repeat(pad)}│`,
659
+ style: theme.math,
660
+ mathAction: {
661
+ source: tex,
662
+ raw,
663
+ rendered: false,
664
+ pendingImage: true
665
+ }
666
+ },
667
+ HARD_BREAK_SEGMENT,
668
+ {
669
+ text: `└${"─".repeat(inner)}┘`,
670
+ style: theme.math
671
+ }
672
+ ];
673
+ }
737
674
  function nodeToBlocks(node, context, theme, keyPrefix) {
738
675
  switch (node.type) {
739
676
  case "paragraph":
@@ -752,6 +689,7 @@ function nodeToBlocks(node, context, theme, keyPrefix) {
752
689
  style: theme.thematicBreak,
753
690
  prefixSegments: context.prefixSegments
754
691
  }];
692
+ case "math_block": return [inlineBlock(keyPrefix, blockMathSegments(stringProp(node, "content"), stringProp(node, "raw") || `$${stringProp(node, "content")}$`, context.math ?? { enabled: true }, theme), context)];
755
693
  case "blockquote": {
756
694
  const quoteSegment = {
757
695
  text: "│ ",
@@ -761,7 +699,8 @@ function nodeToBlocks(node, context, theme, keyPrefix) {
761
699
  prefixSegments: [...context.prefixSegments, quoteSegment],
762
700
  continuationPrefixSegments: [...context.continuationPrefixSegments, quoteSegment],
763
701
  imageResolver: context.imageResolver,
764
- imageSize: context.imageSize
702
+ imageSize: context.imageSize,
703
+ math: context.math
765
704
  };
766
705
  return childSequenceToBlocks(nodeChildren(node), quoteContext, theme, keyPrefix);
767
706
  }
@@ -771,22 +710,30 @@ function nodeToBlocks(node, context, theme, keyPrefix) {
771
710
  case "table": return [blockFromTable(node, keyPrefix, context, theme)];
772
711
  case "inline": return [inlineBlock(keyPrefix, inlineNodeSegments(nodeChildren(node), theme, void 0, {
773
712
  imageResolver: context.imageResolver,
774
- imageSize: context.imageSize
713
+ imageSize: context.imageSize,
714
+ math: context.math
775
715
  }), context)];
776
716
  case "html_block":
777
717
  if ("content" in node) return [blockFromHtmlBlock(node, keyPrefix, context, theme)];
778
718
  break;
779
- case "text": return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme, void 0, { imageResolver: context.imageResolver }), context)];
719
+ case "text": return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme, void 0, {
720
+ imageResolver: context.imageResolver,
721
+ math: context.math
722
+ }), context)];
780
723
  }
781
724
  if (nodeChildren(node).length) return childSequenceToBlocks(nodeChildren(node), context, theme, keyPrefix);
782
- return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme, void 0, { imageResolver: context.imageResolver }), context)];
725
+ return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme, void 0, {
726
+ imageResolver: context.imageResolver,
727
+ math: context.math
728
+ }), context)];
783
729
  }
784
730
  function markdownAstToBlocks(nodes, theme, options = {}) {
785
731
  const blocks = trimBlockArray(childSequenceToBlocks(nodes, {
786
732
  prefixSegments: EMPTY_PREFIX,
787
733
  continuationPrefixSegments: EMPTY_PREFIX,
788
734
  imageResolver: options.imageResolver,
789
- imageSize: options.imageSize
735
+ imageSize: options.imageSize,
736
+ math: options.math
790
737
  }, theme, "md"));
791
738
  return blocks.length ? blocks : [{
792
739
  type: "blank",
@@ -935,7 +882,8 @@ function wrapLineSegments(source, prefixSegments, continuationPrefixSegments, wi
935
882
  style: segment.style,
936
883
  cells,
937
884
  graphic: segment.graphic,
938
- fallbackText: segment.text
885
+ fallbackText: segment.text,
886
+ ...segment.mathAction ? { mathAction: segment.mathAction } : {}
939
887
  });
940
888
  row.remaining -= cells;
941
889
  continue;
@@ -1203,7 +1151,8 @@ function styleSignature(style) {
1203
1151
  function inlineSegmentSignature(segment) {
1204
1152
  const graphic = segment.graphic ? [
1205
1153
  segment.graphic.kind,
1206
- segment.graphic.src,
1154
+ segment.graphic.src ?? "",
1155
+ segment.graphic.tex ?? "",
1207
1156
  segment.graphic.alt ?? "",
1208
1157
  segment.graphic.mime ?? "",
1209
1158
  segment.graphic.base64 ?? "",
@@ -1378,7 +1327,8 @@ const DEFAULT_TUI_MARKDOWN_THEME = Object.freeze({
1378
1327
  }),
1379
1328
  codeBlock: freezeStyle({ fg: "yellowBright" }),
1380
1329
  thematicBreak: freezeStyle({ dim: true }),
1381
- html: freezeStyle({ dim: true })
1330
+ html: freezeStyle({ dim: true }),
1331
+ math: freezeStyle({ fg: "cyanBright" })
1382
1332
  });
1383
1333
  function stableSerialize(value) {
1384
1334
  if (value == null) return "null";
@@ -1404,7 +1354,8 @@ function resolveTuiMarkdownTheme(overrides) {
1404
1354
  listMarker: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.listMarker, overrides.listMarker),
1405
1355
  codeBlock: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.codeBlock, overrides.codeBlock),
1406
1356
  thematicBreak: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.thematicBreak, overrides.thematicBreak),
1407
- html: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.html, overrides.html)
1357
+ html: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.html, overrides.html),
1358
+ math: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.math, overrides.math)
1408
1359
  });
1409
1360
  }
1410
1361
 
@@ -1420,7 +1371,8 @@ function buildMarkdownBlocks(content, parser, options) {
1420
1371
  const nodes = parser.parse(content, options?.final ?? true);
1421
1372
  const blocks = markdownAstToBlocks(nodes, theme, {
1422
1373
  imageResolver: options?.imageResolver,
1423
- imageSize: options?.imageSize
1374
+ imageSize: options?.imageSize,
1375
+ math: options?.math
1424
1376
  });
1425
1377
  return {
1426
1378
  nodes,
@@ -1607,11 +1559,12 @@ function findMarkdownMathActionAt(rows, point, options) {
1607
1559
  if (!math || segmentEnd <= clipStart || segmentStart >= clipEnd) continue;
1608
1560
  const visibleStart = Math.max(segmentStart, clipStart);
1609
1561
  const visibleEnd = Math.min(segmentEnd, clipEnd);
1562
+ const graphicHeight = segment.graphic ? Math.max(1, Math.floor(segment.graphic.displayHeight ?? 1)) : 1;
1610
1563
  const rect = {
1611
1564
  x: r.x + visibleStart - clipStart,
1612
1565
  y: r.y + rowIndex - firstRow,
1613
1566
  w: visibleEnd - visibleStart,
1614
- h: 1
1567
+ h: graphicHeight
1615
1568
  };
1616
1569
  if (point.cellX < rect.x || point.cellY < rect.y || point.cellX >= rect.x + rect.w || point.cellY >= rect.y + rect.h) continue;
1617
1570
  return {
@@ -1627,6 +1580,97 @@ function findMarkdownMathActionAt(rows, point, options) {
1627
1580
  return null;
1628
1581
  }
1629
1582
 
1583
+ //#endregion
1584
+ //#region src/vue/markdown/math.ts
1585
+ let loadedKatex = null;
1586
+ let katexLoad = null;
1587
+ const katexListeners = new Set();
1588
+ function resolveKatexRenderer(value) {
1589
+ if (!value || typeof value !== "object") return null;
1590
+ const renderToString = value.renderToString;
1591
+ return typeof renderToString === "function" ? { renderToString } : null;
1592
+ }
1593
+ function subscribeMarkdownMathRenderer(listener) {
1594
+ katexListeners.add(listener);
1595
+ return () => katexListeners.delete(listener);
1596
+ }
1597
+ async function loadMarkdownMathRenderer() {
1598
+ if (loadedKatex) return true;
1599
+ if (!katexLoad) katexLoad = import("katex").then((mod) => {
1600
+ const renderer = resolveKatexRenderer(mod.default) ?? resolveKatexRenderer(mod);
1601
+ if (!renderer) return false;
1602
+ loadedKatex = renderer;
1603
+ for (const listener of katexListeners) listener();
1604
+ return true;
1605
+ }).catch(() => false);
1606
+ return katexLoad;
1607
+ }
1608
+ const KATEX_TEXT_OPTIONS = Object.freeze({
1609
+ output: "mathml",
1610
+ throwOnError: false,
1611
+ strict: "ignore",
1612
+ trust: false,
1613
+ maxSize: 20,
1614
+ maxExpand: 200
1615
+ });
1616
+ const KATEX_VALIDATE_OPTIONS = Object.freeze({
1617
+ ...KATEX_TEXT_OPTIONS,
1618
+ throwOnError: true
1619
+ });
1620
+ const TEX_SYMBOLS = Object.freeze({
1621
+ alpha: "α",
1622
+ beta: "β",
1623
+ gamma: "γ",
1624
+ delta: "δ",
1625
+ epsilon: "ε",
1626
+ theta: "θ",
1627
+ lambda: "λ",
1628
+ mu: "μ",
1629
+ pi: "π",
1630
+ rho: "ρ",
1631
+ sigma: "σ",
1632
+ tau: "τ",
1633
+ phi: "φ",
1634
+ omega: "ω",
1635
+ Gamma: "Γ",
1636
+ Delta: "Δ",
1637
+ Theta: "Θ",
1638
+ Lambda: "Λ",
1639
+ Pi: "Π",
1640
+ Sigma: "Σ",
1641
+ Phi: "Φ",
1642
+ Omega: "Ω",
1643
+ int: "∫",
1644
+ sum: "∑",
1645
+ prod: "∏",
1646
+ infty: "∞",
1647
+ infinity: "∞",
1648
+ pm: "±",
1649
+ times: "×",
1650
+ cdot: "·",
1651
+ div: "÷",
1652
+ le: "≤",
1653
+ leq: "≤",
1654
+ ge: "≥",
1655
+ geq: "≥",
1656
+ neq: "≠",
1657
+ ne: "≠",
1658
+ approx: "≈",
1659
+ equiv: "≡",
1660
+ to: "→",
1661
+ rightarrow: "→",
1662
+ leftarrow: "←",
1663
+ leftrightarrow: "↔",
1664
+ partial: "∂",
1665
+ nabla: "∇",
1666
+ sqrt: "√"
1667
+ });
1668
+ const SUPPORTED_COMMANDS = new Set([
1669
+ ...Object.keys(TEX_SYMBOLS),
1670
+ "frac",
1671
+ "sqrt"
1672
+ ]);
1673
+
1630
1674
  //#endregion
1631
1675
  //#region src/vue/markdown/render.ts
1632
1676
  const mergedStyleCache = new WeakMap();
@@ -1653,7 +1697,7 @@ function mergeStyle(base, overlay) {
1653
1697
  }
1654
1698
  const activeMarkdownImageGraphics = new WeakMap();
1655
1699
  function markdownImageGraphicId(segment, identity) {
1656
- return `md-image:${stableTerminalGraphicNumericId(`${identity}:${segment.src}:${segment.displayWidth ?? 0}:${segment.displayHeight ?? 0}:${segment.base64 ?? ""}`)}`;
1700
+ return `md-image:${stableTerminalGraphicNumericId(`${identity}:${segment.src ?? segment.tex ?? ""}:${segment.displayWidth ?? 0}:${segment.displayHeight ?? 0}:${segment.base64 ?? ""}`)}`;
1657
1701
  }
1658
1702
  function rememberMarkdownImageGraphic(terminal, id, rect) {
1659
1703
  let active = activeMarkdownImageGraphics.get(terminal);
@@ -1679,7 +1723,7 @@ function clearStaleMarkdownImageGraphicsForRow(terminal, y, keepIds, keepVisible
1679
1723
  }
1680
1724
  }
1681
1725
  function canAttemptMarkdownImageGraphic(terminal, segment) {
1682
- if (segment.kind !== "image" || !segment.base64) return false;
1726
+ if (!segment.base64) return false;
1683
1727
  const output = getTerminalGraphicsOutput(terminal);
1684
1728
  const protocol = output?.capabilities.preferredProtocol;
1685
1729
  return Boolean(output?.capabilities.supported && isTerminalGraphicsProtocol(protocol));
@@ -1693,7 +1737,7 @@ function clearMarkdownImageGraphics(terminal, rect) {
1693
1737
  }
1694
1738
  }
1695
1739
  function queueMarkdownImageGraphic(terminal, segment, identity, rect) {
1696
- if (segment.kind !== "image" || !segment.base64) return "unavailable";
1740
+ if (!segment.base64) return "unavailable";
1697
1741
  const output = getTerminalGraphicsOutput(terminal);
1698
1742
  const protocol = output?.capabilities.preferredProtocol;
1699
1743
  if (!output?.capabilities.supported || !isTerminalGraphicsProtocol(protocol)) return "unavailable";
@@ -1735,7 +1779,7 @@ function queueMarkdownImageGraphic(terminal, segment, identity, rect) {
1735
1779
  sequence,
1736
1780
  resizeSequence,
1737
1781
  clearSequence,
1738
- fallbackText: segment.alt ?? "image"
1782
+ fallbackText: segment.alt ?? (segment.kind === "math" ? segment.raw ?? segment.tex ?? "math" : "image")
1739
1783
  });
1740
1784
  markdownImageDebug(`queue id=${id}`, `accepted=${accepted ? "true" : "false"}`, `x=${rect.x}`, `y=${rect.y}`, `w=${width}`, `h=${height}`, `visibleW=${rect.w}`, `displayW=${segment.displayWidth ?? ""}`, `displayH=${segment.displayHeight ?? ""}`);
1741
1785
  if (accepted) rememberMarkdownImageGraphic(terminal, id, {
@@ -1839,7 +1883,7 @@ function paintMarkdownVisualRow(terminal, row, options) {
1839
1883
  const queuedGraphic = queueResult === "queued";
1840
1884
  const suppressFallback = queuedGraphic || queueResult === "rejected" || graphicAvailable === true;
1841
1885
  if (queuedGraphic) queuedGraphics.add(markdownImageGraphicId(segment.graphic, `${row.key}:${segmentIndex}`));
1842
- terminal.write(suppressFallback ? spaces(visibleCells) : sliceByCellsRange(segment.fallbackText ?? segment.graphic.alt ?? "image", 0, visibleCells) || spaces(visibleCells), {
1886
+ terminal.write(suppressFallback ? spaces(visibleCells) : sliceByCellsRange(segment.fallbackText ?? segment.graphic.alt ?? (segment.graphic.kind === "math" ? segment.graphic.raw ?? segment.graphic.tex ?? "math" : "image"), 0, visibleCells) || spaces(visibleCells), {
1843
1887
  x: cellX,
1844
1888
  y: options.y,
1845
1889
  style: suppressFallback ? options.baseStyle : segmentStyle
@@ -2031,6 +2075,34 @@ const TVirtualMarkdown = defineComponent({
2031
2075
  type: Boolean,
2032
2076
  default: true
2033
2077
  },
2078
+ mathImages: {
2079
+ type: Boolean,
2080
+ default: true
2081
+ },
2082
+ mathCellWidthPx: {
2083
+ type: Number,
2084
+ default: void 0
2085
+ },
2086
+ mathCellHeightPx: {
2087
+ type: Number,
2088
+ default: void 0
2089
+ },
2090
+ mathScale: {
2091
+ type: Number,
2092
+ default: void 0
2093
+ },
2094
+ mathColor: {
2095
+ type: String,
2096
+ default: void 0
2097
+ },
2098
+ mathMaxWidthCells: {
2099
+ type: Number,
2100
+ default: void 0
2101
+ },
2102
+ mathBaselineRatio: {
2103
+ type: Number,
2104
+ default: void 0
2105
+ },
2034
2106
  imageActions: {
2035
2107
  type: Boolean,
2036
2108
  default: false
@@ -2081,7 +2153,42 @@ const TVirtualMarkdown = defineComponent({
2081
2153
  const graphicsOutputVersion = shallowRef(getTerminalGraphicsOutputVersion(terminal));
2082
2154
  const unsubscribeGraphicsOutput = subscribeTerminalGraphicsOutput(terminal, () => {
2083
2155
  graphicsOutputVersion.value = getTerminalGraphicsOutputVersion(terminal);
2156
+ enqueuePendingMathImages();
2157
+ });
2158
+ const unsubscribeMathRenderer = subscribeMarkdownMathRenderer(() => {
2159
+ scheduleRebuild();
2084
2160
  });
2161
+ const unsubscribeMathImage = subscribeMarkdownMathImage(() => {
2162
+ scheduleRebuild();
2163
+ });
2164
+ function mathOptions() {
2165
+ if (props.mathImages === false) return { enabled: false };
2166
+ const resolvedColor = props.mathColor ?? resolveMarkdownMathColor(defaultStyle.value.fg);
2167
+ return {
2168
+ enabled: true,
2169
+ ...props.mathCellWidthPx != null ? { cellWidthPx: props.mathCellWidthPx } : {},
2170
+ ...props.mathCellHeightPx != null ? { cellHeightPx: props.mathCellHeightPx } : {},
2171
+ ...props.mathScale != null ? { scale: props.mathScale } : {},
2172
+ ...resolvedColor != null ? { color: resolvedColor } : {},
2173
+ ...props.mathBaselineRatio != null ? { baselineRatio: props.mathBaselineRatio } : {},
2174
+ maxWidthCells: props.mathMaxWidthCells ?? props.w
2175
+ };
2176
+ }
2177
+ function enqueuePendingMathImages() {
2178
+ if (props.mathImages === false) return;
2179
+ if (!getTerminalGraphicsOutput(terminal)?.capabilities.supported) return;
2180
+ if (!isMarkdownMathImageRendererReady()) {
2181
+ loadMarkdownMathImageRenderer().then((ready) => {
2182
+ if (ready && alive) scheduleRebuild();
2183
+ });
2184
+ return;
2185
+ }
2186
+ const h$1 = Math.max(0, Math.floor(props.h ?? absRect.value.h));
2187
+ enqueueMarkdownMathImages(rows.value, mathOptions(), {
2188
+ firstRow: internalScrollTop.value,
2189
+ lastRow: internalScrollTop.value + h$1
2190
+ });
2191
+ }
2085
2192
  watch(() => `${props.streaming ? 1 : 0}:${(props.customHtmlTags ?? []).join("\0")}`, () => {
2086
2193
  parser.value = markRaw(createTuiMarkdownParser({
2087
2194
  streaming: props.streaming,
@@ -2103,13 +2210,15 @@ const TVirtualMarkdown = defineComponent({
2103
2210
  minHeight: props.imageMinHeight,
2104
2211
  maxHeight: props.imageMaxHeight,
2105
2212
  preserveAspectRatio: props.imagePreserveAspectRatio
2106
- }
2213
+ },
2214
+ math: mathOptions()
2107
2215
  }).blocks;
2108
2216
  return layoutMarkdownBlocksCached(blocks, props.w, layoutCache);
2109
2217
  });
2110
2218
  layoutCache = markRaw(nextLayout.cache);
2111
2219
  const nextRows = nextLayout.rows;
2112
2220
  rows.value = markRaw(nextRows);
2221
+ enqueuePendingMathImages();
2113
2222
  reconcileScrollTop();
2114
2223
  const nextScrollTop = internalScrollTop.value;
2115
2224
  const nextVisibleRows = visibleRowSignatures(nextRows, nextScrollTop);
@@ -2234,6 +2343,9 @@ const TVirtualMarkdown = defineComponent({
2234
2343
  pendingSelectionScrollFocusRemap = false;
2235
2344
  applyControlledScrollTop(props.scrollTop, remap);
2236
2345
  });
2346
+ watch(internalScrollTop, () => {
2347
+ enqueuePendingMathImages();
2348
+ });
2237
2349
  watch([
2238
2350
  () => props.content,
2239
2351
  () => props.blocks,
@@ -2246,7 +2358,14 @@ const TVirtualMarkdown = defineComponent({
2246
2358
  () => props.imageMaxWidth,
2247
2359
  () => props.imageMinHeight,
2248
2360
  () => props.imageMaxHeight,
2249
- () => props.imagePreserveAspectRatio
2361
+ () => props.imagePreserveAspectRatio,
2362
+ () => props.mathImages,
2363
+ () => props.mathCellWidthPx,
2364
+ () => props.mathCellHeightPx,
2365
+ () => props.mathScale,
2366
+ () => props.mathColor,
2367
+ () => props.mathMaxWidthCells,
2368
+ () => props.mathBaselineRatio
2250
2369
  ], () => {
2251
2370
  scheduleRebuild();
2252
2371
  }, { immediate: true });
@@ -2449,6 +2568,8 @@ const TVirtualMarkdown = defineComponent({
2449
2568
  alive = false;
2450
2569
  rebuildVersion++;
2451
2570
  unsubscribeGraphicsOutput();
2571
+ unsubscribeMathRenderer();
2572
+ unsubscribeMathImage();
2452
2573
  clearMarkdownImageGraphics(terminal, fullRect.value);
2453
2574
  });
2454
2575
  useRenderNode(() => ({
@@ -2633,4 +2754,4 @@ function createMarkdownBlockSource(options) {
2633
2754
  }
2634
2755
 
2635
2756
  //#endregion
2636
- export { TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, clearMarkdownImageGraphics, collectVisibleMarkdownImageGraphicIds, createMarkdownBlockSource, createTuiMarkdownParser, findMarkdownImageActionAt, findMarkdownLinkActionAt, findMarkdownMathActionAt, isSafeMarkdownLink, layoutMarkdownBlocks, markdownThemeSignature, paintMarkdownVisualRow };
2757
+ export { TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, clearMarkdownImageGraphics, collectVisibleMarkdownImageGraphicIds, createMarkdownBlockSource, createTuiMarkdownParser, findMarkdownImageActionAt, findMarkdownLinkActionAt, findMarkdownMathActionAt, isSafeMarkdownLink, layoutMarkdownBlocks, loadMarkdownMathRenderer, markdownThemeSignature, paintMarkdownVisualRow, subscribeMarkdownMathRenderer };