import React, { useState } from 'react'; import { Plus, Pencil, Trash2, X } from 'lucide-react'; const MATCH_OPS = ['contains', 'equals', 'gt', 'gte', 'lt', 'lte', 'startswith', 'endswith', 'regex']; function parseMatch(match) { if (typeof match === 'string' && match.includes(':')) { const [op, ...rest] = match.split(':'); return { op: MATCH_OPS.includes(op) ? op : 'contains', value: rest.join(':') }; } return { op: 'contains', value: match || '' }; } const BLANK = { name: '', sensor: '', op: 'gte', matchValue: '', output: '', actuator: '' }; export default function CommandBuilder({ commands, sensors, nodes, onCreate, onDelete }) { const [form, setForm] = useState(BLANK); const [editing, setEditing] = useState(null); // original name being edited const [preservedOps, setPreservedOps] = useState([]); // keep network_ops we don't edit in the UI const [error, setError] = useState(null); const actuators = nodes.filter((n) => n.type === 'actuator'); const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); const resetForm = () => { setForm(BLANK); setEditing(null); setPreservedOps([]); }; const startEdit = (c) => { const { op, value } = parseMatch(c.match); setForm({ name: c.name, sensor: c.sensor, op, matchValue: value, output: c.output, actuator: c.actuator || '', }); setPreservedOps(c.network_ops || []); setEditing(c.name); setError(null); }; const submit = async (e) => { e.preventDefault(); setError(null); if (!form.name || !form.sensor || form.matchValue === '' || !form.output) { setError('Name, sensor, match value, and output are required.'); return; } try { await onCreate({ name: form.name, sensor: form.sensor, match: `${form.op}:${form.matchValue}`, output: form.output, actuator: form.actuator || null, network_ops: preservedOps, }, editing); resetForm(); } catch (err) { setError(err.message); } }; return (
Sample commands are loaded below — edit or delete them, or add your own. A command is IF (sensor matches) THEN (emit output, optionally drive an actuator). Commands fire when you run the simulation.
{error &&{c.sensor} {c.match} THEN {c.output}
{c.actuator && <> → drives {c.actuator}>}
{c.network_ops && c.network_ops.length > 0 && <> {c.network_ops.length} network op(s)>}