Local MCP Server
在Termux中运行本地MCP服务器,支持Ollama模型的文件读取与命令执行。
下载 11
基于 @xyflow/react 构建节点式流程图与可视化编辑器的完整实现指南。
openclaw skills install @anderskev/react-flow-implementation命令、参数、文件名以原文为准
import { useCallback } from 'react';
import { ReactFlow, useNodesState, useEdgesState, addEdge } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{ id: '1', position: { x: 0, y: 0 }, data: { label: '节点 1' } },
{ id: '2', position: { x: 200, y: 100 }, data: { label: '节点 2' } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
export default function 流程图() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges]
);
return (
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
/>
</div>
);
}按顺序执行;不要跳过“看起来没问题”的步骤。
<ReactFlow /> 的打包文件导入了 @xyflow/react/dist/style.css(或等效的 CSS 处理流程),确保节点和连线可见,且点击区域与视觉元素匹配。useReactFlow 作用域边界 — 通过条件**:所有调用 useReactFlow() 的组件都是 <ReactFlowProvider> 的后代,且该提供者包裹的组件树与 <ReactFlow /> 相同(或你有意拆分状态并明确命名两个根节点)。nodeTypes / edgeTypes — 通过条件:这些映射必须是模块级常量**或使用 useMemo 并传入稳定依赖项——不能在渲染 <ReactFlow /> 的组件中每渲染一次就创建新的 { ... } 字面量。Handle id 的节点,所有必须连接到特定处理程序的连线都应正确设置 sourceHandle / targetHandle(或你有意接受默认处理程序解析)。有关 MiniMap、Controls、Background、NodeToolbar 和 NodeResizer 模式的更多信息,请参阅 [ADDITIONAL_COMPONENTS.md](ADDITIONAL_COMPONENTS.md)。
import type { Node, Edge, NodeProps, BuiltInNode } from '@xyflow/react';
// 定义带有数据结构的自定义节点类型
type 自定义节点 = Node<{ value: number; label: string }, 'custom'>;
// 与内置节点组合
type 我的节点 = 自定义节点 | BuiltInNode;
type 我的连线 = Edge<{ weight?: number }>;
// 在整个应用中使用
const [nodes, setNodes] = useNodesState<我的节点>(initialNodes);import { memo } from 'react';
import { Handle, Position, type NodeProps } from '@xyflow/react';
// 定义节点类型
type 计数器节点 = Node<{ count: number }, 'counter'>;
// 始终使用 memo 提升性能
const 计数器节点 = memo(function 计数器节点({ data, isConnectable }: NodeProps<计数器节点>) {
return (
<>
<Handle type="target" position={Position.Top} isConnectable={isConnectable} />
<div className="counter-node">
数量:{data.count}
{/* nodrag 防止在点击按钮时触发拖拽 */}
<button className="nodrag" onClick={() => console.log('点击了')}>
增加
</button>
</div>
<Handle type="source" position={Position.Bottom} isConnectable={isConnectable} />
</>
);
});
// 在组件外部注册,避免重复渲染
const nodeTypes = { counter: 计数器节点 };
// 在 ReactFlow 中使用
<ReactFlow nodeTypes={nodeTypes} ... />// 当节点有多个同类型处理程序时,使用 handle ID
<Handle type="source" position={Position.Right} id="a" />
<Handle type="source" position={Position.Right} id="b" style={{ top: 20 }} />
// 使用特定处理程序连接
const edge = {
id: 'e1-2',
source: '1',
sourceHandle: 'a',
target: '2',
targetHandle: null
};import { BaseEdge, EdgeProps, getSmoothStepPath } from '@xyflow/react';
function 自定义连线({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data }: EdgeProps) {
const [edgePath, labelX, labelY] = getSmoothStepPath({
sourceX, sourceY, sourcePosition,
targetX, targetY, targetPosition,
});
return (
<>
<BaseEdge id={id} path={edgePath} />
<text x={labelX} y={labelY} className="edge-label">{data?.label}</text>
</>
);
}
const edgeTypes = { custom: 自定义连线 };// 外部状态与变更处理器
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[]
);
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>import { useReactFlow, ReactFlowProvider } from '@xyflow/react';
function 流程控制() {
const {
getNodes, setNodes, addNodes, updateNodeData,
getEdges, setEdges, addEdges,
fitView, zoomIn, zoomOut, setViewport,
deleteElements, toObject,
} = useReactFlow();
const 添加节点 = () => {
addNodes({ id: `${Date.now()}`, position: { x: 100, y: 100 }, data: { label: '新节点' } });
};
return <button onClick={添加节点}>添加节点</button>;
}
// 使用 useReactFlow 时必须包裹在 provider 内
function App() {
return (
<ReactFlowProvider>
<流程图 />
<流程控制 />
</ReactFlowProvider>
);
}const { updateNodeData } = useReactFlow();
// 合并现有数据
updateNodeData(节点ID, { label: '已更新' });
// 完全替换数据
updateNodeData(节点ID, { newField: 'value' }, { replace: true });// 初始渲染时自动适配视图
<ReactFlow fitView fitViewOptions={{ padding: 0.2, maxZoom: 1 }} />
// 程序化控制
const { fitView, setViewport, getViewport, zoomTo } = useReactFlow();
// 适配特定节点
fitView({ nodes: [{ id: '1' }, { id: '2' }], duration: 500 });
// 设置精确的视口
setViewport({ x: 100, y: 100, zoom: 1.5 }, { duration: 300 });const isValidConnection = useCallback((connection: Connection) => {
// 防止自连接
if (connection.source === connection.target) return false;
// 自定义验证逻辑
const sourceNode = getNode(connection.source);
const targetNode = getNode(connection.target);
return sourceNode?.type !== targetNode?.type;
}, []);
<ReactFlow isValidConnection={isValidConnection} /><ReactFlow
// 核心数据
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
// 自定义类型(在组件外部定义)
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
// 连接相关
onConnect={onConnect}
connectionMode={ConnectionMode.Loose} // 允许目标到目标的连接
isValidConnection={isValidConnection}
// 视口设置
fitView
minZoom={0.1}
maxZoom={4}
defaultViewport={{ x: 0, y: 0, zoom: 1 }}
// 交互行为
nodesDraggable={true}
nodesConnectable={true}
elementsSelectable={true}
panOnDrag={true}
zoomOnScroll={true}
// 附加组件
<MiniMap />
<Controls />
<Background variant={BackgroundVariant.Dots} />
</ReactFlow>| 类名 | 效果 |
|---|---|
nodrag | 点击元素时禁止拖动 |
nowheel | 禁止通过鼠标滚轮缩放 |
nopan | 禁止通过元素触发平移 |
nokey | 禁止键盘事件(用于输入框) |
详见 [ADDITIONAL_COMPONENTS.md](ADDITIONAL_COMPONENTS.md),包含 MiniMap、Controls、Background、NodeToolbar、NodeResizer。
已收录 5 个 Skill