fixes and better mermaid
This commit is contained in:
83
App.tsx
83
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 (
|
||||
<div className="space-y-3">
|
||||
|
@ -3,7 +3,7 @@ import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises7: Exercise[] = [
|
||||
{
|
||||
id: 'swa-6-1-ddd-tactical',
|
||||
id: 'swa-7-1-ddd-tactical',
|
||||
title: '6.1 - SWA - Domain-Driven Design (Tactical)',
|
||||
parts: [
|
||||
{
|
||||
|
@ -512,9 +512,16 @@ CMD [ "serve", "-s", "dist", "-l", "8000" ]
|
||||
- **Real-time Preview**: Users can see their Mermaid diagram rendered as they type
|
||||
- **Error Handling**: Clear error messages for invalid Mermaid syntax
|
||||
- **Unique Rendering**: Each render uses a unique ID to prevent caching issues that could cause diagrams to disappear
|
||||
- **DOM Cleanup**: Proper cleanup of mermaid-generated DOM elements to prevent accumulation of leftover elements from failed renders
|
||||
- **Consistent Styling**: Matches the existing design language of the application
|
||||
- **Accessibility**: Proper ARIA labels and semantic HTML structure
|
||||
|
||||
**Important Implementation Details**:
|
||||
|
||||
- **DOM Element Cleanup**: Added `finally` block in render function to clean up DOM elements that mermaid creates, especially important for failed renders which would otherwise leave orphaned elements
|
||||
- **Component Unmount Cleanup**: Added cleanup `useEffect` to remove any lingering mermaid elements when component unmounts
|
||||
- **Unique ID Generation**: Uses `useRef` counter to generate unique IDs for each render attempt, preventing mermaid's internal caching issues
|
||||
|
||||
This enhancement makes the application more suitable for software architecture exercises that require diagram creation and visualization.
|
||||
|
||||
---
|
||||
|
Reference in New Issue
Block a user