- Chat interface with OpenAI GPT integration - Automatic diagram generation from text descriptions - Tldraw canvas with Dagre layout engine - REST API instead of WebSocket approach 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
100 lines
2.5 KiB
TypeScript
100 lines
2.5 KiB
TypeScript
/**
|
|
* Diagram Parser Utility
|
|
*
|
|
* Extracts and validates diagram JSON from AI responses
|
|
*/
|
|
|
|
import { GraphModel } from '@/types/graph';
|
|
|
|
/**
|
|
* Extract diagram JSON from AI response text
|
|
* Looks for content between [DIAGRAM_START] and [DIAGRAM_END] markers
|
|
*/
|
|
export function extractDiagramFromResponse(response: string): GraphModel | null {
|
|
try {
|
|
const startMarker = '[DIAGRAM_START]';
|
|
const endMarker = '[DIAGRAM_END]';
|
|
|
|
const startIndex = response.indexOf(startMarker);
|
|
const endIndex = response.indexOf(endMarker);
|
|
|
|
if (startIndex === -1 || endIndex === -1) {
|
|
console.log('No diagram markers found in response');
|
|
return null;
|
|
}
|
|
|
|
const jsonStr = response.substring(
|
|
startIndex + startMarker.length,
|
|
endIndex
|
|
).trim();
|
|
|
|
const diagram = JSON.parse(jsonStr) as GraphModel;
|
|
|
|
// Validate diagram structure
|
|
if (!validateDiagram(diagram)) {
|
|
console.error('Invalid diagram structure');
|
|
return null;
|
|
}
|
|
|
|
return diagram;
|
|
} catch (error) {
|
|
console.error('Failed to extract diagram from response:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate diagram structure
|
|
*/
|
|
export function validateDiagram(diagram: any): diagram is GraphModel {
|
|
if (!diagram || typeof diagram !== 'object') {
|
|
return false;
|
|
}
|
|
|
|
if (!Array.isArray(diagram.nodes) || !Array.isArray(diagram.edges)) {
|
|
return false;
|
|
}
|
|
|
|
// Validate nodes
|
|
for (const node of diagram.nodes) {
|
|
if (!node.id || !node.label || !node.type) {
|
|
console.error('Invalid node structure:', node);
|
|
return false;
|
|
}
|
|
|
|
const validTypes = ['start', 'process', 'decision', 'end', 'data', 'default'];
|
|
if (!validTypes.includes(node.type)) {
|
|
console.error('Invalid node type:', node.type);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Validate edges
|
|
for (const edge of diagram.edges) {
|
|
if (!edge.id || !edge.source || !edge.target) {
|
|
console.error('Invalid edge structure:', edge);
|
|
return false;
|
|
}
|
|
|
|
// Check that source and target nodes exist
|
|
const sourceExists = diagram.nodes.some((n: any) => n.id === edge.source);
|
|
const targetExists = diagram.nodes.some((n: any) => n.id === edge.target);
|
|
|
|
if (!sourceExists || !targetExists) {
|
|
console.error('Edge references non-existent node:', edge);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Remove diagram markers from response text (for display)
|
|
*/
|
|
export function stripDiagramMarkers(response: string): string {
|
|
return response
|
|
.replace(/\[DIAGRAM_START\][\s\S]*?\[DIAGRAM_END\]/g, '[Diagram generated]')
|
|
.trim();
|
|
}
|