Create new workflow nodes for the MiCRA system...
Guide for creating new workflow nodes in the MiCRA system.
Before implementing, read these reference implementations to understand exact patterns:
frontend/src/components/workflow/nodes/ImageGenerationNode.tsx - Image generation with aspect ratio and reference imagefrontend/src/components/workflow/nodes/TextGenerationNode.tsx - Text generation with preset management frontend/src/components/workflow/nodes/ImageMatchingNode.tsx - Multi-image selection and matchingFollow the exact patterns from these files. They demonstrate:
A complete node often requires 4 files:
frontend/src/components/workflow/nodes/YourNodeNameNode.tsxfrontend/src/lib/fastapi/your-api-name.tsbackend/app/api/v1/your_endpoint.pybackend/app/api/routes.pyThe OPTIONAL files are only required if the functionality contained in the node requires backend processing. Some behaviour may only include manipulation of local data, in which case these are not necessary.
const config: NodeConfig = {
type: "your-node-type", // kebab-case, unique
label: "Your Node Name",
description: "What this node does",
inputs: [{ id: "input1", label: "Label", type: "string" }],
outputs: [{ id: "output1", label: "Label", type: "json" }],
};
Port Types: 'string' | 'file' | 'image' | 'json' | 'image[]'
WorkflowNodeWrapper with nodeThemes.indigo|emerald|amberuseWorkflowStore((state) => state.nodes[id])node.inputsuseEffectidle → running → completed/errornode.outputsexport interface YourRequest { field1: string }
export interface YourResponse { success: boolean; data?: any; error?: string }
export async function yourApiFunction(request: YourRequest): Promise<YourResponse> {
return apiClient.request<YourResponse>('/v1/your-endpoint/action', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request)
})
}
router = APIRouter(prefix="/your-endpoint", tags=["your-tag"])
class YourRequest(BaseModel):
field1: str = Field(..., min_length=1)
class YourResponse(BaseModel):
success: bool
data: Optional[dict] = None
error: Optional[str] = None
@router.post("/action", response_model=YourResponse)
async def your_action(request: YourRequest):
# Implementation
return YourResponse(success=True, data=result)
Add to backend/app/api/routes.py:
from .v1 import your_endpoint
api_router.include_router(your_endpoint.router, prefix="/v1", tags=["your-tag"])
Image Bucket Access:
const imageBucket = useWorkflowStore((state) => state.imageBucket);
const selectedImage = imageBucket.find(img => img.id === imageId);
Multiple Image Selection:
const [selectedImageIds, setSelectedImageIds] = useState<Set<string>>(new Set());
const selectedImages = imageBucket.filter(img => selectedImageIds.has(img.id));
Preset Loading:
const [presets, setPresets] = useState<Preset[]>([]);
useEffect(() => { loadPresets(); }, []);
WorkflowNodeWrapper with themeuseEffecthandleExecute validates inputsrunning → completed/errorroutes.py