decant-core 1.2.4 → 1.2.6

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/ai/chatgpt.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ChatParser } from "./base.js";
2
2
  import { convertToMarkdown } from "../utils/html-to-markdown.js";
3
+ import { normalizeLatexMath } from "../utils/latex-math.js";
3
4
  import {
4
5
  collectMountedTurnMessages,
5
6
  findChatGPTScrollRoot,
@@ -534,6 +535,8 @@ function cleanMarkdownFromApi(text, citeMap, imageGroupMap) {
534
535
  },
535
536
  );
536
537
 
538
+ text = normalizeLatexMath(text);
539
+
537
540
  return text;
538
541
  }
539
542
 
@@ -14,11 +14,12 @@ export function sanitizeResponseContainer(container) {
14
14
  : [];
15
15
  unwanted.forEach((el) => el.remove());
16
16
 
17
- // 2. Remove base64 inline images to prevent megabyte-scale text walls in markdown exports
17
+ // 2. Remove base64 inline images to prevent megabyte-scale text walls in markdown exports,
18
+ // while preserving math LaTeX equation images marked with data-xpm-latex
18
19
  const images = clone.querySelectorAll ? clone.querySelectorAll("img") : [];
19
20
  images.forEach((img) => {
20
21
  const src = img.getAttribute("src") || "";
21
- if (src.startsWith("data:image/")) {
22
+ if (src.startsWith("data:image/") && !img.hasAttribute("data-xpm-latex")) {
22
23
  img.remove();
23
24
  }
24
25
  });
package/ai/index.js CHANGED
@@ -30,6 +30,7 @@ export { ChubParser } from "./chub.js";
30
30
 
31
31
  // Utilities
32
32
  export { convertToMarkdown, cleanMarkdown } from "../utils/html-to-markdown.js";
33
+ export { normalizeLatexMath, cleanLatexMath } from "../utils/latex-math.js";
33
34
 
34
35
  // Detection
35
36
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decant-core",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Shared AI chat platform parsers and detection for Covai browser extensions.",
5
5
  "type": "module",
6
6
  "main": "ai/index.js",
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Clean up LaTeX math formulas by unescaping common markdown parser artifacts.
3
+ * @param {string} latex - Raw LaTeX math string
4
+ * @returns {string} Cleaned LaTeX math
5
+ */
6
+ export function cleanLatexMath(latex) {
7
+ if (!latex || typeof latex !== "string") return "";
8
+ return latex
9
+ .replace(/\\\\([a-zA-Z]+)/g, "\\$1")
10
+ .replace(/\\([_\][*])/g, "$1");
11
+ }
12
+
13
+ /**
14
+ * Standardize LaTeX math delimiters in markdown text.
15
+ * Converts LaTeX bracket delimiters (\[...\] and \(...\)) to standard markdown
16
+ * math delimiters ($$...$$ and $...$) while protecting code blocks and inline code.
17
+ *
18
+ * @param {string} text - Input text/markdown
19
+ * @returns {string} Text with normalized LaTeX math delimiters
20
+ */
21
+ export function normalizeLatexMath(text) {
22
+ if (!text || typeof text !== "string") return "";
23
+
24
+ const placeholders = [];
25
+ let tokenCounter = 0;
26
+
27
+ // 1. Protect fenced code blocks (``` ... ``` or ~~~ ... ~~~)
28
+ let processed = text.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, (match) => {
29
+ const id = `@@MATH_CODE_BLOCK_${tokenCounter++}@@`;
30
+ placeholders.push({ id, content: match });
31
+ return id;
32
+ });
33
+
34
+ // 2. Protect inline code (`...`)
35
+ processed = processed.replace(/`([^`\n]+?)`/g, (match) => {
36
+ const id = `@@MATH_INLINE_CODE_${tokenCounter++}@@`;
37
+ placeholders.push({ id, content: match });
38
+ return id;
39
+ });
40
+
41
+ // 3. Protect existing display math ($$ ... $$) and clean any escaped LaTeX syntax
42
+ processed = processed.replace(/\$\$([\s\S]*?)\$\$/g, (match, math) => {
43
+ const id = `@@MATH_DISPLAY_${tokenCounter++}@@`;
44
+ placeholders.push({ id, content: `$$${cleanLatexMath(math)}$$` });
45
+ return id;
46
+ });
47
+
48
+ // 4. Protect existing inline math ($ ... $) and clean any escaped LaTeX syntax
49
+ processed = processed.replace(/\$([^$\n]+?)\$/g, (match, math) => {
50
+ const id = `@@MATH_INLINE_${tokenCounter++}@@`;
51
+ placeholders.push({ id, content: `$${cleanLatexMath(math)}$` });
52
+ return id;
53
+ });
54
+
55
+ // 5. Convert display math: \[ ... \] or \\[ ... \\]
56
+ processed = processed.replace(
57
+ /(?:\\{1,2}\[)([\s\S]+?)(?:\\{1,2}\])/g,
58
+ (match, math) => {
59
+ return `$$${cleanLatexMath(math).trim()}$$`;
60
+ },
61
+ );
62
+
63
+ // 6. Convert inline math: \( ... \) or \\( ... \\)
64
+ processed = processed.replace(
65
+ /(?:\\{1,2}\()([\s\S]+?)(?:\\{1,2}\))/g,
66
+ (match, math) => {
67
+ return `$${cleanLatexMath(math).trim()}$`;
68
+ },
69
+ );
70
+
71
+ // 7. Collapse excessive blank lines outside protected code
72
+ processed = processed.replace(/\n{3,}/g, "\n\n");
73
+
74
+ // 8. Restore protected items in reverse order
75
+ for (let i = placeholders.length - 1; i >= 0; i--) {
76
+ const { id, content } = placeholders[i];
77
+ processed = processed.replace(id, () => content);
78
+ }
79
+
80
+ return processed;
81
+ }