@procaaso/alphinity-ui-components 1.0.5 → 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,
@@ -78,6 +81,9 @@ __export(index_exports, {
78
81
  mockSymbolLibrary: () => mockSymbolLibrary,
79
82
  nodeHasPort: () => nodeHasPort,
80
83
  overlayStyles: () => overlayStyles,
84
+ registerSymbol: () => registerSymbol,
85
+ registerSymbols: () => registerSymbols,
86
+ useDeviceControls: () => useDeviceControls,
81
87
  useDrag: () => useDrag,
82
88
  useKeyboardShortcuts: () => useKeyboardShortcuts,
83
89
  useSelection: () => useSelection,
@@ -799,14 +805,721 @@ var StatusIndicator = ({
799
805
  ] });
800
806
  };
801
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
+
802
1515
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
803
- var import_react6 = __toESM(require("react"), 1);
1516
+ var import_react7 = __toESM(require("react"), 1);
804
1517
  var import_material3 = require("@mui/material");
805
1518
 
806
1519
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.styles.tsx
807
1520
  var import_material = require("@mui/material");
808
1521
  var import_styled = __toESM(require("@emotion/styled"), 1);
809
- var import_jsx_runtime7 = require("react/jsx-runtime");
1522
+ var import_jsx_runtime9 = require("react/jsx-runtime");
810
1523
  var FullscreenContainer = (0, import_styled.default)(import_material.Box)(
811
1524
  ({ leftCollapsed, rightCollapsed }) => {
812
1525
  const getGridTemplateAreas = () => {
@@ -931,7 +1644,7 @@ var DisplayModeToggle = (0, import_styled.default)(import_material.IconButton)((
931
1644
  transform: "translateX(-50%) scale(1.05)"
932
1645
  }
933
1646
  }));
934
- var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1647
+ var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
935
1648
  "div",
936
1649
  {
937
1650
  style: {
@@ -943,7 +1656,7 @@ var ChevronLeft = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
943
1656
  }
944
1657
  }
945
1658
  );
946
- var ChevronRight = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1659
+ var ChevronRight = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
947
1660
  "div",
948
1661
  {
949
1662
  style: {
@@ -1054,10 +1767,10 @@ var ScrollableSVGWrapper = (0, import_styled.default)("div")({
1054
1767
  });
1055
1768
 
1056
1769
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.overlays.tsx
1057
- var import_react5 = require("react");
1770
+ var import_react6 = require("react");
1058
1771
  var import_material2 = require("@mui/material");
1059
1772
  var import_styled2 = __toESM(require("@emotion/styled"), 1);
1060
- var import_jsx_runtime8 = require("react/jsx-runtime");
1773
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1061
1774
  var baseOverlayStyles = {
1062
1775
  tank: {
1063
1776
  background: "linear-gradient(135deg, rgba(69, 90, 120, 0.95) 0%, rgba(52, 73, 94, 0.95) 100%)",
@@ -1119,8 +1832,8 @@ var SVGLockedOverlay = ({
1119
1832
  onClick,
1120
1833
  containerSelector = ".tff-svg-container"
1121
1834
  }) => {
1122
- const [svgOffset, setSvgOffset] = (0, import_react5.useState)({ x: 0, y: 0 });
1123
- (0, import_react5.useEffect)(() => {
1835
+ const [svgOffset, setSvgOffset] = (0, import_react6.useState)({ x: 0, y: 0 });
1836
+ (0, import_react6.useEffect)(() => {
1124
1837
  const updateSvgOffset = () => {
1125
1838
  const containerElement = document.querySelector(containerSelector);
1126
1839
  const svgElement = containerElement?.querySelector("svg");
@@ -1140,7 +1853,7 @@ var SVGLockedOverlay = ({
1140
1853
  const pixelX = svgX + svgOffset.x;
1141
1854
  const pixelY = svgY + svgOffset.y;
1142
1855
  const appliedStyle = theme ? { ...overlayStyles[theme], ...style } : style;
1143
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1856
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1144
1857
  import_material2.Box,
1145
1858
  {
1146
1859
  className: `svg-locked-overlay ${className}`.trim(),
@@ -1204,7 +1917,7 @@ var CardUnit = (0, import_styled2.default)("div")({
1204
1917
  });
1205
1918
 
1206
1919
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx
1207
- var import_jsx_runtime9 = require("react/jsx-runtime");
1920
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1208
1921
  var TrendLine = ({
1209
1922
  values,
1210
1923
  width = 60,
@@ -1214,7 +1927,7 @@ var TrendLine = ({
1214
1927
  backgroundColor = "rgba(255, 255, 255, 0.1)"
1215
1928
  }) => {
1216
1929
  if (!values || values.length < 2) {
1217
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1930
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1218
1931
  "div",
1219
1932
  {
1220
1933
  style: {
@@ -1226,7 +1939,7 @@ var TrendLine = ({
1226
1939
  alignItems: "center",
1227
1940
  justifyContent: "center"
1228
1941
  },
1229
- 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" })
1230
1943
  }
1231
1944
  );
1232
1945
  }
@@ -1238,7 +1951,7 @@ var TrendLine = ({
1238
1951
  const x = 4 + i / (values.length - 1) * (width - 8);
1239
1952
  return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
1240
1953
  }).join(" ");
1241
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1954
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1242
1955
  "div",
1243
1956
  {
1244
1957
  style: {
@@ -1249,8 +1962,8 @@ var TrendLine = ({
1249
1962
  position: "relative",
1250
1963
  overflow: "hidden"
1251
1964
  },
1252
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
1253
- /* @__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)(
1254
1967
  "path",
1255
1968
  {
1256
1969
  d: pathData,
@@ -1261,7 +1974,7 @@ var TrendLine = ({
1261
1974
  strokeLinejoin: "round"
1262
1975
  }
1263
1976
  ),
1264
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1977
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1265
1978
  "circle",
1266
1979
  {
1267
1980
  cx: 4 + (width - 8),
@@ -1299,7 +2012,7 @@ var CircularGauge = ({
1299
2012
  const borderColor = isHex ? hexToRgba(color, 0.9) : color;
1300
2013
  const backgroundColor = isHex ? hexToRgba(color, 0.15) : color;
1301
2014
  const borderStrokeColor = isHex ? hexToRgba(color, 0.4) : color;
1302
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2015
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1303
2016
  "div",
1304
2017
  {
1305
2018
  style: {
@@ -1308,7 +2021,7 @@ var CircularGauge = ({
1308
2021
  alignItems: "center"
1309
2022
  },
1310
2023
  children: [
1311
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2024
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1312
2025
  "div",
1313
2026
  {
1314
2027
  style: {
@@ -1320,8 +2033,8 @@ var CircularGauge = ({
1320
2033
  border: `1px solid ${borderStrokeColor}`
1321
2034
  },
1322
2035
  children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
1324
- /* @__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)(
1325
2038
  "circle",
1326
2039
  {
1327
2040
  cx: size / 2,
@@ -1332,7 +2045,7 @@ var CircularGauge = ({
1332
2045
  strokeWidth: 1
1333
2046
  }
1334
2047
  ),
1335
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2048
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1336
2049
  "circle",
1337
2050
  {
1338
2051
  cx: size / 2,
@@ -1343,7 +2056,7 @@ var CircularGauge = ({
1343
2056
  fill: "transparent"
1344
2057
  }
1345
2058
  ),
1346
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2059
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1347
2060
  "circle",
1348
2061
  {
1349
2062
  cx: size / 2,
@@ -1359,7 +2072,7 @@ var CircularGauge = ({
1359
2072
  }
1360
2073
  )
1361
2074
  ] }),
1362
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2075
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1363
2076
  "div",
1364
2077
  {
1365
2078
  style: {
@@ -1375,14 +2088,14 @@ var CircularGauge = ({
1375
2088
  },
1376
2089
  children: [
1377
2090
  value.toFixed(1),
1378
- /* @__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 })
1379
2092
  ]
1380
2093
  }
1381
2094
  )
1382
2095
  ]
1383
2096
  }
1384
2097
  ),
1385
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2098
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1386
2099
  "div",
1387
2100
  {
1388
2101
  style: {
@@ -1413,7 +2126,7 @@ var Sparkline = ({ data, width, height, color }) => {
1413
2126
  const y = height - 8 - (value - min) / range * (height - 8);
1414
2127
  return `${x + 4},${y + 4}`;
1415
2128
  }).join(" ");
1416
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2129
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1417
2130
  "div",
1418
2131
  {
1419
2132
  style: {
@@ -1427,7 +2140,7 @@ var Sparkline = ({ data, width, height, color }) => {
1427
2140
  justifyContent: "center",
1428
2141
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
1429
2142
  },
1430
- 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" }) })
1431
2144
  }
1432
2145
  );
1433
2146
  };
@@ -1441,8 +2154,8 @@ var EquipmentIndicatorWithSparkline = ({
1441
2154
  sparklineOffset = { x: 40, y: 0 }
1442
2155
  }) => {
1443
2156
  const hasSparkline = sparklineData && sparklineData.length > 1 && Math.max(...sparklineData) !== Math.min(...sparklineData);
1444
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1445
- /* @__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)(
1446
2159
  "div",
1447
2160
  {
1448
2161
  style: {
@@ -1463,7 +2176,7 @@ var EquipmentIndicatorWithSparkline = ({
1463
2176
  children: label
1464
2177
  }
1465
2178
  ) }),
1466
- 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 }) })
1467
2180
  ] });
1468
2181
  };
1469
2182
  var EquipmentIndicator = ({
@@ -1476,7 +2189,7 @@ var EquipmentIndicator = ({
1476
2189
  className
1477
2190
  }) => {
1478
2191
  const overlayStyle = style !== void 0 ? { transform: "translate(-50%, -50%)", ...style } : { transform: "translate(-50%, -50%)" };
1479
- 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)(
1480
2193
  "div",
1481
2194
  {
1482
2195
  style: {
@@ -1500,7 +2213,7 @@ var EquipmentIndicator = ({
1500
2213
  };
1501
2214
 
1502
2215
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
1503
- var import_jsx_runtime10 = require("react/jsx-runtime");
2216
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1504
2217
  function FullscreenWorkspace({
1505
2218
  svgContent,
1506
2219
  overlays,
@@ -1528,10 +2241,10 @@ function FullscreenWorkspace({
1528
2241
  className = "",
1529
2242
  ...rest
1530
2243
  }) {
1531
- const [leftCollapsedState, setLeftCollapsedState] = import_react6.default.useState(defaultLeftCollapsed);
1532
- const [rightCollapsedState, setRightCollapsedState] = import_react6.default.useState(defaultRightCollapsed);
1533
- const [internalDisplayMode, setInternalDisplayMode] = import_react6.default.useState(displayMode ?? defaultDisplayMode);
1534
- 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(
1535
2248
  tabs && tabs.length > 0 ? tabs[0].value : ""
1536
2249
  );
1537
2250
  const leftCollapsed = leftCollapsedProp ?? leftCollapsedState;
@@ -1539,10 +2252,10 @@ function FullscreenWorkspace({
1539
2252
  const isDisplayModeControlled = displayMode !== void 0;
1540
2253
  const currentDisplayMode = isDisplayModeControlled ? displayMode : internalDisplayMode;
1541
2254
  const isTabControlled = activeTab !== void 0 && activeTab !== null;
1542
- const resolvedTabs = import_react6.default.useMemo(() => tabs ?? [], [tabs]);
2255
+ const resolvedTabs = import_react7.default.useMemo(() => tabs ?? [], [tabs]);
1543
2256
  const defaultTabValue = resolvedTabs.length > 0 ? resolvedTabs[0].value : "";
1544
2257
  const currentTab = isTabControlled ? activeTab : internalTab || defaultTabValue;
1545
- import_react6.default.useEffect(() => {
2258
+ import_react7.default.useEffect(() => {
1546
2259
  if (resolvedTabs.length > 0) {
1547
2260
  const values = resolvedTabs.map((tab) => tab.value);
1548
2261
  if (!values.includes(currentTab)) {
@@ -1551,7 +2264,7 @@ function FullscreenWorkspace({
1551
2264
  }
1552
2265
  }
1553
2266
  }, [resolvedTabs, activeTab, currentTab, defaultTabValue]);
1554
- import_react6.default.useEffect(() => {
2267
+ import_react7.default.useEffect(() => {
1555
2268
  if (displayMode !== void 0) {
1556
2269
  setInternalDisplayMode(displayMode);
1557
2270
  }
@@ -1586,7 +2299,7 @@ function FullscreenWorkspace({
1586
2299
  const currentTabContent = resolvedTabs.find((tab) => tab.value === currentTab)?.content ?? null;
1587
2300
  const showLeftToggle = Boolean(leftPanel);
1588
2301
  const showRightToggle = Boolean(rightPanel);
1589
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2302
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1590
2303
  FullscreenContainer,
1591
2304
  {
1592
2305
  leftCollapsed,
@@ -1594,25 +2307,25 @@ function FullscreenWorkspace({
1594
2307
  className: `tff-svg-container ${className}`.trim(),
1595
2308
  ...rest,
1596
2309
  children: [
1597
- showLeftToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2310
+ showLeftToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1598
2311
  LeftToggleButton,
1599
2312
  {
1600
2313
  collapsed: leftCollapsed,
1601
2314
  onClick: handleLeftToggle,
1602
2315
  "aria-label": leftCollapsed ? "Expand left panel" : "Collapse left panel",
1603
- 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, {})
1604
2317
  }
1605
2318
  ),
1606
- showRightToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2319
+ showRightToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1607
2320
  RightToggleButton,
1608
2321
  {
1609
2322
  collapsed: rightCollapsed,
1610
2323
  onClick: handleRightToggle,
1611
2324
  "aria-label": rightCollapsed ? "Expand right panel" : "Collapse right panel",
1612
- 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, {})
1613
2326
  }
1614
2327
  ),
1615
- showDisplayModeToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2328
+ showDisplayModeToggle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1616
2329
  DisplayModeToggle,
1617
2330
  {
1618
2331
  mode: currentDisplayMode,
@@ -1621,9 +2334,9 @@ function FullscreenWorkspace({
1621
2334
  children: currentDisplayMode === "dashboard" ? "Dashboard Mode" : "Standard Mode"
1622
2335
  }
1623
2336
  ),
1624
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(SVGArea, { children: [
1625
- showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ZoomControls, { children: [
1626
- /* @__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)(
1627
2340
  ZoomButton,
1628
2341
  {
1629
2342
  onClick: onZoomIn,
@@ -1633,7 +2346,7 @@ function FullscreenWorkspace({
1633
2346
  children: "+"
1634
2347
  }
1635
2348
  ),
1636
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2349
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1637
2350
  ZoomButton,
1638
2351
  {
1639
2352
  onClick: onZoomOut,
@@ -1643,7 +2356,7 @@ function FullscreenWorkspace({
1643
2356
  children: "-"
1644
2357
  }
1645
2358
  ),
1646
- onResetZoom && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2359
+ onResetZoom && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1647
2360
  ZoomButton,
1648
2361
  {
1649
2362
  onClick: onResetZoom,
@@ -1654,44 +2367,44 @@ function FullscreenWorkspace({
1654
2367
  }
1655
2368
  )
1656
2369
  ] }),
1657
- /* @__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: [
1658
2371
  svgContent,
1659
2372
  overlays
1660
2373
  ] }) })
1661
2374
  ] }),
1662
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LeftPanel, { collapsed: leftCollapsed, children: resolvedTabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1663
- /* @__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)(
1664
2377
  PanelTabs,
1665
2378
  {
1666
2379
  value: currentTab,
1667
2380
  onChange: handleTabChangeInternal,
1668
2381
  "aria-label": "Left panel tabs",
1669
- 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))
1670
2383
  }
1671
2384
  ),
1672
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PanelContent, { children: currentTabContent })
2385
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PanelContent, { children: currentTabContent })
1673
2386
  ] }) : leftPanel }),
1674
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RightPanel, { collapsed: rightCollapsed, children: rightPanel }),
1675
- /* @__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 })
1676
2389
  ]
1677
2390
  }
1678
2391
  );
1679
2392
  }
1680
2393
 
1681
2394
  // src/theme/ThemeContext.tsx
1682
- var import_react7 = require("react");
1683
- var import_jsx_runtime11 = require("react/jsx-runtime");
1684
- 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);
1685
2398
  var useTheme = () => {
1686
- const context = (0, import_react7.useContext)(ThemeContext);
2399
+ const context = (0, import_react8.useContext)(ThemeContext);
1687
2400
  if (!context) {
1688
2401
  throw new Error("useTheme must be used within a ThemeProvider");
1689
2402
  }
1690
2403
  return context;
1691
2404
  };
1692
2405
  var ThemeProvider = ({ children }) => {
1693
- const [theme, setThemeState] = (0, import_react7.useState)("modern");
1694
- (0, import_react7.useEffect)(() => {
2406
+ const [theme, setThemeState] = (0, import_react8.useState)("modern");
2407
+ (0, import_react8.useEffect)(() => {
1695
2408
  document.body.className = "";
1696
2409
  document.body.classList.add(`theme-${theme}`);
1697
2410
  document.documentElement.className = "";
@@ -1703,11 +2416,11 @@ var ThemeProvider = ({ children }) => {
1703
2416
  const setTheme = (newTheme) => {
1704
2417
  setThemeState(newTheme);
1705
2418
  };
1706
- 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 });
1707
2420
  };
1708
2421
 
1709
2422
  // src/theme/ThemeToggle.tsx
1710
- var import_jsx_runtime12 = require("react/jsx-runtime");
2423
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1711
2424
  var ThemeToggle = ({
1712
2425
  size = "medium",
1713
2426
  position = "top-right",
@@ -1778,15 +2491,15 @@ var ThemeToggle = ({
1778
2491
  transition: "all 0.2s ease",
1779
2492
  boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)"
1780
2493
  };
1781
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
1782
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
1783
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: toggleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: thumbStyle }) }),
1784
- /* @__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" })
1785
2498
  ] });
1786
2499
  };
1787
2500
 
1788
2501
  // src/components/PIDCanvas/PIDCanvas.tsx
1789
- var import_react15 = require("react");
2502
+ var import_react16 = require("react");
1790
2503
 
1791
2504
  // src/diagram/symbols/mockSymbolLibrary.ts
1792
2505
  var mockSymbolLibrary = {
@@ -1794,13 +2507,13 @@ var mockSymbolLibrary = {
1794
2507
  id: "valve-gate",
1795
2508
  name: "Gate Valve (Powered)",
1796
2509
  category: "valve",
1797
- viewBox: { x: 0, y: 0, width: 52, height: 52 },
2510
+ viewBox: { x: 0, y: 0, width: 26, height: 26 },
1798
2511
  ports: [
1799
- { id: "inlet", x: 0, y: 36, type: "inlet", direction: "in" },
1800
- { id: "outlet", x: 52, y: 36, type: "outlet", direction: "out" }
2512
+ { id: "inlet", x: 0, y: 18, type: "inlet", direction: "in" },
2513
+ { id: "outlet", x: 26, y: 18, type: "outlet", direction: "out" }
1801
2514
  ],
1802
2515
  svgContent: `
1803
- <g>
2516
+ <g transform="scale(0.5)">
1804
2517
  <!-- Actuator box -->
1805
2518
  <rect x="16.74" y="0" width="18.02" height="18.02" fill="var(--symbol-fill, #edeeef)" stroke="var(--symbol-stroke, #666666)" pointer-events="all"/>
1806
2519
  <!-- Actuator stem -->
@@ -1818,13 +2531,13 @@ var mockSymbolLibrary = {
1818
2531
  id: "pump-positive-displacement",
1819
2532
  name: "Positive Displacement Pump",
1820
2533
  category: "pump",
1821
- viewBox: { x: 0, y: 0, width: 56.25, height: 56.25 },
2534
+ viewBox: { x: 0, y: 0, width: 28.125, height: 28.125 },
1822
2535
  ports: [
1823
- { id: "inlet", x: 0, y: 28.125, type: "inlet", direction: "in" },
1824
- { id: "outlet", x: 56.25, y: 28.125, type: "outlet", direction: "out" }
2536
+ { id: "inlet", x: 0, y: 14.0625, type: "inlet", direction: "in" },
2537
+ { id: "outlet", x: 28.125, y: 14.0625, type: "outlet", direction: "out" }
1825
2538
  ],
1826
2539
  svgContent: `
1827
- <g transform="scale(0.5625)">
2540
+ <g transform="scale(0.28125)">
1828
2541
  <!-- Outer circle -->
1829
2542
  <ellipse
1830
2543
  cx="50"
@@ -1866,13 +2579,13 @@ var mockSymbolLibrary = {
1866
2579
  id: "vannefold-bottom",
1867
2580
  name: "Vannefold Block (Bottom)",
1868
2581
  category: "valve",
1869
- viewBox: { x: -0.5, y: -0.5, width: 88, height: 67.785156 },
2582
+ viewBox: { x: -0.25, y: -0.25, width: 44, height: 33.892578 },
1870
2583
  ports: [
1871
- { id: "bottom-inlet", x: 0, y: 25, type: "control", direction: "in" },
1872
- { id: "top-outlet", x: 48, y: 65, type: "control", direction: "out" }
2584
+ { id: "bottom-inlet", x: 0, y: 12.5, type: "control", direction: "in" },
2585
+ { id: "top-outlet", x: 24, y: 32.5, type: "control", direction: "out" }
1873
2586
  ],
1874
2587
  svgContent: `
1875
- <g>
2588
+ <g transform="scale(0.5)">
1876
2589
  <!-- Bottom inlet pipe -->
1877
2590
  <path
1878
2591
  d="M 3,29.785156 H 0 v -10 h 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16"
@@ -1930,13 +2643,13 @@ var mockSymbolLibrary = {
1930
2643
  id: "vannefold-middle",
1931
2644
  name: "Vannefold Block (Middle)",
1932
2645
  category: "valve",
1933
- viewBox: { x: -0.5, y: -0.5, width: 95, height: 53.097656 },
2646
+ viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 26.548828 },
1934
2647
  ports: [
1935
- { id: "left-inlet", x: 0, y: 27.3125, type: "control", direction: "in" },
1936
- { id: "right-outlet", x: 95, y: 27.3125, type: "control", direction: "out" }
2648
+ { id: "left-inlet", x: 0, y: 13.65625, type: "control", direction: "in" },
2649
+ { id: "right-outlet", x: 47.5, y: 13.65625, type: "control", direction: "out" }
1937
2650
  ],
1938
2651
  svgContent: `
1939
- <g>
2652
+ <g transform="scale(0.5)">
1940
2653
  <!-- Left inlet pipe -->
1941
2654
  <path
1942
2655
  d="M 3,22.3125 H 0 v 10 H 3 m 14,-10 h 3 v 10 h -3 m -15,-8 h 16 m -16,6 h 16"
@@ -1985,13 +2698,13 @@ var mockSymbolLibrary = {
1985
2698
  id: "vannefold-top",
1986
2699
  name: "Vannefold Block (Top)",
1987
2700
  category: "valve",
1988
- viewBox: { x: -0.5, y: -0.5, width: 95, height: 60.785156 },
2701
+ viewBox: { x: -0.25, y: -0.25, width: 47.5, height: 30.392578 },
1989
2702
  ports: [
1990
- { id: "bottom-inlet", x: 0, y: 35, type: "control", direction: "in" },
1991
- { id: "top-outlet", x: 48, y: 0, type: "control", direction: "out" }
2703
+ { id: "bottom-inlet", x: 0, y: 17.5, type: "control", direction: "in" },
2704
+ { id: "top-outlet", x: 24, y: 0, type: "control", direction: "out" }
1992
2705
  ],
1993
2706
  svgContent: `
1994
- <g>
2707
+ <g transform="scale(0.5)">
1995
2708
  <!-- Bottom inlet pipe -->
1996
2709
  <path
1997
2710
  d="M 3,30 H 0 V 40 H 3 M 17,30 h 3 V 40 H 17 M 2,32 H 18 M 2,38 H 18"
@@ -2041,13 +2754,13 @@ var mockSymbolLibrary = {
2041
2754
  id: "vannefold-top-top-inlet",
2042
2755
  name: "Vannefold Block (Top, Top Inlet)",
2043
2756
  category: "valve",
2044
- viewBox: { x: 0, y: 0, width: 88, height: 67.576004 },
2757
+ viewBox: { x: 0, y: 0, width: 44, height: 33.788002 },
2045
2758
  ports: [
2046
- { id: "top-inlet", x: 47, y: 0, type: "control", direction: "in" },
2047
- { id: "bottom-outlet", x: 0, y: 42, type: "control", direction: "out" }
2759
+ { id: "top-inlet", x: 23.5, y: 0, type: "control", direction: "in" },
2760
+ { id: "bottom-outlet", x: 0, y: 21, type: "control", direction: "out" }
2048
2761
  ],
2049
2762
  svgContent: `
2050
- <g>
2763
+ <g transform="scale(0.5)">
2051
2764
  <!-- Top inlet pipe flange -->
2052
2765
  <path
2053
2766
  d="M 52,3 V 1.3598666e-8 H 42 V 3 m 10,14 v 3 H 42 V 17 M 50,2 V 18 M 44,2 v 16"
@@ -2105,13 +2818,13 @@ var mockSymbolLibrary = {
2105
2818
  id: "vannefold-left",
2106
2819
  name: "Vannefold Block (Left)",
2107
2820
  category: "valve",
2108
- viewBox: { x: 0, y: 0, width: 67.785156, height: 88.5 },
2821
+ viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },
2109
2822
  ports: [
2110
- { id: "left-inlet", x: 0, y: 41.5, type: "control", direction: "in" },
2111
- { id: "right-outlet", x: 42, y: 87, type: "control", direction: "out" }
2823
+ { id: "left-inlet", x: 0, y: 20.75, type: "control", direction: "in" },
2824
+ { id: "right-outlet", x: 21, y: 43.5, type: "control", direction: "out" }
2112
2825
  ],
2113
2826
  svgContent: `
2114
- <g>
2827
+ <g transform="scale(0.5)">
2115
2828
  <!-- Left inlet pipe flange -->
2116
2829
  <path
2117
2830
  d="M 3,46.5 H 2.8789924e-8 v -10 H 3 m 14,10 h 3 v -10 h -3 m -15,8 h 16 m -16,-6 h 16"
@@ -2168,13 +2881,13 @@ var mockSymbolLibrary = {
2168
2881
  id: "vannefold-right",
2169
2882
  name: "Vannefold Block (Right)",
2170
2883
  category: "valve",
2171
- viewBox: { x: 0, y: 0, width: 67.785156, height: 88.5 },
2884
+ viewBox: { x: 0, y: 0, width: 33.892578, height: 44.25 },
2172
2885
  ports: [
2173
- { id: "right-inlet", x: 67.785156, y: 41.5, type: "control", direction: "in" },
2174
- { id: "left-outlet", x: 24.785156, y: 87.5, type: "control", direction: "out" }
2886
+ { id: "right-inlet", x: 33.892578, y: 20.75, type: "control", direction: "in" },
2887
+ { id: "left-outlet", x: 12.392578, y: 43.75, type: "control", direction: "out" }
2175
2888
  ],
2176
2889
  svgContent: `
2177
- <g>
2890
+ <g transform="scale(0.5)">
2178
2891
  <!-- Right inlet pipe flange -->
2179
2892
  <path
2180
2893
  d="m 63.785156,46.5 h 3 v -10 h -3 m -14,10 h -3 v -10 h 3 m 15,8 h -16 m 16,-6 h -16"
@@ -2231,16 +2944,16 @@ var mockSymbolLibrary = {
2231
2944
  id: "pixer",
2232
2945
  name: "Pixer",
2233
2946
  category: "pump",
2234
- viewBox: { x: 0, y: 0, width: 50.625, height: 46.6875 },
2947
+ viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },
2235
2948
  ports: [
2236
- { id: "right-outlet", x: 50.625, y: 30.9375, type: "control", direction: "out" },
2237
- { id: "left-outlet", x: 0, y: 30.9375, type: "control", direction: "out" },
2238
- { id: "left-inlet", x: 50.625, y: 19.6875, type: "control", direction: "in" },
2239
- { id: "right-inlet", x: 0, y: 19.6875, type: "control", direction: "in" },
2240
- { id: "top-inlet", x: 25.3125, y: 0, type: "control", direction: "in" }
2949
+ { id: "right-outlet", x: 25.3125, y: 15.46875, type: "control", direction: "out" },
2950
+ { id: "left-outlet", x: 0, y: 15.46875, type: "control", direction: "out" },
2951
+ { id: "left-inlet", x: 25.3125, y: 9.84375, type: "control", direction: "in" },
2952
+ { id: "right-inlet", x: 0, y: 9.84375, type: "control", direction: "in" },
2953
+ { id: "top-inlet", x: 12.65625, y: 0, type: "control", direction: "in" }
2241
2954
  ],
2242
2955
  svgContent: `
2243
- <g transform="scale(0.196875) translate(-195, -186)">
2956
+ <g transform="scale(0.0984375) translate(-195, -186)">
2244
2957
  <!-- Top mounting flange -->
2245
2958
  <rect
2246
2959
  width="141.313"
@@ -2505,16 +3218,16 @@ var mockSymbolLibrary = {
2505
3218
  id: "pixer-barb",
2506
3219
  name: "Pixer (Hose Barb)",
2507
3220
  category: "pump",
2508
- viewBox: { x: 0, y: 0, width: 50.625, height: 46.6875 },
3221
+ viewBox: { x: 0, y: 0, width: 25.3125, height: 23.34375 },
2509
3222
  ports: [
2510
- { id: "right-outlet", x: 50.625, y: 30.9375, type: "control", direction: "out" },
2511
- { id: "left-outlet", x: 0, y: 30.9375, type: "control", direction: "out" },
2512
- { id: "left-inlet", x: 50.625, y: 19.6875, type: "control", direction: "in" },
2513
- { id: "right-inlet", x: 0, y: 19.6875, type: "control", direction: "in" },
2514
- { id: "top-inlet", x: 25.3125, y: 0, type: "control", direction: "in" }
3223
+ { id: "right-outlet", x: 25.3125, y: 15.46875, type: "control", direction: "out" },
3224
+ { id: "left-outlet", x: 0, y: 15.46875, type: "control", direction: "out" },
3225
+ { id: "left-inlet", x: 25.3125, y: 9.84375, type: "control", direction: "in" },
3226
+ { id: "right-inlet", x: 0, y: 9.84375, type: "control", direction: "in" },
3227
+ { id: "top-inlet", x: 12.65625, y: 0, type: "control", direction: "in" }
2515
3228
  ],
2516
3229
  svgContent: `
2517
- <g transform="scale(0.196875) translate(-195, -186)">
3230
+ <g transform="scale(0.0984375) translate(-195, -186)">
2518
3231
  <!-- Hose barb ridges at top -->
2519
3232
  <rect
2520
3233
  width="20"
@@ -2796,15 +3509,15 @@ var mockSymbolLibrary = {
2796
3509
  id: "vessel-balloon",
2797
3510
  name: "Vessel",
2798
3511
  category: "vessel",
2799
- viewBox: { x: 0, y: 0, width: 50.625, height: 91 },
3512
+ viewBox: { x: 0, y: 0, width: 25.3125, height: 45.5 },
2800
3513
  ports: [
2801
- { id: "top", type: "inlet", x: 25.3125, y: 0 },
2802
- { id: "left", type: "inlet", x: 5, y: 39 },
2803
- { id: "right", type: "outlet", x: 45.625, y: 39 },
2804
- { id: "bottom", type: "outlet", x: 25.3125, y: 91 }
3514
+ { id: "top", type: "inlet", x: 12.65625, y: 0 },
3515
+ { id: "left", type: "inlet", x: 2.5, y: 19.5 },
3516
+ { id: "right", type: "outlet", x: 22.8125, y: 19.5 },
3517
+ { id: "bottom", type: "outlet", x: 12.65625, y: 45.5 }
2805
3518
  ],
2806
3519
  svgContent: `
2807
- <g transform="scale(0.196875) translate(-195, -100)">
3520
+ <g transform="scale(0.0984375) translate(-195, -100)">
2808
3521
  <!-- Top opening nozzle -->
2809
3522
  <rect
2810
3523
  width="65"
@@ -2869,19 +3582,53 @@ var mockSymbolLibrary = {
2869
3582
  tags: ["vessel", "storage", "tank", "balloon"]
2870
3583
  }
2871
3584
  },
3585
+ "hollow-fiber": {
3586
+ id: "hollow-fiber",
3587
+ name: "Hollow Fiber",
3588
+ category: "other",
3589
+ viewBox: { x: 0, y: 0, width: 30, height: 109 },
3590
+ ports: [
3591
+ { id: "top-left", type: "outlet", x: 10, y: 2.75 },
3592
+ { id: "top-right", type: "outlet", x: 24.5, y: 22.25 },
3593
+ { id: "bottom-left", type: "inlet", x: 10, y: 106.25 },
3594
+ { id: "bottom-right", type: "inlet", x: 24.5, y: 87.25 }
3595
+ ],
3596
+ svgContent: `
3597
+ <g transform="scale(0.5)">
3598
+ <!-- Top left flange -->
3599
+ <rect x="10.5" y="0.5" width="20" height="10" rx="1.5" ry="1.5" fill="var(--symbol-fill, #eaeaea)" stroke="var(--symbol-stroke, #000000)" />
3600
+
3601
+ <!-- Top right flange -->
3602
+ <rect x="39.5" y="39.5" width="20" height="10" rx="1.5" ry="1.5" fill="var(--symbol-fill, #eaeaea)" stroke="var(--symbol-stroke, #000000)" />
3603
+
3604
+ <!-- Bottom left flange -->
3605
+ <rect x="10.5" y="207.5" width="20" height="10" rx="1.5" ry="1.5" fill="var(--symbol-fill, #eaeaea)" stroke="var(--symbol-stroke, #000000)" />
3606
+
3607
+ <!-- Bottom right flange -->
3608
+ <rect x="39.5" y="169.5" width="20" height="10" rx="1.5" ry="1.5" fill="var(--symbol-fill, #eaeaea)" stroke="var(--symbol-stroke, #000000)" />
3609
+
3610
+ <!-- Main vessel body -->
3611
+ <rect x="0.5" y="9.5" width="40" height="199.5" rx="6" ry="6" fill="var(--symbol-fill, #eaeaea)" stroke="var(--symbol-stroke, #000000)" />
3612
+ </g>
3613
+ `,
3614
+ metadata: {
3615
+ description: "Hollow fiber filter module with multiple inlet/outlet connections",
3616
+ tags: ["filter", "hollow-fiber", "filtration", "separation", "membrane"]
3617
+ }
3618
+ },
2872
3619
  "psr-f": {
2873
3620
  id: "psr-f",
2874
3621
  name: "PSR F",
2875
3622
  category: "instrument",
2876
- viewBox: { x: 0, y: 0, width: 57, height: 57 },
3623
+ viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },
2877
3624
  ports: [
2878
- { id: "top", type: "signal", x: 28.5, y: 14.25 },
2879
- { id: "right", type: "signal", x: 42.75, y: 28.5 },
2880
- { id: "bottom", type: "signal", x: 28.5, y: 42.75 },
2881
- { id: "left", type: "signal", x: 14.25, y: 28.5 }
3625
+ { id: "top", type: "signal", x: 14.25, y: 7.125 },
3626
+ { id: "right", type: "signal", x: 21.375, y: 14.25 },
3627
+ { id: "bottom", type: "signal", x: 14.25, y: 21.375 },
3628
+ { id: "left", type: "signal", x: 7.125, y: 14.25 }
2882
3629
  ],
2883
3630
  svgContent: `
2884
- <g transform="scale(0.5) translate(28.5, 28.5)">
3631
+ <g transform="scale(0.25) translate(28.5, 28.5)">
2885
3632
  <!-- Circle background -->
2886
3633
  <ellipse
2887
3634
  cx="28.5"
@@ -2915,15 +3662,15 @@ var mockSymbolLibrary = {
2915
3662
  id: "psr-p",
2916
3663
  name: "PSR P",
2917
3664
  category: "instrument",
2918
- viewBox: { x: 0, y: 0, width: 57, height: 57 },
3665
+ viewBox: { x: 0, y: 0, width: 28.5, height: 28.5 },
2919
3666
  ports: [
2920
- { id: "top", type: "signal", x: 28.5, y: 14.25 },
2921
- { id: "right", type: "signal", x: 42.75, y: 28.5 },
2922
- { id: "bottom", type: "signal", x: 28.5, y: 42.75 },
2923
- { id: "left", type: "signal", x: 14.25, y: 28.5 }
3667
+ { id: "top", type: "signal", x: 14.25, y: 7.125 },
3668
+ { id: "right", type: "signal", x: 21.375, y: 14.25 },
3669
+ { id: "bottom", type: "signal", x: 14.25, y: 21.375 },
3670
+ { id: "left", type: "signal", x: 7.125, y: 14.25 }
2924
3671
  ],
2925
3672
  svgContent: `
2926
- <g transform="scale(0.5) translate(28.5, 28.5)">
3673
+ <g transform="scale(0.25) translate(28.5, 28.5)">
2927
3674
  <!-- Circle background -->
2928
3675
  <ellipse
2929
3676
  cx="28.5"
@@ -2954,15 +3701,26 @@ var mockSymbolLibrary = {
2954
3701
  }
2955
3702
  }
2956
3703
  };
3704
+ var customSymbols = {};
3705
+ function registerSymbol(symbol) {
3706
+ customSymbols[symbol.id] = symbol;
3707
+ }
3708
+ function registerSymbols(symbols) {
3709
+ if (Array.isArray(symbols)) {
3710
+ symbols.forEach((symbol) => registerSymbol(symbol));
3711
+ } else {
3712
+ Object.values(symbols).forEach((symbol) => registerSymbol(symbol));
3713
+ }
3714
+ }
2957
3715
  function getSymbolDefinition(symbolId) {
2958
- return mockSymbolLibrary[symbolId];
3716
+ return customSymbols[symbolId] ?? mockSymbolLibrary[symbolId];
2959
3717
  }
2960
3718
  function getAvailableSymbols() {
2961
- return Object.keys(mockSymbolLibrary);
3719
+ return [...Object.keys(customSymbols), ...Object.keys(mockSymbolLibrary)];
2962
3720
  }
2963
3721
 
2964
3722
  // src/components/PIDCanvas/NodeRenderer.tsx
2965
- var import_jsx_runtime13 = require("react/jsx-runtime");
3723
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2966
3724
  function NodeRenderer({
2967
3725
  nodes: nodes2,
2968
3726
  selectedNodeIds,
@@ -2973,13 +3731,13 @@ function NodeRenderer({
2973
3731
  dragOffset = { x: 0, y: 0 },
2974
3732
  enableDrag = false
2975
3733
  }) {
2976
- 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) => {
2977
3735
  const symbol = getSymbolDefinition(node.symbolId);
2978
3736
  const isSelected = selectedNodeIds?.has(node.id) ?? false;
2979
3737
  const isDragging = draggingNodeId === node.id;
2980
3738
  if (!symbol) {
2981
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { children: [
2982
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3739
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("g", { children: [
3740
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2983
3741
  "circle",
2984
3742
  {
2985
3743
  cx: node.transform.x,
@@ -2991,7 +3749,7 @@ function NodeRenderer({
2991
3749
  strokeWidth: "2"
2992
3750
  }
2993
3751
  ),
2994
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3752
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2995
3753
  "text",
2996
3754
  {
2997
3755
  x: node.transform.x,
@@ -3048,7 +3806,7 @@ function NodeRenderer({
3048
3806
  };
3049
3807
  const fillColor = node.customColors?.fill ?? getStatusColor(node.status);
3050
3808
  const strokeColor = node.customColors?.stroke ?? "rgb(0, 0, 0)";
3051
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3809
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3052
3810
  "g",
3053
3811
  {
3054
3812
  "data-node-id": node.id,
@@ -3081,7 +3839,7 @@ function NodeRenderer({
3081
3839
  }
3082
3840
  },
3083
3841
  children: [
3084
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3842
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3085
3843
  "rect",
3086
3844
  {
3087
3845
  x: "-5",
@@ -3096,7 +3854,7 @@ function NodeRenderer({
3096
3854
  pointerEvents: "none"
3097
3855
  }
3098
3856
  ),
3099
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3857
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3100
3858
  "g",
3101
3859
  {
3102
3860
  dangerouslySetInnerHTML: { __html: symbol.svgContent },
@@ -3104,7 +3862,7 @@ function NodeRenderer({
3104
3862
  style: { pointerEvents: "auto" }
3105
3863
  }
3106
3864
  ),
3107
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3865
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3108
3866
  "text",
3109
3867
  {
3110
3868
  x: symbol.viewBox.width / 2 + (node.labelOffset?.x ?? 0),
@@ -3125,7 +3883,7 @@ function NodeRenderer({
3125
3883
  }
3126
3884
 
3127
3885
  // src/components/PIDCanvas/PipeRenderer.tsx
3128
- var import_jsx_runtime14 = require("react/jsx-runtime");
3886
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3129
3887
  function PipeRenderer({
3130
3888
  pipes,
3131
3889
  selectedPipeIds,
@@ -3140,7 +3898,7 @@ function PipeRenderer({
3140
3898
  waypointDragOffset,
3141
3899
  onPipeSegmentClick
3142
3900
  }) {
3143
- 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) => {
3144
3902
  const isSelected = selectedPipeIds?.has(pipe.id) ?? false;
3145
3903
  const isEditing = enableWaypointEditing && editingPipeId === pipe.id;
3146
3904
  const showEditingControls = enableWaypointEditing && isSelected;
@@ -3159,7 +3917,7 @@ function PipeRenderer({
3159
3917
  const strokeWidth = isSelected ? (pipeStyle.strokeWidth || 3) + 2 : pipeStyle.strokeWidth || 3;
3160
3918
  const strokeDasharray = pipeStyle.strokeDasharray;
3161
3919
  const opacity = pipeStyle.opacity !== void 0 ? pipeStyle.opacity : 1;
3162
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3920
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3163
3921
  "g",
3164
3922
  {
3165
3923
  "data-pipe-id": pipe.id,
@@ -3172,8 +3930,8 @@ function PipeRenderer({
3172
3930
  cursor: onPipeClick ? "pointer" : "default"
3173
3931
  },
3174
3932
  children: [
3175
- !enableWaypointEditing && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3176
- 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)(
3177
3935
  "polyline",
3178
3936
  {
3179
3937
  points: pointsString,
@@ -3185,7 +3943,7 @@ function PipeRenderer({
3185
3943
  pointerEvents: "stroke"
3186
3944
  }
3187
3945
  ),
3188
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3946
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3189
3947
  "polyline",
3190
3948
  {
3191
3949
  points: pointsString,
@@ -3209,7 +3967,7 @@ function PipeRenderer({
3209
3967
  const waypointSize = isSelectedWaypoint ? 8 : isDragging ? 8 : showEditingControls ? 6 : isSelected ? 3 : 2;
3210
3968
  const waypointOpacity = isSelectedWaypoint ? 1 : isDragging ? 1 : showEditingControls ? 0.9 : isSelected ? 0.8 : 0.5;
3211
3969
  const waypointColor = isSelectedWaypoint ? "#3b82f6" : isEndpoint && showEditingControls ? "#f59e0b" : isDragging ? "#10b981" : stroke;
3212
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3970
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3213
3971
  "circle",
3214
3972
  {
3215
3973
  cx: point.x,
@@ -3246,12 +4004,12 @@ function PipeRenderer({
3246
4004
  `${pipe.id}-point-${idx}`
3247
4005
  );
3248
4006
  }),
3249
- 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) => {
3250
4008
  const nextPoint = displayPoints[idx + 1];
3251
4009
  const midX = (point.x + nextPoint.x) / 2;
3252
4010
  const midY = (point.y + nextPoint.y) / 2;
3253
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("g", { children: [
3254
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4011
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("g", { children: [
4012
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3255
4013
  "circle",
3256
4014
  {
3257
4015
  cx: midX,
@@ -3267,7 +4025,7 @@ function PipeRenderer({
3267
4025
  }
3268
4026
  }
3269
4027
  ),
3270
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4028
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3271
4029
  "circle",
3272
4030
  {
3273
4031
  cx: midX,
@@ -3283,7 +4041,7 @@ function PipeRenderer({
3283
4041
  )
3284
4042
  ] }, `${pipe.id}-segment-${idx}`);
3285
4043
  }) }),
3286
- 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)(
3287
4045
  "text",
3288
4046
  {
3289
4047
  x: pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].x + (pipe.labelOffset?.x ?? 0),
@@ -3305,7 +4063,7 @@ function PipeRenderer({
3305
4063
  }
3306
4064
 
3307
4065
  // src/components/PIDCanvas/DataOverlay.tsx
3308
- var import_jsx_runtime15 = require("react/jsx-runtime");
4066
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3309
4067
  function DataOverlay({
3310
4068
  nodeId,
3311
4069
  x,
@@ -3325,7 +4083,7 @@ function DataOverlay({
3325
4083
  const offsetX = display?.offsetX ?? 0;
3326
4084
  const offsetY = display?.offsetY ?? 0;
3327
4085
  if (!showValue) {
3328
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, {});
4086
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, {});
3329
4087
  }
3330
4088
  const formatValue = (val) => {
3331
4089
  if (typeof val === "number") {
@@ -3343,9 +4101,14 @@ function DataOverlay({
3343
4101
  fault: { bg: "#dc2626", text: "#ffffff", border: "#b91c1c" },
3344
4102
  off: { bg: "#6b7280", text: "#ffffff", border: "#4b5563" }
3345
4103
  };
3346
- const customColor = telemetry.statusColors?.[value.status];
3347
- const defaultColorSet = defaultStatusColors[value.status] || defaultStatusColors.normal;
3348
- 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();
3349
4112
  const colors = backgroundColor && backgroundColor !== "status" ? { bg: backgroundColor, text: textColor || "#ffffff", border: backgroundColor } : statusColors;
3350
4113
  const finalTextColor = textColor || colors.text;
3351
4114
  const formattedValue = formatValue(value.value);
@@ -3372,8 +4135,8 @@ function DataOverlay({
3372
4135
  const overlayWidth = 80 * scale;
3373
4136
  const displayX = x + offsetX;
3374
4137
  const displayY = y + offsetY;
3375
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
3376
- /* @__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)(
3377
4140
  "rect",
3378
4141
  {
3379
4142
  x: displayX - overlayWidth / 2,
@@ -3388,7 +4151,7 @@ function DataOverlay({
3388
4151
  filter: "drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
3389
4152
  }
3390
4153
  ),
3391
- label && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4154
+ label && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3392
4155
  "text",
3393
4156
  {
3394
4157
  x: displayX,
@@ -3401,7 +4164,7 @@ function DataOverlay({
3401
4164
  children: label
3402
4165
  }
3403
4166
  ),
3404
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4167
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3405
4168
  "text",
3406
4169
  {
3407
4170
  x: displayX,
@@ -3414,7 +4177,7 @@ function DataOverlay({
3414
4177
  children: displayText
3415
4178
  }
3416
4179
  ),
3417
- showStatus && value.status !== "normal" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4180
+ showStatus && value.status !== "normal" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3418
4181
  "circle",
3419
4182
  {
3420
4183
  cx: displayX + 35 * scale,
@@ -3422,7 +4185,7 @@ function DataOverlay({
3422
4185
  r: 3 * scale,
3423
4186
  fill: finalTextColor,
3424
4187
  opacity: "0.9",
3425
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4188
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3426
4189
  "animate",
3427
4190
  {
3428
4191
  attributeName: "opacity",
@@ -3433,7 +4196,7 @@ function DataOverlay({
3433
4196
  )
3434
4197
  }
3435
4198
  ),
3436
- 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)(
3437
4200
  "text",
3438
4201
  {
3439
4202
  x: displayX,
@@ -3450,7 +4213,7 @@ function DataOverlay({
3450
4213
  ]
3451
4214
  }
3452
4215
  ),
3453
- 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)(
3454
4217
  "path",
3455
4218
  {
3456
4219
  d: sparklinePath,
@@ -3466,8 +4229,8 @@ function DataOverlay({
3466
4229
  }
3467
4230
 
3468
4231
  // src/components/PIDCanvas/PositionPanel.tsx
3469
- var import_react8 = require("react");
3470
- var import_jsx_runtime16 = require("react/jsx-runtime");
4232
+ var import_react9 = require("react");
4233
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3471
4234
  function PositionPanel({
3472
4235
  selectedNode,
3473
4236
  selectedWaypoint,
@@ -3475,9 +4238,9 @@ function PositionPanel({
3475
4238
  onWaypointPositionChange,
3476
4239
  position = "top-right"
3477
4240
  }) {
3478
- const [x, setX] = (0, import_react8.useState)("");
3479
- const [y, setY] = (0, import_react8.useState)("");
3480
- (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)(() => {
3481
4244
  if (selectedNode) {
3482
4245
  setX(selectedNode.transform.x.toFixed(2));
3483
4246
  setY(selectedNode.transform.y.toFixed(2));
@@ -3536,7 +4299,7 @@ function PositionPanel({
3536
4299
  "bottom-left": { bottom: 10, left: 10 },
3537
4300
  "bottom-right": { bottom: 10, right: 10 }
3538
4301
  };
3539
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4302
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3540
4303
  "div",
3541
4304
  {
3542
4305
  style: {
@@ -3553,11 +4316,11 @@ function PositionPanel({
3553
4316
  fontSize: "13px"
3554
4317
  },
3555
4318
  children: [
3556
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { marginBottom: "8px", fontWeight: 600, color: "#333" }, children: "Position" }),
3557
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
3558
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3559
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: { width: "16px", color: "#666" }, children: "X:" }),
3560
- /* @__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)(
3561
4324
  "input",
3562
4325
  {
3563
4326
  type: "number",
@@ -3575,9 +4338,9 @@ function PositionPanel({
3575
4338
  }
3576
4339
  )
3577
4340
  ] }),
3578
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3579
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: { width: "16px", color: "#666" }, children: "Y:" }),
3580
- /* @__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)(
3581
4344
  "input",
3582
4345
  {
3583
4346
  type: "number",
@@ -3596,14 +4359,14 @@ function PositionPanel({
3596
4359
  )
3597
4360
  ] })
3598
4361
  ] }),
3599
- /* @__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)" })
3600
4363
  ]
3601
4364
  }
3602
4365
  );
3603
4366
  }
3604
4367
 
3605
4368
  // src/diagram/hooks/useViewBox.ts
3606
- var import_react9 = require("react");
4369
+ var import_react10 = require("react");
3607
4370
  function useViewBox(options) {
3608
4371
  const {
3609
4372
  initialViewBox,
@@ -3614,16 +4377,16 @@ function useViewBox(options) {
3614
4377
  zoomSensitivity = 1e-3,
3615
4378
  allowLeftClickPan = true
3616
4379
  } = options;
3617
- const [viewBox, setViewBox] = (0, import_react9.useState)(initialViewBox);
3618
- const svgRef = (0, import_react9.useRef)(null);
3619
- const isPanningRef = (0, import_react9.useRef)(false);
3620
- const lastMousePosRef = (0, import_react9.useRef)({ x: 0, y: 0 });
3621
- const spaceKeyDownRef = (0, import_react9.useRef)(false);
3622
- const lastTouchDistanceRef = (0, import_react9.useRef)(null);
3623
- 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)(() => {
3624
4387
  setViewBox(initialViewBox);
3625
4388
  }, [initialViewBox]);
3626
- const zoomIn = (0, import_react9.useCallback)(() => {
4389
+ const zoomIn = (0, import_react10.useCallback)(() => {
3627
4390
  setViewBox((prev) => {
3628
4391
  const currentZoom = initialViewBox.width / prev.width;
3629
4392
  const newZoom = Math.min(1 / minZoom, currentZoom * 1.25);
@@ -3640,7 +4403,7 @@ function useViewBox(options) {
3640
4403
  };
3641
4404
  });
3642
4405
  }, [initialViewBox, minZoom]);
3643
- const zoomOut = (0, import_react9.useCallback)(() => {
4406
+ const zoomOut = (0, import_react10.useCallback)(() => {
3644
4407
  setViewBox((prev) => {
3645
4408
  const currentZoom = initialViewBox.width / prev.width;
3646
4409
  const newZoom = Math.max(1 / maxZoom, currentZoom * 0.8);
@@ -3657,7 +4420,7 @@ function useViewBox(options) {
3657
4420
  };
3658
4421
  });
3659
4422
  }, [initialViewBox, maxZoom]);
3660
- const fitToContent = (0, import_react9.useCallback)((bounds, padding = 50) => {
4423
+ const fitToContent = (0, import_react10.useCallback)((bounds, padding = 50) => {
3661
4424
  const contentWidth = bounds.maxX - bounds.minX;
3662
4425
  const contentHeight = bounds.maxY - bounds.minY;
3663
4426
  if (contentWidth === 0 || contentHeight === 0) {
@@ -3685,7 +4448,7 @@ function useViewBox(options) {
3685
4448
  height: newHeight
3686
4449
  });
3687
4450
  }, [initialViewBox]);
3688
- (0, import_react9.useEffect)(() => {
4451
+ (0, import_react10.useEffect)(() => {
3689
4452
  if (!enableZoom || !svgRef.current) return;
3690
4453
  const svg = svgRef.current;
3691
4454
  const handleWheel = (e) => {
@@ -3718,7 +4481,7 @@ function useViewBox(options) {
3718
4481
  svg.addEventListener("wheel", handleWheel, { passive: false });
3719
4482
  return () => svg.removeEventListener("wheel", handleWheel);
3720
4483
  }, [enableZoom, zoomSensitivity, minZoom, initialViewBox]);
3721
- (0, import_react9.useEffect)(() => {
4484
+ (0, import_react10.useEffect)(() => {
3722
4485
  if (!enablePan || !svgRef.current) return;
3723
4486
  const svg = svgRef.current;
3724
4487
  const handleMouseDown = (e) => {
@@ -3794,7 +4557,7 @@ function useViewBox(options) {
3794
4557
  window.removeEventListener("keyup", handleKeyUp);
3795
4558
  };
3796
4559
  }, [enablePan, allowLeftClickPan]);
3797
- (0, import_react9.useEffect)(() => {
4560
+ (0, import_react10.useEffect)(() => {
3798
4561
  if (!svgRef.current) return;
3799
4562
  if (!enablePan && !enableZoom) return;
3800
4563
  const svg = svgRef.current;
@@ -3885,7 +4648,7 @@ function useViewBox(options) {
3885
4648
  svg.removeEventListener("touchend", handleTouchEnd);
3886
4649
  };
3887
4650
  }, [enablePan, enableZoom, minZoom, initialViewBox]);
3888
- const screenToWorld = (0, import_react9.useCallback)(
4651
+ const screenToWorld = (0, import_react10.useCallback)(
3889
4652
  (screenX, screenY) => {
3890
4653
  const svg = svgRef.current;
3891
4654
  if (!svg) return { x: screenX, y: screenY };
@@ -3924,15 +4687,15 @@ function useViewBox(options) {
3924
4687
  }
3925
4688
 
3926
4689
  // src/diagram/hooks/useSelection.ts
3927
- var import_react10 = require("react");
4690
+ var import_react11 = require("react");
3928
4691
  function useSelection(options = {}) {
3929
4692
  const {
3930
4693
  multiSelect = true,
3931
4694
  initialSelection = { selectedNodeIds: /* @__PURE__ */ new Set(), selectedPipeIds: /* @__PURE__ */ new Set() },
3932
4695
  onSelectionChange
3933
4696
  } = options;
3934
- const [selection, setSelection] = (0, import_react10.useState)(initialSelection);
3935
- const notifyChange = (0, import_react10.useCallback)(
4697
+ const [selection, setSelection] = (0, import_react11.useState)(initialSelection);
4698
+ const notifyChange = (0, import_react11.useCallback)(
3936
4699
  (newSelection) => {
3937
4700
  if (onSelectionChange) {
3938
4701
  onSelectionChange(newSelection);
@@ -3940,15 +4703,15 @@ function useSelection(options = {}) {
3940
4703
  },
3941
4704
  [onSelectionChange]
3942
4705
  );
3943
- const isNodeSelected = (0, import_react10.useCallback)(
4706
+ const isNodeSelected = (0, import_react11.useCallback)(
3944
4707
  (nodeId) => selection.selectedNodeIds.has(nodeId),
3945
4708
  [selection.selectedNodeIds]
3946
4709
  );
3947
- const isPipeSelected = (0, import_react10.useCallback)(
4710
+ const isPipeSelected = (0, import_react11.useCallback)(
3948
4711
  (pipeId) => selection.selectedPipeIds.has(pipeId),
3949
4712
  [selection.selectedPipeIds]
3950
4713
  );
3951
- const selectNode = (0, import_react10.useCallback)(
4714
+ const selectNode = (0, import_react11.useCallback)(
3952
4715
  (nodeId, isMultiSelect = false) => {
3953
4716
  setSelection((prev) => {
3954
4717
  const newNodeIds = new Set(isMultiSelect && multiSelect ? prev.selectedNodeIds : []);
@@ -3967,7 +4730,7 @@ function useSelection(options = {}) {
3967
4730
  },
3968
4731
  [multiSelect, notifyChange]
3969
4732
  );
3970
- const selectPipe = (0, import_react10.useCallback)(
4733
+ const selectPipe = (0, import_react11.useCallback)(
3971
4734
  (pipeId, isMultiSelect = false) => {
3972
4735
  setSelection((prev) => {
3973
4736
  const newPipeIds = new Set(isMultiSelect && multiSelect ? prev.selectedPipeIds : []);
@@ -3986,7 +4749,7 @@ function useSelection(options = {}) {
3986
4749
  },
3987
4750
  [multiSelect, notifyChange]
3988
4751
  );
3989
- const clearSelection = (0, import_react10.useCallback)(() => {
4752
+ const clearSelection = (0, import_react11.useCallback)(() => {
3990
4753
  const newSelection = {
3991
4754
  selectedNodeIds: /* @__PURE__ */ new Set(),
3992
4755
  selectedPipeIds: /* @__PURE__ */ new Set()
@@ -3994,7 +4757,7 @@ function useSelection(options = {}) {
3994
4757
  setSelection(newSelection);
3995
4758
  notifyChange(newSelection);
3996
4759
  }, [notifyChange]);
3997
- const selectNodes = (0, import_react10.useCallback)(
4760
+ const selectNodes = (0, import_react11.useCallback)(
3998
4761
  (nodeIds) => {
3999
4762
  const newSelection = {
4000
4763
  selectedNodeIds: new Set(nodeIds),
@@ -4005,7 +4768,7 @@ function useSelection(options = {}) {
4005
4768
  },
4006
4769
  [notifyChange]
4007
4770
  );
4008
- const selectPipes = (0, import_react10.useCallback)(
4771
+ const selectPipes = (0, import_react11.useCallback)(
4009
4772
  (pipeIds) => {
4010
4773
  const newSelection = {
4011
4774
  selectedNodeIds: /* @__PURE__ */ new Set(),
@@ -4029,23 +4792,23 @@ function useSelection(options = {}) {
4029
4792
  }
4030
4793
 
4031
4794
  // src/diagram/hooks/useTelemetry.ts
4032
- var import_react11 = require("react");
4795
+ var import_react12 = require("react");
4033
4796
  function useTelemetry(options = {}) {
4034
4797
  const {
4035
4798
  initialBindings = [],
4036
4799
  onTelemetryChange,
4037
4800
  refreshInterval = 0
4038
4801
  } = options;
4039
- const [telemetry, setTelemetry] = (0, import_react11.useState)(() => {
4802
+ const [telemetry, setTelemetry] = (0, import_react12.useState)(() => {
4040
4803
  const map = /* @__PURE__ */ new Map();
4041
4804
  initialBindings.forEach((binding) => {
4042
4805
  map.set(binding.nodeId, binding);
4043
4806
  });
4044
4807
  return map;
4045
4808
  });
4046
- const telemetryRef = (0, import_react11.useRef)(telemetry);
4809
+ const telemetryRef = (0, import_react12.useRef)(telemetry);
4047
4810
  telemetryRef.current = telemetry;
4048
- const notifyChange = (0, import_react11.useCallback)(
4811
+ const notifyChange = (0, import_react12.useCallback)(
4049
4812
  (newTelemetry) => {
4050
4813
  if (onTelemetryChange) {
4051
4814
  onTelemetryChange(newTelemetry);
@@ -4053,7 +4816,7 @@ function useTelemetry(options = {}) {
4053
4816
  },
4054
4817
  [onTelemetryChange]
4055
4818
  );
4056
- const updateTelemetry = (0, import_react11.useCallback)(
4819
+ const updateTelemetry = (0, import_react12.useCallback)(
4057
4820
  (nodeId, value) => {
4058
4821
  setTelemetry((prev) => {
4059
4822
  const newMap = new Map(prev);
@@ -4083,7 +4846,7 @@ function useTelemetry(options = {}) {
4083
4846
  },
4084
4847
  [notifyChange]
4085
4848
  );
4086
- const updateTelemetryBatch = (0, import_react11.useCallback)(
4849
+ const updateTelemetryBatch = (0, import_react12.useCallback)(
4087
4850
  (updates) => {
4088
4851
  setTelemetry((prev) => {
4089
4852
  const newMap = new Map(prev);
@@ -4137,16 +4900,16 @@ function useTelemetry(options = {}) {
4137
4900
  },
4138
4901
  [notifyChange]
4139
4902
  );
4140
- const getTelemetry = (0, import_react11.useCallback)(
4903
+ const getTelemetry = (0, import_react12.useCallback)(
4141
4904
  (nodeId) => telemetryRef.current.get(nodeId),
4142
4905
  []
4143
4906
  );
4144
- const clearTelemetry = (0, import_react11.useCallback)(() => {
4907
+ const clearTelemetry = (0, import_react12.useCallback)(() => {
4145
4908
  const newMap = /* @__PURE__ */ new Map();
4146
4909
  setTelemetry(newMap);
4147
4910
  notifyChange(newMap);
4148
4911
  }, [notifyChange]);
4149
- const setBindings = (0, import_react11.useCallback)(
4912
+ const setBindings = (0, import_react12.useCallback)(
4150
4913
  (bindings) => {
4151
4914
  const newMap = /* @__PURE__ */ new Map();
4152
4915
  bindings.forEach((binding) => {
@@ -4157,7 +4920,7 @@ function useTelemetry(options = {}) {
4157
4920
  },
4158
4921
  [notifyChange]
4159
4922
  );
4160
- (0, import_react11.useEffect)(() => {
4923
+ (0, import_react12.useEffect)(() => {
4161
4924
  if (refreshInterval <= 0) return;
4162
4925
  const intervalId = setInterval(() => {
4163
4926
  notifyChange(telemetryRef.current);
@@ -4175,10 +4938,10 @@ function useTelemetry(options = {}) {
4175
4938
  }
4176
4939
 
4177
4940
  // src/diagram/hooks/useTelemetryStatus.ts
4178
- var import_react12 = require("react");
4941
+ var import_react13 = require("react");
4179
4942
  function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4180
4943
  const { enabled = true, mapStatus } = options;
4181
- (0, import_react12.useEffect)(() => {
4944
+ (0, import_react13.useEffect)(() => {
4182
4945
  if (!enabled) return;
4183
4946
  const defaultMapStatus = (telemetryStatus) => {
4184
4947
  switch (telemetryStatus) {
@@ -4217,7 +4980,7 @@ function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4217
4980
  }
4218
4981
 
4219
4982
  // src/diagram/hooks/useDrag.ts
4220
- var import_react13 = require("react");
4983
+ var import_react14 = require("react");
4221
4984
  function useDrag(options = {}) {
4222
4985
  const {
4223
4986
  onDragStart,
@@ -4227,23 +4990,23 @@ function useDrag(options = {}) {
4227
4990
  dragThreshold = 3,
4228
4991
  screenToWorld = (x, y) => ({ x, y })
4229
4992
  } = options;
4230
- const [dragState, setDragState] = (0, import_react13.useState)({
4993
+ const [dragState, setDragState] = (0, import_react14.useState)({
4231
4994
  isDragging: false,
4232
4995
  startPosition: null,
4233
4996
  offset: { x: 0, y: 0 },
4234
4997
  draggedId: null
4235
4998
  });
4236
- const [isListening, setIsListening] = (0, import_react13.useState)(false);
4237
- const dragStartRef = (0, import_react13.useRef)(null);
4238
- const hasMovedRef = (0, import_react13.useRef)(false);
4239
- 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)(
4240
5003
  (value) => {
4241
5004
  if (snapToGrid <= 0) return value;
4242
5005
  return Math.round(value / snapToGrid) * snapToGrid;
4243
5006
  },
4244
5007
  [snapToGrid]
4245
5008
  );
4246
- const handleMouseMove = (0, import_react13.useCallback)(
5009
+ const handleMouseMove = (0, import_react14.useCallback)(
4247
5010
  (event) => {
4248
5011
  if (!dragStartRef.current) return;
4249
5012
  const { id, screenStart: _screenStart, itemPosition, clickOffset } = dragStartRef.current;
@@ -4284,7 +5047,7 @@ function useDrag(options = {}) {
4284
5047
  },
4285
5048
  [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]
4286
5049
  );
4287
- const handleMouseUp = (0, import_react13.useCallback)(
5050
+ const handleMouseUp = (0, import_react14.useCallback)(
4288
5051
  (event) => {
4289
5052
  if (!dragStartRef.current || !hasMovedRef.current) {
4290
5053
  dragStartRef.current = null;
@@ -4321,7 +5084,7 @@ function useDrag(options = {}) {
4321
5084
  },
4322
5085
  [screenToWorld, applySnap, onDragEnd]
4323
5086
  );
4324
- const handleTouchMove = (0, import_react13.useCallback)(
5087
+ const handleTouchMove = (0, import_react14.useCallback)(
4325
5088
  (event) => {
4326
5089
  if (!dragStartRef.current || event.touches.length !== 1) return;
4327
5090
  const touch = event.touches[0];
@@ -4364,7 +5127,7 @@ function useDrag(options = {}) {
4364
5127
  },
4365
5128
  [screenToWorld, dragThreshold, applySnap, onDragStart, onDragMove]
4366
5129
  );
4367
- const handleTouchEnd = (0, import_react13.useCallback)(
5130
+ const handleTouchEnd = (0, import_react14.useCallback)(
4368
5131
  (event) => {
4369
5132
  if (!dragStartRef.current || !hasMovedRef.current) {
4370
5133
  dragStartRef.current = null;
@@ -4402,7 +5165,7 @@ function useDrag(options = {}) {
4402
5165
  },
4403
5166
  [screenToWorld, applySnap, onDragEnd]
4404
5167
  );
4405
- (0, import_react13.useEffect)(() => {
5168
+ (0, import_react14.useEffect)(() => {
4406
5169
  if (!isListening) return;
4407
5170
  document.addEventListener("mousemove", handleMouseMove);
4408
5171
  document.addEventListener("mouseup", handleMouseUp);
@@ -4417,7 +5180,7 @@ function useDrag(options = {}) {
4417
5180
  document.removeEventListener("touchcancel", handleTouchEnd);
4418
5181
  };
4419
5182
  }, [isListening, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
4420
- const startDrag = (0, import_react13.useCallback)(
5183
+ const startDrag = (0, import_react14.useCallback)(
4421
5184
  (id, event, itemPosition) => {
4422
5185
  event.stopPropagation();
4423
5186
  let clientX;
@@ -4447,7 +5210,7 @@ function useDrag(options = {}) {
4447
5210
  },
4448
5211
  [screenToWorld]
4449
5212
  );
4450
- const cancelDrag = (0, import_react13.useCallback)(() => {
5213
+ const cancelDrag = (0, import_react14.useCallback)(() => {
4451
5214
  dragStartRef.current = null;
4452
5215
  hasMovedRef.current = false;
4453
5216
  setIsListening(false);
@@ -4458,7 +5221,7 @@ function useDrag(options = {}) {
4458
5221
  draggedId: null
4459
5222
  });
4460
5223
  }, []);
4461
- const isDraggingItem = (0, import_react13.useCallback)(
5224
+ const isDraggingItem = (0, import_react14.useCallback)(
4462
5225
  (id) => dragState.isDragging && dragState.draggedId === id,
4463
5226
  [dragState.isDragging, dragState.draggedId]
4464
5227
  );
@@ -4471,7 +5234,7 @@ function useDrag(options = {}) {
4471
5234
  }
4472
5235
 
4473
5236
  // src/diagram/hooks/useKeyboardShortcuts.ts
4474
- var import_react14 = require("react");
5237
+ var import_react15 = require("react");
4475
5238
  function useKeyboardShortcuts(options) {
4476
5239
  const {
4477
5240
  onDelete,
@@ -4482,7 +5245,7 @@ function useKeyboardShortcuts(options) {
4482
5245
  onSelectAll,
4483
5246
  enabled = true
4484
5247
  } = options;
4485
- (0, import_react14.useEffect)(() => {
5248
+ (0, import_react15.useEffect)(() => {
4486
5249
  if (!enabled) return;
4487
5250
  const handleKeyDown = (event) => {
4488
5251
  const target = event.target;
@@ -4591,7 +5354,7 @@ function nodeHasPort(node, portId) {
4591
5354
  }
4592
5355
 
4593
5356
  // src/components/PIDCanvas/PIDCanvas.tsx
4594
- var import_jsx_runtime17 = require("react/jsx-runtime");
5357
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4595
5358
  function PIDCanvas({
4596
5359
  diagram,
4597
5360
  className = "",
@@ -4618,21 +5381,21 @@ function PIDCanvas({
4618
5381
  telemetry,
4619
5382
  ...props
4620
5383
  }) {
4621
- const [localDiagram, setLocalDiagram] = (0, import_react15.useState)(diagram);
4622
- const [dragOverPosition, setDragOverPosition] = (0, import_react15.useState)(null);
4623
- const [isConnecting, setIsConnecting] = (0, import_react15.useState)(false);
4624
- const [connectionSource, setConnectionSource] = (0, import_react15.useState)(null);
4625
- const [connectionSourcePort, setConnectionSourcePort] = (0, import_react15.useState)(null);
4626
- const [connectionCursor, setConnectionCursor] = (0, import_react15.useState)(null);
4627
- const [hoveredNode, setHoveredNode] = (0, import_react15.useState)(null);
4628
- const [hoveredPort, setHoveredPort] = (0, import_react15.useState)(null);
4629
- const [selectedWaypoint, setSelectedWaypoint] = (0, import_react15.useState)(null);
4630
- const historyStack = (0, import_react15.useRef)([]);
4631
- const redoStack = (0, import_react15.useRef)([]);
4632
- const isUndoRedoAction = (0, import_react15.useRef)(false);
4633
- const isInternalChange = (0, import_react15.useRef)(false);
4634
- const lastInternalDiagram = (0, import_react15.useRef)(null);
4635
- (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)(() => {
4636
5399
  const isExternal = !isInternalChange.current && diagram !== lastInternalDiagram.current;
4637
5400
  if (isExternal) {
4638
5401
  setLocalDiagram(diagram);
@@ -4654,7 +5417,7 @@ function PIDCanvas({
4654
5417
  enablePan: features.pan ?? true,
4655
5418
  enableZoom: features.zoom ?? true
4656
5419
  });
4657
- const calculateContentBounds = (0, import_react15.useCallback)(() => {
5420
+ const calculateContentBounds = (0, import_react16.useCallback)(() => {
4658
5421
  const nodes2 = localDiagram.nodes.filter((n) => n.visible !== false);
4659
5422
  if (nodes2.length === 0) {
4660
5423
  return null;
@@ -4677,7 +5440,7 @@ function PIDCanvas({
4677
5440
  });
4678
5441
  return { minX, minY, maxX, maxY };
4679
5442
  }, [localDiagram.nodes]);
4680
- (0, import_react15.useEffect)(() => {
5443
+ (0, import_react16.useEffect)(() => {
4681
5444
  if (onMounted) {
4682
5445
  const controls = {
4683
5446
  fitToContent: (padding = 50) => {
@@ -4718,8 +5481,8 @@ function PIDCanvas({
4718
5481
  });
4719
5482
  const dragEnabled = features.dragNodes ?? false;
4720
5483
  const selectionEnabled = features.selection ?? false;
4721
- const containerRef = (0, import_react15.useRef)(null);
4722
- const pushToHistory = (0, import_react15.useCallback)((currentDiagram) => {
5484
+ const containerRef = (0, import_react16.useRef)(null);
5485
+ const pushToHistory = (0, import_react16.useCallback)((currentDiagram) => {
4723
5486
  if (isUndoRedoAction.current) return;
4724
5487
  const snapshot = JSON.parse(JSON.stringify(currentDiagram));
4725
5488
  historyStack.current.push(snapshot);
@@ -4728,7 +5491,7 @@ function PIDCanvas({
4728
5491
  }
4729
5492
  redoStack.current = [];
4730
5493
  }, []);
4731
- const undo = (0, import_react15.useCallback)(() => {
5494
+ const undo = (0, import_react16.useCallback)(() => {
4732
5495
  if (historyStack.current.length === 0) return;
4733
5496
  isUndoRedoAction.current = true;
4734
5497
  const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));
@@ -4742,7 +5505,7 @@ function PIDCanvas({
4742
5505
  isUndoRedoAction.current = false;
4743
5506
  }, 0);
4744
5507
  }, [localDiagram, onDiagramChange]);
4745
- const redo = (0, import_react15.useCallback)(() => {
5508
+ const redo = (0, import_react16.useCallback)(() => {
4746
5509
  if (redoStack.current.length === 0) return;
4747
5510
  isUndoRedoAction.current = true;
4748
5511
  const currentSnapshot = JSON.parse(JSON.stringify(localDiagram));
@@ -4756,7 +5519,7 @@ function PIDCanvas({
4756
5519
  isUndoRedoAction.current = false;
4757
5520
  }, 0);
4758
5521
  }, [localDiagram, onDiagramChange]);
4759
- (0, import_react15.useEffect)(() => {
5522
+ (0, import_react16.useEffect)(() => {
4760
5523
  const handleKeyDown = (e) => {
4761
5524
  const isCtrlOrCmd = e.ctrlKey || e.metaKey;
4762
5525
  if (isCtrlOrCmd && e.key === "z" && !e.shiftKey) {
@@ -4773,7 +5536,7 @@ function PIDCanvas({
4773
5536
  window.addEventListener("keydown", handleKeyDown);
4774
5537
  return () => window.removeEventListener("keydown", handleKeyDown);
4775
5538
  }, [undo, redo]);
4776
- (0, import_react15.useEffect)(() => {
5539
+ (0, import_react16.useEffect)(() => {
4777
5540
  if (!features.dragNodes) return;
4778
5541
  const handleKeyDown = (e) => {
4779
5542
  if (selection.selectedNodeIds.size === 0 && !selectedWaypoint) return;
@@ -4858,7 +5621,7 @@ function PIDCanvas({
4858
5621
  onNodeMove,
4859
5622
  pushToHistory
4860
5623
  ]);
4861
- const handlePositionChange = (0, import_react15.useCallback)(
5624
+ const handlePositionChange = (0, import_react16.useCallback)(
4862
5625
  (nodeId, x, y) => {
4863
5626
  pushToHistory(localDiagram);
4864
5627
  const updatedNodes = localDiagram.nodes.map(
@@ -4880,7 +5643,7 @@ function PIDCanvas({
4880
5643
  },
4881
5644
  [localDiagram, onDiagramChange, onNodeMove, pushToHistory]
4882
5645
  );
4883
- const handleWaypointPositionChange = (0, import_react15.useCallback)(
5646
+ const handleWaypointPositionChange = (0, import_react16.useCallback)(
4884
5647
  (pipeId, pointIndex, x, y) => {
4885
5648
  pushToHistory(localDiagram);
4886
5649
  const updatedPipes = localDiagram.pipes.map((pipe) => {
@@ -4898,7 +5661,7 @@ function PIDCanvas({
4898
5661
  },
4899
5662
  [localDiagram, onDiagramChange, pushToHistory]
4900
5663
  );
4901
- const handleDragEnd = (0, import_react15.useCallback)(
5664
+ const handleDragEnd = (0, import_react16.useCallback)(
4902
5665
  (nodeId, offset, finalPosition) => {
4903
5666
  const dragDistance = Math.sqrt(offset.x ** 2 + offset.y ** 2);
4904
5667
  if (dragDistance < 2) {
@@ -4979,7 +5742,7 @@ function PIDCanvas({
4979
5742
  onDiagramChange?.(updatedDiagram);
4980
5743
  }
4981
5744
  });
4982
- const handleNodeDragStart = (0, import_react15.useCallback)(
5745
+ const handleNodeDragStart = (0, import_react16.useCallback)(
4983
5746
  (nodeId, event) => {
4984
5747
  const node = localDiagram.nodes.find((n) => n.id === nodeId);
4985
5748
  if (!node) return;
@@ -4987,7 +5750,7 @@ function PIDCanvas({
4987
5750
  },
4988
5751
  [localDiagram.nodes, startDrag]
4989
5752
  );
4990
- const handleWaypointClick = (0, import_react15.useCallback)(
5753
+ const handleWaypointClick = (0, import_react16.useCallback)(
4991
5754
  (pipeId, pointIndex) => {
4992
5755
  setSelectedWaypoint({ pipeId, pointIndex });
4993
5756
  if (selectionEnabled) {
@@ -4996,7 +5759,7 @@ function PIDCanvas({
4996
5759
  },
4997
5760
  [selectionEnabled, clearSelection]
4998
5761
  );
4999
- const handleWaypointDragStart = (0, import_react15.useCallback)(
5762
+ const handleWaypointDragStart = (0, import_react16.useCallback)(
5000
5763
  (pipeId, pointIndex, event) => {
5001
5764
  const pipe = localDiagram.pipes.find((p) => p.id === pipeId);
5002
5765
  if (!pipe || pointIndex >= pipe.routePoints.length) return;
@@ -5005,7 +5768,7 @@ function PIDCanvas({
5005
5768
  },
5006
5769
  [localDiagram.pipes, startWaypointDrag]
5007
5770
  );
5008
- const handlePipeSegmentClick = (0, import_react15.useCallback)(
5771
+ const handlePipeSegmentClick = (0, import_react16.useCallback)(
5009
5772
  (pipeId, position, segmentIndex) => {
5010
5773
  pushToHistory(localDiagram);
5011
5774
  const updatedPipes = localDiagram.pipes.map((pipe) => {
@@ -5024,7 +5787,7 @@ function PIDCanvas({
5024
5787
  [localDiagram, onDiagramChange, pushToHistory]
5025
5788
  );
5026
5789
  const telemetryEnabled = features.telemetry ?? false;
5027
- const handleDelete = (0, import_react15.useCallback)(() => {
5790
+ const handleDelete = (0, import_react16.useCallback)(() => {
5028
5791
  if (!selectionEnabled) return;
5029
5792
  const { selectedNodeIds, selectedPipeIds } = selection;
5030
5793
  if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;
@@ -5053,13 +5816,13 @@ function PIDCanvas({
5053
5816
  onDelete,
5054
5817
  pushToHistory
5055
5818
  ]);
5056
- const handleCopy = (0, import_react15.useCallback)(() => {
5819
+ const handleCopy = (0, import_react16.useCallback)(() => {
5057
5820
  if (!selectionEnabled) return;
5058
5821
  const { selectedNodeIds, selectedPipeIds } = selection;
5059
5822
  if (selectedNodeIds.size === 0 && selectedPipeIds.size === 0) return;
5060
5823
  onItemsCopied?.(Array.from(selectedNodeIds), Array.from(selectedPipeIds));
5061
5824
  }, [selection, selectionEnabled, onItemsCopied]);
5062
- const handlePaste = (0, import_react15.useCallback)(() => {
5825
+ const handlePaste = (0, import_react16.useCallback)(() => {
5063
5826
  if (!selectionEnabled) return;
5064
5827
  onPaste?.();
5065
5828
  }, [selectionEnabled, onPaste]);
@@ -5119,7 +5882,7 @@ function PIDCanvas({
5119
5882
  }
5120
5883
  setSelectedWaypoint(null);
5121
5884
  };
5122
- const handleDragOver = (0, import_react15.useCallback)(
5885
+ const handleDragOver = (0, import_react16.useCallback)(
5123
5886
  (event) => {
5124
5887
  if (!features.allowSymbolDrop) return;
5125
5888
  event.preventDefault();
@@ -5130,7 +5893,7 @@ function PIDCanvas({
5130
5893
  },
5131
5894
  [features.allowSymbolDrop, screenToWorld, svgRef]
5132
5895
  );
5133
- const handleDrop = (0, import_react15.useCallback)(
5896
+ const handleDrop = (0, import_react16.useCallback)(
5134
5897
  (event) => {
5135
5898
  if (!features.allowSymbolDrop) return;
5136
5899
  event.preventDefault();
@@ -5143,10 +5906,10 @@ function PIDCanvas({
5143
5906
  },
5144
5907
  [features.allowSymbolDrop, screenToWorld, onSymbolDrop, svgRef]
5145
5908
  );
5146
- const handleDragLeave = (0, import_react15.useCallback)(() => {
5909
+ const handleDragLeave = (0, import_react16.useCallback)(() => {
5147
5910
  setDragOverPosition(null);
5148
5911
  }, []);
5149
- const handleMouseMove = (0, import_react15.useCallback)(
5912
+ const handleMouseMove = (0, import_react16.useCallback)(
5150
5913
  (event) => {
5151
5914
  const connectionModeEnabled = features.connectionMode ?? false;
5152
5915
  if (connectionModeEnabled) {
@@ -5183,7 +5946,7 @@ function PIDCanvas({
5183
5946
  const viewBox = controlledViewBox;
5184
5947
  const displayDiagram = localDiagram;
5185
5948
  const selectedNode = selectionEnabled && selection.selectedNodeIds.size === 1 ? localDiagram.nodes.find((n) => selection.selectedNodeIds.has(n.id)) || null : null;
5186
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5949
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5187
5950
  "div",
5188
5951
  {
5189
5952
  ref: containerRef,
@@ -5198,7 +5961,7 @@ function PIDCanvas({
5198
5961
  },
5199
5962
  ...props,
5200
5963
  children: [
5201
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5964
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5202
5965
  "svg",
5203
5966
  {
5204
5967
  ref: svgRef,
@@ -5217,15 +5980,15 @@ function PIDCanvas({
5217
5980
  onDrop: handleDrop,
5218
5981
  onDragLeave: handleDragLeave,
5219
5982
  children: [
5220
- features.showGrid !== false ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
5221
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("defs", { children: [
5222
- /* @__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" }) }),
5223
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
5224
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
5225
- /* @__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" })
5226
5989
  ] })
5227
5990
  ] }),
5228
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5991
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5229
5992
  "rect",
5230
5993
  {
5231
5994
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5235,7 +5998,7 @@ function PIDCanvas({
5235
5998
  fill: "url(#grid-major)"
5236
5999
  }
5237
6000
  )
5238
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6001
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5239
6002
  "rect",
5240
6003
  {
5241
6004
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5245,7 +6008,7 @@ function PIDCanvas({
5245
6008
  fill: features.backgroundColor ?? "#ffffff"
5246
6009
  }
5247
6010
  ),
5248
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6011
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5249
6012
  PipeRenderer,
5250
6013
  {
5251
6014
  pipes: displayDiagram.pipes,
@@ -5259,12 +6022,12 @@ function PIDCanvas({
5259
6022
  onPipeSegmentClick: void 0
5260
6023
  }
5261
6024
  ),
5262
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6025
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5263
6026
  NodeRenderer,
5264
6027
  {
5265
6028
  nodes: displayDiagram.nodes,
5266
6029
  selectedNodeIds: selectionEnabled ? selection.selectedNodeIds : void 0,
5267
- onNodeClick: selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
6030
+ onNodeClick: onNodeClick ? handleNodeClick : selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
5268
6031
  onNodeDoubleClick: selectionEnabled ? handleNodeDoubleClick : void 0,
5269
6032
  enableDrag: dragEnabled && !isConnecting,
5270
6033
  onNodeDragStart: dragEnabled && !isConnecting ? handleNodeDragStart : void 0,
@@ -5272,7 +6035,7 @@ function PIDCanvas({
5272
6035
  dragOffset: dragState.offset
5273
6036
  }
5274
6037
  ),
5275
- (features.editPipeRoutes ?? false) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6038
+ (features.editPipeRoutes ?? false) && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5276
6039
  PipeRenderer,
5277
6040
  {
5278
6041
  pipes: displayDiagram.pipes,
@@ -5289,14 +6052,14 @@ function PIDCanvas({
5289
6052
  onPipeSegmentClick: handlePipeSegmentClick
5290
6053
  }
5291
6054
  ),
5292
- 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) => {
5293
6056
  const binding = telemetry.get(node.id);
5294
6057
  if (!binding) return null;
5295
6058
  const symbol = getSymbolDefinition(node.symbolId);
5296
6059
  if (!symbol) return null;
5297
6060
  const overlayX = node.transform.x + symbol.viewBox.width / 2;
5298
6061
  const overlayY = node.transform.y + symbol.viewBox.height + 50;
5299
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6062
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5300
6063
  DataOverlay,
5301
6064
  {
5302
6065
  nodeId: node.id,
@@ -5308,7 +6071,7 @@ function PIDCanvas({
5308
6071
  `telemetry-${node.id}`
5309
6072
  );
5310
6073
  }) }),
5311
- dragOverPosition && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6074
+ dragOverPosition && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5312
6075
  "circle",
5313
6076
  {
5314
6077
  cx: dragOverPosition.x,
@@ -5321,7 +6084,7 @@ function PIDCanvas({
5321
6084
  pointerEvents: "none"
5322
6085
  }
5323
6086
  ),
5324
- 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: (() => {
5325
6088
  const sourceNode = displayDiagram.nodes.find((n) => n.id === connectionSource);
5326
6089
  if (!sourceNode) return null;
5327
6090
  const symbol = getSymbolDefinition(sourceNode.symbolId);
@@ -5337,8 +6100,8 @@ function PIDCanvas({
5337
6100
  }
5338
6101
  }
5339
6102
  }
5340
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
5341
- /* @__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)(
5342
6105
  "circle",
5343
6106
  {
5344
6107
  cx: lineStartX,
@@ -5350,7 +6113,7 @@ function PIDCanvas({
5350
6113
  opacity: 0.6
5351
6114
  }
5352
6115
  ),
5353
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6116
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5354
6117
  "line",
5355
6118
  {
5356
6119
  x1: lineStartX,
@@ -5363,7 +6126,7 @@ function PIDCanvas({
5363
6126
  opacity: 0.8
5364
6127
  }
5365
6128
  ),
5366
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6129
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5367
6130
  "circle",
5368
6131
  {
5369
6132
  cx: connectionCursor.x,
@@ -5375,7 +6138,7 @@ function PIDCanvas({
5375
6138
  )
5376
6139
  ] });
5377
6140
  })() }),
5378
- 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) => {
5379
6142
  const symbol = getSymbolDefinition(node.symbolId);
5380
6143
  if (!symbol?.ports) return null;
5381
6144
  return symbol.ports.map((port) => {
@@ -5383,8 +6146,8 @@ function PIDCanvas({
5383
6146
  if (!portPos) return null;
5384
6147
  const isHovered = hoveredPort === port.id && hoveredNode === node.id;
5385
6148
  const isSourcePort = node.id === connectionSource && port.id === connectionSourcePort;
5386
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("g", { children: [
5387
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6149
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("g", { children: [
6150
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5388
6151
  "circle",
5389
6152
  {
5390
6153
  cx: portPos.x,
@@ -5417,7 +6180,7 @@ function PIDCanvas({
5417
6180
  }
5418
6181
  }
5419
6182
  ),
5420
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6183
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5421
6184
  "circle",
5422
6185
  {
5423
6186
  cx: portPos.x,
@@ -5436,7 +6199,7 @@ function PIDCanvas({
5436
6199
  ]
5437
6200
  }
5438
6201
  ),
5439
- features.dragNodes && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6202
+ features.dragNodes && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5440
6203
  PositionPanel,
5441
6204
  {
5442
6205
  selectedNode,
@@ -5456,16 +6219,16 @@ function PIDCanvas({
5456
6219
  }
5457
6220
 
5458
6221
  // src/components/SymbolLibrary/SymbolLibrary.tsx
5459
- var import_react16 = require("react");
5460
- var import_jsx_runtime18 = require("react/jsx-runtime");
6222
+ var import_react17 = require("react");
6223
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5461
6224
  function SymbolLibrary({
5462
6225
  className = "",
5463
6226
  onSymbolDragStart,
5464
6227
  showCategories = true,
5465
6228
  categories = ["Valves", "Pumps", "Tanks", "Instruments", "Displays"]
5466
6229
  }) {
5467
- const [selectedCategory, setSelectedCategory] = (0, import_react16.useState)("all");
5468
- const [searchQuery, setSearchQuery] = (0, import_react16.useState)("");
6230
+ const [selectedCategory, setSelectedCategory] = (0, import_react17.useState)("all");
6231
+ const [searchQuery, setSearchQuery] = (0, import_react17.useState)("");
5469
6232
  const symbolIds = getAvailableSymbols();
5470
6233
  const allSymbols = symbolIds.map((id) => getSymbolDefinition(id)).filter((symbol) => symbol !== void 0);
5471
6234
  const categoryMap = {
@@ -5486,9 +6249,9 @@ function SymbolLibrary({
5486
6249
  event.dataTransfer.effectAllowed = "copy";
5487
6250
  onSymbolDragStart?.(symbol.id, event);
5488
6251
  };
5489
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `symbol-library ${className}`.trim(), style: styles.container, children: [
5490
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { style: styles.title, children: "Symbol Library" }) }),
5491
- /* @__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)(
5492
6255
  "input",
5493
6256
  {
5494
6257
  type: "text",
@@ -5498,8 +6261,8 @@ function SymbolLibrary({
5498
6261
  style: styles.searchInput
5499
6262
  }
5500
6263
  ) }),
5501
- showCategories && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: styles.categories, children: [
5502
- /* @__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)(
5503
6266
  "button",
5504
6267
  {
5505
6268
  onClick: () => setSelectedCategory("all"),
@@ -5510,7 +6273,7 @@ function SymbolLibrary({
5510
6273
  children: "All"
5511
6274
  }
5512
6275
  ),
5513
- categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6276
+ categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5514
6277
  "button",
5515
6278
  {
5516
6279
  onClick: () => setSelectedCategory(category),
@@ -5523,7 +6286,7 @@ function SymbolLibrary({
5523
6286
  category
5524
6287
  ))
5525
6288
  ] }),
5526
- /* @__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)(
5527
6290
  "div",
5528
6291
  {
5529
6292
  draggable: true,
@@ -5531,15 +6294,15 @@ function SymbolLibrary({
5531
6294
  style: styles.symbolCard,
5532
6295
  title: symbol.metadata?.description || symbol.name,
5533
6296
  children: [
5534
- /* @__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)(
5535
6298
  "svg",
5536
6299
  {
5537
6300
  viewBox: `${symbol.viewBox.x} ${symbol.viewBox.y} ${symbol.viewBox.width} ${symbol.viewBox.height}`,
5538
6301
  style: styles.symbolSvg,
5539
- 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 } })
5540
6303
  }
5541
6304
  ) }),
5542
- /* @__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 })
5543
6306
  ]
5544
6307
  },
5545
6308
  symbol.id
@@ -5648,7 +6411,7 @@ var styles = {
5648
6411
  };
5649
6412
 
5650
6413
  // src/components/NodeConfigPanel/NodeConfigPanel.tsx
5651
- var import_jsx_runtime19 = require("react/jsx-runtime");
6414
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5652
6415
  function NodeConfigPanel({
5653
6416
  node,
5654
6417
  binding,
@@ -5667,15 +6430,15 @@ function NodeConfigPanel({
5667
6430
  padding: "20px",
5668
6431
  ...style
5669
6432
  };
5670
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className, style: defaultStyle, children: [
5671
- /* @__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: [
5672
6435
  "Configure: ",
5673
6436
  node.id
5674
6437
  ] }),
5675
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
5676
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Node Settings" }),
5677
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label:" }),
5678
- /* @__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)(
5679
6442
  "input",
5680
6443
  {
5681
6444
  type: "text",
@@ -5691,10 +6454,10 @@ function NodeConfigPanel({
5691
6454
  }
5692
6455
  }
5693
6456
  ),
5694
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5695
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5696
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset X:" }),
5697
- /* @__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)(
5698
6461
  "input",
5699
6462
  {
5700
6463
  type: "number",
@@ -5715,9 +6478,9 @@ function NodeConfigPanel({
5715
6478
  }
5716
6479
  )
5717
6480
  ] }),
5718
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5719
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset Y:" }),
5720
- /* @__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)(
5721
6484
  "input",
5722
6485
  {
5723
6486
  type: "number",
@@ -5739,9 +6502,9 @@ function NodeConfigPanel({
5739
6502
  )
5740
6503
  ] })
5741
6504
  ] }),
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 Font Size:" }),
5744
- /* @__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)(
5745
6508
  "input",
5746
6509
  {
5747
6510
  type: "number",
@@ -5761,9 +6524,9 @@ function NodeConfigPanel({
5761
6524
  }
5762
6525
  )
5763
6526
  ] }),
5764
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5765
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
5766
- /* @__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)(
5767
6530
  "input",
5768
6531
  {
5769
6532
  type: "number",
@@ -5788,9 +6551,9 @@ function NodeConfigPanel({
5788
6551
  )
5789
6552
  ] })
5790
6553
  ] }),
5791
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
5792
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Telemetry Data" }),
5793
- !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)(
5794
6557
  "button",
5795
6558
  {
5796
6559
  onClick: () => {
@@ -5833,9 +6596,9 @@ function NodeConfigPanel({
5833
6596
  },
5834
6597
  children: "+ Add Telemetry"
5835
6598
  }
5836
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5837
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Initial Value:" }),
5838
- /* @__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)(
5839
6602
  "input",
5840
6603
  {
5841
6604
  type: "number",
@@ -5857,8 +6620,8 @@ function NodeConfigPanel({
5857
6620
  }
5858
6621
  }
5859
6622
  ),
5860
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Unit:" }),
5861
- /* @__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)(
5862
6625
  "input",
5863
6626
  {
5864
6627
  type: "text",
@@ -5880,8 +6643,8 @@ function NodeConfigPanel({
5880
6643
  }
5881
6644
  }
5882
6645
  ),
5883
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
5884
- /* @__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)(
5885
6648
  "input",
5886
6649
  {
5887
6650
  type: "checkbox",
@@ -5903,8 +6666,8 @@ function NodeConfigPanel({
5903
6666
  ),
5904
6667
  "Show Sparkline"
5905
6668
  ] }),
5906
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginTop: "12px" }, children: [
5907
- /* @__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)(
5908
6671
  "label",
5909
6672
  {
5910
6673
  style: {
@@ -5916,10 +6679,10 @@ function NodeConfigPanel({
5916
6679
  children: "Telemetry Position Offset:"
5917
6680
  }
5918
6681
  ),
5919
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5920
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5921
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "X Offset:" }),
5922
- /* @__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)(
5923
6686
  "input",
5924
6687
  {
5925
6688
  type: "number",
@@ -5946,9 +6709,9 @@ function NodeConfigPanel({
5946
6709
  }
5947
6710
  )
5948
6711
  ] }),
5949
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5950
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Y Offset:" }),
5951
- /* @__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)(
5952
6715
  "input",
5953
6716
  {
5954
6717
  type: "number",
@@ -5976,8 +6739,8 @@ function NodeConfigPanel({
5976
6739
  )
5977
6740
  ] })
5978
6741
  ] }),
5979
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginTop: "16px" }, children: [
5980
- /* @__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)(
5981
6744
  "label",
5982
6745
  {
5983
6746
  style: {
@@ -5989,10 +6752,10 @@ function NodeConfigPanel({
5989
6752
  children: "Display Colors:"
5990
6753
  }
5991
6754
  ),
5992
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
5993
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Text Color:" }),
5994
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
5995
- /* @__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)(
5996
6759
  "input",
5997
6760
  {
5998
6761
  type: "color",
@@ -6018,7 +6781,7 @@ function NodeConfigPanel({
6018
6781
  }
6019
6782
  }
6020
6783
  ),
6021
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6784
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6022
6785
  "input",
6023
6786
  {
6024
6787
  type: "text",
@@ -6048,10 +6811,10 @@ function NodeConfigPanel({
6048
6811
  )
6049
6812
  ] })
6050
6813
  ] }),
6051
- binding.display?.showSparkline && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6052
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Sparkline Color:" }),
6053
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6054
- /* @__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)(
6055
6818
  "input",
6056
6819
  {
6057
6820
  type: "color",
@@ -6077,7 +6840,7 @@ function NodeConfigPanel({
6077
6840
  }
6078
6841
  }
6079
6842
  ),
6080
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6843
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6081
6844
  "input",
6082
6845
  {
6083
6846
  type: "text",
@@ -6107,9 +6870,9 @@ function NodeConfigPanel({
6107
6870
  )
6108
6871
  ] })
6109
6872
  ] }),
6110
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6111
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Background:" }),
6112
- /* @__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)(
6113
6876
  "input",
6114
6877
  {
6115
6878
  type: "text",
@@ -6137,20 +6900,20 @@ function NodeConfigPanel({
6137
6900
  }
6138
6901
  }
6139
6902
  ),
6140
- /* @__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" })
6141
6904
  ] })
6142
6905
  ] }),
6143
6906
  " "
6144
6907
  ] })
6145
6908
  ] })
6146
6909
  ] }),
6147
- binding && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6148
- /* @__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" }),
6149
6912
  ["highHigh", "high", "low", "lowLow"].map((key) => {
6150
6913
  const label = key === "highHigh" ? "High-High (\u2265)" : key === "high" ? "High (\u2265)" : key === "low" ? "Low (\u2264)" : "Low-Low (\u2264)";
6151
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6152
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: label }),
6153
- /* @__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)(
6154
6917
  "input",
6155
6918
  {
6156
6919
  type: "number",
@@ -6174,8 +6937,8 @@ function NodeConfigPanel({
6174
6937
  ] }, key);
6175
6938
  })
6176
6939
  ] }),
6177
- binding && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "20px" }, children: [
6178
- /* @__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" }),
6179
6942
  ["normal", "warning", "alarm", "fault", "off"].map((status) => {
6180
6943
  const defaultColors = {
6181
6944
  normal: "#10b981",
@@ -6185,13 +6948,13 @@ function NodeConfigPanel({
6185
6948
  off: "#6b7280"
6186
6949
  };
6187
6950
  const label = status === "normal" ? "Normal" : status === "warning" ? "Warning" : status === "alarm" ? "Alarm" : status === "fault" ? "Fault" : "Off";
6188
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: "8px" }, children: [
6189
- /* @__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: [
6190
6953
  label,
6191
6954
  ":"
6192
6955
  ] }),
6193
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6194
- /* @__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)(
6195
6958
  "input",
6196
6959
  {
6197
6960
  type: "color",
@@ -6212,7 +6975,7 @@ function NodeConfigPanel({
6212
6975
  }
6213
6976
  }
6214
6977
  ),
6215
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6978
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6216
6979
  "input",
6217
6980
  {
6218
6981
  type: "text",
@@ -6239,7 +7002,7 @@ function NodeConfigPanel({
6239
7002
  ] }, status);
6240
7003
  })
6241
7004
  ] }),
6242
- /* @__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)(
6243
7006
  "button",
6244
7007
  {
6245
7008
  onClick: onRemove,
@@ -6478,8 +7241,280 @@ var DEFAULT_THRESHOLDS = {
6478
7241
  lowLow: 15
6479
7242
  };
6480
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
+
6481
7516
  // src/diagram/hooks/useSimulation.ts
6482
- var import_react17 = require("react");
7517
+ var import_react19 = require("react");
6483
7518
  function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6484
7519
  const {
6485
7520
  interval = 2e3,
@@ -6490,7 +7525,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6490
7525
  historyLength = 10,
6491
7526
  generateValue
6492
7527
  } = options;
6493
- (0, import_react17.useEffect)(() => {
7528
+ (0, import_react19.useEffect)(() => {
6494
7529
  if (!enabled) return;
6495
7530
  const intervalId = setInterval(() => {
6496
7531
  const updates = [];
@@ -6548,6 +7583,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6548
7583
  ControlPanel,
6549
7584
  DEFAULT_THRESHOLDS,
6550
7585
  DashboardCard,
7586
+ DeviceControlPanel,
6551
7587
  DisplayModeToggle,
6552
7588
  EquipmentIndicator,
6553
7589
  EquipmentIndicatorWithSparkline,
@@ -6559,6 +7595,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6559
7595
  LeftToggleButton,
6560
7596
  LegacyValueEntry,
6561
7597
  NodeConfigPanel,
7598
+ NodeControlsPanel,
6562
7599
  PIDCanvas,
6563
7600
  PanelContent,
6564
7601
  PanelTabs,
@@ -6578,6 +7615,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6578
7615
  ZoomButton,
6579
7616
  ZoomControls,
6580
7617
  calculateThresholdStatus,
7618
+ createControlBinding,
6581
7619
  exampleDiagram,
6582
7620
  exportDiagram,
6583
7621
  generateRoutePoints,
@@ -6588,6 +7626,9 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6588
7626
  mockSymbolLibrary,
6589
7627
  nodeHasPort,
6590
7628
  overlayStyles,
7629
+ registerSymbol,
7630
+ registerSymbols,
7631
+ useDeviceControls,
6591
7632
  useDrag,
6592
7633
  useKeyboardShortcuts,
6593
7634
  useSelection,