feat: add mermaid diagram support
This commit is contained in:
73
App.tsx
73
App.tsx
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import { Dialog } from "@headlessui/react";
|
||||
import { Cog6ToothIcon } from "@heroicons/react/24/outline";
|
||||
import { Exercise, CheckResult, ExercisePartType } from './types';
|
||||
@ -18,6 +18,7 @@ import Editor from 'react-simple-code-editor';
|
||||
import Prism from 'prismjs/components/prism-core';
|
||||
import 'prismjs/components/prism-clike';
|
||||
import 'prismjs/components/prism-c';
|
||||
import mermaid from 'mermaid';
|
||||
|
||||
|
||||
// --- GEMINI API SETUP & HELPERS ---
|
||||
@ -167,6 +168,68 @@ const TextAreaEditor: React.FC<{
|
||||
)
|
||||
};
|
||||
|
||||
const MermaidDiagram: React.FC<{ value: string; onChange: (value: string) => void; placeholder?: string }> = ({ value, onChange, placeholder }) => {
|
||||
const [diagramHtml, setDiagramHtml] = useState<string>('');
|
||||
const [error, setError] = useState<string>('');
|
||||
const renderCountRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (value.trim()) {
|
||||
const renderDiagram = async () => {
|
||||
try {
|
||||
setError('');
|
||||
renderCountRef.current += 1;
|
||||
const uniqueId = `mermaid-diagram-${renderCountRef.current}`;
|
||||
const { svg } = await mermaid.render(uniqueId, value);
|
||||
setDiagramHtml(svg);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Mermaid diagram rendering failed');
|
||||
setDiagramHtml('');
|
||||
}
|
||||
};
|
||||
renderDiagram();
|
||||
} else {
|
||||
setDiagramHtml('');
|
||||
setError('');
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className="w-full h-48 p-3 bg-slate-50 border-2 border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 font-mono text-slate-800"
|
||||
aria-label="Mermaid diagram input"
|
||||
/>
|
||||
{error && (
|
||||
<div className="p-3 bg-red-50 border-2 border-red-200 rounded-lg">
|
||||
<p className="text-red-700 text-sm font-medium">Mermaid Error:</p>
|
||||
<p className="text-red-600 text-sm">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
{diagramHtml && (
|
||||
<div className="p-4 bg-white border-2 border-slate-200 rounded-lg">
|
||||
<p className="text-slate-600 text-sm mb-2 font-medium">Preview:</p>
|
||||
<div
|
||||
className="mermaid-preview"
|
||||
dangerouslySetInnerHTML={{ __html: diagramHtml }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
interface CodeDiffViewerProps {
|
||||
oldCode: string;
|
||||
@ -320,11 +383,17 @@ const ExerciseSheet: React.FC<ExerciseSheetProps> = ({ exercise, onNext, ai }) =
|
||||
onChange={(value) => handleInputChange(part.id, value)}
|
||||
placeholder={`// Code für Teil ${part.id}) hier eingeben...`}
|
||||
/>
|
||||
) : part.type === 'mermaid' ? (
|
||||
<MermaidDiagram
|
||||
value={inputs[part.id] || ''}
|
||||
onChange={(value) => handleInputChange(part.id, value)}
|
||||
placeholder="Mermaid-Diagramm hier eingeben..."
|
||||
/>
|
||||
) : (
|
||||
<TextAreaEditor
|
||||
value={inputs[part.id] || ''}
|
||||
onChange={(value) => handleInputChange(part.id, value)}
|
||||
placeholder={part.type === 'mermaid' ? 'Antwort in Mermaid-Syntax hier eingeben...' : `Antwort hier eingeben...`}
|
||||
placeholder={`Antwort hier eingeben...`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user