465 lines
15 KiB
Markdown
465 lines
15 KiB
Markdown
# SVG-Based Wireframe Generation - Integration Guide
|
|
|
|
This guide explains how to implement and integrate the SVG-based wireframe generation system that converts natural language prompts into precise, scalable vector graphics.
|
|
|
|
## 🎯 **Why SVG Instead of JSON?**
|
|
|
|
### **Advantages of SVG Approach:**
|
|
1. **Precise Positioning**: Exact coordinates and dimensions
|
|
2. **Better Performance**: Direct rendering without parsing overhead
|
|
3. **Scalable Graphics**: Vector-based, resolution-independent
|
|
4. **Rich Styling**: Colors, gradients, shadows, and effects
|
|
5. **Standard Format**: Widely supported across platforms
|
|
|
|
### **Comparison:**
|
|
| Aspect | JSON Approach | SVG Approach |
|
|
|--------|---------------|--------------|
|
|
| **Precision** | Approximate positioning | Exact positioning |
|
|
| **Performance** | Slower (parsing + generation) | Faster (direct rendering) |
|
|
| **Styling** | Limited color options | Full CSS styling support |
|
|
| **Complexity** | Simple shapes only | Complex paths and effects |
|
|
| **Maintenance** | Frontend logic heavy | Backend logic heavy |
|
|
|
|
## 🏗️ **System Architecture**
|
|
|
|
```
|
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
│ Frontend │ │ Backend │ │ Claude AI │
|
|
│ (React) │◄──►│ (Flask) │◄──►│ (API) │
|
|
│ │ │ │ │ │
|
|
│ • tldraw Canvas │ │ • Prompt │ │ • Natural │
|
|
│ • SVG Parser │ │ Processing │ │ Language │
|
|
│ • Response │ │ • SVG Generation │ │ Analysis │
|
|
│ Handler │ │ • Response │ │ • Layout │
|
|
└─────────────────┘ │ Routing │ │ Generation │
|
|
└──────────────────┘ └─────────────────┘
|
|
```
|
|
|
|
## 🔄 **Data Flow**
|
|
|
|
### **1. User Input**
|
|
```
|
|
User types: "Dashboard with header, sidebar, and 3 stats cards"
|
|
```
|
|
|
|
### **2. Frontend Request**
|
|
```typescript
|
|
const response = await fetch('/generate-wireframe', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ prompt: userPrompt })
|
|
})
|
|
```
|
|
|
|
### **3. Backend Processing**
|
|
```python
|
|
# Flask backend receives prompt
|
|
@app.route('/generate-wireframe', methods=['POST'])
|
|
def generate_wireframe():
|
|
prompt = request.json.get('prompt')
|
|
|
|
# Send to Claude AI
|
|
claude_response = call_claude_api(prompt)
|
|
|
|
# Generate SVG from AI response
|
|
svg_content = generate_svg_wireframe(claude_response)
|
|
|
|
# Return SVG with proper content type
|
|
return svg_content, 200, {'Content-Type': 'image/svg+xml'}
|
|
```
|
|
|
|
### **4. SVG Response**
|
|
```xml
|
|
<svg width="800" height="600" viewBox="0 0 800 600">
|
|
<defs>
|
|
<filter id="shadow" y="-40%" x="-40%" width="180%" height="180%">
|
|
<feDropShadow dx="1" dy="1" stdDeviation="1.2" flood-opacity=".5"/>
|
|
</filter>
|
|
</defs>
|
|
<g>
|
|
<!-- Header -->
|
|
<rect x="0" y="0" width="800" height="60" fill="#f0f0f0"/>
|
|
<text x="20" y="35" font-family="Arial" font-size="16">Dashboard Header</text>
|
|
|
|
<!-- Sidebar -->
|
|
<rect x="0" y="60" width="200" height="540" fill="#e0e0e0"/>
|
|
<text x="20" y="85" font-family="Arial" font-size="14">Navigation</text>
|
|
|
|
<!-- Stats Cards -->
|
|
<rect x="220" y="80" width="160" height="120" fill="#ffffff" filter="url(#shadow)"/>
|
|
<text x="240" y="100" font-family="Arial" font-size="12">Stats Card 1</text>
|
|
|
|
<rect x="400" y="80" width="160" height="120" fill="#ffffff" filter="url(#shadow)"/>
|
|
<text x="420" y="100" font-family="Arial" font-size="12">Stats Card 2</text>
|
|
|
|
<rect x="580" y="80" width="160" height="120" fill="#ffffff" filter="url(#shadow)"/>
|
|
<text x="600" y="100" font-family="Arial" font-size="12">Stats Card 3</text>
|
|
</g>
|
|
</svg>
|
|
```
|
|
|
|
### **5. Frontend Rendering**
|
|
```typescript
|
|
// Check response type
|
|
const contentType = response.headers.get('content-type')
|
|
|
|
if (contentType && contentType.includes('image/svg+xml')) {
|
|
// Handle SVG response
|
|
const svgString = await response.text()
|
|
await parseSVGAndRender(editor, svgString)
|
|
} else {
|
|
// Fallback to JSON
|
|
const data = await response.json()
|
|
await generateWireframeFromSpec(editor, data.wireframe)
|
|
}
|
|
```
|
|
|
|
## 🔧 **Implementation Steps**
|
|
|
|
### **Step 1: Backend SVG Generation**
|
|
|
|
#### **1.1 Install Dependencies**
|
|
```bash
|
|
pip install flask flask-cors anthropic
|
|
```
|
|
|
|
#### **1.2 Create SVG Generator**
|
|
```python
|
|
import xml.etree.ElementTree as ET
|
|
|
|
def generate_svg_wireframe(layout_spec):
|
|
"""Generate SVG wireframe from layout specification"""
|
|
|
|
# Create SVG root element
|
|
svg = ET.Element('svg', {
|
|
'width': '800',
|
|
'height': '600',
|
|
'viewBox': '0 0 800 600',
|
|
'xmlns': 'http://www.w3.org/2000/svg'
|
|
})
|
|
|
|
# Add definitions (filters, gradients)
|
|
defs = ET.SubElement(svg, 'defs')
|
|
shadow_filter = ET.SubElement(defs, 'filter', {
|
|
'id': 'shadow',
|
|
'y': '-40%', 'x': '-40%',
|
|
'width': '180%', 'height': '180%'
|
|
})
|
|
ET.SubElement(shadow_filter, 'feDropShadow', {
|
|
'dx': '1', 'dy': '1',
|
|
'stdDeviation': '1.2',
|
|
'flood-opacity': '.5'
|
|
})
|
|
|
|
# Create main group
|
|
main_group = ET.SubElement(svg, 'g')
|
|
|
|
# Generate layout elements
|
|
generate_header(main_group, layout_spec.get('header', {}))
|
|
generate_sidebar(main_group, layout_spec.get('sidebar', {}))
|
|
generate_main_content(main_group, layout_spec.get('main_content', {}))
|
|
generate_footer(main_group, layout_spec.get('footer', {}))
|
|
|
|
return ET.tostring(svg, encoding='unicode')
|
|
|
|
def generate_header(group, header_spec):
|
|
"""Generate header section"""
|
|
if not header_spec.get('enabled', False):
|
|
return
|
|
|
|
# Header background
|
|
ET.SubElement(group, 'rect', {
|
|
'x': '0', 'y': '0',
|
|
'width': '800', 'height': '60',
|
|
'fill': '#f0f0f0'
|
|
})
|
|
|
|
# Header text
|
|
ET.SubElement(group, 'text', {
|
|
'x': '20', 'y': '35',
|
|
'font-family': 'Arial',
|
|
'font-size': '16',
|
|
'fill': '#333333'
|
|
}).text = header_spec.get('title', 'Header')
|
|
```
|
|
|
|
#### **1.3 Update Flask Endpoint**
|
|
```python
|
|
@app.route('/generate-wireframe', methods=['POST'])
|
|
def generate_wireframe():
|
|
try:
|
|
prompt = request.json.get('prompt')
|
|
if not prompt:
|
|
return jsonify({'error': 'Prompt is required'}), 400
|
|
|
|
# Call Claude AI
|
|
claude_response = call_claude_api(prompt)
|
|
|
|
# Parse AI response and generate SVG
|
|
layout_spec = parse_claude_response(claude_response)
|
|
svg_content = generate_svg_wireframe(layout_spec)
|
|
|
|
# Return SVG with proper headers
|
|
response = make_response(svg_content)
|
|
response.headers['Content-Type'] = 'image/svg+xml'
|
|
response.headers['Cache-Control'] = 'no-cache'
|
|
return response
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error generating wireframe: {str(e)}")
|
|
return jsonify({'error': 'Internal server error'}), 500
|
|
```
|
|
|
|
### **Step 2: Frontend SVG Parsing**
|
|
|
|
#### **2.1 SVG Parser Functions**
|
|
```typescript
|
|
const parseSVGAndRender = async (editor: Editor, svgString: string) => {
|
|
try {
|
|
// Parse SVG string
|
|
const parser = new DOMParser()
|
|
const svgDoc = parser.parseFromString(svgString, 'image/svg+xml')
|
|
const svgElement = svgDoc.querySelector('svg')
|
|
|
|
if (!svgElement) {
|
|
throw new Error('Invalid SVG content')
|
|
}
|
|
|
|
// Get dimensions
|
|
const viewBox = svgElement.getAttribute('viewBox')?.split(' ').map(Number) || [0, 0, 800, 600]
|
|
const [, , svgWidth, svgHeight] = viewBox
|
|
|
|
// Create main frame
|
|
editor.createShape({
|
|
id: createShapeId(),
|
|
type: "frame",
|
|
x: 50, y: 50,
|
|
props: {
|
|
w: Math.max(800, svgWidth),
|
|
h: Math.max(600, svgHeight),
|
|
name: "SVG Wireframe",
|
|
},
|
|
})
|
|
|
|
// Render SVG elements
|
|
await renderSVGElements(editor, svgElement, 50, 50, svgWidth, svgHeight)
|
|
|
|
} catch (error) {
|
|
console.error('SVG parsing error:', error)
|
|
// Fallback to basic wireframe
|
|
await generateFallbackWireframe(editor, "SVG parsing failed")
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **2.2 Element Renderers**
|
|
```typescript
|
|
const renderSVGRect = async (editor: Editor, element: SVGElement, offsetX: number, offsetY: number) => {
|
|
const x = parseFloat(element.getAttribute('x') || '0') + offsetX
|
|
const y = parseFloat(element.getAttribute('y') || '0') + offsetY
|
|
const width = parseFloat(element.getAttribute('width') || '100')
|
|
const height = parseFloat(element.getAttribute('height') || '100')
|
|
const fill = element.getAttribute('fill') || 'none'
|
|
const stroke = element.getAttribute('stroke') || 'black'
|
|
|
|
editor.createShape({
|
|
id: createShapeId(),
|
|
type: "geo",
|
|
x, y,
|
|
props: {
|
|
w: Math.max(10, width),
|
|
h: Math.max(10, height),
|
|
geo: "rectangle",
|
|
fill: fill === 'none' ? 'none' : 'semi',
|
|
color: mapColorToTldraw(stroke),
|
|
},
|
|
})
|
|
}
|
|
```
|
|
|
|
## 🎨 **SVG Styling and Effects**
|
|
|
|
### **Shadows and Filters**
|
|
```xml
|
|
<defs>
|
|
<filter id="shadow" y="-40%" x="-40%" width="180%" height="180%">
|
|
<feDropShadow dx="1" dy="1" stdDeviation="1.2" flood-opacity=".5"/>
|
|
</filter>
|
|
|
|
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
|
|
<feMerge>
|
|
<feMergeNode in="coloredBlur"/>
|
|
<feMergeNode in="SourceGraphic"/>
|
|
</feMerge>
|
|
</filter>
|
|
</defs>
|
|
```
|
|
|
|
### **Gradients**
|
|
```xml
|
|
<defs>
|
|
<linearGradient id="headerGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" style="stop-color:#4facfe;stop-opacity:1" />
|
|
<stop offset="100%" style="stop-color:#00f2fe;stop-opacity:1" />
|
|
</linearGradient>
|
|
</defs>
|
|
|
|
<rect x="0" y="0" width="800" height="60" fill="url(#headerGradient)"/>
|
|
```
|
|
|
|
### **Text Styling**
|
|
```xml
|
|
<text x="20" y="35"
|
|
font-family="Arial, sans-serif"
|
|
font-size="16"
|
|
font-weight="bold"
|
|
fill="#333333"
|
|
text-anchor="start">
|
|
Dashboard Header
|
|
</text>
|
|
```
|
|
|
|
## 🔄 **Response Type Detection**
|
|
|
|
### **Content-Type Based Routing**
|
|
```typescript
|
|
const generateFromPrompt = async (prompt: string) => {
|
|
try {
|
|
const response = await fetch('/generate-wireframe', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ prompt })
|
|
})
|
|
|
|
// Detect response type
|
|
const contentType = response.headers.get('content-type')
|
|
|
|
if (contentType && contentType.includes('image/svg+xml')) {
|
|
// SVG response - parse and render
|
|
const svgString = await response.text()
|
|
await parseSVGAndRender(editor, svgString)
|
|
} else {
|
|
// JSON response - fallback processing
|
|
const data = await response.json()
|
|
await generateWireframeFromSpec(editor, data.wireframe)
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Generation error:', error)
|
|
await generateFallbackWireframe(editor, prompt)
|
|
}
|
|
}
|
|
```
|
|
|
|
## 🧪 **Testing and Validation**
|
|
|
|
### **Backend Testing**
|
|
```python
|
|
def test_svg_generation():
|
|
"""Test SVG generation functionality"""
|
|
|
|
# Test layout specification
|
|
layout_spec = {
|
|
'header': {'enabled': True, 'title': 'Test Header'},
|
|
'sidebar': {'enabled': True, 'width': 200},
|
|
'main_content': {'sections': []},
|
|
'footer': {'enabled': True, 'height': 60}
|
|
}
|
|
|
|
# Generate SVG
|
|
svg_content = generate_svg_wireframe(layout_spec)
|
|
|
|
# Validate SVG structure
|
|
assert '<svg' in svg_content
|
|
assert 'width="800"' in svg_content
|
|
assert 'height="600"' in svg_content
|
|
assert 'Test Header' in svg_content
|
|
|
|
print("✅ SVG generation test passed")
|
|
|
|
if __name__ == '__main__':
|
|
test_svg_generation()
|
|
```
|
|
|
|
### **Frontend Testing**
|
|
```typescript
|
|
const testSVGParsing = async () => {
|
|
const testSVG = `
|
|
<svg width="100" height="100" viewBox="0 0 100 100">
|
|
<rect x="10" y="10" width="80" height="80" fill="#f0f0f0"/>
|
|
<text x="20" y="60">Test</text>
|
|
</svg>
|
|
`
|
|
|
|
try {
|
|
await parseSVGAndRender(mockEditor, testSVG)
|
|
console.log('✅ SVG parsing test passed')
|
|
} catch (error) {
|
|
console.error('❌ SVG parsing test failed:', error)
|
|
}
|
|
}
|
|
```
|
|
|
|
## 🚀 **Performance Optimization**
|
|
|
|
### **SVG Optimization Techniques**
|
|
1. **Minimize DOM Elements**: Use groups for related elements
|
|
2. **Optimize Paths**: Simplify complex paths
|
|
3. **Reduce Attributes**: Use CSS classes for common styles
|
|
4. **Compression**: Gzip SVG responses
|
|
|
|
### **Caching Strategies**
|
|
```python
|
|
from functools import lru_cache
|
|
|
|
@lru_cache(maxsize=100)
|
|
def generate_cached_svg(prompt_hash):
|
|
"""Cache SVG generation for repeated prompts"""
|
|
return generate_svg_wireframe(get_cached_layout(prompt_hash))
|
|
```
|
|
|
|
## 🔮 **Future Enhancements**
|
|
|
|
### **Advanced SVG Features**
|
|
- **Animations**: CSS animations and transitions
|
|
- **Interactivity**: Click handlers and hover effects
|
|
- **Responsive Design**: ViewBox scaling and media queries
|
|
- **Accessibility**: ARIA labels and screen reader support
|
|
|
|
### **Integration Possibilities**
|
|
- **Design Systems**: Consistent component libraries
|
|
- **Export Options**: PNG, PDF, and other formats
|
|
- **Collaboration**: Real-time editing and version control
|
|
- **Analytics**: Usage tracking and performance metrics
|
|
|
|
---
|
|
|
|
## 📋 **Implementation Checklist**
|
|
|
|
- [ ] Backend SVG generation functions
|
|
- [ ] Frontend SVG parsing and rendering
|
|
- [ ] Response type detection and routing
|
|
- [ ] Error handling and fallback mechanisms
|
|
- [ ] Testing and validation
|
|
- [ ] Performance optimization
|
|
- [ ] Documentation and examples
|
|
|
|
## 🆘 **Troubleshooting**
|
|
|
|
### **Common Issues**
|
|
1. **SVG Not Rendering**: Check content-type headers
|
|
2. **Parsing Errors**: Validate SVG XML structure
|
|
3. **Performance Issues**: Optimize SVG complexity
|
|
4. **CORS Problems**: Configure proper origins
|
|
|
|
### **Debug Tips**
|
|
- Use browser dev tools to inspect SVG responses
|
|
- Check network tab for content-type headers
|
|
- Validate SVG content with online validators
|
|
- Monitor console for parsing errors
|
|
|
|
---
|
|
|
|
**This integration guide provides a comprehensive approach to implementing SVG-based wireframe generation. The system offers better performance, precision, and styling capabilities compared to JSON-based approaches.**
|