Timothy J. Baek 9 kuukautta sitten
vanhempi
commit
6aefc79807

+ 1 - 4
src/lib/components/chat/Messages/MarkdownInlineTokens.svelte

@@ -37,10 +37,7 @@
 		</del>
 		</del>
 	{:else if token.type === 'inlineKatex'}
 	{:else if token.type === 'inlineKatex'}
 		{#if token.text}
 		{#if token.text}
-			<KatexRenderer
-				content={revertSanitizedResponseContent(token.text)}
-				displayMode={token?.displayMode ?? false}
-			/>
+			<KatexRenderer content={revertSanitizedResponseContent(token.text)} displayMode={false} />
 		{/if}
 		{/if}
 	{:else if token.type === 'text'}
 	{:else if token.type === 'text'}
 		{token.raw}
 		{token.raw}

+ 7 - 0
src/lib/components/chat/Messages/MarkdownTokens.svelte

@@ -116,6 +116,13 @@
 				displayMode={token?.displayMode ?? false}
 				displayMode={token?.displayMode ?? false}
 			/>
 			/>
 		{/if}
 		{/if}
+	{:else if token.type === 'blockKatex'}
+		{#if token.text}
+			<KatexRenderer
+				content={revertSanitizedResponseContent(token.text)}
+				displayMode={token?.displayMode ?? false}
+			/>
+		{/if}
 	{:else if token.type === 'space'}
 	{:else if token.type === 'space'}
 		{''}
 		{''}
 	{:else}
 	{:else}

+ 117 - 71
src/lib/utils/katex-extension.ts

@@ -1,83 +1,129 @@
 import katex from 'katex';
 import katex from 'katex';
 
 
-const inlineRule =
-	/^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1(?=[\s?!\.,:?!。,:]|$)/;
-const inlineRuleNonStandard = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1/; // Non-standard, even if there are no spaces before and after $ or $$, try to parse
-
-const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/;
-
-export default function (options = {}) {
-	return {
-		extensions: [
-			inlineKatex(options, createRenderer(options, false)),
-			blockKatex(options, createRenderer(options, true))
-		]
-	};
+const DELIMITER_LIST = [
+    { left: '$$', right: '$$', display: false },
+    { left: '$', right: '$', display: false },
+    { left: '\\pu{', right: '}', display: false },
+    { left: '\\ce{', right: '}', display: false },
+    { left: '\\(', right: '\\)', display: false },
+    { left: '( ', right: ' )', display: false },
+    { left: '\\[', right: '\\]', display: true },
+    { left: '[', right: ']', display: true }
+]
+
+// const DELIMITER_LIST = [
+//     { left: '$$', right: '$$', display: false },
+//     { left: '$', right: '$', display: false },
+// ];
+
+// const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1(?=[\s?!\.,:?!。,:]|$)/;
+// const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/;
+
+let inlinePatterns = [];
+let blockPatterns = [];
+
+function escapeRegex(string) {
+    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
+
+function generateRegexRules(delimiters) {
+    delimiters.forEach(delimiter => {
+        const { left, right } = delimiter;
+        // Ensure regex-safe delimiters
+        const escapedLeft = escapeRegex(left);
+        const escapedRight = escapeRegex(right);
+
+        // Inline pattern - Capture group $1, token content, followed by end delimiter and normal punctuation marks.
+        // Example: $text$
+        inlinePatterns.push(`${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}`);
+
+        // Block pattern - Starts and ends with the delimiter on new lines. Example:
+        // $$\ncontent here\n$$
+        blockPatterns.push(`${escapedLeft}\n((?:\\\\[^]|[^\\\\])+?)\n${escapedRight}`);
+    });
+
+    const inlineRule = new RegExp(`^(${inlinePatterns.join('|')})(?=[\\s?!.,:?!。,:]|$)`, 'u');
+    const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?:\n|$)`, 'u');
+
+    return { inlineRule, blockRule };
+}
+
+const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST);
+
+
+
+export default function(options = {}) {
+  return {
+    extensions: [
+      inlineKatex(options, createRenderer(options, false)),
+      blockKatex(options, createRenderer(options, true)),
+    ],
+  };
 }
 }
 
 
 function createRenderer(options, newlineAfter) {
 function createRenderer(options, newlineAfter) {
-	return (token) =>
-		katex.renderToString(token.text, { ...options, displayMode: token.displayMode }) +
-		(newlineAfter ? '\n' : '');
+  return (token) => katex.renderToString(token.text, { ...options, displayMode: token.displayMode }) + (newlineAfter ? '\n' : '');
 }
 }
 
 
 function inlineKatex(options, renderer) {
 function inlineKatex(options, renderer) {
-	const nonStandard = options && options.nonStandard;
-	const ruleReg = nonStandard ? inlineRuleNonStandard : inlineRule;
-	return {
-		name: 'inlineKatex',
-		level: 'inline',
-		start(src) {
-			let index;
-			let indexSrc = src;
-
-			while (indexSrc) {
-				index = indexSrc.indexOf('$');
-				if (index === -1) {
-					return;
-				}
-				const f = nonStandard ? index > -1 : index === 0 || indexSrc.charAt(index - 1) === ' ';
-				if (f) {
-					const possibleKatex = indexSrc.substring(index);
-
-					if (possibleKatex.match(ruleReg)) {
-						return index;
-					}
-				}
-
-				indexSrc = indexSrc.substring(index + 1).replace(/^\$+/, '');
-			}
-		},
-		tokenizer(src, tokens) {
-			const match = src.match(ruleReg);
-			if (match) {
-				return {
-					type: 'inlineKatex',
-					raw: match[0],
-					text: match[2].trim(),
-					displayMode: match[1].length === 2
-				};
-			}
-		},
-		renderer
-	};
+  const ruleReg =  inlineRule;
+  return {
+    name: 'inlineKatex',
+    level: 'inline',
+    start(src) {
+      let index;
+      let indexSrc = src;
+
+      while (indexSrc) {
+        index = indexSrc.indexOf('$');
+        if (index === -1) {
+          return;
+        }
+        const f = index === 0 || indexSrc.charAt(index - 1) === ' ';
+        if (f) {
+          const possibleKatex = indexSrc.substring(index);
+
+          if (possibleKatex.match(ruleReg)) {
+            return index;
+          }
+        }
+
+        indexSrc = indexSrc.substring(index + 1).replace(/^\$+/, '');
+      }
+    },
+    tokenizer(src, tokens) {
+      const match = src.match(ruleReg);
+
+      if (match) {
+        console.log(match)
+        const text = match.slice(2).filter((item) => item).find((item) => item.trim());
+
+        return {
+          type: 'inlineKatex',
+          raw: match[0],
+          text: text,
+        };
+      }
+    },
+    renderer,
+  };
 }
 }
 
 
 function blockKatex(options, renderer) {
 function blockKatex(options, renderer) {
-	return {
-		name: 'blockKatex',
-		level: 'block',
-		tokenizer(src, tokens) {
-			const match = src.match(blockRule);
-			if (match) {
-				return {
-					type: 'blockKatex',
-					raw: match[0],
-					text: match[2].trim(),
-					displayMode: match[1].length === 2
-				};
-			}
-		},
-		renderer
-	};
+  return {
+    name: 'blockKatex',
+    level: 'block',
+    tokenizer(src, tokens) {
+      const match = src.match(blockRule);
+      if (match) {
+        return {
+          type: 'blockKatex',
+          raw: match[0],
+          text: match[0],
+        };
+      }
+    },
+    renderer,
+  };
 }
 }
+