Jake Shore 96e52666c5 MCPEngine full sync — studio scaffold, factory v2, server updates, state.json — 2026-02-12
=== NEW ===
- studio/ — MCPEngine Studio scaffold (Next.js monorepo, build plan)
- docs/FACTORY-V2.md — Factory v2 architecture doc
- docs/CALENDLY_MCP_BUILD_SUMMARY.md — Calendly MCP build report

=== UPDATED SERVERS ===
- fieldedge: Added jobs-tools, UI build script, main entry update
- lightspeed: Updated main + server entry points
- squarespace: Added collection-browser + page-manager apps
- toast: Added main + server entry points

=== INFRA ===
- infra/command-center/state.json — Updated pipeline state
- infra/command-center/FACTORY-V2.md — Factory v2 operator playbook
2026-02-12 17:58:33 -05:00

147 lines
3.3 KiB
TypeScript

'use client';
import React, { useCallback, useMemo } from 'react';
import {
ReactFlow,
Background,
Controls,
MiniMap,
type Node,
type Edge,
type OnNodesChange,
type OnEdgesChange,
type OnConnect,
type NodeTypes,
type EdgeTypes,
BackgroundVariant,
ConnectionMode,
addEdge,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { ToolNode } from './ToolNode';
import { GroupNode } from './GroupNode';
import { ConnectionEdge } from './ConnectionEdge';
import { CanvasToolbar } from './CanvasToolbar';
import { useCanvasState } from '../../hooks/useCanvasState';
import type { ToolDefinition } from '@mcpengine/ai-pipeline/types';
interface ToolCanvasProps {
tools: ToolDefinition[];
onToolSelect: (toolName: string | null) => void;
onToolsChange: (tools: ToolDefinition[]) => void;
}
const nodeTypes: NodeTypes = {
tool: ToolNode,
group: GroupNode,
};
const edgeTypes: EdgeTypes = {
connection: ConnectionEdge,
};
export function ToolCanvas({ tools, onToolSelect, onToolsChange }: ToolCanvasProps) {
const {
nodes,
edges,
setNodes,
setEdges,
selectedNodeId,
selectNode,
} = useCanvasState();
const onNodesChange: OnNodesChange = useCallback(
(changes) => {
setNodes(changes);
},
[setNodes]
);
const onEdgesChange: OnEdgesChange = useCallback(
(changes) => {
setEdges(changes);
},
[setEdges]
);
const onConnect: OnConnect = useCallback(
(connection) => {
useCanvasState.setState((state) => ({
edges: addEdge(
{ ...connection, type: 'connection', animated: true },
state.edges
),
}));
},
[]
);
const onNodeClick = useCallback(
(_: React.MouseEvent, node: Node) => {
selectNode(node.id);
onToolSelect(node.id);
},
[selectNode, onToolSelect]
);
const onPaneClick = useCallback(() => {
selectNode(null);
onToolSelect(null);
}, [selectNode, onToolSelect]);
const defaultEdgeOptions = useMemo(
() => ({
type: 'connection',
animated: true,
}),
[]
);
return (
<div className="relative h-full w-full bg-[#0A0F1E]">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
defaultEdgeOptions={defaultEdgeOptions}
connectionMode={ConnectionMode.Loose}
fitView
proOptions={{ hideAttribution: true }}
className="bg-[#0A0F1E]"
>
<Background
variant={BackgroundVariant.Dots}
gap={20}
size={1}
color="#1E293B"
/>
<Controls
showZoom={false}
showFitView={false}
showInteractive={false}
className="hidden"
/>
<MiniMap
nodeColor={(node) => {
if (node.id === selectedNodeId) return '#6366F1';
return '#374151';
}}
maskColor="rgba(10, 15, 30, 0.8)"
className="!bg-gray-900 !border-gray-700 rounded-lg"
pannable
zoomable
/>
</ReactFlow>
<CanvasToolbar />
</div>
);
}