@simon_he/vue-tui 1.1.3 → 1.1.5

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/README.md +18 -18
  2. package/dist/{TMermaidText-frVr8Ysb.js → TMermaidText-wLnMMvVS.js} +1 -1
  3. package/dist/{TSelect-Dx34M1pF.js → TSelect-CMBOOFwK.js} +5 -5
  4. package/dist/{TTree-DdAjCPHu.js → TTree-kk8ntrpg.js} +6 -5
  5. package/dist/agent/mermaid.d.cts +2 -2
  6. package/dist/agent/mermaid.d.ts +2 -2
  7. package/dist/agent.cjs +542 -266
  8. package/dist/agent.js +25 -82
  9. package/dist/{block-source-Crro3mxi.js → block-source-DhzajB9H.js} +299 -239
  10. package/dist/cli.cjs +57 -16
  11. package/dist/cli.js +70 -15
  12. package/dist/core.js +3 -3
  13. package/dist/{hyperlink-_1FAj7Wq.js → create-terminal-Db_-UGVQ.js} +474 -102
  14. package/dist/{dom-renderer-DXx8fWzD.js → dom-renderer-BfwAEQMf.js} +3 -2
  15. package/dist/experimental.cjs +103 -66
  16. package/dist/experimental.js +20 -74
  17. package/dist/hyperlink-clbb_ha_.js +100 -0
  18. package/dist/index.js +8 -8
  19. package/dist/{linkify-BjI4zVfY.js → linkify-CL9wwnZN.js} +3 -3
  20. package/dist/markdown.cjs +521 -212
  21. package/dist/markdown.d.cts +3 -0
  22. package/dist/markdown.d.ts +3 -0
  23. package/dist/markdown.js +75 -8
  24. package/dist/math-image-BZhmfUeu.js +308 -0
  25. package/dist/mermaid-N3kGTHFy.js +1697 -0
  26. package/dist/mermaid.cjs +1460 -6
  27. package/dist/mermaid.d.cts +4 -0
  28. package/dist/mermaid.d.ts +4 -0
  29. package/dist/mermaid.js +7 -5
  30. package/dist/{rect-DUpeDfBg.js → rect-JzWMJxxc.js} +1 -1
  31. package/dist/renderer/cli/stdout-renderer.d.ts +44 -0
  32. package/dist/renderer/terminal-graphics.d.ts +61 -0
  33. package/dist/renderer-dom.js +4 -4
  34. package/dist/{terminal-graphics-Bbe83wSr.js → terminal-graphics-2FTRg9x3.js} +90 -2
  35. package/dist/{tlog-theme-i6S_Oftd.js → tlog-theme-B91Wv7Zj.js} +5 -4
  36. package/dist/vue/components/TAgentTerminalGraphic.d.ts +1 -1
  37. package/dist/vue/components/TMarkdownText.d.ts +70 -2
  38. package/dist/vue/components/TMermaidImage.d.ts +920 -0
  39. package/dist/vue/components/TMermaidText.d.ts +2 -2
  40. package/dist/vue/components/TVirtualMarkdown.d.ts +70 -2
  41. package/dist/vue/markdown/ast.d.ts +5 -0
  42. package/dist/vue/markdown/document.d.ts +3 -0
  43. package/dist/vue/markdown/math-image.d.ts +68 -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 +284 -240
  47. package/dist/vue/mermaid/mermaid-image.d.ts +73 -0
  48. package/dist/vue.js +9 -9
  49. package/package.json +24 -10
  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
@@ -4,6 +4,9 @@ export { createMarkdownBlockSource } from "./vue/markdown/block-source.js";
4
4
  export { buildMarkdownBlocks, buildMarkdownVisualRows } from "./vue/markdown/document.js";
5
5
  export { layoutMarkdownBlocks } from "./vue/markdown/layout.js";
6
6
  export { loadMarkdownMathRenderer } from "./vue/markdown/math.js";
7
+ export { clearMarkdownMathImageCache, enqueueMarkdownMathImages, getCachedMarkdownMathImage, getMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, setMarkdownMathRasterizer, subscribeMarkdownMathImage, } from "./vue/markdown/math-image.js";
8
+ export type { TuiMarkdownMathImageCells, TuiMarkdownMathImageOptions, TuiMarkdownMathMode, TuiMarkdownMathRasterizer, } from "./vue/markdown/math-image.js";
9
+ export type { TuiMarkdownMathOptions } from "./vue/markdown/ast.js";
7
10
  export { createTuiMarkdownParser, isSafeMarkdownLink } from "./vue/markdown/parser.js";
8
11
  export type { TuiMarkdownBlockSource, TuiMarkdownBlockSourceOptions, TuiMarkdownBlockSourceSnapshot, } from "./vue/markdown/block-source.js";
9
12
  export type { TuiMarkdownLayoutOptions } from "./vue/markdown/layout.js";
@@ -4,6 +4,9 @@ export { createMarkdownBlockSource } from "./vue/markdown/block-source.js";
4
4
  export { buildMarkdownBlocks, buildMarkdownVisualRows } from "./vue/markdown/document.js";
5
5
  export { layoutMarkdownBlocks } from "./vue/markdown/layout.js";
6
6
  export { loadMarkdownMathRenderer } from "./vue/markdown/math.js";
7
+ export { clearMarkdownMathImageCache, enqueueMarkdownMathImages, getCachedMarkdownMathImage, getMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, setMarkdownMathRasterizer, subscribeMarkdownMathImage, } from "./vue/markdown/math-image.js";
8
+ export type { TuiMarkdownMathImageCells, TuiMarkdownMathImageOptions, TuiMarkdownMathMode, TuiMarkdownMathRasterizer, } from "./vue/markdown/math-image.js";
9
+ export type { TuiMarkdownMathOptions } from "./vue/markdown/ast.js";
7
10
  export { createTuiMarkdownParser, isSafeMarkdownLink } from "./vue/markdown/parser.js";
8
11
  export type { TuiMarkdownBlockSource, TuiMarkdownBlockSourceOptions, TuiMarkdownBlockSourceSnapshot, } from "./vue/markdown/block-source.js";
9
12
  export type { TuiMarkdownLayoutOptions } from "./vue/markdown/layout.js";
package/dist/markdown.js CHANGED
@@ -1,12 +1,13 @@
1
- import "./hyperlink-_1FAj7Wq.js";
2
- import "./width-GqllnV8C.js";
3
- import { EventZIndexContextKey, intersectRect, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-DUpeDfBg.js";
1
+ import "./create-terminal-Db_-UGVQ.js";
2
+ import "./hyperlink-clbb_ha_.js";
3
+ import { EventZIndexContextKey, intersectRect, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-JzWMJxxc.js";
4
4
  import "./env-Cl8qmNFN.js";
5
5
  import "./terminal-selection-4W1SJj4c.js";
6
6
  import "./debug-logger-CmsGI4of.js";
7
7
  import "./wheel-scroll-DASkEZr-.js";
8
- import { getTerminalGraphicsOutputVersion, subscribeTerminalGraphicsOutput } from "./terminal-graphics-Bbe83wSr.js";
9
- import { TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, clearMarkdownImageGraphics, collectVisibleMarkdownImageGraphicIds, createMarkdownBlockSource, createTuiMarkdownParser, findMarkdownImageActionAt, findMarkdownLinkActionAt, findMarkdownMathActionAt, isSafeMarkdownLink, layoutMarkdownBlocks, loadMarkdownMathRenderer, markdownThemeSignature, paintMarkdownVisualRow, subscribeMarkdownMathRenderer } from "./block-source-Crro3mxi.js";
8
+ import { getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, subscribeTerminalGraphicsOutput } from "./terminal-graphics-2FTRg9x3.js";
9
+ import { TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, clearMarkdownImageGraphics, collectVisibleMarkdownImageGraphicIds, createMarkdownBlockSource, createTuiMarkdownParser, findMarkdownImageActionAt, findMarkdownLinkActionAt, findMarkdownMathActionAt, isSafeMarkdownLink, layoutMarkdownBlocks, loadMarkdownMathRenderer, markdownThemeSignature, paintMarkdownVisualRow, subscribeMarkdownMathRenderer } from "./block-source-DhzajB9H.js";
10
+ import { clearMarkdownMathImageCache, enqueueMarkdownMathImages, getCachedMarkdownMathImage, getMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, resolveMarkdownMathColor, setMarkdownMathRasterizer, subscribeMarkdownMathImage } from "./math-image-BZhmfUeu.js";
10
11
  import { computed, defineComponent, getCurrentInstance, h, inject, markRaw, onBeforeUnmount, shallowRef, watch } from "vue";
11
12
 
12
13
  //#region src/vue/components/TMarkdownText.ts
@@ -85,6 +86,34 @@ const TMarkdownText = defineComponent({
85
86
  type: Boolean,
86
87
  default: true
87
88
  },
89
+ mathImages: {
90
+ type: Boolean,
91
+ default: true
92
+ },
93
+ mathCellWidthPx: {
94
+ type: Number,
95
+ default: void 0
96
+ },
97
+ mathCellHeightPx: {
98
+ type: Number,
99
+ default: void 0
100
+ },
101
+ mathScale: {
102
+ type: Number,
103
+ default: void 0
104
+ },
105
+ mathColor: {
106
+ type: String,
107
+ default: void 0
108
+ },
109
+ mathMaxWidthCells: {
110
+ type: Number,
111
+ default: void 0
112
+ },
113
+ mathBaselineRatio: {
114
+ type: Number,
115
+ default: void 0
116
+ },
88
117
  imageActions: {
89
118
  type: Boolean,
90
119
  default: false
@@ -126,16 +155,44 @@ const TMarkdownText = defineComponent({
126
155
  const graphicsOutputVersion = shallowRef(getTerminalGraphicsOutputVersion(terminal));
127
156
  const unsubscribeGraphicsOutput = subscribeTerminalGraphicsOutput(terminal, () => {
128
157
  graphicsOutputVersion.value = getTerminalGraphicsOutputVersion(terminal);
158
+ enqueuePendingMathImages();
129
159
  });
130
160
  const unsubscribeMathRenderer = subscribeMarkdownMathRenderer(() => {
131
161
  scheduleRebuild();
132
162
  });
163
+ const unsubscribeMathImage = subscribeMarkdownMathImage(() => {
164
+ scheduleRebuild();
165
+ });
133
166
  watch(() => `${props.streaming ? 1 : 0}:${(props.customHtmlTags ?? []).join("\0")}`, () => {
134
167
  parser.value = markRaw(createTuiMarkdownParser({
135
168
  streaming: props.streaming,
136
169
  customHtmlTags: props.customHtmlTags
137
170
  }));
138
171
  });
172
+ function mathOptions() {
173
+ if (props.mathImages === false) return { enabled: false };
174
+ const resolvedColor = props.mathColor ?? resolveMarkdownMathColor(defaultStyle.value.fg);
175
+ return {
176
+ enabled: true,
177
+ ...props.mathCellWidthPx != null ? { cellWidthPx: props.mathCellWidthPx } : {},
178
+ ...props.mathCellHeightPx != null ? { cellHeightPx: props.mathCellHeightPx } : {},
179
+ ...props.mathScale != null ? { scale: props.mathScale } : {},
180
+ ...resolvedColor != null ? { color: resolvedColor } : {},
181
+ ...props.mathBaselineRatio != null ? { baselineRatio: props.mathBaselineRatio } : {},
182
+ maxWidthCells: props.mathMaxWidthCells ?? props.w
183
+ };
184
+ }
185
+ function enqueuePendingMathImages() {
186
+ if (props.mathImages === false) return;
187
+ if (!getTerminalGraphicsOutput(terminal)?.capabilities.supported) return;
188
+ if (!isMarkdownMathImageRendererReady()) {
189
+ loadMarkdownMathImageRenderer().then((ready) => {
190
+ if (ready && alive) scheduleRebuild();
191
+ });
192
+ return;
193
+ }
194
+ enqueueMarkdownMathImages(rows.value, mathOptions());
195
+ }
139
196
  function rebuildRows() {
140
197
  rows.value = markRaw(withTextWidthProvider(widthProvider, () => buildMarkdownVisualRows(props.content, props.w, parser.value, {
141
198
  final: props.final,
@@ -147,9 +204,11 @@ const TMarkdownText = defineComponent({
147
204
  minHeight: props.imageMinHeight,
148
205
  maxHeight: props.imageMaxHeight,
149
206
  preserveAspectRatio: props.imagePreserveAspectRatio
150
- }
207
+ },
208
+ math: mathOptions()
151
209
  })));
152
210
  documentVersion.value++;
211
+ enqueuePendingMathImages();
153
212
  }
154
213
  function scheduleRebuild() {
155
214
  const currentVersion = ++rebuildVersion;
@@ -181,7 +240,14 @@ const TMarkdownText = defineComponent({
181
240
  () => props.imageMaxWidth,
182
241
  () => props.imageMinHeight,
183
242
  () => props.imageMaxHeight,
184
- () => props.imagePreserveAspectRatio
243
+ () => props.imagePreserveAspectRatio,
244
+ () => props.mathImages,
245
+ () => props.mathCellWidthPx,
246
+ () => props.mathCellHeightPx,
247
+ () => props.mathScale,
248
+ () => props.mathColor,
249
+ () => props.mathMaxWidthCells,
250
+ () => props.mathBaselineRatio
185
251
  ], () => {
186
252
  scheduleRebuild();
187
253
  }, { immediate: true });
@@ -190,6 +256,7 @@ const TMarkdownText = defineComponent({
190
256
  rebuildVersion++;
191
257
  unsubscribeGraphicsOutput();
192
258
  unsubscribeMathRenderer();
259
+ unsubscribeMathImage();
193
260
  clearMarkdownImageGraphics(terminal, fullRect.value);
194
261
  });
195
262
  const fullRect = computed(() => {
@@ -351,4 +418,4 @@ const TMarkdownText = defineComponent({
351
418
  });
352
419
 
353
420
  //#endregion
354
- export { TMarkdownText, TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, createMarkdownBlockSource, createTuiMarkdownParser, isSafeMarkdownLink, layoutMarkdownBlocks, loadMarkdownMathRenderer };
421
+ export { TMarkdownText, TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, clearMarkdownMathImageCache, createMarkdownBlockSource, createTuiMarkdownParser, enqueueMarkdownMathImages, getCachedMarkdownMathImage, getMarkdownMathImage, isMarkdownMathImageRendererReady, isSafeMarkdownLink, layoutMarkdownBlocks, loadMarkdownMathImageRenderer, loadMarkdownMathRenderer, setMarkdownMathRasterizer, subscribeMarkdownMathImage };
@@ -0,0 +1,308 @@
1
+ //#region src/vue/markdown/math-image.ts
2
+ const DEFAULT_MATH_IMAGE_OPTIONS = Object.freeze({
3
+ cellWidthPx: 8,
4
+ cellHeightPx: 16,
5
+ scale: 2,
6
+ color: "#f8f8f2",
7
+ maxWidthCells: 0,
8
+ baselineRatio: .78
9
+ });
10
+ const MAX_CACHE_ENTRIES = 64;
11
+ const MAX_FAILED_ENTRIES = 128;
12
+ const mathImageCache = new Map();
13
+ const mathImageFailed = new Set();
14
+ const mathImageInflight = new Map();
15
+ const mathImageListeners = new Set();
16
+ let customRasterizer = null;
17
+ let builtinRasterizerLoad = null;
18
+ let builtinRasterizerReady = false;
19
+ /**
20
+ * Map common terminal foreground style values to hex so rasterized formulas
21
+ * stay readable on any theme (a light formula on a light background would be
22
+ * invisible). Unknown values resolve to `undefined` and the caller falls back
23
+ * to a neutral default.
24
+ */
25
+ const NAMED_STYLE_FG_HEX = Object.freeze({
26
+ black: "#000000",
27
+ red: "#e53935",
28
+ green: "#43a047",
29
+ yellow: "#fdd835",
30
+ blue: "#1e88e5",
31
+ magenta: "#d81b60",
32
+ cyan: "#00acc1",
33
+ white: "#ffffff",
34
+ gray: "#9e9e9e",
35
+ grey: "#9e9e9e",
36
+ blackBright: "#4a4a4a",
37
+ redBright: "#ff6e6e",
38
+ greenBright: "#69db7c",
39
+ yellowBright: "#ffe066",
40
+ blueBright: "#74c0fc",
41
+ magentaBright: "#f783ac",
42
+ cyanBright: "#3bc9db",
43
+ whiteBright: "#f8f8f2",
44
+ grayBright: "#bdbdbd",
45
+ greyBright: "#bdbdbd"
46
+ });
47
+ function resolveMarkdownMathColor(fg) {
48
+ if (typeof fg !== "string" || !fg) return void 0;
49
+ const trimmed = fg.trim();
50
+ if (trimmed.startsWith("#") && /^#[0-9a-f]{3,8}$/i.test(trimmed)) return trimmed.toLowerCase();
51
+ return NAMED_STYLE_FG_HEX[trimmed.toLowerCase()];
52
+ }
53
+ function normalizeMathImageOptions(options) {
54
+ return {
55
+ cellWidthPx: Math.max(1, Math.floor(options?.cellWidthPx ?? DEFAULT_MATH_IMAGE_OPTIONS.cellWidthPx)),
56
+ cellHeightPx: Math.max(1, Math.floor(options?.cellHeightPx ?? DEFAULT_MATH_IMAGE_OPTIONS.cellHeightPx)),
57
+ scale: Math.max(1, Math.floor(options?.scale ?? DEFAULT_MATH_IMAGE_OPTIONS.scale)),
58
+ color: options?.color ?? DEFAULT_MATH_IMAGE_OPTIONS.color,
59
+ maxWidthCells: Math.max(0, Math.floor(options?.maxWidthCells ?? 0)),
60
+ baselineRatio: clamp01(options?.baselineRatio ?? DEFAULT_MATH_IMAGE_OPTIONS.baselineRatio)
61
+ };
62
+ }
63
+ function clamp01(value) {
64
+ if (!Number.isFinite(value)) return .78;
65
+ return Math.min(1, Math.max(0, value));
66
+ }
67
+ /**
68
+ * Install a custom TeX -> PNG rasterizer (e.g. consumers that already have a
69
+ * katex/mathjax pipeline). Passing `null` restores the built-in lazy loader.
70
+ */
71
+ function setMarkdownMathRasterizer(rasterizer) {
72
+ customRasterizer = rasterizer;
73
+ }
74
+ function subscribeMarkdownMathImage(listener) {
75
+ mathImageListeners.add(listener);
76
+ return () => mathImageListeners.delete(listener);
77
+ }
78
+ /**
79
+ * True when a rasterizer is confirmed available (a custom one was installed or
80
+ * the built-in MathJax+resvg stack finished loading). Components should gate
81
+ * image production on this so they never spend time rasterizing formulas while
82
+ * the math engine is still loading or missing.
83
+ */
84
+ function isMarkdownMathImageRendererReady() {
85
+ return customRasterizer != null || builtinRasterizerReady;
86
+ }
87
+ /**
88
+ * Ensure the math image rasterizer is loaded (or resolved as unavailable).
89
+ * Resolves `true` once a rasterizer is usable. Safe to call repeatedly; the
90
+ * underlying load is shared and its outcome is cached.
91
+ */
92
+ async function loadMarkdownMathImageRenderer() {
93
+ if (isMarkdownMathImageRendererReady()) return true;
94
+ const rasterizer = customRasterizer ?? await loadBuiltinMathRasterizer();
95
+ builtinRasterizerReady = rasterizer != null;
96
+ return builtinRasterizerReady;
97
+ }
98
+ function hashMathImageKey(value) {
99
+ let hash = 2166136261;
100
+ for (let i = 0; i < value.length; i++) {
101
+ hash ^= value.charCodeAt(i);
102
+ hash = Math.imul(hash, 16777619);
103
+ }
104
+ return (hash >>> 0).toString(36);
105
+ }
106
+ function mathImageCacheKey(tex, mode, options) {
107
+ return hashMathImageKey([
108
+ mode,
109
+ tex,
110
+ options.cellWidthPx,
111
+ options.cellHeightPx,
112
+ options.scale,
113
+ options.maxWidthCells,
114
+ options.color,
115
+ options.baselineRatio
116
+ ].join(""));
117
+ }
118
+ function notifyMathImageListeners() {
119
+ for (const listener of mathImageListeners) try {
120
+ listener();
121
+ } catch {}
122
+ }
123
+ /** Synchronous cache lookup used by the markdown AST/layout phase. */
124
+ function getCachedMarkdownMathImage(tex, mode, options) {
125
+ return mathImageCache.get(mathImageCacheKey(tex, mode, normalizeMathImageOptions(options))) ?? null;
126
+ }
127
+ /**
128
+ * Drop all cached rasterized formulas (PNG + failed lookups). Useful for long
129
+ * running processes that change cell metrics, and for tests.
130
+ */
131
+ function clearMarkdownMathImageCache() {
132
+ mathImageCache.clear();
133
+ mathImageFailed.clear();
134
+ }
135
+ function evictOldestMathImageEntry() {
136
+ const oldest = mathImageCache.keys().next().value;
137
+ if (oldest != null) mathImageCache.delete(oldest);
138
+ }
139
+ /**
140
+ * Resolve a TeX formula to a PNG base64 + cell size. Cached per
141
+ * (tex, mode, cell metrics, color). In-flight requests are deduped. When a new
142
+ * image finishes, `subscribeMarkdownMathImage` listeners are notified so
143
+ * components can rebuild/re-paint with the real size.
144
+ */
145
+ async function getMarkdownMathImage(tex, mode, options) {
146
+ const source = String(tex ?? "").trim();
147
+ if (!source) return null;
148
+ const normalized = normalizeMathImageOptions(options);
149
+ const key = mathImageCacheKey(source, mode, normalized);
150
+ const cached = mathImageCache.get(key);
151
+ if (cached) return cached;
152
+ if (mathImageFailed.has(key)) return null;
153
+ const inflight = mathImageInflight.get(key);
154
+ if (inflight) return inflight;
155
+ const pending = (async () => {
156
+ try {
157
+ const rasterizer = customRasterizer ?? await loadBuiltinMathRasterizer();
158
+ if (!rasterizer) return null;
159
+ const result = await rasterizer(source, mode, normalized);
160
+ if (result && result.base64) {
161
+ mathImageCache.set(key, result);
162
+ if (mathImageCache.size > MAX_CACHE_ENTRIES) evictOldestMathImageEntry();
163
+ notifyMathImageListeners();
164
+ } else if (result === null) {
165
+ mathImageFailed.add(key);
166
+ if (mathImageFailed.size > MAX_FAILED_ENTRIES) {
167
+ const oldest = mathImageFailed.values().next().value;
168
+ if (oldest != null) mathImageFailed.delete(oldest);
169
+ }
170
+ }
171
+ return result;
172
+ } catch {
173
+ return null;
174
+ } finally {
175
+ mathImageInflight.delete(key);
176
+ }
177
+ })();
178
+ mathImageInflight.set(key, pending);
179
+ return pending;
180
+ }
181
+ /**
182
+ * Kick off rasterization for every math graphic segment in the given row range
183
+ * that does not have a cached PNG yet. Safe to call after every row rebuild;
184
+ * in-flight and cached requests are deduped inside getMarkdownMathImage. Pass a
185
+ * viewport to avoid rasterizing off-screen formulas (e.g. virtualized views).
186
+ */
187
+ function enqueueMarkdownMathImages(rows, options, viewport) {
188
+ const first = Math.max(0, Math.floor(viewport?.firstRow ?? 0));
189
+ const last = Math.min(rows.length, Math.max(first, Math.ceil(viewport?.lastRow ?? rows.length)));
190
+ for (let index = first; index < last; index++) {
191
+ const row = rows[index];
192
+ if (!row) continue;
193
+ for (const segment of row.segments) {
194
+ const graphic = segment.graphic;
195
+ if (graphic?.kind === "math") {
196
+ if (!graphic.base64 && graphic.tex) getMarkdownMathImage(graphic.tex, "display", options);
197
+ continue;
198
+ }
199
+ const pending = segment.mathAction?.pendingImage;
200
+ if (pending && segment.mathAction?.source) getMarkdownMathImage(segment.mathAction.source, segment.mathAction.mode ?? "display", options);
201
+ }
202
+ }
203
+ }
204
+ function base64FromBytes(bytes) {
205
+ const chunk = 32768;
206
+ let binary = "";
207
+ for (let offset = 0; offset < bytes.length; offset += chunk) binary += String.fromCharCode(...bytes.subarray(offset, offset + chunk));
208
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(bytes).toString("base64");
209
+ }
210
+ function extractSvgMarkup(containerMarkup) {
211
+ const start = containerMarkup.indexOf("<svg");
212
+ if (start < 0) return "";
213
+ const end = containerMarkup.lastIndexOf("</svg>");
214
+ if (end < start) return "";
215
+ return containerMarkup.slice(start, end + 6);
216
+ }
217
+ /**
218
+ * resvg is a Node-only native addon. Keeping the specifier in a variable (a
219
+ * non-literal dynamic import) stops browser bundlers (vite/rolldown) from
220
+ * statically resolving it and failing on its platform `.node` binding; at
221
+ * runtime a browser load fails and the rasterizer falls back to raw text.
222
+ */
223
+ const RESVG_MODULE_ID = "@resvg/resvg-js";
224
+ function loadBuiltinMathRasterizer() {
225
+ if (!builtinRasterizerLoad) builtinRasterizerLoad = (async () => {
226
+ try {
227
+ const [{ mathjax }, { TeX }, { SVG }, { liteAdaptor }, { RegisterHTMLHandler }, { AllPackages }, { Resvg }] = await Promise.all([
228
+ import("mathjax-full/js/mathjax.js"),
229
+ import("mathjax-full/js/input/tex.js"),
230
+ import("mathjax-full/js/output/svg.js"),
231
+ import("mathjax-full/js/adaptors/liteAdaptor.js"),
232
+ import("mathjax-full/js/handlers/html.js"),
233
+ import("mathjax-full/js/input/tex/AllPackages.js"),
234
+ import(RESVG_MODULE_ID)
235
+ ]);
236
+ const adaptor = liteAdaptor();
237
+ RegisterHTMLHandler(adaptor);
238
+ const texInput = new TeX({
239
+ packages: AllPackages,
240
+ formatError: () => {
241
+ throw new Error("Malformed TeX");
242
+ }
243
+ });
244
+ const svgOutput = new SVG({ fontCache: "none" });
245
+ const document = mathjax.document("", {
246
+ InputJax: texInput,
247
+ OutputJax: svgOutput
248
+ });
249
+ return async (source, mode, options) => {
250
+ const node = document.convert(source, { display: mode === "display" });
251
+ let svg = extractSvgMarkup(adaptor.outerHTML(node));
252
+ if (!svg) return null;
253
+ const widthEx = Number(/width="([\d.]+)ex"/.exec(svg)?.[1] ?? 0);
254
+ const heightEx = Number(/height="([\d.]+)ex"/.exec(svg)?.[1] ?? 0);
255
+ if (!(widthEx > 0) || !(heightEx > 0)) return null;
256
+ const exPx = options.cellHeightPx / 2 * options.scale;
257
+ if (mode === "inline") {
258
+ const naturalH = heightEx * exPx;
259
+ const naturalW = widthEx * exPx;
260
+ if (naturalH > options.cellHeightPx * options.scale * 2) return null;
261
+ const rectH = options.cellHeightPx * options.scale;
262
+ const k = rectH / naturalH;
263
+ const contentW = Math.max(1, Math.round(naturalW * k));
264
+ const widthCells = Math.max(1, Math.ceil(contentW / (options.cellWidthPx * options.scale)));
265
+ const rectW = widthCells * options.cellWidthPx * options.scale;
266
+ const verticalAlignEx = Number(/vertical-align:\s*(-?[\d.]+)ex/.exec(svg)?.[1] ?? 0);
267
+ const formulaBaseline = (heightEx - Math.abs(verticalAlignEx)) * exPx * k;
268
+ const terminalBaseline = options.baselineRatio * rectH;
269
+ const offsetY = terminalBaseline - formulaBaseline;
270
+ const inner = svg.replace(/style="vertical-align:[^"]*"/, "").replace(/width="[\d.]+ex"/, `width="${contentW}px"`).replace(/height="[\d.]+ex"/, `height="${rectH}px"`);
271
+ const wrapped = `<svg xmlns="http://www.w3.org/2000/svg" color="${options.color}" width="${rectW}px" height="${rectH}px" viewBox="0 0 ${rectW} ${rectH}"><g transform="translate(0 ${offsetY.toFixed(2)})">${inner}</g></svg>`;
272
+ const renderedInline = new Resvg(wrapped, { background: "rgba(0,0,0,0)" }).render();
273
+ const pngInline = renderedInline.asPng();
274
+ if (!pngInline || pngInline.length < 8) return null;
275
+ return {
276
+ base64: base64FromBytes(pngInline),
277
+ widthCells,
278
+ heightCells: 1
279
+ };
280
+ }
281
+ let widthPx = Math.max(1, Math.round(widthEx * exPx));
282
+ let heightPx = Math.max(1, Math.round(heightEx * exPx));
283
+ if (options.maxWidthCells > 0) {
284
+ const maxPx = options.maxWidthCells * options.cellWidthPx * options.scale;
285
+ if (widthPx > maxPx) {
286
+ heightPx = Math.max(1, Math.round(heightPx * (maxPx / widthPx)));
287
+ widthPx = maxPx;
288
+ }
289
+ }
290
+ svg = svg.replace(/style="vertical-align:[^"]*"/, "").replace(/width="[\d.]+ex"/, `width="${widthPx}px"`).replace(/height="[\d.]+ex"/, `height="${heightPx}px"`).replace(/<svg/, `<svg color="${options.color}"`);
291
+ const rendered = new Resvg(svg, { background: "rgba(0,0,0,0)" }).render();
292
+ const png = rendered.asPng();
293
+ if (!png || png.length < 8) return null;
294
+ return {
295
+ base64: base64FromBytes(png),
296
+ widthCells: Math.max(1, Math.round(widthPx / (options.cellWidthPx * options.scale))),
297
+ heightCells: Math.max(1, Math.round(heightPx / (options.cellHeightPx * options.scale)))
298
+ };
299
+ };
300
+ } catch {
301
+ return null;
302
+ }
303
+ })();
304
+ return builtinRasterizerLoad;
305
+ }
306
+
307
+ //#endregion
308
+ export { clearMarkdownMathImageCache, enqueueMarkdownMathImages, getCachedMarkdownMathImage, getMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, resolveMarkdownMathColor, setMarkdownMathRasterizer, subscribeMarkdownMathImage };