feat: implement WebSocket-based Mermaid conversion with auto-sync
- Add WebSocket message handler for mermaid_convert type - Make handleWebSocketMessage async to support conversion - Add automatic backend sync after diagram generation - Integrate convertMermaidToExcalidraw utility - Remove test button and handleMermaidTest function - Fix: Add missing Excalidraw CSS import for proper UI rendering - Enhance server endpoint with WebSocket broadcast support - Add mermaid_convert to WebSocketMessageType union
This commit is contained in:
+34
-38
@@ -215,7 +215,7 @@ function App(): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
const handleWebSocketMessage = (data: WebSocketMessage): void => {
|
||||
const handleWebSocketMessage = async (data: WebSocketMessage): Promise<void> => {
|
||||
if (!excalidrawAPI) {
|
||||
return
|
||||
}
|
||||
@@ -294,6 +294,39 @@ function App(): JSX.Element {
|
||||
console.log(`Server sync status: ${data.count} elements`)
|
||||
break
|
||||
|
||||
case 'mermaid_convert':
|
||||
console.log('Received Mermaid conversion request from MCP')
|
||||
if ((data as any).mermaidDiagram) {
|
||||
try {
|
||||
const result = await convertMermaidToExcalidraw((data as any).mermaidDiagram, (data as any).config || DEFAULT_MERMAID_CONFIG)
|
||||
|
||||
if (result.error) {
|
||||
console.error('Mermaid conversion error:', result.error)
|
||||
return
|
||||
}
|
||||
|
||||
if (result.elements && result.elements.length > 0) {
|
||||
const convertedElements = convertToExcalidrawElements(result.elements as any, { regenerateIds: false })
|
||||
excalidrawAPI.updateScene({
|
||||
elements: convertedElements,
|
||||
captureUpdate: CaptureUpdateAction.IMMEDIATELY
|
||||
})
|
||||
|
||||
if (result.files) {
|
||||
excalidrawAPI.addFiles(Object.values(result.files))
|
||||
}
|
||||
|
||||
console.log('Mermaid diagram converted successfully:', result.elements.length, 'elements')
|
||||
|
||||
// Sync to backend automatically after creating elements
|
||||
await syncToBackend()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error converting Mermaid diagram from WebSocket:', error)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
console.log('Unknown WebSocket message type:', data.type)
|
||||
}
|
||||
@@ -400,42 +433,6 @@ function App(): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
const handleMermaidTest = async (): Promise<void> => {
|
||||
if (!excalidrawAPI) return
|
||||
|
||||
const testMermaid = `graph TD
|
||||
A[Start] --> B{Decision}
|
||||
B -->|Yes| C[Do Something]
|
||||
B -->|No| D[Do Something Else]
|
||||
C --> E[End]
|
||||
D --> E`
|
||||
|
||||
try {
|
||||
const result = await convertMermaidToExcalidraw(testMermaid, DEFAULT_MERMAID_CONFIG)
|
||||
|
||||
if (result.error) {
|
||||
console.error('Mermaid conversion error:', result.error)
|
||||
return
|
||||
}
|
||||
|
||||
if (result.elements && result.elements.length > 0) {
|
||||
const convertedElements = convertToExcalidrawElements(result.elements as any, { regenerateIds: false })
|
||||
excalidrawAPI.updateScene({
|
||||
elements: convertedElements,
|
||||
captureUpdate: CaptureUpdateAction.IMMEDIATELY
|
||||
})
|
||||
|
||||
if (result.files) {
|
||||
excalidrawAPI.addFiles(Object.values(result.files))
|
||||
}
|
||||
|
||||
console.log('Mermaid diagram converted successfully:', result.elements.length, 'elements')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error converting Mermaid diagram:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
{/* Header */}
|
||||
@@ -474,7 +471,6 @@ function App(): JSX.Element {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button className="btn-secondary" onClick={handleMermaidTest}>Test Mermaid</button>
|
||||
<button className="btn-secondary" onClick={clearCanvas}>Clear Canvas</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import '@excalidraw/excalidraw/index.css'
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
|
||||
+9
-2
@@ -416,13 +416,20 @@ app.post('/api/elements/from-mermaid', (req: Request, res: Response) => {
|
||||
hasConfig: !!config
|
||||
});
|
||||
|
||||
// Broadcast to all WebSocket clients to process the Mermaid diagram
|
||||
broadcast({
|
||||
type: 'mermaid_convert',
|
||||
mermaidDiagram,
|
||||
config: config || {},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Return the diagram for frontend processing
|
||||
// The frontend will handle the actual conversion using mermaid-to-excalidraw
|
||||
res.json({
|
||||
success: true,
|
||||
mermaidDiagram,
|
||||
config: config || {},
|
||||
message: 'Mermaid diagram received. Process on frontend with DOM access.'
|
||||
message: 'Mermaid diagram sent to frontend for conversion.'
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error processing Mermaid diagram:', error);
|
||||
|
||||
+9
-1
@@ -175,7 +175,8 @@ export type WebSocketMessageType =
|
||||
| 'element_deleted'
|
||||
| 'elements_batch_created'
|
||||
| 'elements_synced'
|
||||
| 'sync_status';
|
||||
| 'sync_status'
|
||||
| 'mermaid_convert';
|
||||
|
||||
export interface InitialElementsMessage extends WebSocketMessage {
|
||||
type: 'initial_elements';
|
||||
@@ -208,6 +209,13 @@ export interface SyncStatusMessage extends WebSocketMessage {
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface MermaidConvertMessage extends WebSocketMessage {
|
||||
type: 'mermaid_convert';
|
||||
mermaidDiagram: string;
|
||||
config?: MermaidConfig;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
// Mermaid conversion types
|
||||
export interface MermaidConfig {
|
||||
startOnLoad?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user