@procaaso/alphinity-ui-components 1.0.6 → 1.0.7

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