diff --git a/App.tsx b/App.tsx index b1909a5..217efce 100644 --- a/App.tsx +++ b/App.tsx @@ -27,37 +27,51 @@ import mermaid from 'mermaid'; const extractJson = (text: string): any => { let jsonString = text.trim(); + // Remove BOM and other invisible characters + jsonString = jsonString.replace(/^\uFEFF/, ''); // Remove BOM + jsonString = jsonString.replace(/^[\u200B-\u200D\uFEFF]/g, ''); // Remove zero-width characters + const fenceRegex = /^```(?:json)?\s*\n?(.*?)\n?\s*```$/s; const match = jsonString.match(fenceRegex); if (match && match[1]) { jsonString = match[1].trim(); } - const lastBracket = jsonString.lastIndexOf('}'); - if (lastBracket === -1) { - throw new Error("No closing brace '}' found in API response."); - } - - let openBraceCount = 0; - let firstBracket = -1; - for (let i = lastBracket; i >= 0; i--) { - if (jsonString[i] === '}') { - openBraceCount++; - } else if (jsonString[i] === '{') { - openBraceCount--; + // Try to parse directly first (most common case) + try { + return JSON.parse(jsonString); + } catch (directParseError) { + // If direct parsing fails, try the bracket extraction method + const lastBracket = jsonString.lastIndexOf('}'); + if (lastBracket === -1) { + throw new Error("No closing brace '}' found in API response."); } - if (openBraceCount === 0) { - firstBracket = i; - break; + + let openBraceCount = 0; + let firstBracket = -1; + for (let i = lastBracket; i >= 0; i--) { + if (jsonString[i] === '}') { + openBraceCount++; + } else if (jsonString[i] === '{') { + openBraceCount--; + } + if (openBraceCount === 0) { + firstBracket = i; + break; + } + } + + if (firstBracket === -1) { + throw new Error("Could not find matching opening brace '{' in API response."); + } + + const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1); + try { + return JSON.parse(finalJsonString); + } catch (finalParseError) { + throw new Error(`JSON parsing failed: ${finalParseError.message}. Raw text: ${jsonString.substring(0, 100)}...`); } } - - if (firstBracket === -1) { - throw new Error("Could not find matching opening brace '{' in API response."); - } - - const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1); - return JSON.parse(finalJsonString); }; @@ -184,15 +198,23 @@ const MermaidDiagram: React.FC<{ value: string; onChange: (value: string) => voi useEffect(() => { if (value.trim()) { const renderDiagram = async () => { + renderCountRef.current += 1; + const uniqueId = `mermaid-diagram-${renderCountRef.current}`; + try { setError(''); - renderCountRef.current += 1; - const uniqueId = `mermaid-diagram-${renderCountRef.current}`; + setDiagramHtml(''); const { svg } = await mermaid.render(uniqueId, value); setDiagramHtml(svg); } catch (err) { setError(err instanceof Error ? err.message : 'Mermaid diagram rendering failed'); setDiagramHtml(''); + } finally { + // Clean up the DOM element that mermaid might have created + const element = document.getElementById(uniqueId); + if (element && element.parentNode) { + element.parentNode.removeChild(element); + } } }; renderDiagram(); @@ -201,6 +223,19 @@ const MermaidDiagram: React.FC<{ value: string; onChange: (value: string) => voi setError(''); } }, [value]); + + // Cleanup function to remove any lingering mermaid elements + useEffect(() => { + return () => { + // Clean up any mermaid elements that might be left in the DOM + const mermaidElements = document.querySelectorAll('[id^="mermaid-diagram-"]'); + mermaidElements.forEach(element => { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + }); + }; + }, []); return (