@procaaso/alphinity-ui-components 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  ControlPanel: () => ControlPanel,
39
39
  DEFAULT_THRESHOLDS: () => DEFAULT_THRESHOLDS,
40
40
  DashboardCard: () => DashboardCard,
41
+ DeviceControlPanel: () => DeviceControlPanel,
41
42
  DisplayModeToggle: () => DisplayModeToggle,
42
43
  EquipmentIndicator: () => EquipmentIndicator,
43
44
  EquipmentIndicatorWithSparkline: () => EquipmentIndicatorWithSparkline,
@@ -49,6 +50,7 @@ __export(index_exports, {
49
50
  LeftToggleButton: () => LeftToggleButton,
50
51
  LegacyValueEntry: () => LegacyValueEntry,
51
52
  NodeConfigPanel: () => NodeConfigPanel,
53
+ NodeControlsPanel: () => NodeControlsPanel,
52
54
  PIDCanvas: () => PIDCanvas,
53
55
  PanelContent: () => PanelContent,
54
56
  PanelTabs: () => PanelTabs,
@@ -68,6 +70,7 @@ __export(index_exports, {
68
70
  ZoomButton: () => ZoomButton,
69
71
  ZoomControls: () => ZoomControls,
70
72
  calculateThresholdStatus: () => calculateThresholdStatus,
73
+ createControlBinding: () => createControlBinding,
71
74
  exampleDiagram: () => exampleDiagram,
72
75
  exportDiagram: () => exportDiagram,
73
76
  generateRoutePoints: () => generateRoutePoints,
@@ -80,6 +83,7 @@ __export(index_exports, {
80
83
  overlayStyles: () => overlayStyles,
81
84
  registerSymbol: () => registerSymbol,
82
85
  registerSymbols: () => registerSymbols,
86
+ useDeviceControls: () => useDeviceControls,
83
87
  useDrag: () => useDrag,
84
88
  useKeyboardShortcuts: () => useKeyboardShortcuts,
85
89
  useSelection: () => useSelection,
@@ -801,14 +805,723 @@ var StatusIndicator = ({
801
805
  ] });
802
806
  };
803
807
 
808
+ // src/components/NodeControlsPanel/NodeControlsPanel.tsx
809
+ var import_jsx_runtime7 = require("react/jsx-runtime");
810
+ var NodeControlsPanel = ({
811
+ config,
812
+ onClose,
813
+ position = "bottom",
814
+ touchOptimized = true,
815
+ className = "",
816
+ style,
817
+ ...props
818
+ }) => {
819
+ if (!config) return null;
820
+ const isBottom = position === "bottom";
821
+ const touchClass = touchOptimized ? "touch-optimized" : "";
822
+ const containerStyle = {
823
+ position: "fixed",
824
+ zIndex: 9999,
825
+ background: "white",
826
+ boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.15)",
827
+ borderRadius: isBottom ? "16px 16px 0 0" : "0",
828
+ animation: isBottom ? "slideUp 0.3s ease-out" : "slideInRight 0.3s ease-out",
829
+ display: "flex",
830
+ flexDirection: "column",
831
+ ...isBottom ? {
832
+ left: 0,
833
+ right: 0,
834
+ bottom: 0,
835
+ maxHeight: "70vh",
836
+ borderTop: "1px solid #e5e7eb"
837
+ } : {
838
+ right: 0,
839
+ top: 0,
840
+ bottom: 0,
841
+ width: touchOptimized ? "400px" : "320px",
842
+ borderLeft: "1px solid #e5e7eb"
843
+ },
844
+ ...style
845
+ };
846
+ const headerStyle = {
847
+ display: "flex",
848
+ alignItems: "center",
849
+ justifyContent: "space-between",
850
+ padding: touchOptimized ? "1.25rem 1.5rem" : "1rem",
851
+ borderBottom: "2px solid #e5e7eb",
852
+ background: "#f9fafb",
853
+ flexShrink: 0
854
+ };
855
+ const titleStyle = {
856
+ margin: 0,
857
+ fontSize: touchOptimized ? "1.5rem" : "1.25rem",
858
+ fontWeight: 600,
859
+ color: "#111827"
860
+ };
861
+ const closeButtonStyle = {
862
+ background: "#ef4444",
863
+ color: "white",
864
+ border: "none",
865
+ borderRadius: "8px",
866
+ width: touchOptimized ? "56px" : "44px",
867
+ height: touchOptimized ? "56px" : "44px",
868
+ fontSize: touchOptimized ? "1.75rem" : "1.5rem",
869
+ fontWeight: "bold",
870
+ cursor: "pointer",
871
+ display: "flex",
872
+ alignItems: "center",
873
+ justifyContent: "center",
874
+ transition: "all 0.2s ease",
875
+ flexShrink: 0
876
+ };
877
+ const contentStyle = {
878
+ padding: touchOptimized ? "1.5rem" : "1rem",
879
+ overflowY: "auto",
880
+ flex: 1
881
+ };
882
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
884
+ "div",
885
+ {
886
+ style: {
887
+ position: "fixed",
888
+ inset: 0,
889
+ background: "rgba(0, 0, 0, 0.3)",
890
+ zIndex: 9998,
891
+ animation: "fadeIn 0.3s ease-out"
892
+ },
893
+ onClick: onClose
894
+ }
895
+ ),
896
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
897
+ "div",
898
+ {
899
+ className: `node-controls-panel ${touchClass} ${className}`.trim(),
900
+ style: containerStyle,
901
+ ...props,
902
+ children: [
903
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: headerStyle, children: [
904
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: titleStyle, children: config.title }),
905
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
906
+ "button",
907
+ {
908
+ style: closeButtonStyle,
909
+ onClick: onClose,
910
+ onMouseEnter: (e) => {
911
+ e.currentTarget.style.background = "#dc2626";
912
+ e.currentTarget.style.transform = "scale(1.05)";
913
+ },
914
+ onMouseLeave: (e) => {
915
+ e.currentTarget.style.background = "#ef4444";
916
+ e.currentTarget.style.transform = "scale(1)";
917
+ },
918
+ "aria-label": "Close",
919
+ children: "\u2715"
920
+ }
921
+ )
922
+ ] }),
923
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: contentStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
924
+ ControlPanel,
925
+ {
926
+ title: config.nodeId,
927
+ selectedMode: config.currentMode,
928
+ modeOptions: config.modes,
929
+ onModeChange: config.onModeChange,
930
+ setpointValue: config.setpoint,
931
+ onSetpointChange: config.onSetpointChange,
932
+ secondSetpointValue: config.secondSetpoint,
933
+ onSecondSetpointChange: config.onSecondSetpointChange,
934
+ modeConfigs: config.modeConfigs,
935
+ status: config.status,
936
+ setpointReadOnly: config.readOnly,
937
+ showStartStopButtons: !!(config.onStart || config.onStop),
938
+ isRunning: config.isRunning,
939
+ onStart: config.onStart,
940
+ onStop: config.onStop,
941
+ startButtonText: config.startButtonText,
942
+ stopButtonText: config.stopButtonText,
943
+ size: touchOptimized ? "large" : "medium"
944
+ }
945
+ ) })
946
+ ]
947
+ }
948
+ ),
949
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: `
950
+ @keyframes slideUp {
951
+ from {
952
+ transform: translateY(100%);
953
+ opacity: 0;
954
+ }
955
+ to {
956
+ transform: translateY(0);
957
+ opacity: 1;
958
+ }
959
+ }
960
+
961
+ @keyframes slideInRight {
962
+ from {
963
+ transform: translateX(100%);
964
+ opacity: 0;
965
+ }
966
+ to {
967
+ transform: translateX(0);
968
+ opacity: 1;
969
+ }
970
+ }
971
+
972
+ @keyframes fadeIn {
973
+ from {
974
+ opacity: 0;
975
+ }
976
+ to {
977
+ opacity: 1;
978
+ }
979
+ }
980
+
981
+ .node-controls-panel.touch-optimized button {
982
+ min-height: 56px;
983
+ font-size: 1.125rem;
984
+ }
985
+
986
+ .node-controls-panel.touch-optimized input {
987
+ min-height: 56px;
988
+ font-size: 1.125rem;
989
+ }
990
+ ` })
991
+ ] });
992
+ };
993
+
994
+ // src/components/DeviceControlPanel/DeviceControlPanel.tsx
995
+ var import_react5 = require("react");
996
+ var import_jsx_runtime8 = require("react/jsx-runtime");
997
+ function DeviceControlPanel({
998
+ binding,
999
+ onClose,
1000
+ position = "bottom",
1001
+ touchOptimized = true,
1002
+ onModeChange,
1003
+ onParameterChange,
1004
+ onStart,
1005
+ onStop,
1006
+ onCustomAction
1007
+ }) {
1008
+ const [localParameterValues, setLocalParameterValues] = (0, import_react5.useState)({});
1009
+ (0, import_react5.useEffect)(() => {
1010
+ if (binding) {
1011
+ const currentParams2 = binding.parameters[binding.state.currentMode] || [];
1012
+ const values = {};
1013
+ currentParams2.forEach((param) => {
1014
+ values[param.id] = param.value;
1015
+ });
1016
+ setLocalParameterValues(values);
1017
+ }
1018
+ }, [binding, binding?.state.currentMode]);
1019
+ if (!binding) return null;
1020
+ const currentMode = binding.state.currentMode;
1021
+ const currentParams = binding.parameters[currentMode] || [];
1022
+ const touchSize = touchOptimized ? 56 : 40;
1023
+ const currentModeObj = binding.modes.find((m) => m.value === currentMode);
1024
+ const activeCapabilities = currentModeObj?.capabilities ?? binding.capabilities ?? {};
1025
+ const backgroundColor = binding.theme?.backgroundColor || "#edeeef";
1026
+ const surfaceColor = "#f5f6f7";
1027
+ const borderColor = "#d2d2d2";
1028
+ const textColor = binding.theme?.textColor || "#000000";
1029
+ const mutedTextColor = "#666666";
1030
+ const selectedColor = "#b4d0fe";
1031
+ const statusColors = {
1032
+ normal: binding.theme?.statusColors?.normal || "#888888",
1033
+ // Gray (no alarm)
1034
+ warning: binding.theme?.statusColors?.warning || "#FFD700",
1035
+ // Yellow
1036
+ alarm: binding.theme?.statusColors?.alarm || "#FF0000",
1037
+ // Red
1038
+ fault: binding.theme?.statusColors?.fault || "#FF0000",
1039
+ // Red
1040
+ off: binding.theme?.statusColors?.off || "#666666",
1041
+ // Dark gray
1042
+ starting: binding.theme?.statusColors?.starting || "#90EE90",
1043
+ // Light green
1044
+ stopping: binding.theme?.statusColors?.stopping || "#FFD700"
1045
+ // Yellow
1046
+ };
1047
+ const statusColor = statusColors[binding.state.status || "normal"];
1048
+ const handleModeChange = (mode) => {
1049
+ onModeChange?.(mode);
1050
+ };
1051
+ const handleParameterCommit = (parameterId, value) => {
1052
+ onParameterChange?.(parameterId, value);
1053
+ };
1054
+ const handleParameterInput = (parameterId, value) => {
1055
+ setLocalParameterValues((prev) => ({ ...prev, [parameterId]: value }));
1056
+ };
1057
+ const panelStyle = {
1058
+ position: "fixed",
1059
+ ...position === "bottom" ? {
1060
+ bottom: 0,
1061
+ left: 0,
1062
+ right: 0,
1063
+ animation: "slideUp 0.3s ease-out"
1064
+ } : {
1065
+ right: 0,
1066
+ top: 0,
1067
+ bottom: 0,
1068
+ width: "400px",
1069
+ animation: "slideIn 0.3s ease-out"
1070
+ },
1071
+ backgroundColor,
1072
+ color: textColor,
1073
+ borderRadius: position === "bottom" ? "8px 8px 0 0" : "8px 0 0 8px",
1074
+ boxShadow: "none",
1075
+ border: `2px solid ${borderColor}`,
1076
+ padding: "20px",
1077
+ zIndex: 9999,
1078
+ maxHeight: position === "bottom" ? "70vh" : "100vh",
1079
+ overflowY: "auto",
1080
+ ...binding.display?.style
1081
+ };
1082
+ const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;
1083
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: panelStyle, className: binding.display?.className, children: [
1084
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1085
+ "div",
1086
+ {
1087
+ style: {
1088
+ display: "flex",
1089
+ justifyContent: "space-between",
1090
+ alignItems: "center",
1091
+ marginBottom: "16px",
1092
+ paddingBottom: "12px",
1093
+ borderBottom: `2px solid ${borderColor}`
1094
+ },
1095
+ children: [
1096
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1098
+ "h3",
1099
+ {
1100
+ style: {
1101
+ margin: 0,
1102
+ fontSize: "14px",
1103
+ fontWeight: 600,
1104
+ fontFamily: "Arial, sans-serif",
1105
+ letterSpacing: "0.5px",
1106
+ textTransform: "uppercase",
1107
+ color: textColor
1108
+ },
1109
+ children: titleText
1110
+ }
1111
+ ),
1112
+ binding.deviceType && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1113
+ "div",
1114
+ {
1115
+ style: {
1116
+ fontSize: "11px",
1117
+ color: mutedTextColor,
1118
+ marginTop: "4px",
1119
+ fontFamily: "Arial, sans-serif"
1120
+ },
1121
+ children: binding.deviceType
1122
+ }
1123
+ )
1124
+ ] }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1126
+ "button",
1127
+ {
1128
+ onClick: onClose,
1129
+ style: {
1130
+ width: touchSize * 0.7,
1131
+ height: touchSize * 0.7,
1132
+ backgroundColor: surfaceColor,
1133
+ color: textColor,
1134
+ border: `2px solid ${borderColor}`,
1135
+ borderRadius: "2px",
1136
+ fontSize: "18px",
1137
+ cursor: "pointer",
1138
+ fontWeight: "bold",
1139
+ fontFamily: "Arial, sans-serif"
1140
+ },
1141
+ children: "\xD7"
1142
+ }
1143
+ )
1144
+ ]
1145
+ }
1146
+ ),
1147
+ binding.state.status && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1148
+ "div",
1149
+ {
1150
+ style: {
1151
+ display: "flex",
1152
+ alignItems: "center",
1153
+ gap: "12px",
1154
+ padding: "10px 12px",
1155
+ backgroundColor: surfaceColor,
1156
+ border: `1px solid ${borderColor}`,
1157
+ borderRadius: "6px",
1158
+ marginBottom: "16px"
1159
+ },
1160
+ children: [
1161
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1162
+ "div",
1163
+ {
1164
+ style: {
1165
+ width: "10px",
1166
+ height: "10px",
1167
+ borderRadius: "50%",
1168
+ backgroundColor: statusColor,
1169
+ border: "1px solid #333"
1170
+ }
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: 1 }, children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1175
+ "div",
1176
+ {
1177
+ style: {
1178
+ fontWeight: 600,
1179
+ textTransform: "uppercase",
1180
+ fontSize: "11px",
1181
+ fontFamily: "Arial, sans-serif",
1182
+ letterSpacing: "0.5px",
1183
+ color: textColor
1184
+ },
1185
+ children: binding.state.status
1186
+ }
1187
+ ),
1188
+ binding.state.statusMessage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1189
+ "div",
1190
+ {
1191
+ style: {
1192
+ fontSize: "10px",
1193
+ color: mutedTextColor,
1194
+ marginTop: "2px",
1195
+ fontFamily: "Arial, sans-serif"
1196
+ },
1197
+ children: binding.state.statusMessage
1198
+ }
1199
+ )
1200
+ ] }),
1201
+ binding.state.isRunning !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1202
+ "div",
1203
+ {
1204
+ style: {
1205
+ padding: "4px 10px",
1206
+ backgroundColor: binding.state.isRunning ? selectedColor : surfaceColor,
1207
+ border: `1px solid ${borderColor}`,
1208
+ borderRadius: "2px",
1209
+ fontSize: "10px",
1210
+ fontWeight: 600,
1211
+ fontFamily: "Arial, sans-serif",
1212
+ letterSpacing: "0.5px",
1213
+ color: textColor
1214
+ },
1215
+ children: binding.state.isRunning ? "RUNNING" : "STOPPED"
1216
+ }
1217
+ )
1218
+ ]
1219
+ }
1220
+ ),
1221
+ binding.display?.showModeSelector !== false && binding.modes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginBottom: "16px" }, children: [
1222
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1223
+ "label",
1224
+ {
1225
+ style: {
1226
+ display: "block",
1227
+ marginBottom: "8px",
1228
+ fontSize: "11px",
1229
+ fontWeight: 600,
1230
+ textTransform: "uppercase",
1231
+ letterSpacing: "0.5px",
1232
+ fontFamily: "Arial, sans-serif",
1233
+ color: mutedTextColor
1234
+ },
1235
+ children: "Mode"
1236
+ }
1237
+ ),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" }, children: binding.modes.map((mode) => {
1239
+ const isSelected = mode.value === currentMode;
1240
+ const isEnabled = mode.enabled !== false;
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1242
+ "button",
1243
+ {
1244
+ onClick: () => isEnabled && handleModeChange(mode.value),
1245
+ disabled: !isEnabled,
1246
+ style: {
1247
+ flex: 1,
1248
+ minWidth: "80px",
1249
+ height: touchSize,
1250
+ backgroundColor: isSelected ? selectedColor : surfaceColor,
1251
+ color: isSelected ? "#000000" : textColor,
1252
+ border: `1px solid ${isSelected ? "#b4d0fe" : borderColor}`,
1253
+ borderRadius: "6px",
1254
+ fontSize: "11px",
1255
+ fontWeight: 600,
1256
+ fontFamily: "Arial, sans-serif",
1257
+ letterSpacing: "0.5px",
1258
+ textTransform: "uppercase",
1259
+ cursor: isEnabled ? "pointer" : "not-allowed",
1260
+ opacity: isEnabled ? 1 : 0.4
1261
+ },
1262
+ children: mode.label
1263
+ },
1264
+ mode.value
1265
+ );
1266
+ }) })
1267
+ ] }),
1268
+ currentParams.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginBottom: "16px" }, children: [
1269
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1270
+ "label",
1271
+ {
1272
+ style: {
1273
+ display: "block",
1274
+ marginBottom: "8px",
1275
+ fontSize: "11px",
1276
+ fontWeight: 600,
1277
+ textTransform: "uppercase",
1278
+ letterSpacing: "0.5px",
1279
+ fontFamily: "Arial, sans-serif",
1280
+ color: mutedTextColor
1281
+ },
1282
+ children: "Parameters"
1283
+ }
1284
+ ),
1285
+ currentParams.map((param) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1286
+ "div",
1287
+ {
1288
+ style: {
1289
+ marginBottom: "10px",
1290
+ padding: "10px",
1291
+ backgroundColor: surfaceColor,
1292
+ border: `1px solid ${borderColor}`,
1293
+ borderRadius: "6px"
1294
+ },
1295
+ children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1297
+ "div",
1298
+ {
1299
+ style: { display: "flex", justifyContent: "space-between", marginBottom: "8px" },
1300
+ children: [
1301
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1302
+ "span",
1303
+ {
1304
+ style: {
1305
+ fontSize: "11px",
1306
+ fontWeight: 600,
1307
+ textTransform: "uppercase",
1308
+ letterSpacing: "0.5px",
1309
+ fontFamily: "Arial, sans-serif",
1310
+ color: mutedTextColor
1311
+ },
1312
+ children: param.label
1313
+ }
1314
+ ),
1315
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1316
+ "span",
1317
+ {
1318
+ style: {
1319
+ fontSize: "13px",
1320
+ fontWeight: 700,
1321
+ fontFamily: "Arial, sans-serif",
1322
+ color: textColor
1323
+ },
1324
+ children: [
1325
+ localParameterValues[param.id] ?? param.value,
1326
+ " ",
1327
+ param.unit && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { color: mutedTextColor, fontSize: "10px", fontWeight: 600 }, children: param.unit })
1328
+ ]
1329
+ }
1330
+ )
1331
+ ]
1332
+ }
1333
+ ),
1334
+ param.type === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1335
+ "button",
1336
+ {
1337
+ onClick: () => {
1338
+ const newValue = !localParameterValues[param.id];
1339
+ handleParameterInput(param.id, newValue);
1340
+ handleParameterCommit(param.id, newValue);
1341
+ },
1342
+ disabled: param.readOnly,
1343
+ style: {
1344
+ width: "100%",
1345
+ height: touchSize,
1346
+ backgroundColor: localParameterValues[param.id] ? "#3f3f46" : surfaceColor,
1347
+ color: localParameterValues[param.id] ? textColor : mutedTextColor,
1348
+ border: `1px solid ${localParameterValues[param.id] ? "#52525b" : borderColor}`,
1349
+ borderRadius: "4px",
1350
+ fontSize: "12px",
1351
+ fontWeight: 600,
1352
+ fontFamily: "monospace",
1353
+ letterSpacing: "0.5px",
1354
+ cursor: param.readOnly ? "not-allowed" : "pointer",
1355
+ transition: "all 0.15s"
1356
+ },
1357
+ children: localParameterValues[param.id] ? "ON" : "OFF"
1358
+ }
1359
+ ) : param.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1360
+ "select",
1361
+ {
1362
+ value: localParameterValues[param.id] || param.value,
1363
+ onChange: (e) => {
1364
+ handleParameterInput(param.id, e.target.value);
1365
+ handleParameterCommit(param.id, e.target.value);
1366
+ },
1367
+ disabled: param.readOnly,
1368
+ style: {
1369
+ width: "100%",
1370
+ height: touchSize,
1371
+ backgroundColor: surfaceColor,
1372
+ color: textColor,
1373
+ border: `1px solid ${borderColor}`,
1374
+ borderRadius: "4px",
1375
+ padding: "0 12px",
1376
+ fontSize: "12px",
1377
+ fontFamily: "monospace",
1378
+ cursor: "pointer"
1379
+ },
1380
+ children: param.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1381
+ }
1382
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1383
+ "input",
1384
+ {
1385
+ type: param.type === "number" ? "number" : "text",
1386
+ value: localParameterValues[param.id] ?? param.value,
1387
+ onChange: (e) => handleParameterInput(
1388
+ param.id,
1389
+ param.type === "number" ? +e.target.value : e.target.value
1390
+ ),
1391
+ onBlur: () => handleParameterCommit(param.id, localParameterValues[param.id]),
1392
+ disabled: param.readOnly,
1393
+ min: param.min,
1394
+ max: param.max,
1395
+ step: param.step,
1396
+ style: {
1397
+ width: "100%",
1398
+ height: touchSize,
1399
+ backgroundColor: "#ffffff",
1400
+ color: textColor,
1401
+ border: `1px solid ${borderColor}`,
1402
+ borderRadius: "6px",
1403
+ padding: "0 12px",
1404
+ fontSize: "14px",
1405
+ fontFamily: "Arial, sans-serif",
1406
+ fontWeight: 600
1407
+ }
1408
+ }
1409
+ )
1410
+ ]
1411
+ },
1412
+ param.id
1413
+ ))
1414
+ ] }),
1415
+ binding.display?.showStartStop !== false && (activeCapabilities?.canStart || activeCapabilities?.canStop) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: "8px", marginBottom: "16px" }, children: [
1416
+ activeCapabilities?.canStart && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1417
+ "button",
1418
+ {
1419
+ onClick: onStart,
1420
+ disabled: binding.state.isRunning,
1421
+ style: {
1422
+ flex: 1,
1423
+ height: touchSize,
1424
+ backgroundColor: binding.state.isRunning ? "#a3a3a3" : "#10b981",
1425
+ color: "#ffffff",
1426
+ border: "none",
1427
+ borderRadius: "6px",
1428
+ fontSize: "12px",
1429
+ fontWeight: 700,
1430
+ fontFamily: "Arial, sans-serif",
1431
+ letterSpacing: "0.5px",
1432
+ cursor: binding.state.isRunning ? "not-allowed" : "pointer",
1433
+ opacity: binding.state.isRunning ? 0.5 : 1
1434
+ },
1435
+ children: "START"
1436
+ }
1437
+ ),
1438
+ activeCapabilities?.canStop && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1439
+ "button",
1440
+ {
1441
+ onClick: onStop,
1442
+ disabled: !binding.state.isRunning,
1443
+ style: {
1444
+ flex: 1,
1445
+ height: touchSize,
1446
+ backgroundColor: !binding.state.isRunning ? "#a3a3a3" : "#ef4444",
1447
+ color: "#ffffff",
1448
+ border: "none",
1449
+ borderRadius: "6px",
1450
+ fontSize: "12px",
1451
+ fontWeight: 700,
1452
+ fontFamily: "Arial, sans-serif",
1453
+ letterSpacing: "0.5px",
1454
+ cursor: !binding.state.isRunning ? "not-allowed" : "pointer",
1455
+ opacity: !binding.state.isRunning ? 0.5 : 1
1456
+ },
1457
+ children: "STOP"
1458
+ }
1459
+ )
1460
+ ] }),
1461
+ activeCapabilities?.customActions && activeCapabilities.customActions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: activeCapabilities.customActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1462
+ "button",
1463
+ {
1464
+ onClick: () => onCustomAction?.(action.id),
1465
+ style: {
1466
+ flex: 1,
1467
+ minWidth: "120px",
1468
+ height: touchSize,
1469
+ backgroundColor: surfaceColor,
1470
+ color: textColor,
1471
+ border: `1px solid ${borderColor}`,
1472
+ borderRadius: "6px",
1473
+ fontSize: "11px",
1474
+ fontWeight: 600,
1475
+ fontFamily: "monospace",
1476
+ letterSpacing: "0.5px",
1477
+ textTransform: "uppercase",
1478
+ cursor: "pointer",
1479
+ transition: "all 0.15s"
1480
+ },
1481
+ children: [
1482
+ action.icon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { marginRight: "6px" }, children: action.icon }),
1483
+ action.label
1484
+ ]
1485
+ },
1486
+ action.id
1487
+ )) }),
1488
+ binding.state.lastCommandResult && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1489
+ "div",
1490
+ {
1491
+ style: {
1492
+ marginTop: "16px",
1493
+ padding: "10px 12px",
1494
+ backgroundColor: surfaceColor,
1495
+ border: `1px solid ${binding.state.lastCommandResult.success ? "#52525b" : borderColor}`,
1496
+ borderRadius: "4px",
1497
+ fontSize: "11px",
1498
+ fontFamily: "monospace",
1499
+ color: binding.state.lastCommandResult.success ? textColor : mutedTextColor
1500
+ },
1501
+ children: binding.state.lastCommandResult.message
1502
+ }
1503
+ ),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `
1505
+ @keyframes slideUp {
1506
+ from { transform: translateY(100%); }
1507
+ to { transform: translateY(0); }
1508
+ }
1509
+ @keyframes slideIn {
1510
+ from { transform: translateX(100%); }
1511
+ to { transform: translateX(0); }
1512
+ }
1513
+ ` })
1514
+ ] }) });
1515
+ }
1516
+
804
1517
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
805
- var import_react6 = __toESM(require("react"), 1);
1518
+ var import_react7 = __toESM(require("react"), 1);
806
1519
  var import_material3 = require("@mui/material");
807
1520
 
808
1521
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.styles.tsx
809
1522
  var import_material = require("@mui/material");
810
1523
  var import_styled = __toESM(require("@emotion/styled"), 1);
811
- var import_jsx_runtime7 = require("react/jsx-runtime");
1524
+ var import_jsx_runtime9 = require("react/jsx-runtime");
812
1525
  var FullscreenContainer = (0, import_styled.default)(import_material.Box)(
813
1526
  ({ leftCollapsed, rightCollapsed }) => {
814
1527
  const getGridTemplateAreas = () => {
@@ -933,7 +1646,7 @@ var DisplayModeToggle = (0, import_styled.default)(import_material.IconButton)((
933
1646
  transform: "translateX(-50%) scale(1.05)"
934
1647
  }
935
1648
  }));
936
- var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1649
+ var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
937
1650
  "div",
938
1651
  {
939
1652
  style: {
@@ -945,7 +1658,7 @@ var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
945
1658
  }
946
1659
  }
947
1660
  );
948
- var ChevronRight = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1661
+ var ChevronRight = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
949
1662
  "div",
950
1663
  {
951
1664
  style: {
@@ -1056,10 +1769,10 @@ var ScrollableSVGWrapper = (0, import_styled.default)("div")({
1056
1769
  });
1057
1770
 
1058
1771
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.overlays.tsx
1059
- var import_react5 = require("react");
1772
+ var import_react6 = require("react");
1060
1773
  var import_material2 = require("@mui/material");
1061
1774
  var import_styled2 = __toESM(require("@emotion/styled"), 1);
1062
- var import_jsx_runtime8 = require("react/jsx-runtime");
1775
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1063
1776
  var baseOverlayStyles = {
1064
1777
  tank: {
1065
1778
  background: "linear-gradient(135deg, rgba(69, 90, 120, 0.95) 0%, rgba(52, 73, 94, 0.95) 100%)",
@@ -1121,8 +1834,8 @@ var SVGLockedOverlay = ({
1121
1834
  onClick,
1122
1835
  containerSelector = ".tff-svg-container"
1123
1836
  }) => {
1124
- const [svgOffset, setSvgOffset] = (0, import_react5.useState)({ x: 0, y: 0 });
1125
- (0, import_react5.useEffect)(() => {
1837
+ const [svgOffset, setSvgOffset] = (0, import_react6.useState)({ x: 0, y: 0 });
1838
+ (0, import_react6.useEffect)(() => {
1126
1839
  const updateSvgOffset = () => {
1127
1840
  const containerElement = document.querySelector(containerSelector);
1128
1841
  const svgElement = containerElement?.querySelector("svg");
@@ -1142,7 +1855,7 @@ var SVGLockedOverlay = ({
1142
1855
  const pixelX = svgX + svgOffset.x;
1143
1856
  const pixelY = svgY + svgOffset.y;
1144
1857
  const appliedStyle = theme ? { ...overlayStyles[theme], ...style } : style;
1145
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1858
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1146
1859
  import_material2.Box,
1147
1860
  {
1148
1861
  className: `svg-locked-overlay ${className}`.trim(),
@@ -1206,7 +1919,7 @@ var CardUnit = (0, import_styled2.default)("div")({
1206
1919
  });
1207
1920
 
1208
1921
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx
1209
- var import_jsx_runtime9 = require("react/jsx-runtime");
1922
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1210
1923
  var TrendLine = ({
1211
1924
  values,
1212
1925
  width = 60,
@@ -1216,7 +1929,7 @@ var TrendLine = ({
1216
1929
  backgroundColor = "rgba(255, 255, 255, 0.1)"
1217
1930
  }) => {
1218
1931
  if (!values || values.length < 2) {
1219
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1932
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1220
1933
  "div",
1221
1934
  {
1222
1935
  style: {
@@ -1228,7 +1941,7 @@ var TrendLine = ({
1228
1941
  alignItems: "center",
1229
1942
  justifyContent: "center"
1230
1943
  },
1231
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" })
1944
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" })
1232
1945
  }
1233
1946
  );
1234
1947
  }
@@ -1240,7 +1953,7 @@ var TrendLine = ({
1240
1953
  const x = 4 + i / (values.length - 1) * (width - 8);
1241
1954
  return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
1242
1955
  }).join(" ");
1243
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1956
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1244
1957
  "div",
1245
1958
  {
1246
1959
  style: {
@@ -1251,8 +1964,8 @@ var TrendLine = ({
1251
1964
  position: "relative",
1252
1965
  overflow: "hidden"
1253
1966
  },
1254
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1967
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1256
1969
  "path",
1257
1970
  {
1258
1971
  d: pathData,
@@ -1263,7 +1976,7 @@ var TrendLine = ({
1263
1976
  strokeLinejoin: "round"
1264
1977
  }
1265
1978
  ),
1266
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1979
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1267
1980
  "circle",
1268
1981
  {
1269
1982
  cx: 4 + (width - 8),
@@ -1301,7 +2014,7 @@ var CircularGauge = ({
1301
2014
  const borderColor = isHex ? hexToRgba(color, 0.9) : color;
1302
2015
  const backgroundColor = isHex ? hexToRgba(color, 0.15) : color;
1303
2016
  const borderStrokeColor = isHex ? hexToRgba(color, 0.4) : color;
1304
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2017
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1305
2018
  "div",
1306
2019
  {
1307
2020
  style: {
@@ -1310,7 +2023,7 @@ var CircularGauge = ({
1310
2023
  alignItems: "center"
1311
2024
  },
1312
2025
  children: [
1313
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2026
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1314
2027
  "div",
1315
2028
  {
1316
2029
  style: {
@@ -1322,8 +2035,8 @@ var CircularGauge = ({
1322
2035
  border: `1px solid ${borderStrokeColor}`
1323
2036
  },
1324
2037
  children: [
1325
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
1326
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2038
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
2039
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1327
2040
  "circle",
1328
2041
  {
1329
2042
  cx: size / 2,
@@ -1334,7 +2047,7 @@ var CircularGauge = ({
1334
2047
  strokeWidth: 1
1335
2048
  }
1336
2049
  ),
1337
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2050
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1338
2051
  "circle",
1339
2052
  {
1340
2053
  cx: size / 2,
@@ -1345,7 +2058,7 @@ var CircularGauge = ({
1345
2058
  fill: "transparent"
1346
2059
  }
1347
2060
  ),
1348
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2061
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1349
2062
  "circle",
1350
2063
  {
1351
2064
  cx: size / 2,
@@ -1361,7 +2074,7 @@ var CircularGauge = ({
1361
2074
  }
1362
2075
  )
1363
2076
  ] }),
1364
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2077
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1365
2078
  "div",
1366
2079
  {
1367
2080
  style: {
@@ -1377,14 +2090,14 @@ var CircularGauge = ({
1377
2090
  },
1378
2091
  children: [
1379
2092
  value.toFixed(1),
1380
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "8px", opacity: 0.9, color: "white" }, children: unit })
2093
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: "8px", opacity: 0.9, color: "white" }, children: unit })
1381
2094
  ]
1382
2095
  }
1383
2096
  )
1384
2097
  ]
1385
2098
  }
1386
2099
  ),
1387
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2100
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1388
2101
  "div",
1389
2102
  {
1390
2103
  style: {
@@ -1415,7 +2128,7 @@ var Sparkline = ({ data, width, height, color }) => {
1415
2128
  const y = height - 8 - (value - min) / range * (height - 8);
1416
2129
  return `${x + 4},${y + 4}`;
1417
2130
  }).join(" ");
1418
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2131
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1419
2132
  "div",
1420
2133
  {
1421
2134
  style: {
@@ -1429,7 +2142,7 @@ var Sparkline = ({ data, width, height, color }) => {
1429
2142
  justifyContent: "center",
1430
2143
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
1431
2144
  },
1432
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width, height, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) })
2145
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width, height, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) })
1433
2146
  }
1434
2147
  );
1435
2148
  };
@@ -1443,8 +2156,8 @@ var EquipmentIndicatorWithSparkline = ({
1443
2156
  sparklineOffset = { x: 40, y: 0 }
1444
2157
  }) => {
1445
2158
  const hasSparkline = sparklineData && sparklineData.length > 1 && Math.max(...sparklineData) !== Math.min(...sparklineData);
1446
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2159
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2160
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1448
2161
  "div",
1449
2162
  {
1450
2163
  style: {
@@ -1465,7 +2178,7 @@ var EquipmentIndicatorWithSparkline = ({
1465
2178
  children: label
1466
2179
  }
1467
2180
  ) }),
1468
- hasSparkline && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Sparkline, { data: sparklineData, width: 60, height: 20, color }) })
2181
+ hasSparkline && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Sparkline, { data: sparklineData, width: 60, height: 20, color }) })
1469
2182
  ] });
1470
2183
  };
1471
2184
  var EquipmentIndicator = ({
@@ -1478,7 +2191,7 @@ var EquipmentIndicator = ({
1478
2191
  className
1479
2192
  }) => {
1480
2193
  const overlayStyle = style !== void 0 ? { transform: "translate(-50%, -50%)", ...style } : { transform: "translate(-50%, -50%)" };
1481
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2194
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1482
2195
  "div",
1483
2196
  {
1484
2197
  style: {
@@ -1502,7 +2215,7 @@ var EquipmentIndicator = ({
1502
2215
  };
1503
2216
 
1504
2217
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
1505
- var import_jsx_runtime10 = require("react/jsx-runtime");
2218
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1506
2219
  function FullscreenWorkspace({
1507
2220
  svgContent,
1508
2221
  overlays,
@@ -1530,10 +2243,10 @@ function FullscreenWorkspace({
1530
2243
  className = "",
1531
2244
  ...rest
1532
2245
  }) {
1533
- const [leftCollapsedState, setLeftCollapsedState] = import_react6.default.useState(defaultLeftCollapsed);
1534
- const [rightCollapsedState, setRightCollapsedState] = import_react6.default.useState(defaultRightCollapsed);
1535
- const [internalDisplayMode, setInternalDisplayMode] = import_react6.default.useState(displayMode ?? defaultDisplayMode);
1536
- const [internalTab, setInternalTab] = import_react6.default.useState(
2246
+ const [leftCollapsedState, setLeftCollapsedState] = import_react7.default.useState(defaultLeftCollapsed);
2247
+ const [rightCollapsedState, setRightCollapsedState] = import_react7.default.useState(defaultRightCollapsed);
2248
+ const [internalDisplayMode, setInternalDisplayMode] = import_react7.default.useState(displayMode ?? defaultDisplayMode);
2249
+ const [internalTab, setInternalTab] = import_react7.default.useState(
1537
2250
  tabs && tabs.length > 0 ? tabs[0].value : ""
1538
2251
  );
1539
2252
  const leftCollapsed = leftCollapsedProp ?? leftCollapsedState;
@@ -1541,10 +2254,10 @@ function FullscreenWorkspace({
1541
2254
  const isDisplayModeControlled = displayMode !== void 0;
1542
2255
  const currentDisplayMode = isDisplayModeControlled ? displayMode : internalDisplayMode;
1543
2256
  const isTabControlled = activeTab !== void 0 && activeTab !== null;
1544
- const resolvedTabs = import_react6.default.useMemo(() => tabs ?? [], [tabs]);
2257
+ const resolvedTabs = import_react7.default.useMemo(() => tabs ?? [], [tabs]);
1545
2258
  const defaultTabValue = resolvedTabs.length > 0 ? resolvedTabs[0].value : "";
1546
2259
  const currentTab = isTabControlled ? activeTab : internalTab || defaultTabValue;
1547
- import_react6.default.useEffect(() => {
2260
+ import_react7.default.useEffect(() => {
1548
2261
  if (resolvedTabs.length > 0) {
1549
2262
  const values = resolvedTabs.map((tab) => tab.value);
1550
2263
  if (!values.includes(currentTab)) {
@@ -1553,7 +2266,7 @@ function FullscreenWorkspace({
1553
2266
  }
1554
2267
  }
1555
2268
  }, [resolvedTabs, activeTab, currentTab, defaultTabValue]);
1556
- import_react6.default.useEffect(() => {
2269
+ import_react7.default.useEffect(() => {
1557
2270
  if (displayMode !== void 0) {
1558
2271
  setInternalDisplayMode(displayMode);
1559
2272
  }
@@ -1588,7 +2301,7 @@ function FullscreenWorkspace({
1588
2301
  const currentTabContent = resolvedTabs.find((tab) => tab.value === currentTab)?.content ?? null;
1589
2302
  const showLeftToggle = Boolean(leftPanel);
1590
2303
  const showRightToggle = Boolean(rightPanel);
1591
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2304
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1592
2305
  FullscreenContainer,
1593
2306
  {
1594
2307
  leftCollapsed,
@@ -1596,25 +2309,25 @@ function FullscreenWorkspace({
1596
2309
  className: `tff-svg-container ${className}`.trim(),
1597
2310
  ...rest,
1598
2311
  children: [
1599
- showLeftToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2312
+ showLeftToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1600
2313
  LeftToggleButton,
1601
2314
  {
1602
2315
  collapsed: leftCollapsed,
1603
2316
  onClick: handleLeftToggle,
1604
2317
  "aria-label": leftCollapsed ? "Expand left panel" : "Collapse left panel",
1605
- children: leftCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChevronRight, {}) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChevronLeft, {})
2318
+ children: leftCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronRight, {}) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronLeft, {})
1606
2319
  }
1607
2320
  ),
1608
- showRightToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2321
+ showRightToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1609
2322
  RightToggleButton,
1610
2323
  {
1611
2324
  collapsed: rightCollapsed,
1612
2325
  onClick: handleRightToggle,
1613
2326
  "aria-label": rightCollapsed ? "Expand right panel" : "Collapse right panel",
1614
- children: rightCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChevronLeft, {}) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChevronRight, {})
2327
+ children: rightCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronLeft, {}) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronRight, {})
1615
2328
  }
1616
2329
  ),
1617
- showDisplayModeToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2330
+ showDisplayModeToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1618
2331
  DisplayModeToggle,
1619
2332
  {
1620
2333
  mode: currentDisplayMode,
@@ -1623,9 +2336,9 @@ function FullscreenWorkspace({
1623
2336
  children: currentDisplayMode === "dashboard" ? "Dashboard Mode" : "Standard Mode"
1624
2337
  }
1625
2338
  ),
1626
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(SVGArea, { children: [
1627
- showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ZoomControls, { children: [
1628
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2339
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(SVGArea, { children: [
2340
+ showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(ZoomControls, { children: [
2341
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1629
2342
  ZoomButton,
1630
2343
  {
1631
2344
  onClick: onZoomIn,
@@ -1635,7 +2348,7 @@ function FullscreenWorkspace({
1635
2348
  children: "+"
1636
2349
  }
1637
2350
  ),
1638
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2351
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1639
2352
  ZoomButton,
1640
2353
  {
1641
2354
  onClick: onZoomOut,
@@ -1645,7 +2358,7 @@ function FullscreenWorkspace({
1645
2358
  children: "-"
1646
2359
  }
1647
2360
  ),
1648
- onResetZoom && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2361
+ onResetZoom && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1649
2362
  ZoomButton,
1650
2363
  {
1651
2364
  onClick: onResetZoom,
@@ -1656,44 +2369,44 @@ function FullscreenWorkspace({
1656
2369
  }
1657
2370
  )
1658
2371
  ] }),
1659
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScrollableSVGWrapper, { className: "svg-scroll-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(FixedSVGContainer, { className: "svg-container", children: [
2372
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScrollableSVGWrapper, { className: "svg-scroll-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(FixedSVGContainer, { className: "svg-container", children: [
1660
2373
  svgContent,
1661
2374
  overlays
1662
2375
  ] }) })
1663
2376
  ] }),
1664
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LeftPanel, { collapsed: leftCollapsed, children: resolvedTabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1665
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2377
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LeftPanel, { collapsed: leftCollapsed, children: resolvedTabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2378
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1666
2379
  PanelTabs,
1667
2380
  {
1668
2381
  value: currentTab,
1669
2382
  onChange: handleTabChangeInternal,
1670
2383
  "aria-label": "Left panel tabs",
1671
- children: resolvedTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material3.Tab, { label: tab.label, value: tab.value }, tab.value))
2384
+ children: resolvedTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material3.Tab, { label: tab.label, value: tab.value }, tab.value))
1672
2385
  }
1673
2386
  ),
1674
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PanelContent, { children: currentTabContent })
2387
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PanelContent, { children: currentTabContent })
1675
2388
  ] }) : leftPanel }),
1676
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RightPanel, { collapsed: rightCollapsed, children: rightPanel }),
1677
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FooterPanel, { children: footer })
2389
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RightPanel, { collapsed: rightCollapsed, children: rightPanel }),
2390
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FooterPanel, { children: footer })
1678
2391
  ]
1679
2392
  }
1680
2393
  );
1681
2394
  }
1682
2395
 
1683
2396
  // src/theme/ThemeContext.tsx
1684
- var import_react7 = require("react");
1685
- var import_jsx_runtime11 = require("react/jsx-runtime");
1686
- var ThemeContext = (0, import_react7.createContext)(void 0);
2397
+ var import_react8 = require("react");
2398
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2399
+ var ThemeContext = (0, import_react8.createContext)(void 0);
1687
2400
  var useTheme = () => {
1688
- const context = (0, import_react7.useContext)(ThemeContext);
2401
+ const context = (0, import_react8.useContext)(ThemeContext);
1689
2402
  if (!context) {
1690
2403
  throw new Error("useTheme must be used within a ThemeProvider");
1691
2404
  }
1692
2405
  return context;
1693
2406
  };
1694
2407
  var ThemeProvider = ({ children }) => {
1695
- const [theme, setThemeState] = (0, import_react7.useState)("modern");
1696
- (0, import_react7.useEffect)(() => {
2408
+ const [theme, setThemeState] = (0, import_react8.useState)("modern");
2409
+ (0, import_react8.useEffect)(() => {
1697
2410
  document.body.className = "";
1698
2411
  document.body.classList.add(`theme-${theme}`);
1699
2412
  document.documentElement.className = "";
@@ -1705,11 +2418,11 @@ var ThemeProvider = ({ children }) => {
1705
2418
  const setTheme = (newTheme) => {
1706
2419
  setThemeState(newTheme);
1707
2420
  };
1708
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
2421
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
1709
2422
  };
1710
2423
 
1711
2424
  // src/theme/ThemeToggle.tsx
1712
- var import_jsx_runtime12 = require("react/jsx-runtime");
2425
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1713
2426
  var ThemeToggle = ({
1714
2427
  size = "medium",
1715
2428
  position = "top-right",
@@ -1780,15 +2493,15 @@ var ThemeToggle = ({
1780
2493
  transition: "all 0.2s ease",
1781
2494
  boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)"
1782
2495
  };
1783
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
1784
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
1785
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: toggleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: thumbStyle }) }),
1786
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { fontSize: "0.75rem", opacity: 0.8 }, children: theme === "modern" ? "Industrial Design" : "High Performance" })
2496
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
2497
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
2498
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: toggleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: thumbStyle }) }),
2499
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { fontSize: "0.75rem", opacity: 0.8 }, children: theme === "modern" ? "Industrial Design" : "High Performance" })
1787
2500
  ] });
1788
2501
  };
1789
2502
 
1790
2503
  // src/components/PIDCanvas/PIDCanvas.tsx
1791
- var import_react15 = require("react");
2504
+ var import_react16 = require("react");
1792
2505
 
1793
2506
  // src/diagram/symbols/mockSymbolLibrary.ts
1794
2507
  var mockSymbolLibrary = {
@@ -3009,7 +3722,7 @@ function getAvailableSymbols() {
3009
3722
  }
3010
3723
 
3011
3724
  // src/components/PIDCanvas/NodeRenderer.tsx
3012
- var import_jsx_runtime13 = require("react/jsx-runtime");
3725
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3013
3726
  function NodeRenderer({
3014
3727
  nodes: nodes2,
3015
3728
  selectedNodeIds,
@@ -3020,13 +3733,13 @@ function NodeRenderer({
3020
3733
  dragOffset = { x: 0, y: 0 },
3021
3734
  enableDrag = false
3022
3735
  }) {
3023
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("g", { id: "layer-nodes", children: nodes2.filter((node) => node.visible !== false).map((node) => {
3736
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("g", { id: "layer-nodes", children: nodes2.filter((node) => node.visible !== false).map((node) => {
3024
3737
  const symbol = getSymbolDefinition(node.symbolId);
3025
3738
  const isSelected = selectedNodeIds?.has(node.id) ?? false;
3026
3739
  const isDragging = draggingNodeId === node.id;
3027
3740
  if (!symbol) {
3028
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { children: [
3029
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3741
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("g", { children: [
3742
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3030
3743
  "circle",
3031
3744
  {
3032
3745
  cx: node.transform.x,
@@ -3038,7 +3751,7 @@ function NodeRenderer({
3038
3751
  strokeWidth: "2"
3039
3752
  }
3040
3753
  ),
3041
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3754
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3042
3755
  "text",
3043
3756
  {
3044
3757
  x: node.transform.x,
@@ -3095,7 +3808,7 @@ function NodeRenderer({
3095
3808
  };
3096
3809
  const fillColor = node.customColors?.fill ?? getStatusColor(node.status);
3097
3810
  const strokeColor = node.customColors?.stroke ?? "rgb(0, 0, 0)";
3098
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3811
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3099
3812
  "g",
3100
3813
  {
3101
3814
  "data-node-id": node.id,
@@ -3128,7 +3841,7 @@ function NodeRenderer({
3128
3841
  }
3129
3842
  },
3130
3843
  children: [
3131
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3844
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3132
3845
  "rect",
3133
3846
  {
3134
3847
  x: "-5",
@@ -3143,7 +3856,7 @@ function NodeRenderer({
3143
3856
  pointerEvents: "none"
3144
3857
  }
3145
3858
  ),
3146
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3859
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3147
3860
  "g",
3148
3861
  {
3149
3862
  dangerouslySetInnerHTML: { __html: symbol.svgContent },
@@ -3151,7 +3864,7 @@ function NodeRenderer({
3151
3864
  style: { pointerEvents: "auto" }
3152
3865
  }
3153
3866
  ),
3154
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3867
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3155
3868
  "text",
3156
3869
  {
3157
3870
  x: symbol.viewBox.width / 2 + (node.labelOffset?.x ?? 0),
@@ -3172,7 +3885,7 @@ function NodeRenderer({
3172
3885
  }
3173
3886
 
3174
3887
  // src/components/PIDCanvas/PipeRenderer.tsx
3175
- var import_jsx_runtime14 = require("react/jsx-runtime");
3888
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3176
3889
  function PipeRenderer({
3177
3890
  pipes,
3178
3891
  selectedPipeIds,
@@ -3187,7 +3900,7 @@ function PipeRenderer({
3187
3900
  waypointDragOffset,
3188
3901
  onPipeSegmentClick
3189
3902
  }) {
3190
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("g", { id: "layer-pipes", children: pipes.filter((pipe) => pipe.visible !== false).map((pipe) => {
3903
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("g", { id: "layer-pipes", children: pipes.filter((pipe) => pipe.visible !== false).map((pipe) => {
3191
3904
  const isSelected = selectedPipeIds?.has(pipe.id) ?? false;
3192
3905
  const isEditing = enableWaypointEditing && editingPipeId === pipe.id;
3193
3906
  const showEditingControls = enableWaypointEditing && isSelected;
@@ -3206,7 +3919,7 @@ function PipeRenderer({
3206
3919
  const strokeWidth = isSelected ? (pipeStyle.strokeWidth || 3) + 2 : pipeStyle.strokeWidth || 3;
3207
3920
  const strokeDasharray = pipeStyle.strokeDasharray;
3208
3921
  const opacity = pipeStyle.opacity !== void 0 ? pipeStyle.opacity : 1;
3209
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3922
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3210
3923
  "g",
3211
3924
  {
3212
3925
  "data-pipe-id": pipe.id,
@@ -3219,8 +3932,8 @@ function PipeRenderer({
3219
3932
  cursor: onPipeClick ? "pointer" : "default"
3220
3933
  },
3221
3934
  children: [
3222
- !enableWaypointEditing && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3223
- onPipeClick && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3935
+ !enableWaypointEditing && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3936
+ onPipeClick && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3224
3937
  "polyline",
3225
3938
  {
3226
3939
  points: pointsString,
@@ -3232,7 +3945,7 @@ function PipeRenderer({
3232
3945
  pointerEvents: "stroke"
3233
3946
  }
3234
3947
  ),
3235
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3948
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3236
3949
  "polyline",
3237
3950
  {
3238
3951
  points: pointsString,
@@ -3256,7 +3969,7 @@ function PipeRenderer({
3256
3969
  const waypointSize = isSelectedWaypoint ? 8 : isDragging ? 8 : showEditingControls ? 6 : isSelected ? 3 : 2;
3257
3970
  const waypointOpacity = isSelectedWaypoint ? 1 : isDragging ? 1 : showEditingControls ? 0.9 : isSelected ? 0.8 : 0.5;
3258
3971
  const waypointColor = isSelectedWaypoint ? "#3b82f6" : isEndpoint && showEditingControls ? "#f59e0b" : isDragging ? "#10b981" : stroke;
3259
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3972
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3260
3973
  "circle",
3261
3974
  {
3262
3975
  cx: point.x,
@@ -3293,12 +4006,12 @@ function PipeRenderer({
3293
4006
  `${pipe.id}-point-${idx}`
3294
4007
  );
3295
4008
  }),
3296
- showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: displayPoints.slice(0, -1).map((point, idx) => {
4009
+ showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: displayPoints.slice(0, -1).map((point, idx) => {
3297
4010
  const nextPoint = displayPoints[idx + 1];
3298
4011
  const midX = (point.x + nextPoint.x) / 2;
3299
4012
  const midY = (point.y + nextPoint.y) / 2;
3300
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("g", { children: [
3301
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4013
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("g", { children: [
4014
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3302
4015
  "circle",
3303
4016
  {
3304
4017
  cx: midX,
@@ -3314,7 +4027,7 @@ function PipeRenderer({
3314
4027
  }
3315
4028
  }
3316
4029
  ),
3317
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4030
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3318
4031
  "circle",
3319
4032
  {
3320
4033
  cx: midX,
@@ -3330,7 +4043,7 @@ function PipeRenderer({
3330
4043
  )
3331
4044
  ] }, `${pipe.id}-segment-${idx}`);
3332
4045
  }) }),
3333
- pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4046
+ pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3334
4047
  "text",
3335
4048
  {
3336
4049
  x: pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].x + (pipe.labelOffset?.x ?? 0),
@@ -3352,7 +4065,7 @@ function PipeRenderer({
3352
4065
  }
3353
4066
 
3354
4067
  // src/components/PIDCanvas/DataOverlay.tsx
3355
- var import_jsx_runtime15 = require("react/jsx-runtime");
4068
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3356
4069
  function DataOverlay({
3357
4070
  nodeId,
3358
4071
  x,
@@ -3372,7 +4085,7 @@ function DataOverlay({
3372
4085
  const offsetX = display?.offsetX ?? 0;
3373
4086
  const offsetY = display?.offsetY ?? 0;
3374
4087
  if (!showValue) {
3375
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, {});
4088
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, {});
3376
4089
  }
3377
4090
  const formatValue = (val) => {
3378
4091
  if (typeof val === "number") {
@@ -3390,9 +4103,14 @@ function DataOverlay({
3390
4103
  fault: { bg: "#dc2626", text: "#ffffff", border: "#b91c1c" },
3391
4104
  off: { bg: "#6b7280", text: "#ffffff", border: "#4b5563" }
3392
4105
  };
3393
- const customColor = telemetry.statusColors?.[value.status];
3394
- const defaultColorSet = defaultStatusColors[value.status] || defaultStatusColors.normal;
3395
- const statusColors = customColor ? { bg: customColor, text: "#ffffff", border: customColor } : defaultColorSet;
4106
+ const getStatusColors = () => {
4107
+ const customColor = telemetry.statusColors?.[value.status];
4108
+ if (customColor) {
4109
+ return { bg: customColor, text: "#ffffff", border: customColor };
4110
+ }
4111
+ return defaultStatusColors[value.status] || defaultStatusColors.normal;
4112
+ };
4113
+ const statusColors = getStatusColors();
3396
4114
  const colors = backgroundColor && backgroundColor !== "status" ? { bg: backgroundColor, text: textColor || "#ffffff", border: backgroundColor } : statusColors;
3397
4115
  const finalTextColor = textColor || colors.text;
3398
4116
  const formattedValue = formatValue(value.value);
@@ -3419,8 +4137,8 @@ function DataOverlay({
3419
4137
  const overlayWidth = 80 * scale;
3420
4138
  const displayX = x + offsetX;
3421
4139
  const displayY = y + offsetY;
3422
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
3423
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4140
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
4141
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3424
4142
  "rect",
3425
4143
  {
3426
4144
  x: displayX - overlayWidth / 2,
@@ -3435,7 +4153,7 @@ function DataOverlay({
3435
4153
  filter: "drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
3436
4154
  }
3437
4155
  ),
3438
- label && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4156
+ label && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3439
4157
  "text",
3440
4158
  {
3441
4159
  x: displayX,
@@ -3448,7 +4166,7 @@ function DataOverlay({
3448
4166
  children: label
3449
4167
  }
3450
4168
  ),
3451
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4169
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3452
4170
  "text",
3453
4171
  {
3454
4172
  x: displayX,
@@ -3461,7 +4179,7 @@ function DataOverlay({
3461
4179
  children: displayText
3462
4180
  }
3463
4181
  ),
3464
- showStatus && value.status !== "normal" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4182
+ showStatus && value.status !== "normal" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3465
4183
  "circle",
3466
4184
  {
3467
4185
  cx: displayX + 35 * scale,
@@ -3469,7 +4187,7 @@ function DataOverlay({
3469
4187
  r: 3 * scale,
3470
4188
  fill: finalTextColor,
3471
4189
  opacity: "0.9",
3472
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4190
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3473
4191
  "animate",
3474
4192
  {
3475
4193
  attributeName: "opacity",
@@ -3480,7 +4198,7 @@ function DataOverlay({
3480
4198
  )
3481
4199
  }
3482
4200
  ),
3483
- value.quality !== void 0 && value.quality < 100 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4201
+ value.quality !== void 0 && value.quality < 100 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3484
4202
  "text",
3485
4203
  {
3486
4204
  x: displayX,
@@ -3497,7 +4215,7 @@ function DataOverlay({
3497
4215
  ]
3498
4216
  }
3499
4217
  ),
3500
- hasSparkline && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("g", { transform: `translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4218
+ hasSparkline && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("g", { transform: `translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3501
4219
  "path",
3502
4220
  {
3503
4221
  d: sparklinePath,
@@ -3513,8 +4231,8 @@ function DataOverlay({
3513
4231
  }
3514
4232
 
3515
4233
  // src/components/PIDCanvas/PositionPanel.tsx
3516
- var import_react8 = require("react");
3517
- var import_jsx_runtime16 = require("react/jsx-runtime");
4234
+ var import_react9 = require("react");
4235
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3518
4236
  function PositionPanel({
3519
4237
  selectedNode,
3520
4238
  selectedWaypoint,
@@ -3522,9 +4240,9 @@ function PositionPanel({
3522
4240
  onWaypointPositionChange,
3523
4241
  position = "top-right"
3524
4242
  }) {
3525
- const [x, setX] = (0, import_react8.useState)("");
3526
- const [y, setY] = (0, import_react8.useState)("");
3527
- (0, import_react8.useEffect)(() => {
4243
+ const [x, setX] = (0, import_react9.useState)("");
4244
+ const [y, setY] = (0, import_react9.useState)("");
4245
+ (0, import_react9.useEffect)(() => {
3528
4246
  if (selectedNode) {
3529
4247
  setX(selectedNode.transform.x.toFixed(2));
3530
4248
  setY(selectedNode.transform.y.toFixed(2));
@@ -3583,7 +4301,7 @@ function PositionPanel({
3583
4301
  "bottom-left": { bottom: 10, left: 10 },
3584
4302
  "bottom-right": { bottom: 10, right: 10 }
3585
4303
  };
3586
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4304
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3587
4305
  "div",
3588
4306
  {
3589
4307
  style: {
@@ -3600,11 +4318,11 @@ function PositionPanel({
3600
4318
  fontSize: "13px"
3601
4319
  },
3602
4320
  children: [
3603
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { marginBottom: "8px", fontWeight: 600, color: "#333" }, children: "Position" }),
3604
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
3605
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3606
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: { width: "16px", color: "#666" }, children: "X:" }),
3607
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4321
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { marginBottom: "8px", fontWeight: 600, color: "#333" }, children: "Position" }),
4322
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
4323
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
4324
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { style: { width: "16px", color: "#666" }, children: "X:" }),
4325
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3608
4326
  "input",
3609
4327
  {
3610
4328
  type: "number",
@@ -3622,9 +4340,9 @@ function PositionPanel({
3622
4340
  }
3623
4341
  )
3624
4342
  ] }),
3625
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3626
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: { width: "16px", color: "#666" }, children: "Y:" }),
3627
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4343
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
4344
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { style: { width: "16px", color: "#666" }, children: "Y:" }),
4345
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3628
4346
  "input",
3629
4347
  {
3630
4348
  type: "number",
@@ -3643,14 +4361,14 @@ function PositionPanel({
3643
4361
  )
3644
4362
  ] })
3645
4363
  ] }),
3646
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { marginTop: "8px", fontSize: "11px", color: "#888" }, children: "Use arrow keys to nudge (\xB11px)" })
4364
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { marginTop: "8px", fontSize: "11px", color: "#888" }, children: "Use arrow keys to nudge (\xB11px)" })
3647
4365
  ]
3648
4366
  }
3649
4367
  );
3650
4368
  }
3651
4369
 
3652
4370
  // src/diagram/hooks/useViewBox.ts
3653
- var import_react9 = require("react");
4371
+ var import_react10 = require("react");
3654
4372
  function useViewBox(options) {
3655
4373
  const {
3656
4374
  initialViewBox,
@@ -3661,16 +4379,16 @@ function useViewBox(options) {
3661
4379
  zoomSensitivity = 1e-3,
3662
4380
  allowLeftClickPan = true
3663
4381
  } = options;
3664
- const [viewBox, setViewBox] = (0, import_react9.useState)(initialViewBox);
3665
- const svgRef = (0, import_react9.useRef)(null);
3666
- const isPanningRef = (0, import_react9.useRef)(false);
3667
- const lastMousePosRef = (0, import_react9.useRef)({ x: 0, y: 0 });
3668
- const spaceKeyDownRef = (0, import_react9.useRef)(false);
3669
- const lastTouchDistanceRef = (0, import_react9.useRef)(null);
3670
- const resetViewBox = (0, import_react9.useCallback)(() => {
4382
+ const [viewBox, setViewBox] = (0, import_react10.useState)(initialViewBox);
4383
+ const svgRef = (0, import_react10.useRef)(null);
4384
+ const isPanningRef = (0, import_react10.useRef)(false);
4385
+ const lastMousePosRef = (0, import_react10.useRef)({ x: 0, y: 0 });
4386
+ const spaceKeyDownRef = (0, import_react10.useRef)(false);
4387
+ const lastTouchDistanceRef = (0, import_react10.useRef)(null);
4388
+ const resetViewBox = (0, import_react10.useCallback)(() => {
3671
4389
  setViewBox(initialViewBox);
3672
4390
  }, [initialViewBox]);
3673
- const zoomIn = (0, import_react9.useCallback)(() => {
4391
+ const zoomIn = (0, import_react10.useCallback)(() => {
3674
4392
  setViewBox((prev) => {
3675
4393
  const currentZoom = initialViewBox.width / prev.width;
3676
4394
  const newZoom = Math.min(1 / minZoom, currentZoom * 1.25);
@@ -3687,7 +4405,7 @@ function useViewBox(options) {
3687
4405
  };
3688
4406
  });
3689
4407
  }, [initialViewBox, minZoom]);
3690
- const zoomOut = (0, import_react9.useCallback)(() => {
4408
+ const zoomOut = (0, import_react10.useCallback)(() => {
3691
4409
  setViewBox((prev) => {
3692
4410
  const currentZoom = initialViewBox.width / prev.width;
3693
4411
  const newZoom = Math.max(1 / maxZoom, currentZoom * 0.8);
@@ -3704,7 +4422,7 @@ function useViewBox(options) {
3704
4422
  };
3705
4423
  });
3706
4424
  }, [initialViewBox, maxZoom]);
3707
- const fitToContent = (0, import_react9.useCallback)((bounds, padding = 50) => {
4425
+ const fitToContent = (0, import_react10.useCallback)((bounds, padding = 50) => {
3708
4426
  const contentWidth = bounds.maxX - bounds.minX;
3709
4427
  const contentHeight = bounds.maxY - bounds.minY;
3710
4428
  if (contentWidth === 0 || contentHeight === 0) {
@@ -3732,7 +4450,7 @@ function useViewBox(options) {
3732
4450
  height: newHeight
3733
4451
  });
3734
4452
  }, [initialViewBox]);
3735
- (0, import_react9.useEffect)(() => {
4453
+ (0, import_react10.useEffect)(() => {
3736
4454
  if (!enableZoom || !svgRef.current) return;
3737
4455
  const svg = svgRef.current;
3738
4456
  const handleWheel = (e) => {
@@ -3765,7 +4483,7 @@ function useViewBox(options) {
3765
4483
  svg.addEventListener("wheel", handleWheel, { passive: false });
3766
4484
  return () => svg.removeEventListener("wheel", handleWheel);
3767
4485
  }, [enableZoom, zoomSensitivity, minZoom, initialViewBox]);
3768
- (0, import_react9.useEffect)(() => {
4486
+ (0, import_react10.useEffect)(() => {
3769
4487
  if (!enablePan || !svgRef.current) return;
3770
4488
  const svg = svgRef.current;
3771
4489
  const handleMouseDown = (e) => {
@@ -3841,7 +4559,7 @@ function useViewBox(options) {
3841
4559
  window.removeEventListener("keyup", handleKeyUp);
3842
4560
  };
3843
4561
  }, [enablePan, allowLeftClickPan]);
3844
- (0, import_react9.useEffect)(() => {
4562
+ (0, import_react10.useEffect)(() => {
3845
4563
  if (!svgRef.current) return;
3846
4564
  if (!enablePan && !enableZoom) return;
3847
4565
  const svg = svgRef.current;
@@ -3932,7 +4650,7 @@ function useViewBox(options) {
3932
4650
  svg.removeEventListener("touchend", handleTouchEnd);
3933
4651
  };
3934
4652
  }, [enablePan, enableZoom, minZoom, initialViewBox]);
3935
- const screenToWorld = (0, import_react9.useCallback)(
4653
+ const screenToWorld = (0, import_react10.useCallback)(
3936
4654
  (screenX, screenY) => {
3937
4655
  const svg = svgRef.current;
3938
4656
  if (!svg) return { x: screenX, y: screenY };
@@ -3971,15 +4689,15 @@ function useViewBox(options) {
3971
4689
  }
3972
4690
 
3973
4691
  // src/diagram/hooks/useSelection.ts
3974
- var import_react10 = require("react");
4692
+ var import_react11 = require("react");
3975
4693
  function useSelection(options = {}) {
3976
4694
  const {
3977
4695
  multiSelect = true,
3978
4696
  initialSelection = { selectedNodeIds: /* @__PURE__ */ new Set(), selectedPipeIds: /* @__PURE__ */ new Set() },
3979
4697
  onSelectionChange
3980
4698
  } = options;
3981
- const [selection, setSelection] = (0, import_react10.useState)(initialSelection);
3982
- const notifyChange = (0, import_react10.useCallback)(
4699
+ const [selection, setSelection] = (0, import_react11.useState)(initialSelection);
4700
+ const notifyChange = (0, import_react11.useCallback)(
3983
4701
  (newSelection) => {
3984
4702
  if (onSelectionChange) {
3985
4703
  onSelectionChange(newSelection);
@@ -3987,15 +4705,15 @@ function useSelection(options = {}) {
3987
4705
  },
3988
4706
  [onSelectionChange]
3989
4707
  );
3990
- const isNodeSelected = (0, import_react10.useCallback)(
4708
+ const isNodeSelected = (0, import_react11.useCallback)(
3991
4709
  (nodeId) => selection.selectedNodeIds.has(nodeId),
3992
4710
  [selection.selectedNodeIds]
3993
4711
  );
3994
- const isPipeSelected = (0, import_react10.useCallback)(
4712
+ const isPipeSelected = (0, import_react11.useCallback)(
3995
4713
  (pipeId) => selection.selectedPipeIds.has(pipeId),
3996
4714
  [selection.selectedPipeIds]
3997
4715
  );
3998
- const selectNode = (0, import_react10.useCallback)(
4716
+ const selectNode = (0, import_react11.useCallback)(
3999
4717
  (nodeId, isMultiSelect = false) => {
4000
4718
  setSelection((prev) => {
4001
4719
  const newNodeIds = new Set(isMultiSelect && multiSelect ? prev.selectedNodeIds : []);
@@ -4014,7 +4732,7 @@ function useSelection(options = {}) {
4014
4732
  },
4015
4733
  [multiSelect, notifyChange]
4016
4734
  );
4017
- const selectPipe = (0, import_react10.useCallback)(
4735
+ const selectPipe = (0, import_react11.useCallback)(
4018
4736
  (pipeId, isMultiSelect = false) => {
4019
4737
  setSelection((prev) => {
4020
4738
  const newPipeIds = new Set(isMultiSelect && multiSelect ? prev.selectedPipeIds : []);
@@ -4033,7 +4751,7 @@ function useSelection(options = {}) {
4033
4751
  },
4034
4752
  [multiSelect, notifyChange]
4035
4753
  );
4036
- const clearSelection = (0, import_react10.useCallback)(() => {
4754
+ const clearSelection = (0, import_react11.useCallback)(() => {
4037
4755
  const newSelection = {
4038
4756
  selectedNodeIds: /* @__PURE__ */ new Set(),
4039
4757
  selectedPipeIds: /* @__PURE__ */ new Set()
@@ -4041,7 +4759,7 @@ function useSelection(options = {}) {
4041
4759
  setSelection(newSelection);
4042
4760
  notifyChange(newSelection);
4043
4761
  }, [notifyChange]);
4044
- const selectNodes = (0, import_react10.useCallback)(
4762
+ const selectNodes = (0, import_react11.useCallback)(
4045
4763
  (nodeIds) => {
4046
4764
  const newSelection = {
4047
4765
  selectedNodeIds: new Set(nodeIds),
@@ -4052,7 +4770,7 @@ function useSelection(options = {}) {
4052
4770
  },
4053
4771
  [notifyChange]
4054
4772
  );
4055
- const selectPipes = (0, import_react10.useCallback)(
4773
+ const selectPipes = (0, import_react11.useCallback)(
4056
4774
  (pipeIds) => {
4057
4775
  const newSelection = {
4058
4776
  selectedNodeIds: /* @__PURE__ */ new Set(),
@@ -4076,23 +4794,23 @@ function useSelection(options = {}) {
4076
4794
  }
4077
4795
 
4078
4796
  // src/diagram/hooks/useTelemetry.ts
4079
- var import_react11 = require("react");
4797
+ var import_react12 = require("react");
4080
4798
  function useTelemetry(options = {}) {
4081
4799
  const {
4082
4800
  initialBindings = [],
4083
4801
  onTelemetryChange,
4084
4802
  refreshInterval = 0
4085
4803
  } = options;
4086
- const [telemetry, setTelemetry] = (0, import_react11.useState)(() => {
4804
+ const [telemetry, setTelemetry] = (0, import_react12.useState)(() => {
4087
4805
  const map = /* @__PURE__ */ new Map();
4088
4806
  initialBindings.forEach((binding) => {
4089
4807
  map.set(binding.nodeId, binding);
4090
4808
  });
4091
4809
  return map;
4092
4810
  });
4093
- const telemetryRef = (0, import_react11.useRef)(telemetry);
4811
+ const telemetryRef = (0, import_react12.useRef)(telemetry);
4094
4812
  telemetryRef.current = telemetry;
4095
- const notifyChange = (0, import_react11.useCallback)(
4813
+ const notifyChange = (0, import_react12.useCallback)(
4096
4814
  (newTelemetry) => {
4097
4815
  if (onTelemetryChange) {
4098
4816
  onTelemetryChange(newTelemetry);
@@ -4100,7 +4818,7 @@ function useTelemetry(options = {}) {
4100
4818
  },
4101
4819
  [onTelemetryChange]
4102
4820
  );
4103
- const updateTelemetry = (0, import_react11.useCallback)(
4821
+ const updateTelemetry = (0, import_react12.useCallback)(
4104
4822
  (nodeId, value) => {
4105
4823
  setTelemetry((prev) => {
4106
4824
  const newMap = new Map(prev);
@@ -4130,7 +4848,7 @@ function useTelemetry(options = {}) {
4130
4848
  },
4131
4849
  [notifyChange]
4132
4850
  );
4133
- const updateTelemetryBatch = (0, import_react11.useCallback)(
4851
+ const updateTelemetryBatch = (0, import_react12.useCallback)(
4134
4852
  (updates) => {
4135
4853
  setTelemetry((prev) => {
4136
4854
  const newMap = new Map(prev);
@@ -4184,16 +4902,16 @@ function useTelemetry(options = {}) {
4184
4902
  },
4185
4903
  [notifyChange]
4186
4904
  );
4187
- const getTelemetry = (0, import_react11.useCallback)(
4905
+ const getTelemetry = (0, import_react12.useCallback)(
4188
4906
  (nodeId) => telemetryRef.current.get(nodeId),
4189
4907
  []
4190
4908
  );
4191
- const clearTelemetry = (0, import_react11.useCallback)(() => {
4909
+ const clearTelemetry = (0, import_react12.useCallback)(() => {
4192
4910
  const newMap = /* @__PURE__ */ new Map();
4193
4911
  setTelemetry(newMap);
4194
4912
  notifyChange(newMap);
4195
4913
  }, [notifyChange]);
4196
- const setBindings = (0, import_react11.useCallback)(
4914
+ const setBindings = (0, import_react12.useCallback)(
4197
4915
  (bindings) => {
4198
4916
  const newMap = /* @__PURE__ */ new Map();
4199
4917
  bindings.forEach((binding) => {
@@ -4204,7 +4922,7 @@ function useTelemetry(options = {}) {
4204
4922
  },
4205
4923
  [notifyChange]
4206
4924
  );
4207
- (0, import_react11.useEffect)(() => {
4925
+ (0, import_react12.useEffect)(() => {
4208
4926
  if (refreshInterval <= 0) return;
4209
4927
  const intervalId = setInterval(() => {
4210
4928
  notifyChange(telemetryRef.current);
@@ -4222,10 +4940,10 @@ function useTelemetry(options = {}) {
4222
4940
  }
4223
4941
 
4224
4942
  // src/diagram/hooks/useTelemetryStatus.ts
4225
- var import_react12 = require("react");
4943
+ var import_react13 = require("react");
4226
4944
  function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4227
4945
  const { enabled = true, mapStatus } = options;
4228
- (0, import_react12.useEffect)(() => {
4946
+ (0, import_react13.useEffect)(() => {
4229
4947
  if (!enabled) return;
4230
4948
  const defaultMapStatus = (telemetryStatus) => {
4231
4949
  switch (telemetryStatus) {
@@ -4264,7 +4982,7 @@ function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4264
4982
  }
4265
4983
 
4266
4984
  // src/diagram/hooks/useDrag.ts
4267
- var import_react13 = require("react");
4985
+ var import_react14 = require("react");
4268
4986
  function useDrag(options = {}) {
4269
4987
  const {
4270
4988
  onDragStart,
@@ -4274,23 +4992,23 @@ function useDrag(options = {}) {
4274
4992
  dragThreshold = 3,
4275
4993
  screenToWorld = (x, y) => ({ x, y })
4276
4994
  } = options;
4277
- const [dragState, setDragState] = (0, import_react13.useState)({
4995
+ const [dragState, setDragState] = (0, import_react14.useState)({
4278
4996
  isDragging: false,
4279
4997
  startPosition: null,
4280
4998
  offset: { x: 0, y: 0 },
4281
4999
  draggedId: null
4282
5000
  });
4283
- const [isListening, setIsListening] = (0, import_react13.useState)(false);
4284
- const dragStartRef = (0, import_react13.useRef)(null);
4285
- const hasMovedRef = (0, import_react13.useRef)(false);
4286
- const applySnap = (0, import_react13.useCallback)(
5001
+ const [isListening, setIsListening] = (0, import_react14.useState)(false);
5002
+ const dragStartRef = (0, import_react14.useRef)(null);
5003
+ const hasMovedRef = (0, import_react14.useRef)(false);
5004
+ const applySnap = (0, import_react14.useCallback)(
4287
5005
  (value) => {
4288
5006
  if (snapToGrid <= 0) return value;
4289
5007
  return Math.round(value / snapToGrid) * snapToGrid;
4290
5008
  },
4291
5009
  [snapToGrid]
4292
5010
  );
4293
- const handleMouseMove = (0, import_react13.useCallback)(
5011
+ const handleMouseMove = (0, import_react14.useCallback)(
4294
5012
  (event) => {
4295
5013
  if (!dragStartRef.current) return;
4296
5014
  const { id, screenStart: _screenStart, itemPosition, clickOffset } = dragStartRef.current;
@@ -4331,7 +5049,7 @@ function useDrag(options = {}) {
4331
5049
  },
4332
5050
  [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]
4333
5051
  );
4334
- const handleMouseUp = (0, import_react13.useCallback)(
5052
+ const handleMouseUp = (0, import_react14.useCallback)(
4335
5053
  (event) => {
4336
5054
  if (!dragStartRef.current || !hasMovedRef.current) {
4337
5055
  dragStartRef.current = null;
@@ -4368,7 +5086,7 @@ function useDrag(options = {}) {
4368
5086
  },
4369
5087
  [screenToWorld, applySnap, onDragEnd]
4370
5088
  );
4371
- const handleTouchMove = (0, import_react13.useCallback)(
5089
+ const handleTouchMove = (0, import_react14.useCallback)(
4372
5090
  (event) => {
4373
5091
  if (!dragStartRef.current || event.touches.length !== 1) return;
4374
5092
  const touch = event.touches[0];
@@ -4411,7 +5129,7 @@ function useDrag(options = {}) {
4411
5129
  },
4412
5130
  [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]
4413
5131
  );
4414
- const handleTouchEnd = (0, import_react13.useCallback)(
5132
+ const handleTouchEnd = (0, import_react14.useCallback)(
4415
5133
  (event) => {
4416
5134
  if (!dragStartRef.current || !hasMovedRef.current) {
4417
5135
  dragStartRef.current = null;
@@ -4449,7 +5167,7 @@ function useDrag(options = {}) {
4449
5167
  },
4450
5168
  [screenToWorld, applySnap, onDragEnd]
4451
5169
  );
4452
- (0, import_react13.useEffect)(() => {
5170
+ (0, import_react14.useEffect)(() => {
4453
5171
  if (!isListening) return;
4454
5172
  document.addEventListener("mousemove", handleMouseMove);
4455
5173
  document.addEventListener("mouseup", handleMouseUp);
@@ -4464,7 +5182,7 @@ function useDrag(options = {}) {
4464
5182
  document.removeEventListener("touchcancel", handleTouchEnd);
4465
5183
  };
4466
5184
  }, [isListening, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
4467
- const startDrag = (0, import_react13.useCallback)(
5185
+ const startDrag = (0, import_react14.useCallback)(
4468
5186
  (id, event, itemPosition) => {
4469
5187
  event.stopPropagation();
4470
5188
  let clientX;
@@ -4494,7 +5212,7 @@ function useDrag(options = {}) {
4494
5212
  },
4495
5213
  [screenToWorld]
4496
5214
  );
4497
- const cancelDrag = (0, import_react13.useCallback)(() => {
5215
+ const cancelDrag = (0, import_react14.useCallback)(() => {
4498
5216
  dragStartRef.current = null;
4499
5217
  hasMovedRef.current = false;
4500
5218
  setIsListening(false);
@@ -4505,7 +5223,7 @@ function useDrag(options = {}) {
4505
5223
  draggedId: null
4506
5224
  });
4507
5225
  }, []);
4508
- const isDraggingItem = (0, import_react13.useCallback)(
5226
+ const isDraggingItem = (0, import_react14.useCallback)(
4509
5227
  (id) => dragState.isDragging && dragState.draggedId === id,
4510
5228
  [dragState.isDragging, dragState.draggedId]
4511
5229
  );
@@ -4518,7 +5236,7 @@ function useDrag(options = {}) {
4518
5236
  }
4519
5237
 
4520
5238
  // src/diagram/hooks/useKeyboardShortcuts.ts
4521
- var import_react14 = require("react");
5239
+ var import_react15 = require("react");
4522
5240
  function useKeyboardShortcuts(options) {
4523
5241
  const {
4524
5242
  onDelete,
@@ -4529,7 +5247,7 @@ function useKeyboardShortcuts(options) {
4529
5247
  onSelectAll,
4530
5248
  enabled = true
4531
5249
  } = options;
4532
- (0, import_react14.useEffect)(() => {
5250
+ (0, import_react15.useEffect)(() => {
4533
5251
  if (!enabled) return;
4534
5252
  const handleKeyDown = (event) => {
4535
5253
  const target = event.target;
@@ -4638,7 +5356,7 @@ function nodeHasPort(node, portId) {
4638
5356
  }
4639
5357
 
4640
5358
  // src/components/PIDCanvas/PIDCanvas.tsx
4641
- var import_jsx_runtime17 = require("react/jsx-runtime");
5359
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4642
5360
  function PIDCanvas({
4643
5361
  diagram,
4644
5362
  className = "",
@@ -4665,21 +5383,21 @@ function PIDCanvas({
4665
5383
  telemetry,
4666
5384
  ...props
4667
5385
  }) {
4668
- const [localDiagram, setLocalDiagram] = (0, import_react15.useState)(diagram);
4669
- const [dragOverPosition, setDragOverPosition] = (0, import_react15.useState)(null);
4670
- const [isConnecting, setIsConnecting] = (0, import_react15.useState)(false);
4671
- const [connectionSource, setConnectionSource] = (0, import_react15.useState)(null);
4672
- const [connectionSourcePort, setConnectionSourcePort] = (0, import_react15.useState)(null);
4673
- const [connectionCursor, setConnectionCursor] = (0, import_react15.useState)(null);
4674
- const [hoveredNode, setHoveredNode] = (0, import_react15.useState)(null);
4675
- const [hoveredPort, setHoveredPort] = (0, import_react15.useState)(null);
4676
- const [selectedWaypoint, setSelectedWaypoint] = (0, import_react15.useState)(null);
4677
- const historyStack = (0, import_react15.useRef)([]);
4678
- const redoStack = (0, import_react15.useRef)([]);
4679
- const isUndoRedoAction = (0, import_react15.useRef)(false);
4680
- const isInternalChange = (0, import_react15.useRef)(false);
4681
- const lastInternalDiagram = (0, import_react15.useRef)(null);
4682
- (0, import_react15.useEffect)(() => {
5386
+ const [localDiagram, setLocalDiagram] = (0, import_react16.useState)(diagram);
5387
+ const [dragOverPosition, setDragOverPosition] = (0, import_react16.useState)(null);
5388
+ const [isConnecting, setIsConnecting] = (0, import_react16.useState)(false);
5389
+ const [connectionSource, setConnectionSource] = (0, import_react16.useState)(null);
5390
+ const [connectionSourcePort, setConnectionSourcePort] = (0, import_react16.useState)(null);
5391
+ const [connectionCursor, setConnectionCursor] = (0, import_react16.useState)(null);
5392
+ const [hoveredNode, setHoveredNode] = (0, import_react16.useState)(null);
5393
+ const [hoveredPort, setHoveredPort] = (0, import_react16.useState)(null);
5394
+ const [selectedWaypoint, setSelectedWaypoint] = (0, import_react16.useState)(null);
5395
+ const historyStack = (0, import_react16.useRef)([]);
5396
+ const redoStack = (0, import_react16.useRef)([]);
5397
+ const isUndoRedoAction = (0, import_react16.useRef)(false);
5398
+ const isInternalChange = (0, import_react16.useRef)(false);
5399
+ const lastInternalDiagram = (0, import_react16.useRef)(null);
5400
+ (0, import_react16.useEffect)(() => {
4683
5401
  const isExternal = !isInternalChange.current && diagram !== lastInternalDiagram.current;
4684
5402
  if (isExternal) {
4685
5403
  setLocalDiagram(diagram);
@@ -4701,7 +5419,7 @@ function PIDCanvas({
4701
5419
  enablePan: features.pan ?? true,
4702
5420
  enableZoom: features.zoom ?? true
4703
5421
  });
4704
- const calculateContentBounds = (0, import_react15.useCallback)(() => {
5422
+ const calculateContentBounds = (0, import_react16.useCallback)(() => {
4705
5423
  const nodes2 = localDiagram.nodes.filter((n) => n.visible !== false);
4706
5424
  if (nodes2.length === 0) {
4707
5425
  return null;
@@ -4724,7 +5442,7 @@ function PIDCanvas({
4724
5442
  });
4725
5443
  return { minX, minY, maxX, maxY };
4726
5444
  }, [localDiagram.nodes]);
4727
- (0, import_react15.useEffect)(() => {
5445
+ (0, import_react16.useEffect)(() => {
4728
5446
  if (onMounted) {
4729
5447
  const controls = {
4730
5448
  fitToContent: (padding = 50) => {
@@ -4765,8 +5483,8 @@ function PIDCanvas({
4765
5483
  });
4766
5484
  const dragEnabled = features.dragNodes ?? false;
4767
5485
  const selectionEnabled = features.selection ?? false;
4768
- const containerRef = (0, import_react15.useRef)(null);
4769
- const pushToHistory = (0, import_react15.useCallback)((currentDiagram) => {
5486
+ const containerRef = (0, import_react16.useRef)(null);
5487
+ const pushToHistory = (0, import_react16.useCallback)((currentDiagram) => {
4770
5488
  if (isUndoRedoAction.current) return;
4771
5489
  const snapshot = JSON.parse(JSON.stringify(currentDiagram));
4772
5490
  historyStack.current.push(snapshot);
@@ -4775,7 +5493,7 @@ function PIDCanvas({
4775
5493
  }
4776
5494
  redoStack.current = [];
4777
5495
  }, []);
4778
- const undo = (0, import_react15.useCallback)(() => {
5496
+ const undo = (0, import_react16.useCallback)(() => {
4779
5497
  if (historyStack.current.length === 0) return;
4780
5498
  isUndoRedoAction.current = true;
4781
5499
  const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));
@@ -4789,7 +5507,7 @@ function PIDCanvas({
4789
5507
  isUndoRedoAction.current = false;
4790
5508
  }, 0);
4791
5509
  }, [localDiagram, onDiagramChange]);
4792
- const redo = (0, import_react15.useCallback)(() => {
5510
+ const redo = (0, import_react16.useCallback)(() => {
4793
5511
  if (redoStack.current.length === 0) return;
4794
5512
  isUndoRedoAction.current = true;
4795
5513
  const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));
@@ -4803,7 +5521,7 @@ function PIDCanvas({
4803
5521
  isUndoRedoAction.current = false;
4804
5522
  }, 0);
4805
5523
  }, [localDiagram, onDiagramChange]);
4806
- (0, import_react15.useEffect)(() => {
5524
+ (0, import_react16.useEffect)(() => {
4807
5525
  const handleKeyDown = (e) => {
4808
5526
  const isCtrlOrCmd = e.ctrlKey || e.metaKey;
4809
5527
  if (isCtrlOrCmd && e.key === "z" && !e.shiftKey) {
@@ -4820,7 +5538,7 @@ function PIDCanvas({
4820
5538
  window.addEventListener("keydown", handleKeyDown);
4821
5539
  return () => window.removeEventListener("keydown", handleKeyDown);
4822
5540
  }, [undo, redo]);
4823
- (0, import_react15.useEffect)(() => {
5541
+ (0, import_react16.useEffect)(() => {
4824
5542
  if (!features.dragNodes) return;
4825
5543
  const handleKeyDown = (e) => {
4826
5544
  if (selection.selectedNodeIds.size === 0 && !selectedWaypoint) return;
@@ -4905,7 +5623,7 @@ function PIDCanvas({
4905
5623
  onNodeMove,
4906
5624
  pushToHistory
4907
5625
  ]);
4908
- const handlePositionChange = (0, import_react15.useCallback)(
5626
+ const handlePositionChange = (0, import_react16.useCallback)(
4909
5627
  (nodeId, x, y) => {
4910
5628
  pushToHistory(localDiagram);
4911
5629
  const updatedNodes = localDiagram.nodes.map(
@@ -4927,7 +5645,7 @@ function PIDCanvas({
4927
5645
  },
4928
5646
  [localDiagram, onDiagramChange, onNodeMove, pushToHistory]
4929
5647
  );
4930
- const handleWaypointPositionChange = (0, import_react15.useCallback)(
5648
+ const handleWaypointPositionChange = (0, import_react16.useCallback)(
4931
5649
  (pipeId, pointIndex, x, y) => {
4932
5650
  pushToHistory(localDiagram);
4933
5651
  const updatedPipes = localDiagram.pipes.map((pipe) => {
@@ -4945,7 +5663,7 @@ function PIDCanvas({
4945
5663
  },
4946
5664
  [localDiagram, onDiagramChange, pushToHistory]
4947
5665
  );
4948
- const handleDragEnd = (0, import_react15.useCallback)(
5666
+ const handleDragEnd = (0, import_react16.useCallback)(
4949
5667
  (nodeId, offset, finalPosition) => {
4950
5668
  const dragDistance = Math.sqrt(offset.x ** 2 + offset.y ** 2);
4951
5669
  if (dragDistance < 2) {
@@ -5026,7 +5744,7 @@ function PIDCanvas({
5026
5744
  onDiagramChange?.(updatedDiagram);
5027
5745
  }
5028
5746
  });
5029
- const handleNodeDragStart = (0, import_react15.useCallback)(
5747
+ const handleNodeDragStart = (0, import_react16.useCallback)(
5030
5748
  (nodeId, event) => {
5031
5749
  const node = localDiagram.nodes.find((n) => n.id === nodeId);
5032
5750
  if (!node) return;
@@ -5034,7 +5752,7 @@ function PIDCanvas({
5034
5752
  },
5035
5753
  [localDiagram.nodes, startDrag]
5036
5754
  );
5037
- const handleWaypointClick = (0, import_react15.useCallback)(
5755
+ const handleWaypointClick = (0, import_react16.useCallback)(
5038
5756
  (pipeId, pointIndex) => {
5039
5757
  setSelectedWaypoint({ pipeId, pointIndex });
5040
5758
  if (selectionEnabled) {
@@ -5043,7 +5761,7 @@ function PIDCanvas({
5043
5761
  },
5044
5762
  [selectionEnabled, clearSelection]
5045
5763
  );
5046
- const handleWaypointDragStart = (0, import_react15.useCallback)(
5764
+ const handleWaypointDragStart = (0, import_react16.useCallback)(
5047
5765
  (pipeId, pointIndex, event) => {
5048
5766
  const pipe = localDiagram.pipes.find((p) => p.id === pipeId);
5049
5767
  if (!pipe || pointIndex >= pipe.routePoints.length) return;
@@ -5052,7 +5770,7 @@ function PIDCanvas({
5052
5770
  },
5053
5771
  [localDiagram.pipes, startWaypointDrag]
5054
5772
  );
5055
- const handlePipeSegmentClick = (0, import_react15.useCallback)(
5773
+ const handlePipeSegmentClick = (0, import_react16.useCallback)(
5056
5774
  (pipeId, position, segmentIndex) => {
5057
5775
  pushToHistory(localDiagram);
5058
5776
  const updatedPipes = localDiagram.pipes.map((pipe) => {
@@ -5071,7 +5789,7 @@ function PIDCanvas({
5071
5789
  [localDiagram, onDiagramChange, pushToHistory]
5072
5790
  );
5073
5791
  const telemetryEnabled = features.telemetry ?? false;
5074
- const handleDelete = (0, import_react15.useCallback)(() => {
5792
+ const handleDelete = (0, import_react16.useCallback)(() => {
5075
5793
  if (!selectionEnabled) return;
5076
5794
  const { selectedNodeIds, selectedPipeIds } = selection;
5077
5795
  if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;
@@ -5100,13 +5818,13 @@ function PIDCanvas({
5100
5818
  onDelete,
5101
5819
  pushToHistory
5102
5820
  ]);
5103
- const handleCopy = (0, import_react15.useCallback)(() => {
5821
+ const handleCopy = (0, import_react16.useCallback)(() => {
5104
5822
  if (!selectionEnabled) return;
5105
5823
  const { selectedNodeIds, selectedPipeIds } = selection;
5106
5824
  if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;
5107
5825
  onItemsCopied?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));
5108
5826
  }, [selection, selectionEnabled, onItemsCopied]);
5109
- const handlePaste = (0, import_react15.useCallback)(() => {
5827
+ const handlePaste = (0, import_react16.useCallback)(() => {
5110
5828
  if (!selectionEnabled) return;
5111
5829
  onPaste?.();
5112
5830
  }, [selectionEnabled, onPaste]);
@@ -5166,7 +5884,7 @@ function PIDCanvas({
5166
5884
  }
5167
5885
  setSelectedWaypoint(null);
5168
5886
  };
5169
- const handleDragOver = (0, import_react15.useCallback)(
5887
+ const handleDragOver = (0, import_react16.useCallback)(
5170
5888
  (event) => {
5171
5889
  if (!features.allowSymbolDrop) return;
5172
5890
  event.preventDefault();
@@ -5177,7 +5895,7 @@ function PIDCanvas({
5177
5895
  },
5178
5896
  [features.allowSymbolDrop, screenToWorld, svgRef]
5179
5897
  );
5180
- const handleDrop = (0, import_react15.useCallback)(
5898
+ const handleDrop = (0, import_react16.useCallback)(
5181
5899
  (event) => {
5182
5900
  if (!features.allowSymbolDrop) return;
5183
5901
  event.preventDefault();
@@ -5190,10 +5908,10 @@ function PIDCanvas({
5190
5908
  },
5191
5909
  [features.allowSymbolDrop, screenToWorld, onSymbolDrop, svgRef]
5192
5910
  );
5193
- const handleDragLeave = (0, import_react15.useCallback)(() => {
5911
+ const handleDragLeave = (0, import_react16.useCallback)(() => {
5194
5912
  setDragOverPosition(null);
5195
5913
  }, []);
5196
- const handleMouseMove = (0, import_react15.useCallback)(
5914
+ const handleMouseMove = (0, import_react16.useCallback)(
5197
5915
  (event) => {
5198
5916
  const connectionModeEnabled = features.connectionMode ?? false;
5199
5917
  if (connectionModeEnabled) {
@@ -5230,7 +5948,7 @@ function PIDCanvas({
5230
5948
  const viewBox = controlledViewBox;
5231
5949
  const displayDiagram = localDiagram;
5232
5950
  const selectedNode = selectionEnabled && selection.selectedNodeIds.size === 1 ? localDiagram.nodes.find((n) => selection.selectedNodeIds.has(n.id)) || null : null;
5233
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5951
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5234
5952
  "div",
5235
5953
  {
5236
5954
  ref: containerRef,
@@ -5245,7 +5963,7 @@ function PIDCanvas({
5245
5963
  },
5246
5964
  ...props,
5247
5965
  children: [
5248
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5966
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5249
5967
  "svg",
5250
5968
  {
5251
5969
  ref: svgRef,
@@ -5264,15 +5982,15 @@ function PIDCanvas({
5264
5982
  onDrop: handleDrop,
5265
5983
  onDragLeave: handleDragLeave,
5266
5984
  children: [
5267
- features.showGrid !== false ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
5268
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("defs", { children: [
5269
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pattern", { id: "grid-minor", width: "5", height: "5", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M 5 0 L 0 0 0 5", fill: "none", stroke: "#e5e7eb", strokeWidth: "0.5" }) }),
5270
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
5271
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
5272
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M 25 0 L 0 0 0 25", fill: "none", stroke: "#d1d5db", strokeWidth: "1" })
5985
+ features.showGrid !== false ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5986
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("defs", { children: [
5987
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pattern", { id: "grid-minor", width: "5", height: "5", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M 5 0 L 0 0 0 5", fill: "none", stroke: "#e5e7eb", strokeWidth: "0.5" }) }),
5988
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
5989
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
5990
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M 25 0 L 0 0 0 25", fill: "none", stroke: "#d1d5db", strokeWidth: "1" })
5273
5991
  ] })
5274
5992
  ] }),
5275
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5993
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5276
5994
  "rect",
5277
5995
  {
5278
5996
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5282,7 +6000,7 @@ function PIDCanvas({
5282
6000
  fill: "url(#grid-major)"
5283
6001
  }
5284
6002
  )
5285
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6003
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5286
6004
  "rect",
5287
6005
  {
5288
6006
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5292,7 +6010,7 @@ function PIDCanvas({
5292
6010
  fill: features.backgroundColor ?? "#ffffff"
5293
6011
  }
5294
6012
  ),
5295
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6013
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5296
6014
  PipeRenderer,
5297
6015
  {
5298
6016
  pipes: displayDiagram.pipes,
@@ -5306,12 +6024,12 @@ function PIDCanvas({
5306
6024
  onPipeSegmentClick: void 0
5307
6025
  }
5308
6026
  ),
5309
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6027
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5310
6028
  NodeRenderer,
5311
6029
  {
5312
6030
  nodes: displayDiagram.nodes,
5313
6031
  selectedNodeIds: selectionEnabled ? selection.selectedNodeIds : void 0,
5314
- onNodeClick: selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
6032
+ onNodeClick: onNodeClick ? handleNodeClick : selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
5315
6033
  onNodeDoubleClick: selectionEnabled ? handleNodeDoubleClick : void 0,
5316
6034
  enableDrag: dragEnabled && !isConnecting,
5317
6035
  onNodeDragStart: dragEnabled && !isConnecting ? handleNodeDragStart : void 0,
@@ -5319,7 +6037,7 @@ function PIDCanvas({
5319
6037
  dragOffset: dragState.offset
5320
6038
  }
5321
6039
  ),
5322
- (features.editPipeRoutes ?? false) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6040
+ (features.editPipeRoutes ?? false) && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5323
6041
  PipeRenderer,
5324
6042
  {
5325
6043
  pipes: displayDiagram.pipes,
@@ -5336,14 +6054,14 @@ function PIDCanvas({
5336
6054
  onPipeSegmentClick: handlePipeSegmentClick
5337
6055
  }
5338
6056
  ),
5339
- telemetryEnabled && telemetry && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("g", { id: "layer-telemetry", children: displayDiagram.nodes.map((node) => {
6057
+ telemetryEnabled && telemetry && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("g", { id: "layer-telemetry", children: displayDiagram.nodes.map((node) => {
5340
6058
  const binding = telemetry.get(node.id);
5341
6059
  if (!binding) return null;
5342
6060
  const symbol = getSymbolDefinition(node.symbolId);
5343
6061
  if (!symbol) return null;
5344
6062
  const overlayX = node.transform.x + symbol.viewBox.width / 2;
5345
6063
  const overlayY = node.transform.y + symbol.viewBox.height + 50;
5346
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6064
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5347
6065
  DataOverlay,
5348
6066
  {
5349
6067
  nodeId: node.id,
@@ -5355,7 +6073,7 @@ function PIDCanvas({
5355
6073
  `telemetry-${node.id}`
5356
6074
  );
5357
6075
  }) }),
5358
- dragOverPosition && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6076
+ dragOverPosition && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5359
6077
  "circle",
5360
6078
  {
5361
6079
  cx: dragOverPosition.x,
@@ -5368,7 +6086,7 @@ function PIDCanvas({
5368
6086
  pointerEvents: "none"
5369
6087
  }
5370
6088
  ),
5371
- isConnecting && connectionSource && connectionCursor && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("g", { id: "connection-preview", pointerEvents: "none", children: (() => {
6089
+ isConnecting && connectionSource && connectionCursor && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("g", { id: "connection-preview", pointerEvents: "none", children: (() => {
5372
6090
  const sourceNode = displayDiagram.nodes.find((n) => n.id === connectionSource);
5373
6091
  if (!sourceNode) return null;
5374
6092
  const symbol = getSymbolDefinition(sourceNode.symbolId);
@@ -5384,8 +6102,8 @@ function PIDCanvas({
5384
6102
  }
5385
6103
  }
5386
6104
  }
5387
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
5388
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6105
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
6106
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5389
6107
  "circle",
5390
6108
  {
5391
6109
  cx: lineStartX,
@@ -5397,7 +6115,7 @@ function PIDCanvas({
5397
6115
  opacity: 0.6
5398
6116
  }
5399
6117
  ),
5400
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6118
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5401
6119
  "line",
5402
6120
  {
5403
6121
  x1: lineStartX,
@@ -5410,7 +6128,7 @@ function PIDCanvas({
5410
6128
  opacity: 0.8
5411
6129
  }
5412
6130
  ),
5413
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6131
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5414
6132
  "circle",
5415
6133
  {
5416
6134
  cx: connectionCursor.x,
@@ -5422,7 +6140,7 @@ function PIDCanvas({
5422
6140
  )
5423
6141
  ] });
5424
6142
  })() }),
5425
- features.connectionMode && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("g", { id: "port-indicators", children: displayDiagram.nodes.map((node) => {
6143
+ features.connectionMode && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("g", { id: "port-indicators", children: displayDiagram.nodes.map((node) => {
5426
6144
  const symbol = getSymbolDefinition(node.symbolId);
5427
6145
  if (!symbol?.ports) return null;
5428
6146
  return symbol.ports.map((port) => {
@@ -5430,8 +6148,8 @@ function PIDCanvas({
5430
6148
  if (!portPos) return null;
5431
6149
  const isHovered = hoveredPort === port.id && hoveredNode === node.id;
5432
6150
  const isSourcePort = node.id === connectionSource && port.id === connectionSourcePort;
5433
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("g", { children: [
5434
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6151
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("g", { children: [
6152
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5435
6153
  "circle",
5436
6154
  {
5437
6155
  cx: portPos.x,
@@ -5464,7 +6182,7 @@ function PIDCanvas({
5464
6182
  }
5465
6183
  }
5466
6184
  ),
5467
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6185
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5468
6186
  "circle",
5469
6187
  {
5470
6188
  cx: portPos.x,
@@ -5483,7 +6201,7 @@ function PIDCanvas({
5483
6201
  ]
5484
6202
  }
5485
6203
  ),
5486
- features.dragNodes && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6204
+ features.dragNodes && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5487
6205
  PositionPanel,
5488
6206
  {
5489
6207
  selectedNode,
@@ -5503,16 +6221,16 @@ function PIDCanvas({
5503
6221
  }
5504
6222
 
5505
6223
  // src/components/SymbolLibrary/SymbolLibrary.tsx
5506
- var import_react16 = require("react");
5507
- var import_jsx_runtime18 = require("react/jsx-runtime");
6224
+ var import_react17 = require("react");
6225
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5508
6226
  function SymbolLibrary({
5509
6227
  className = "",
5510
6228
  onSymbolDragStart,
5511
6229
  showCategories = true,
5512
6230
  categories = ["Valves", "Pumps", "Tanks", "Instruments", "Displays"]
5513
6231
  }) {
5514
- const [selectedCategory, setSelectedCategory] = (0, import_react16.useState)("all");
5515
- const [searchQuery, setSearchQuery] = (0, import_react16.useState)("");
6232
+ const [selectedCategory, setSelectedCategory] = (0, import_react17.useState)("all");
6233
+ const [searchQuery, setSearchQuery] = (0, import_react17.useState)("");
5516
6234
  const symbolIds = getAvailableSymbols();
5517
6235
  const allSymbols = symbolIds.map((id) => getSymbolDefinition(id)).filter((symbol) => symbol !== void 0);
5518
6236
  const categoryMap = {
@@ -5533,9 +6251,9 @@ function SymbolLibrary({
5533
6251
  event.dataTransfer.effectAllowed = "copy";
5534
6252
  onSymbolDragStart?.(symbol.id, event);
5535
6253
  };
5536
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `symbol-library ${className}`.trim(), style: styles.container, children: [
5537
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { style: styles.title, children: "Symbol Library" }) }),
5538
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.searchContainer, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6254
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `symbol-library ${className}`.trim(), style: styles.container, children: [
6255
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { style: styles.title, children: "Symbol Library" }) }),
6256
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.searchContainer, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5539
6257
  "input",
5540
6258
  {
5541
6259
  type: "text",
@@ -5545,8 +6263,8 @@ function SymbolLibrary({
5545
6263
  style: styles.searchInput
5546
6264
  }
5547
6265
  ) }),
5548
- showCategories && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: styles.categories, children: [
5549
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6266
+ showCategories && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: styles.categories, children: [
6267
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5550
6268
  "button",
5551
6269
  {
5552
6270
  onClick: () => setSelectedCategory("all"),
@@ -5557,7 +6275,7 @@ function SymbolLibrary({
5557
6275
  children: "All"
5558
6276
  }
5559
6277
  ),
5560
- categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6278
+ categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5561
6279
  "button",
5562
6280
  {
5563
6281
  onClick: () => setSelectedCategory(category),
@@ -5570,7 +6288,7 @@ function SymbolLibrary({
5570
6288
  category
5571
6289
  ))
5572
6290
  ] }),
5573
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.symbolGrid, children: filteredSymbols.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.emptyState, children: "No symbols found" }) : filteredSymbols.map((symbol) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6291
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.symbolGrid, children: filteredSymbols.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.emptyState, children: "No symbols found" }) : filteredSymbols.map((symbol) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5574
6292
  "div",
5575
6293
  {
5576
6294
  draggable: true,
@@ -5578,15 +6296,15 @@ function SymbolLibrary({
5578
6296
  style: styles.symbolCard,
5579
6297
  title: symbol.metadata?.description || symbol.name,
5580
6298
  children: [
5581
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.symbolPreview, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6299
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.symbolPreview, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5582
6300
  "svg",
5583
6301
  {
5584
6302
  viewBox: `${symbol.viewBox.x} ${symbol.viewBox.y} ${symbol.viewBox.width} ${symbol.viewBox.height}`,
5585
6303
  style: styles.symbolSvg,
5586
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("g", { dangerouslySetInnerHTML: { __html: symbol.svgContent } })
6304
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("g", { dangerouslySetInnerHTML: { __html: symbol.svgContent } })
5587
6305
  }
5588
6306
  ) }),
5589
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.symbolName, children: symbol.name })
6307
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: styles.symbolName, children: symbol.name })
5590
6308
  ]
5591
6309
  },
5592
6310
  symbol.id
@@ -5695,7 +6413,7 @@ var styles = {
5695
6413
  };
5696
6414
 
5697
6415
  // src/components/NodeConfigPanel/NodeConfigPanel.tsx
5698
- var import_jsx_runtime19 = require("react/jsx-runtime");
6416
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5699
6417
  function NodeConfigPanel({
5700
6418
  node,
5701
6419
  binding,
@@ -5714,15 +6432,15 @@ function NodeConfigPanel({
5714
6432
  padding: "20px",
5715
6433
  ...style
5716
6434
  };
5717
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className, style: defaultStyle, children: [
5718
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("h2", { style: { margin: "0 0 16px 0", fontSize: "1rem", fontWeight: 600 }, children: [
6435
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, style: defaultStyle, children: [
6436
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("h2", { style: { margin: "0 0 16px 0", fontSize: "1rem", fontWeight: 600 }, children: [
5719
6437
  "Configure: ",
5720
6438
  node.id
5721
6439
  ] }),
5722
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
5723
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Node Settings" }),
5724
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label:" }),
5725
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6440
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6441
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Node Settings" }),
6442
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label:" }),
6443
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5726
6444
  "input",
5727
6445
  {
5728
6446
  type: "text",
@@ -5738,10 +6456,10 @@ function NodeConfigPanel({
5738
6456
  }
5739
6457
  }
5740
6458
  ),
5741
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5742
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5743
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset X:" }),
5744
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6459
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
6460
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6461
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset X:" }),
6462
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5745
6463
  "input",
5746
6464
  {
5747
6465
  type: "number",
@@ -5762,9 +6480,9 @@ function NodeConfigPanel({
5762
6480
  }
5763
6481
  )
5764
6482
  ] }),
5765
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5766
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset Y:" }),
5767
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6483
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6484
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset Y:" }),
6485
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5768
6486
  "input",
5769
6487
  {
5770
6488
  type: "number",
@@ -5786,9 +6504,9 @@ function NodeConfigPanel({
5786
6504
  )
5787
6505
  ] })
5788
6506
  ] }),
5789
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5790
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Font Size:" }),
5791
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6507
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6508
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Font Size:" }),
6509
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5792
6510
  "input",
5793
6511
  {
5794
6512
  type: "number",
@@ -5808,9 +6526,9 @@ function NodeConfigPanel({
5808
6526
  }
5809
6527
  )
5810
6528
  ] }),
5811
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5812
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
5813
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6529
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6530
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
6531
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5814
6532
  "input",
5815
6533
  {
5816
6534
  type: "number",
@@ -5835,9 +6553,9 @@ function NodeConfigPanel({
5835
6553
  )
5836
6554
  ] })
5837
6555
  ] }),
5838
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
5839
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Telemetry Data" }),
5840
- !binding ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6556
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6557
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Telemetry Data" }),
6558
+ !binding ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5841
6559
  "button",
5842
6560
  {
5843
6561
  onClick: () => {
@@ -5880,9 +6598,9 @@ function NodeConfigPanel({
5880
6598
  },
5881
6599
  children: "+ Add Telemetry"
5882
6600
  }
5883
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5884
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Initial Value:" }),
5885
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6601
+ ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6602
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Initial Value:" }),
6603
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5886
6604
  "input",
5887
6605
  {
5888
6606
  type: "number",
@@ -5904,8 +6622,8 @@ function NodeConfigPanel({
5904
6622
  }
5905
6623
  }
5906
6624
  ),
5907
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Unit:" }),
5908
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6625
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Unit:" }),
6626
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5909
6627
  "input",
5910
6628
  {
5911
6629
  type: "text",
@@ -5927,8 +6645,8 @@ function NodeConfigPanel({
5927
6645
  }
5928
6646
  }
5929
6647
  ),
5930
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
5931
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6648
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6649
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5932
6650
  "input",
5933
6651
  {
5934
6652
  type: "checkbox",
@@ -5950,8 +6668,8 @@ function NodeConfigPanel({
5950
6668
  ),
5951
6669
  "Show Sparkline"
5952
6670
  ] }),
5953
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginTop: "12px" }, children: [
5954
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6671
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginTop: "12px" }, children: [
6672
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5955
6673
  "label",
5956
6674
  {
5957
6675
  style: {
@@ -5963,10 +6681,10 @@ function NodeConfigPanel({
5963
6681
  children: "Telemetry Position Offset:"
5964
6682
  }
5965
6683
  ),
5966
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5967
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5968
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "X Offset:" }),
5969
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6684
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
6685
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6686
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "X Offset:" }),
6687
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5970
6688
  "input",
5971
6689
  {
5972
6690
  type: "number",
@@ -5993,9 +6711,9 @@ function NodeConfigPanel({
5993
6711
  }
5994
6712
  )
5995
6713
  ] }),
5996
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5997
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Y Offset:" }),
5998
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6714
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
6715
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Y Offset:" }),
6716
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5999
6717
  "input",
6000
6718
  {
6001
6719
  type: "number",
@@ -6023,8 +6741,8 @@ function NodeConfigPanel({
6023
6741
  )
6024
6742
  ] })
6025
6743
  ] }),
6026
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginTop: "16px" }, children: [
6027
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6744
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginTop: "16px" }, children: [
6745
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6028
6746
  "label",
6029
6747
  {
6030
6748
  style: {
@@ -6036,10 +6754,10 @@ function NodeConfigPanel({
6036
6754
  children: "Display Colors:"
6037
6755
  }
6038
6756
  ),
6039
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6040
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Text Color:" }),
6041
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6042
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6757
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6758
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Text Color:" }),
6759
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6760
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6043
6761
  "input",
6044
6762
  {
6045
6763
  type: "color",
@@ -6065,7 +6783,7 @@ function NodeConfigPanel({
6065
6783
  }
6066
6784
  }
6067
6785
  ),
6068
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6786
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6069
6787
  "input",
6070
6788
  {
6071
6789
  type: "text",
@@ -6095,10 +6813,10 @@ function NodeConfigPanel({
6095
6813
  )
6096
6814
  ] })
6097
6815
  ] }),
6098
- binding.display?.showSparkline && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6099
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Sparkline Color:" }),
6100
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6101
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6816
+ binding.display?.showSparkline && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6817
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Sparkline Color:" }),
6818
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6819
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6102
6820
  "input",
6103
6821
  {
6104
6822
  type: "color",
@@ -6124,7 +6842,7 @@ function NodeConfigPanel({
6124
6842
  }
6125
6843
  }
6126
6844
  ),
6127
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6845
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6128
6846
  "input",
6129
6847
  {
6130
6848
  type: "text",
@@ -6154,9 +6872,9 @@ function NodeConfigPanel({
6154
6872
  )
6155
6873
  ] })
6156
6874
  ] }),
6157
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6158
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Background:" }),
6159
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6875
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6876
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Background:" }),
6877
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6160
6878
  "input",
6161
6879
  {
6162
6880
  type: "text",
@@ -6184,20 +6902,20 @@ function NodeConfigPanel({
6184
6902
  }
6185
6903
  }
6186
6904
  ),
6187
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { fontSize: "0.65rem", color: "#6b7280", marginTop: "2px" }, children: "Use 'status' for status-based color or hex code" })
6905
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { fontSize: "0.65rem", color: "#6b7280", marginTop: "2px" }, children: "Use 'status' for status-based color or hex code" })
6188
6906
  ] })
6189
6907
  ] }),
6190
6908
  " "
6191
6909
  ] })
6192
6910
  ] })
6193
6911
  ] }),
6194
- binding && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6195
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Thresholds" }),
6912
+ binding && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6913
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Thresholds" }),
6196
6914
  ["highHigh", "high", "low", "lowLow"].map((key) => {
6197
6915
  const label = key === "highHigh" ? "High-High (\u2265)" : key === "high" ? "High (\u2265)" : key === "low" ? "Low (\u2264)" : "Low-Low (\u2264)";
6198
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6199
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: label }),
6200
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6916
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6917
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: label }),
6918
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6201
6919
  "input",
6202
6920
  {
6203
6921
  type: "number",
@@ -6221,8 +6939,8 @@ function NodeConfigPanel({
6221
6939
  ] }, key);
6222
6940
  })
6223
6941
  ] }),
6224
- binding && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6225
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Status Colors" }),
6942
+ binding && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6943
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Status Colors" }),
6226
6944
  ["normal", "warning", "alarm", "fault", "off"].map((status) => {
6227
6945
  const defaultColors = {
6228
6946
  normal: "#10b981",
@@ -6232,13 +6950,13 @@ function NodeConfigPanel({
6232
6950
  off: "#6b7280"
6233
6951
  };
6234
6952
  const label = status === "normal" ? "Normal" : status === "warning" ? "Warning" : status === "alarm" ? "Alarm" : status === "fault" ? "Fault" : "Off";
6235
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6236
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6953
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6954
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6237
6955
  label,
6238
6956
  ":"
6239
6957
  ] }),
6240
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6241
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6958
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6959
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6242
6960
  "input",
6243
6961
  {
6244
6962
  type: "color",
@@ -6259,7 +6977,7 @@ function NodeConfigPanel({
6259
6977
  }
6260
6978
  }
6261
6979
  ),
6262
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6980
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6263
6981
  "input",
6264
6982
  {
6265
6983
  type: "text",
@@ -6286,7 +7004,7 @@ function NodeConfigPanel({
6286
7004
  ] }, status);
6287
7005
  })
6288
7006
  ] }),
6289
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { marginTop: "20px", paddingTop: "20px", borderTop: "1px solid #cbd5e1" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7007
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { marginTop: "20px", paddingTop: "20px", borderTop: "1px solid #cbd5e1" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6290
7008
  "button",
6291
7009
  {
6292
7010
  onClick: onRemove,
@@ -6525,8 +7243,280 @@ var DEFAULT_THRESHOLDS = {
6525
7243
  lowLow: 15
6526
7244
  };
6527
7245
 
7246
+ // src/diagram/schema/DeviceControlBinding.ts
7247
+ function createControlBinding(nodeId, deviceConfig, actions) {
7248
+ const parametersByMode = {};
7249
+ deviceConfig.parameters.forEach((param) => {
7250
+ const modes = param.modes || deviceConfig.modes.map((m) => m.value);
7251
+ modes.forEach((mode) => {
7252
+ if (!parametersByMode[mode]) {
7253
+ parametersByMode[mode] = [];
7254
+ }
7255
+ parametersByMode[mode].push({
7256
+ id: param.id,
7257
+ label: param.label,
7258
+ unit: param.unit,
7259
+ min: param.min,
7260
+ max: param.max,
7261
+ value: param.value,
7262
+ type: typeof param.value === "number" ? "number" : "string"
7263
+ });
7264
+ });
7265
+ });
7266
+ return {
7267
+ nodeId,
7268
+ tag: deviceConfig.tag,
7269
+ deviceName: deviceConfig.name,
7270
+ deviceType: deviceConfig.type,
7271
+ modes: deviceConfig.modes,
7272
+ state: {
7273
+ currentMode: deviceConfig.currentMode,
7274
+ isRunning: deviceConfig.isRunning,
7275
+ status: deviceConfig.status
7276
+ },
7277
+ parameters: parametersByMode,
7278
+ capabilities: deviceConfig.capabilities,
7279
+ actions
7280
+ };
7281
+ }
7282
+
7283
+ // src/hooks/useDeviceControls.ts
7284
+ var import_react18 = require("react");
7285
+ function useDeviceControls(initialBindings) {
7286
+ const [controls, setControls] = (0, import_react18.useState)(() => {
7287
+ const map = /* @__PURE__ */ new Map();
7288
+ if (initialBindings) {
7289
+ initialBindings.forEach((binding) => {
7290
+ map.set(binding.nodeId, binding);
7291
+ });
7292
+ }
7293
+ return map;
7294
+ });
7295
+ const updateControl = (0, import_react18.useCallback)((nodeId, updates) => {
7296
+ setControls((prev) => {
7297
+ const newMap = new Map(prev);
7298
+ const existing = newMap.get(nodeId);
7299
+ if (existing) {
7300
+ newMap.set(nodeId, { ...existing, ...updates });
7301
+ } else {
7302
+ if ("tag" in updates && "modes" in updates && "state" in updates && "parameters" in updates && "actions" in updates) {
7303
+ newMap.set(nodeId, { nodeId, ...updates });
7304
+ }
7305
+ }
7306
+ return newMap;
7307
+ });
7308
+ }, []);
7309
+ const updateControlBatch = (0, import_react18.useCallback)((updates) => {
7310
+ setControls((prev) => {
7311
+ const newMap = new Map(prev);
7312
+ Object.entries(updates).forEach(([nodeId, update]) => {
7313
+ const existing = newMap.get(nodeId);
7314
+ if (existing) {
7315
+ newMap.set(nodeId, { ...existing, ...update });
7316
+ }
7317
+ });
7318
+ return newMap;
7319
+ });
7320
+ }, []);
7321
+ const updateDeviceState = (0, import_react18.useCallback)(
7322
+ (nodeId, stateUpdates) => {
7323
+ setControls((prev) => {
7324
+ const newMap = new Map(prev);
7325
+ const existing = newMap.get(nodeId);
7326
+ if (existing) {
7327
+ newMap.set(nodeId, {
7328
+ ...existing,
7329
+ state: { ...existing.state, ...stateUpdates }
7330
+ });
7331
+ }
7332
+ return newMap;
7333
+ });
7334
+ },
7335
+ []
7336
+ );
7337
+ const updateParameter = (0, import_react18.useCallback)(
7338
+ (nodeId, parameterId, value) => {
7339
+ setControls((prev) => {
7340
+ const newMap = new Map(prev);
7341
+ const existing = newMap.get(nodeId);
7342
+ if (existing) {
7343
+ const updatedParameters = { ...existing.parameters };
7344
+ Object.keys(updatedParameters).forEach((mode) => {
7345
+ const paramIndex = updatedParameters[mode].findIndex((p) => p.id === parameterId);
7346
+ if (paramIndex !== -1) {
7347
+ updatedParameters[mode] = [...updatedParameters[mode]];
7348
+ updatedParameters[mode][paramIndex] = {
7349
+ ...updatedParameters[mode][paramIndex],
7350
+ value
7351
+ };
7352
+ }
7353
+ });
7354
+ newMap.set(nodeId, {
7355
+ ...existing,
7356
+ parameters: updatedParameters
7357
+ });
7358
+ }
7359
+ return newMap;
7360
+ });
7361
+ },
7362
+ []
7363
+ );
7364
+ const setControlBinding = (0, import_react18.useCallback)((binding) => {
7365
+ setControls((prev) => {
7366
+ const newMap = new Map(prev);
7367
+ newMap.set(binding.nodeId, binding);
7368
+ return newMap;
7369
+ });
7370
+ }, []);
7371
+ const removeControlBinding = (0, import_react18.useCallback)((nodeId) => {
7372
+ setControls((prev) => {
7373
+ const newMap = new Map(prev);
7374
+ newMap.delete(nodeId);
7375
+ return newMap;
7376
+ });
7377
+ }, []);
7378
+ const getControlBinding = (0, import_react18.useCallback)(
7379
+ (nodeId) => {
7380
+ return controls.get(nodeId);
7381
+ },
7382
+ [controls]
7383
+ );
7384
+ const sendModeChange = (0, import_react18.useCallback)(
7385
+ async (nodeId, mode) => {
7386
+ const binding = controls.get(nodeId);
7387
+ if (!binding) return;
7388
+ updateDeviceState(nodeId, { currentMode: mode });
7389
+ try {
7390
+ await binding.actions.onModeChange?.(mode);
7391
+ updateDeviceState(nodeId, {
7392
+ lastCommandTime: Date.now(),
7393
+ lastCommandResult: { success: true, message: `Mode changed to ${mode}` }
7394
+ });
7395
+ } catch (error) {
7396
+ updateDeviceState(nodeId, {
7397
+ currentMode: binding.state.currentMode,
7398
+ lastCommandResult: {
7399
+ success: false,
7400
+ message: error instanceof Error ? error.message : "Mode change failed"
7401
+ }
7402
+ });
7403
+ }
7404
+ },
7405
+ [controls, updateDeviceState]
7406
+ );
7407
+ const sendParameterChange = (0, import_react18.useCallback)(
7408
+ async (nodeId, parameterId, value) => {
7409
+ const binding = controls.get(nodeId);
7410
+ if (!binding) return;
7411
+ updateParameter(nodeId, parameterId, value);
7412
+ try {
7413
+ await binding.actions.onParameterChange?.(parameterId, value);
7414
+ updateDeviceState(nodeId, {
7415
+ lastCommandTime: Date.now(),
7416
+ lastCommandResult: { success: true, message: `Parameter ${parameterId} updated` }
7417
+ });
7418
+ } catch (error) {
7419
+ updateDeviceState(nodeId, {
7420
+ lastCommandResult: {
7421
+ success: false,
7422
+ message: error instanceof Error ? error.message : "Parameter change failed"
7423
+ }
7424
+ });
7425
+ }
7426
+ },
7427
+ [controls, updateParameter, updateDeviceState]
7428
+ );
7429
+ const sendStartCommand = (0, import_react18.useCallback)(
7430
+ async (nodeId) => {
7431
+ const binding = controls.get(nodeId);
7432
+ if (!binding) return;
7433
+ updateDeviceState(nodeId, { isRunning: true, status: "starting" });
7434
+ try {
7435
+ await binding.actions.onStart?.();
7436
+ updateDeviceState(nodeId, {
7437
+ status: "normal",
7438
+ lastCommandTime: Date.now(),
7439
+ lastCommandResult: { success: true, message: "Device started" }
7440
+ });
7441
+ } catch (error) {
7442
+ updateDeviceState(nodeId, {
7443
+ isRunning: false,
7444
+ status: "fault",
7445
+ lastCommandResult: {
7446
+ success: false,
7447
+ message: error instanceof Error ? error.message : "Start failed"
7448
+ }
7449
+ });
7450
+ }
7451
+ },
7452
+ [controls, updateDeviceState]
7453
+ );
7454
+ const sendStopCommand = (0, import_react18.useCallback)(
7455
+ async (nodeId) => {
7456
+ const binding = controls.get(nodeId);
7457
+ if (!binding) return;
7458
+ updateDeviceState(nodeId, { isRunning: false, status: "stopping" });
7459
+ try {
7460
+ await binding.actions.onStop?.();
7461
+ updateDeviceState(nodeId, {
7462
+ status: "off",
7463
+ lastCommandTime: Date.now(),
7464
+ lastCommandResult: { success: true, message: "Device stopped" }
7465
+ });
7466
+ } catch (error) {
7467
+ updateDeviceState(nodeId, {
7468
+ isRunning: true,
7469
+ status: "fault",
7470
+ lastCommandResult: {
7471
+ success: false,
7472
+ message: error instanceof Error ? error.message : "Stop failed"
7473
+ }
7474
+ });
7475
+ }
7476
+ },
7477
+ [controls, updateDeviceState]
7478
+ );
7479
+ const sendCustomAction = (0, import_react18.useCallback)(
7480
+ async (nodeId, actionId) => {
7481
+ const binding = controls.get(nodeId);
7482
+ if (!binding) return;
7483
+ try {
7484
+ await binding.actions.onCustomAction?.(actionId);
7485
+ updateDeviceState(nodeId, {
7486
+ lastCommandTime: Date.now(),
7487
+ lastCommandResult: { success: true, message: `Action ${actionId} executed` }
7488
+ });
7489
+ } catch (error) {
7490
+ updateDeviceState(nodeId, {
7491
+ lastCommandResult: {
7492
+ success: false,
7493
+ message: error instanceof Error ? error.message : "Action failed"
7494
+ }
7495
+ });
7496
+ }
7497
+ },
7498
+ [controls, updateDeviceState]
7499
+ );
7500
+ return {
7501
+ controls,
7502
+ updateControl,
7503
+ updateControlBatch,
7504
+ updateDeviceState,
7505
+ updateParameter,
7506
+ setControlBinding,
7507
+ removeControlBinding,
7508
+ getControlBinding,
7509
+ // Command helpers
7510
+ sendModeChange,
7511
+ sendParameterChange,
7512
+ sendStartCommand,
7513
+ sendStopCommand,
7514
+ sendCustomAction
7515
+ };
7516
+ }
7517
+
6528
7518
  // src/diagram/hooks/useSimulation.ts
6529
- var import_react17 = require("react");
7519
+ var import_react19 = require("react");
6530
7520
  function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6531
7521
  const {
6532
7522
  interval = 2e3,
@@ -6537,7 +7527,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6537
7527
  historyLength = 10,
6538
7528
  generateValue
6539
7529
  } = options;
6540
- (0, import_react17.useEffect)(() => {
7530
+ (0, import_react19.useEffect)(() => {
6541
7531
  if (!enabled) return;
6542
7532
  const intervalId = setInterval(() => {
6543
7533
  const updates = [];
@@ -6595,6 +7585,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6595
7585
  ControlPanel,
6596
7586
  DEFAULT_THRESHOLDS,
6597
7587
  DashboardCard,
7588
+ DeviceControlPanel,
6598
7589
  DisplayModeToggle,
6599
7590
  EquipmentIndicator,
6600
7591
  EquipmentIndicatorWithSparkline,
@@ -6606,6 +7597,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6606
7597
  LeftToggleButton,
6607
7598
  LegacyValueEntry,
6608
7599
  NodeConfigPanel,
7600
+ NodeControlsPanel,
6609
7601
  PIDCanvas,
6610
7602
  PanelContent,
6611
7603
  PanelTabs,
@@ -6625,6 +7617,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6625
7617
  ZoomButton,
6626
7618
  ZoomControls,
6627
7619
  calculateThresholdStatus,
7620
+ createControlBinding,
6628
7621
  exampleDiagram,
6629
7622
  exportDiagram,
6630
7623
  generateRoutePoints,
@@ -6637,6 +7630,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6637
7630
  overlayStyles,
6638
7631
  registerSymbol,
6639
7632
  registerSymbols,
7633
+ useDeviceControls,
6640
7634
  useDrag,
6641
7635
  useKeyboardShortcuts,
6642
7636
  useSelection,