fixes and better mermaid

This commit is contained in:
2025-07-09 14:20:45 +02:00
parent 43a7647a9c
commit cb069a45be
3 changed files with 67 additions and 25 deletions

83
App.tsx
View File

@ -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">

View File

@ -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: [
{

View File

@ -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.
---