Generates React + TypeScript components from protocol using LLM agents.
src/nodes/code/index.ts
Traverse & Flatten Tree (Post-order DFS)
↓
Classify Nodes
├─ Leaf Components (no children)
└─ Frame Components (with children)
↓
Generate Code (Parallel Processing)
├─ Leaf → generateComponent()
│ ├─ Input: Protocol + Thumbnail
│ └─ Output: Self-contained component
│
└─ Frame → generateFrame()
├─ Input: Protocol + Thumbnail + Child imports
└─ Output: Container composing children
↓
Write Component Files
↓
Mark as Generated (Cache)
↓
Inject Root to App.tsx
↓
Complete
Input:
{
protocol: Protocol,
workspace: { app: string, process: string }
}Output: {} (files written to disk)
- Flatten Tree → Post-order DFS traversal (children first, then parent)
- Classify Nodes → Separate into leaf components vs. frame containers
- Generate Code → Different strategies for each type
- Write Files → Save to
src/components/ - Cache → Mark as generated to avoid regeneration
- Inject → Update
App.tsxwith root component
const isLeaf = !currentNode.children?.length;
if (isLeaf) {
// Leaf Component: No children, self-contained
await generateComponent(currentNode, state, assetFilesList, progressInfo);
} else {
// Frame Component: Has children, composes them
await generateFrame(currentNode, state, assetFilesList, progressInfo);
}Leaf Components:
- No child components
- Self-contained UI elements
- Examples: Button, Icon, Text block, Image
- Generated with full implementation
Frame Components:
- Contains child components
- Acts as layout container
- Examples: Header (contains Logo + Nav), ProductGrid (contains ProductCards)
- Generated with child component imports and composition
Inputs to LLM:
- Protocol data: Component structure, props, elements, layout
- Design thumbnail: Figma design screenshot (visual reference)
- Asset files: Available images in
src/assets/ - Styling config: Tailwind CSS configuration
// For frames (containers with children)
await callModel({
question: generateFramePrompt({
frameDetails: JSON.stringify(node.data),
childrenImports: JSON.stringify(childrenImports),
styling: JSON.stringify(DEFAULT_STYLING),
assetFiles: assetFilesList
}),
imageUrls: state.figmaInfo.thumbnail // 🎨 Visual reference
});
// For components (leaf nodes or reusable)
await callModel({
question: generateComponentPrompt({
componentName,
componentDetails: JSON.stringify(node.data),
styling: JSON.stringify(DEFAULT_STYLING),
assetFiles: assetFilesList
}),
imageUrls: state.figmaInfo.thumbnail // 🎨 Visual reference
});Why thumbnail matters:
- AI sees the actual design, not just data
- Improves visual accuracy (colors, spacing, alignment)
- Helps with semantic understanding of UI purpose
// Generated: src/components/Button/index.tsx
import React from 'react';
interface ButtonProps {
children?: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({ children, onClick }) => (
<button onClick={onClick} className="flex items-center px-4 py-2">
{children}
</button>
);Cached in workspace.checkpoint/checkpoint.json:
{
"generatedComponents": [
"Header",
"Footer"
],
"appInjected": true
}Cache invalidated on protocol changes.
export async function generateCode(state: GraphState) {
const cache = loadCodeCache(state.workspace);
// Generate components (DFS)
const totalComponents = await processNode(state, cache);
// Inject root
await injectRootComponentToApp(state, cache);
logger.printSuccessLog(`Generated ${totalComponents} components`);
}const modelConfig = {
contextWindowTokens: CODE_CONTEXT_WINDOW,
maxOutputTokens: CODE_MAX_OUTPUT,
temperature: 0.2 // Low for consistency
};- Missing protocol → throws
- Component generation failure → logs warning, continues
- Partial success → reports total generated
- TypeScript with strict types
- Tailwind CSS for styling
- Named exports
- One component per directory
Modify prompts in src/nodes/code/prompt.ts