feat: unkown

This commit is contained in:
2025-12-26 15:53:11 +08:00
parent 1226bbe724
commit cffd536fb8
7 changed files with 244 additions and 187 deletions

View File

@@ -292,7 +292,7 @@ function App() {
if (pollingIntervals.current[taskNo]) return;
let attempts = 0;
const maxAttempts = 15;
const maxAttempts = 30;
pollingIntervals.current[taskNo] = setInterval(async () => {
attempts++;
@@ -369,7 +369,7 @@ function App() {
alert('Task timeout or network error.');
}
}
}, 1500); // Poll every 2 seconds
}, 2000); // Poll every 2 seconds
};
const handleUpload = async (uploadFiles: File[]) => {

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import { X, Check, Copy, Download, Code2, Image as ImageIcon, FileText, ChevronRight } from 'lucide-react';
import { RecognitionResult } from '../types';
import { convertMathmlToOmml, wrapOmmlForClipboard } from '../lib/ommlConverter';
interface ExportSidebarProps {
isOpen: boolean;
@@ -84,7 +85,20 @@ export default function ExportSidebar({ isOpen, onClose, result }: ExportSidebar
];
const handleAction = async (option: ExportOption) => {
const content = option.getContent(result);
let content = option.getContent(result);
// Fallback: If Word MathML is missing, try to convert from MathML
if (option.id === 'mathml_word' && !content && result.mathml_content) {
try {
const omml = await convertMathmlToOmml(result.mathml_content);
if (omml) {
content = wrapOmmlForClipboard(omml);
}
} catch (err) {
console.error('Failed to convert MathML to OMML:', err);
}
}
if (!content) return;
try {

View File

@@ -146,3 +146,5 @@ export default function Navbar() {

View File

@@ -2,6 +2,7 @@ import { useState } from 'react';
import { Download, Code2, Check, Copy } from 'lucide-react';
import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import remarkBreaks from 'remark-breaks';
import rehypeKatex from 'rehype-katex';
import 'katex/dist/katex.min.css';
import { RecognitionResult } from '../types';
@@ -18,72 +19,60 @@ interface ResultPanelProps {
*/
function preprocessLatex(content: string): string {
if (!content) return '';
let processed = content;
// Convert literal \n (escaped newlines from OCR) to actual newlines
// This handles cases where the API returns "\\n" as a string instead of actual newline characters
// Fix mixed display math delimiters: $$\[...\]$$ -> $$...$$
// This handles cases where \[ \] are incorrectly nested inside $$ $$
// Note: In JS replace(), $$ means "insert one $", so we need $$$$ to insert $$
processed = processed.replace(/\$\$\s*\\\[/g, '$$$$');
processed = processed.replace(/\\\]\s*\$\$/g, '$$$$');
// Also fix standalone \[ and \] that should be $$ for remark-math compatibility
// Replace \[ with $$ at the start of display math (not inside other math)
processed = processed.replace(/(?<!\$)\\\[(?!\$)/g, '$$$$');
processed = processed.replace(/(?<!\$)\\\](?!\$)/g, '$$$$');
// IMPORTANT: remark-math requires $$ to be on its own line for block math
// Ensure $$ followed by \begin has a newline: $$\begin -> $$\n\begin
processed = processed.replace(/\$\$([^\n$])/g, '$$$$\n$1');
// Ensure content followed by $$ has a newline: content$$ -> content\n$$
processed = processed.replace(/([^\n$])\$\$/g, '$1\n$$$$');
// Fix: \left \{ -> \left\{ (remove space between \left and delimiter)
// // Fix: \left \{ -> \left\{ (remove space between \left and delimiter)
processed = processed.replace(/\\left\s+\\/g, '\\left\\');
processed = processed.replace(/\\left\s+\{/g, '\\left\\{');
processed = processed.replace(/\\left\s+\[/g, '\\left[');
processed = processed.replace(/\\left\s+\(/g, '\\left(');
// Fix: \right \} -> \right\} (remove space between \right and delimiter)
// // Fix: \right \} -> \right\} (remove space between \right and delimiter)
processed = processed.replace(/\\right\s+\\/g, '\\right\\');
processed = processed.replace(/\\right\s+\}/g, '\\right\\}');
processed = processed.replace(/\\right\s+\]/g, '\\right]');
processed = processed.replace(/\\right\s+\)/g, '\\right)');
// Fix: \begin{matrix} with mismatched \left/\right -> use \begin{array}
// This is a more complex issue that requires proper \left/\right pairing
// For now, we'll try to convert problematic patterns
// Replace \left( ... \right. text \right) pattern with ( ... \right. text )
// This fixes the common mispairing issue
processed = processed.replace(/\\left\(([^)]*?)\\right\.\s*(\\text\{[^}]*\})\s*\\right\)/g, '($1\\right. $2)');
return processed;
}
export default function ResultPanel({ result, fileStatus }: ResultPanelProps) {
const [isExportSidebarOpen, setIsExportSidebarOpen] = useState(false);
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
if (result?.markdown_content) {
await navigator.clipboard.writeText(result.markdown_content);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
if (!result) {
if (fileStatus === 'processing' || fileStatus === 'pending') {
return (
<div className="h-full flex flex-col items-center justify-center bg-white text-center p-8">
<div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mb-6"></div>
<div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mb-6"></div>
<h3 className="text-xl font-semibold text-gray-900 mb-2">
{fileStatus === 'pending' ? 'Waiting in queue...' : 'Analyzing...'}
</h3>
<p className="text-gray-500 max-w-sm">
{fileStatus === 'pending'
? 'Your file is in the queue, please wait.'
{fileStatus === 'pending'
? 'Your file is in the queue, please wait.'
: 'Texpixel is processing your file, this may take a moment.'}
</p>
</div>
@@ -125,11 +114,11 @@ export default function ResultPanel({ result, fileStatus }: ResultPanelProps) {
<div className="flex-1 overflow-auto p-8 custom-scrollbar flex justify-center">
<div className="prose prose-blue max-w-3xl w-full prose-headings:font-bold prose-h1:text-2xl prose-h2:text-xl prose-p:leading-relaxed prose-pre:bg-gray-50 prose-pre:border prose-pre:border-gray-100 [&_.katex-display]:text-center">
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[[rehypeKatex, {
throwOnError: false,
remarkPlugins={[remarkMath, remarkBreaks]}
rehypePlugins={[[rehypeKatex, {
throwOnError: false,
errorColor: '#cc0000',
strict: false
strict: false
}]]}
>
{preprocessLatex(result.markdown_content || '')}

50
src/lib/ommlConverter.ts Normal file
View File

@@ -0,0 +1,50 @@
/**
* MathML to OMML Converter
*
* Uses 'mathml2omml' library to convert MathML to Office Math Markup Language (OMML).
* This is a pure JavaScript implementation and does not require external XSLT files.
*/
import { mml2omml } from 'mathml2omml';
/**
* Converts MathML string to OMML string using mathml2omml library.
* @param mathml The MathML content string
* @returns Promise resolving to OMML string
*/
export async function convertMathmlToOmml(mathml: string): Promise<string> {
try {
// The library is synchronous, but we keep the async signature for compatibility
// and potential future changes (e.g. if we move this to a worker).
const omml = mml2omml(mathml);
return omml;
} catch (error) {
console.error('MathML to OMML conversion failed:', error);
return '';
}
}
/**
* Wraps OMML in Word XML clipboard format if needed.
* This helps Word recognize it when pasting as text.
*/
export function wrapOmmlForClipboard(omml: string): string {
// Replace 2006 namespaces with 2004/2003 namespaces for Word 2003 XML compatibility
// This is necessary because the clipboard format uses the older Word XML structure
const compatibleOmml = omml
.replace(/http:\/\/schemas\.openxmlformats\.org\/officeDocument\/2006\/math/g, 'http://schemas.microsoft.com/office/2004/12/omml')
.replace(/http:\/\/schemas\.openxmlformats\.org\/wordprocessingml\/2006\/main/g, 'http://schemas.microsoft.com/office/word/2003/wordml');
// Simple XML declaration wrapper often helps
return `<?xml version="1.0"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
<w:body>
<w:p>
<m:oMathPara>
${compatibleOmml}
</m:oMathPara>
</w:p>
</w:body>
</w:wordDocument>`;
}