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

39
App.tsx
View File

@ -27,12 +27,21 @@ import mermaid from 'mermaid';
const extractJson = (text: string): any => { const extractJson = (text: string): any => {
let jsonString = text.trim(); 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 fenceRegex = /^```(?:json)?\s*\n?(.*?)\n?\s*```$/s;
const match = jsonString.match(fenceRegex); const match = jsonString.match(fenceRegex);
if (match && match[1]) { if (match && match[1]) {
jsonString = match[1].trim(); jsonString = match[1].trim();
} }
// 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('}'); const lastBracket = jsonString.lastIndexOf('}');
if (lastBracket === -1) { if (lastBracket === -1) {
throw new Error("No closing brace '}' found in API response."); throw new Error("No closing brace '}' found in API response.");
@ -57,7 +66,12 @@ const extractJson = (text: string): any => {
} }
const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1); const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1);
try {
return JSON.parse(finalJsonString); return JSON.parse(finalJsonString);
} catch (finalParseError) {
throw new Error(`JSON parsing failed: ${finalParseError.message}. Raw text: ${jsonString.substring(0, 100)}...`);
}
}
}; };
@ -184,15 +198,23 @@ const MermaidDiagram: React.FC<{ value: string; onChange: (value: string) => voi
useEffect(() => { useEffect(() => {
if (value.trim()) { if (value.trim()) {
const renderDiagram = async () => { const renderDiagram = async () => {
try {
setError('');
renderCountRef.current += 1; renderCountRef.current += 1;
const uniqueId = `mermaid-diagram-${renderCountRef.current}`; const uniqueId = `mermaid-diagram-${renderCountRef.current}`;
try {
setError('');
setDiagramHtml('');
const { svg } = await mermaid.render(uniqueId, value); const { svg } = await mermaid.render(uniqueId, value);
setDiagramHtml(svg); setDiagramHtml(svg);
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Mermaid diagram rendering failed'); setError(err instanceof Error ? err.message : 'Mermaid diagram rendering failed');
setDiagramHtml(''); 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(); renderDiagram();
@ -202,6 +224,19 @@ const MermaidDiagram: React.FC<{ value: string; onChange: (value: string) => voi
} }
}, [value]); }, [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 ( return (
<div className="space-y-3"> <div className="space-y-3">
<textarea <textarea

View File

@ -3,7 +3,7 @@ import { Exercise } from '../types';
export const swaExercises7: Exercise[] = [ export const swaExercises7: Exercise[] = [
{ {
id: 'swa-6-1-ddd-tactical', id: 'swa-7-1-ddd-tactical',
title: '6.1 - SWA - Domain-Driven Design (Tactical)', title: '6.1 - SWA - Domain-Driven Design (Tactical)',
parts: [ 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 - **Real-time Preview**: Users can see their Mermaid diagram rendered as they type
- **Error Handling**: Clear error messages for invalid Mermaid syntax - **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 - **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 - **Consistent Styling**: Matches the existing design language of the application
- **Accessibility**: Proper ARIA labels and semantic HTML structure - **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. This enhancement makes the application more suitable for software architecture exercises that require diagram creation and visualization.
--- ---