@procaaso/alphinity-ui-components 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -707,14 +707,721 @@ var StatusIndicator = ({
707
707
  ] });
708
708
  };
709
709
 
710
+ // src/components/NodeControlsPanel/NodeControlsPanel.tsx
711
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
712
+ var NodeControlsPanel = ({
713
+ config,
714
+ onClose,
715
+ position = "bottom",
716
+ touchOptimized = true,
717
+ className = "",
718
+ style,
719
+ ...props
720
+ }) => {
721
+ if (!config) return null;
722
+ const isBottom = position === "bottom";
723
+ const touchClass = touchOptimized ? "touch-optimized" : "";
724
+ const containerStyle = {
725
+ position: "fixed",
726
+ zIndex: 9999,
727
+ background: "white",
728
+ boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.15)",
729
+ borderRadius: isBottom ? "16px 16px 0 0" : "0",
730
+ animation: isBottom ? "slideUp 0.3s ease-out" : "slideInRight 0.3s ease-out",
731
+ display: "flex",
732
+ flexDirection: "column",
733
+ ...isBottom ? {
734
+ left: 0,
735
+ right: 0,
736
+ bottom: 0,
737
+ maxHeight: "70vh",
738
+ borderTop: "1px solid #e5e7eb"
739
+ } : {
740
+ right: 0,
741
+ top: 0,
742
+ bottom: 0,
743
+ width: touchOptimized ? "400px" : "320px",
744
+ borderLeft: "1px solid #e5e7eb"
745
+ },
746
+ ...style
747
+ };
748
+ const headerStyle = {
749
+ display: "flex",
750
+ alignItems: "center",
751
+ justifyContent: "space-between",
752
+ padding: touchOptimized ? "1.25rem 1.5rem" : "1rem",
753
+ borderBottom: "2px solid #e5e7eb",
754
+ background: "#f9fafb",
755
+ flexShrink: 0
756
+ };
757
+ const titleStyle = {
758
+ margin: 0,
759
+ fontSize: touchOptimized ? "1.5rem" : "1.25rem",
760
+ fontWeight: 600,
761
+ color: "#111827"
762
+ };
763
+ const closeButtonStyle = {
764
+ background: "#ef4444",
765
+ color: "white",
766
+ border: "none",
767
+ borderRadius: "8px",
768
+ width: touchOptimized ? "56px" : "44px",
769
+ height: touchOptimized ? "56px" : "44px",
770
+ fontSize: touchOptimized ? "1.75rem" : "1.5rem",
771
+ fontWeight: "bold",
772
+ cursor: "pointer",
773
+ display: "flex",
774
+ alignItems: "center",
775
+ justifyContent: "center",
776
+ transition: "all 0.2s ease",
777
+ flexShrink: 0
778
+ };
779
+ const contentStyle = {
780
+ padding: touchOptimized ? "1.5rem" : "1rem",
781
+ overflowY: "auto",
782
+ flex: 1
783
+ };
784
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
785
+ /* @__PURE__ */ jsx7(
786
+ "div",
787
+ {
788
+ style: {
789
+ position: "fixed",
790
+ inset: 0,
791
+ background: "rgba(0, 0, 0, 0.3)",
792
+ zIndex: 9998,
793
+ animation: "fadeIn 0.3s ease-out"
794
+ },
795
+ onClick: onClose
796
+ }
797
+ ),
798
+ /* @__PURE__ */ jsxs6(
799
+ "div",
800
+ {
801
+ className: `node-controls-panel ${touchClass} ${className}`.trim(),
802
+ style: containerStyle,
803
+ ...props,
804
+ children: [
805
+ /* @__PURE__ */ jsxs6("div", { style: headerStyle, children: [
806
+ /* @__PURE__ */ jsx7("h2", { style: titleStyle, children: config.title }),
807
+ /* @__PURE__ */ jsx7(
808
+ "button",
809
+ {
810
+ style: closeButtonStyle,
811
+ onClick: onClose,
812
+ onMouseEnter: (e) => {
813
+ e.currentTarget.style.background = "#dc2626";
814
+ e.currentTarget.style.transform = "scale(1.05)";
815
+ },
816
+ onMouseLeave: (e) => {
817
+ e.currentTarget.style.background = "#ef4444";
818
+ e.currentTarget.style.transform = "scale(1)";
819
+ },
820
+ "aria-label": "Close",
821
+ children: "\u2715"
822
+ }
823
+ )
824
+ ] }),
825
+ /* @__PURE__ */ jsx7("div", { style: contentStyle, children: /* @__PURE__ */ jsx7(
826
+ ControlPanel,
827
+ {
828
+ title: config.nodeId,
829
+ selectedMode: config.currentMode,
830
+ modeOptions: config.modes,
831
+ onModeChange: config.onModeChange,
832
+ setpointValue: config.setpoint,
833
+ onSetpointChange: config.onSetpointChange,
834
+ secondSetpointValue: config.secondSetpoint,
835
+ onSecondSetpointChange: config.onSecondSetpointChange,
836
+ modeConfigs: config.modeConfigs,
837
+ status: config.status,
838
+ setpointReadOnly: config.readOnly,
839
+ showStartStopButtons: !!(config.onStart || config.onStop),
840
+ isRunning: config.isRunning,
841
+ onStart: config.onStart,
842
+ onStop: config.onStop,
843
+ startButtonText: config.startButtonText,
844
+ stopButtonText: config.stopButtonText,
845
+ size: touchOptimized ? "large" : "medium"
846
+ }
847
+ ) })
848
+ ]
849
+ }
850
+ ),
851
+ /* @__PURE__ */ jsx7("style", { children: `
852
+ @keyframes slideUp {
853
+ from {
854
+ transform: translateY(100%);
855
+ opacity: 0;
856
+ }
857
+ to {
858
+ transform: translateY(0);
859
+ opacity: 1;
860
+ }
861
+ }
862
+
863
+ @keyframes slideInRight {
864
+ from {
865
+ transform: translateX(100%);
866
+ opacity: 0;
867
+ }
868
+ to {
869
+ transform: translateX(0);
870
+ opacity: 1;
871
+ }
872
+ }
873
+
874
+ @keyframes fadeIn {
875
+ from {
876
+ opacity: 0;
877
+ }
878
+ to {
879
+ opacity: 1;
880
+ }
881
+ }
882
+
883
+ .node-controls-panel.touch-optimized button {
884
+ min-height: 56px;
885
+ font-size: 1.125rem;
886
+ }
887
+
888
+ .node-controls-panel.touch-optimized input {
889
+ min-height: 56px;
890
+ font-size: 1.125rem;
891
+ }
892
+ ` })
893
+ ] });
894
+ };
895
+
896
+ // src/components/DeviceControlPanel/DeviceControlPanel.tsx
897
+ import { useState as useState5, useEffect as useEffect2 } from "react";
898
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
899
+ function DeviceControlPanel({
900
+ binding,
901
+ onClose,
902
+ position = "bottom",
903
+ touchOptimized = true,
904
+ onModeChange,
905
+ onParameterChange,
906
+ onStart,
907
+ onStop,
908
+ onCustomAction
909
+ }) {
910
+ const [localParameterValues, setLocalParameterValues] = useState5({});
911
+ useEffect2(() => {
912
+ if (binding) {
913
+ const currentParams2 = binding.parameters[binding.state.currentMode] || [];
914
+ const values = {};
915
+ currentParams2.forEach((param) => {
916
+ values[param.id] = param.value;
917
+ });
918
+ setLocalParameterValues(values);
919
+ }
920
+ }, [binding, binding?.state.currentMode]);
921
+ if (!binding) return null;
922
+ const currentMode = binding.state.currentMode;
923
+ const currentParams = binding.parameters[currentMode] || [];
924
+ const touchSize = touchOptimized ? 56 : 40;
925
+ const backgroundColor = binding.theme?.backgroundColor || "#edeeef";
926
+ const surfaceColor = "#f5f6f7";
927
+ const borderColor = "#d2d2d2";
928
+ const textColor = binding.theme?.textColor || "#000000";
929
+ const mutedTextColor = "#666666";
930
+ const selectedColor = "#b4d0fe";
931
+ const statusColors = {
932
+ normal: binding.theme?.statusColors?.normal || "#888888",
933
+ // Gray (no alarm)
934
+ warning: binding.theme?.statusColors?.warning || "#FFD700",
935
+ // Yellow
936
+ alarm: binding.theme?.statusColors?.alarm || "#FF0000",
937
+ // Red
938
+ fault: binding.theme?.statusColors?.fault || "#FF0000",
939
+ // Red
940
+ off: binding.theme?.statusColors?.off || "#666666",
941
+ // Dark gray
942
+ starting: binding.theme?.statusColors?.starting || "#90EE90",
943
+ // Light green
944
+ stopping: binding.theme?.statusColors?.stopping || "#FFD700"
945
+ // Yellow
946
+ };
947
+ const statusColor = statusColors[binding.state.status || "normal"];
948
+ const handleModeChange = (mode) => {
949
+ onModeChange?.(mode);
950
+ };
951
+ const handleParameterCommit = (parameterId, value) => {
952
+ onParameterChange?.(parameterId, value);
953
+ };
954
+ const handleParameterInput = (parameterId, value) => {
955
+ setLocalParameterValues((prev) => ({ ...prev, [parameterId]: value }));
956
+ };
957
+ const panelStyle = {
958
+ position: "fixed",
959
+ ...position === "bottom" ? {
960
+ bottom: 0,
961
+ left: 0,
962
+ right: 0,
963
+ animation: "slideUp 0.3s ease-out"
964
+ } : {
965
+ right: 0,
966
+ top: 0,
967
+ bottom: 0,
968
+ width: "400px",
969
+ animation: "slideIn 0.3s ease-out"
970
+ },
971
+ backgroundColor,
972
+ color: textColor,
973
+ borderRadius: position === "bottom" ? "8px 8px 0 0" : "8px 0 0 8px",
974
+ boxShadow: "none",
975
+ border: `2px solid ${borderColor}`,
976
+ padding: "20px",
977
+ zIndex: 9999,
978
+ maxHeight: position === "bottom" ? "70vh" : "100vh",
979
+ overflowY: "auto",
980
+ ...binding.display?.style
981
+ };
982
+ const titleText = binding.display?.title || binding.deviceName || binding.tag || binding.nodeId;
983
+ return /* @__PURE__ */ jsx8(Fragment2, { children: /* @__PURE__ */ jsxs7("div", { style: panelStyle, className: binding.display?.className, children: [
984
+ /* @__PURE__ */ jsxs7(
985
+ "div",
986
+ {
987
+ style: {
988
+ display: "flex",
989
+ justifyContent: "space-between",
990
+ alignItems: "center",
991
+ marginBottom: "16px",
992
+ paddingBottom: "12px",
993
+ borderBottom: `2px solid ${borderColor}`
994
+ },
995
+ children: [
996
+ /* @__PURE__ */ jsxs7("div", { children: [
997
+ /* @__PURE__ */ jsx8(
998
+ "h3",
999
+ {
1000
+ style: {
1001
+ margin: 0,
1002
+ fontSize: "14px",
1003
+ fontWeight: 600,
1004
+ fontFamily: "Arial, sans-serif",
1005
+ letterSpacing: "0.5px",
1006
+ textTransform: "uppercase",
1007
+ color: textColor
1008
+ },
1009
+ children: titleText
1010
+ }
1011
+ ),
1012
+ binding.deviceType && /* @__PURE__ */ jsx8(
1013
+ "div",
1014
+ {
1015
+ style: {
1016
+ fontSize: "11px",
1017
+ color: mutedTextColor,
1018
+ marginTop: "4px",
1019
+ fontFamily: "Arial, sans-serif"
1020
+ },
1021
+ children: binding.deviceType
1022
+ }
1023
+ )
1024
+ ] }),
1025
+ /* @__PURE__ */ jsx8(
1026
+ "button",
1027
+ {
1028
+ onClick: onClose,
1029
+ style: {
1030
+ width: touchSize * 0.7,
1031
+ height: touchSize * 0.7,
1032
+ backgroundColor: surfaceColor,
1033
+ color: textColor,
1034
+ border: `2px solid ${borderColor}`,
1035
+ borderRadius: "2px",
1036
+ fontSize: "18px",
1037
+ cursor: "pointer",
1038
+ fontWeight: "bold",
1039
+ fontFamily: "Arial, sans-serif"
1040
+ },
1041
+ children: "\xD7"
1042
+ }
1043
+ )
1044
+ ]
1045
+ }
1046
+ ),
1047
+ binding.state.status && /* @__PURE__ */ jsxs7(
1048
+ "div",
1049
+ {
1050
+ style: {
1051
+ display: "flex",
1052
+ alignItems: "center",
1053
+ gap: "12px",
1054
+ padding: "10px 12px",
1055
+ backgroundColor: surfaceColor,
1056
+ border: `1px solid ${borderColor}`,
1057
+ borderRadius: "6px",
1058
+ marginBottom: "16px"
1059
+ },
1060
+ children: [
1061
+ /* @__PURE__ */ jsx8(
1062
+ "div",
1063
+ {
1064
+ style: {
1065
+ width: "10px",
1066
+ height: "10px",
1067
+ borderRadius: "50%",
1068
+ backgroundColor: statusColor,
1069
+ border: "1px solid #333"
1070
+ }
1071
+ }
1072
+ ),
1073
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1 }, children: [
1074
+ /* @__PURE__ */ jsx8(
1075
+ "div",
1076
+ {
1077
+ style: {
1078
+ fontWeight: 600,
1079
+ textTransform: "uppercase",
1080
+ fontSize: "11px",
1081
+ fontFamily: "Arial, sans-serif",
1082
+ letterSpacing: "0.5px",
1083
+ color: textColor
1084
+ },
1085
+ children: binding.state.status
1086
+ }
1087
+ ),
1088
+ binding.state.statusMessage && /* @__PURE__ */ jsx8(
1089
+ "div",
1090
+ {
1091
+ style: {
1092
+ fontSize: "10px",
1093
+ color: mutedTextColor,
1094
+ marginTop: "2px",
1095
+ fontFamily: "Arial, sans-serif"
1096
+ },
1097
+ children: binding.state.statusMessage
1098
+ }
1099
+ )
1100
+ ] }),
1101
+ binding.state.isRunning !== void 0 && /* @__PURE__ */ jsx8(
1102
+ "div",
1103
+ {
1104
+ style: {
1105
+ padding: "4px 10px",
1106
+ backgroundColor: binding.state.isRunning ? selectedColor : surfaceColor,
1107
+ border: `1px solid ${borderColor}`,
1108
+ borderRadius: "2px",
1109
+ fontSize: "10px",
1110
+ fontWeight: 600,
1111
+ fontFamily: "Arial, sans-serif",
1112
+ letterSpacing: "0.5px",
1113
+ color: textColor
1114
+ },
1115
+ children: binding.state.isRunning ? "RUNNING" : "STOPPED"
1116
+ }
1117
+ )
1118
+ ]
1119
+ }
1120
+ ),
1121
+ binding.display?.showModeSelector !== false && binding.modes.length > 0 && /* @__PURE__ */ jsxs7("div", { style: { marginBottom: "16px" }, children: [
1122
+ /* @__PURE__ */ jsx8(
1123
+ "label",
1124
+ {
1125
+ style: {
1126
+ display: "block",
1127
+ marginBottom: "8px",
1128
+ fontSize: "11px",
1129
+ fontWeight: 600,
1130
+ textTransform: "uppercase",
1131
+ letterSpacing: "0.5px",
1132
+ fontFamily: "Arial, sans-serif",
1133
+ color: mutedTextColor
1134
+ },
1135
+ children: "Mode"
1136
+ }
1137
+ ),
1138
+ /* @__PURE__ */ jsx8("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" }, children: binding.modes.map((mode) => {
1139
+ const isSelected = mode.value === currentMode;
1140
+ const isEnabled = mode.enabled !== false;
1141
+ return /* @__PURE__ */ jsx8(
1142
+ "button",
1143
+ {
1144
+ onClick: () => isEnabled && handleModeChange(mode.value),
1145
+ disabled: !isEnabled,
1146
+ style: {
1147
+ flex: 1,
1148
+ minWidth: "80px",
1149
+ height: touchSize,
1150
+ backgroundColor: isSelected ? selectedColor : surfaceColor,
1151
+ color: isSelected ? "#000000" : textColor,
1152
+ border: `1px solid ${isSelected ? "#b4d0fe" : borderColor}`,
1153
+ borderRadius: "6px",
1154
+ fontSize: "11px",
1155
+ fontWeight: 600,
1156
+ fontFamily: "Arial, sans-serif",
1157
+ letterSpacing: "0.5px",
1158
+ textTransform: "uppercase",
1159
+ cursor: isEnabled ? "pointer" : "not-allowed",
1160
+ opacity: isEnabled ? 1 : 0.4
1161
+ },
1162
+ children: mode.label
1163
+ },
1164
+ mode.value
1165
+ );
1166
+ }) })
1167
+ ] }),
1168
+ currentParams.length > 0 && /* @__PURE__ */ jsxs7("div", { style: { marginBottom: "16px" }, children: [
1169
+ /* @__PURE__ */ jsx8(
1170
+ "label",
1171
+ {
1172
+ style: {
1173
+ display: "block",
1174
+ marginBottom: "8px",
1175
+ fontSize: "11px",
1176
+ fontWeight: 600,
1177
+ textTransform: "uppercase",
1178
+ letterSpacing: "0.5px",
1179
+ fontFamily: "Arial, sans-serif",
1180
+ color: mutedTextColor
1181
+ },
1182
+ children: "Parameters"
1183
+ }
1184
+ ),
1185
+ currentParams.map((param) => /* @__PURE__ */ jsxs7(
1186
+ "div",
1187
+ {
1188
+ style: {
1189
+ marginBottom: "10px",
1190
+ padding: "10px",
1191
+ backgroundColor: surfaceColor,
1192
+ border: `1px solid ${borderColor}`,
1193
+ borderRadius: "6px"
1194
+ },
1195
+ children: [
1196
+ /* @__PURE__ */ jsxs7(
1197
+ "div",
1198
+ {
1199
+ style: { display: "flex", justifyContent: "space-between", marginBottom: "8px" },
1200
+ children: [
1201
+ /* @__PURE__ */ jsx8(
1202
+ "span",
1203
+ {
1204
+ style: {
1205
+ fontSize: "11px",
1206
+ fontWeight: 600,
1207
+ textTransform: "uppercase",
1208
+ letterSpacing: "0.5px",
1209
+ fontFamily: "Arial, sans-serif",
1210
+ color: mutedTextColor
1211
+ },
1212
+ children: param.label
1213
+ }
1214
+ ),
1215
+ /* @__PURE__ */ jsxs7(
1216
+ "span",
1217
+ {
1218
+ style: {
1219
+ fontSize: "13px",
1220
+ fontWeight: 700,
1221
+ fontFamily: "Arial, sans-serif",
1222
+ color: textColor
1223
+ },
1224
+ children: [
1225
+ localParameterValues[param.id] ?? param.value,
1226
+ " ",
1227
+ param.unit && /* @__PURE__ */ jsx8("span", { style: { color: mutedTextColor, fontSize: "10px", fontWeight: 600 }, children: param.unit })
1228
+ ]
1229
+ }
1230
+ )
1231
+ ]
1232
+ }
1233
+ ),
1234
+ param.type === "boolean" ? /* @__PURE__ */ jsx8(
1235
+ "button",
1236
+ {
1237
+ onClick: () => {
1238
+ const newValue = !localParameterValues[param.id];
1239
+ handleParameterInput(param.id, newValue);
1240
+ handleParameterCommit(param.id, newValue);
1241
+ },
1242
+ disabled: param.readOnly,
1243
+ style: {
1244
+ width: "100%",
1245
+ height: touchSize,
1246
+ backgroundColor: localParameterValues[param.id] ? "#3f3f46" : surfaceColor,
1247
+ color: localParameterValues[param.id] ? textColor : mutedTextColor,
1248
+ border: `1px solid ${localParameterValues[param.id] ? "#52525b" : borderColor}`,
1249
+ borderRadius: "4px",
1250
+ fontSize: "12px",
1251
+ fontWeight: 600,
1252
+ fontFamily: "monospace",
1253
+ letterSpacing: "0.5px",
1254
+ cursor: param.readOnly ? "not-allowed" : "pointer",
1255
+ transition: "all 0.15s"
1256
+ },
1257
+ children: localParameterValues[param.id] ? "ON" : "OFF"
1258
+ }
1259
+ ) : param.type === "select" ? /* @__PURE__ */ jsx8(
1260
+ "select",
1261
+ {
1262
+ value: localParameterValues[param.id] || param.value,
1263
+ onChange: (e) => {
1264
+ handleParameterInput(param.id, e.target.value);
1265
+ handleParameterCommit(param.id, e.target.value);
1266
+ },
1267
+ disabled: param.readOnly,
1268
+ style: {
1269
+ width: "100%",
1270
+ height: touchSize,
1271
+ backgroundColor: surfaceColor,
1272
+ color: textColor,
1273
+ border: `1px solid ${borderColor}`,
1274
+ borderRadius: "4px",
1275
+ padding: "0 12px",
1276
+ fontSize: "12px",
1277
+ fontFamily: "monospace",
1278
+ cursor: "pointer"
1279
+ },
1280
+ children: param.options?.map((opt) => /* @__PURE__ */ jsx8("option", { value: opt.value, children: opt.label }, opt.value))
1281
+ }
1282
+ ) : /* @__PURE__ */ jsx8(
1283
+ "input",
1284
+ {
1285
+ type: param.type === "number" ? "number" : "text",
1286
+ value: localParameterValues[param.id] ?? param.value,
1287
+ onChange: (e) => handleParameterInput(
1288
+ param.id,
1289
+ param.type === "number" ? +e.target.value : e.target.value
1290
+ ),
1291
+ onBlur: () => handleParameterCommit(param.id, localParameterValues[param.id]),
1292
+ disabled: param.readOnly,
1293
+ min: param.min,
1294
+ max: param.max,
1295
+ step: param.step,
1296
+ style: {
1297
+ width: "100%",
1298
+ height: touchSize,
1299
+ backgroundColor: "#ffffff",
1300
+ color: textColor,
1301
+ border: `1px solid ${borderColor}`,
1302
+ borderRadius: "6px",
1303
+ padding: "0 12px",
1304
+ fontSize: "14px",
1305
+ fontFamily: "Arial, sans-serif",
1306
+ fontWeight: 600
1307
+ }
1308
+ }
1309
+ )
1310
+ ]
1311
+ },
1312
+ param.id
1313
+ ))
1314
+ ] }),
1315
+ binding.display?.showStartStop !== false && (binding.capabilities?.canStart || binding.capabilities?.canStop) && /* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: "8px", marginBottom: "16px" }, children: [
1316
+ binding.capabilities?.canStart && /* @__PURE__ */ jsx8(
1317
+ "button",
1318
+ {
1319
+ onClick: onStart,
1320
+ disabled: binding.state.isRunning,
1321
+ style: {
1322
+ flex: 1,
1323
+ height: touchSize,
1324
+ backgroundColor: binding.state.isRunning ? "#a3a3a3" : "#10b981",
1325
+ color: "#ffffff",
1326
+ border: "none",
1327
+ borderRadius: "6px",
1328
+ fontSize: "12px",
1329
+ fontWeight: 700,
1330
+ fontFamily: "Arial, sans-serif",
1331
+ letterSpacing: "0.5px",
1332
+ cursor: binding.state.isRunning ? "not-allowed" : "pointer",
1333
+ opacity: binding.state.isRunning ? 0.5 : 1
1334
+ },
1335
+ children: "START"
1336
+ }
1337
+ ),
1338
+ binding.capabilities?.canStop && /* @__PURE__ */ jsx8(
1339
+ "button",
1340
+ {
1341
+ onClick: onStop,
1342
+ disabled: !binding.state.isRunning,
1343
+ style: {
1344
+ flex: 1,
1345
+ height: touchSize,
1346
+ backgroundColor: !binding.state.isRunning ? "#a3a3a3" : "#ef4444",
1347
+ color: "#ffffff",
1348
+ border: "none",
1349
+ borderRadius: "6px",
1350
+ fontSize: "12px",
1351
+ fontWeight: 700,
1352
+ fontFamily: "Arial, sans-serif",
1353
+ letterSpacing: "0.5px",
1354
+ cursor: !binding.state.isRunning ? "not-allowed" : "pointer",
1355
+ opacity: !binding.state.isRunning ? 0.5 : 1
1356
+ },
1357
+ children: "STOP"
1358
+ }
1359
+ )
1360
+ ] }),
1361
+ binding.capabilities?.customActions && binding.capabilities.customActions.length > 0 && /* @__PURE__ */ jsx8("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: binding.capabilities.customActions.map((action) => /* @__PURE__ */ jsxs7(
1362
+ "button",
1363
+ {
1364
+ onClick: () => onCustomAction?.(action.id),
1365
+ style: {
1366
+ flex: 1,
1367
+ minWidth: "120px",
1368
+ height: touchSize,
1369
+ backgroundColor: surfaceColor,
1370
+ color: textColor,
1371
+ border: `1px solid ${borderColor}`,
1372
+ borderRadius: "6px",
1373
+ fontSize: "11px",
1374
+ fontWeight: 600,
1375
+ fontFamily: "monospace",
1376
+ letterSpacing: "0.5px",
1377
+ textTransform: "uppercase",
1378
+ cursor: "pointer",
1379
+ transition: "all 0.15s"
1380
+ },
1381
+ children: [
1382
+ action.icon && /* @__PURE__ */ jsx8("span", { style: { marginRight: "6px" }, children: action.icon }),
1383
+ action.label
1384
+ ]
1385
+ },
1386
+ action.id
1387
+ )) }),
1388
+ binding.state.lastCommandResult && /* @__PURE__ */ jsx8(
1389
+ "div",
1390
+ {
1391
+ style: {
1392
+ marginTop: "16px",
1393
+ padding: "10px 12px",
1394
+ backgroundColor: surfaceColor,
1395
+ border: `1px solid ${binding.state.lastCommandResult.success ? "#52525b" : borderColor}`,
1396
+ borderRadius: "4px",
1397
+ fontSize: "11px",
1398
+ fontFamily: "monospace",
1399
+ color: binding.state.lastCommandResult.success ? textColor : mutedTextColor
1400
+ },
1401
+ children: binding.state.lastCommandResult.message
1402
+ }
1403
+ ),
1404
+ /* @__PURE__ */ jsx8("style", { children: `
1405
+ @keyframes slideUp {
1406
+ from { transform: translateY(100%); }
1407
+ to { transform: translateY(0); }
1408
+ }
1409
+ @keyframes slideIn {
1410
+ from { transform: translateX(100%); }
1411
+ to { transform: translateX(0); }
1412
+ }
1413
+ ` })
1414
+ ] }) });
1415
+ }
1416
+
710
1417
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
711
- import React5 from "react";
1418
+ import React6 from "react";
712
1419
  import { Tab } from "@mui/material";
713
1420
 
714
1421
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.styles.tsx
715
1422
  import { Box, IconButton, Tabs } from "@mui/material";
716
1423
  import styled from "@emotion/styled";
717
- import { jsx as jsx7 } from "react/jsx-runtime";
1424
+ import { jsx as jsx9 } from "react/jsx-runtime";
718
1425
  var FullscreenContainer = styled(Box)(
719
1426
  ({ leftCollapsed, rightCollapsed }) => {
720
1427
  const getGridTemplateAreas = () => {
@@ -839,7 +1546,7 @@ var DisplayModeToggle = styled(IconButton)(({ mode }) => ({
839
1546
  transform: "translateX(-50%) scale(1.05)"
840
1547
  }
841
1548
  }));
842
- var ChevronLeft = () => /* @__PURE__ */ jsx7(
1549
+ var ChevronLeft = () => /* @__PURE__ */ jsx9(
843
1550
  "div",
844
1551
  {
845
1552
  style: {
@@ -851,7 +1558,7 @@ var ChevronLeft = () => /* @__PURE__ */ jsx7(
851
1558
  }
852
1559
  }
853
1560
  );
854
- var ChevronRight = () => /* @__PURE__ */ jsx7(
1561
+ var ChevronRight = () => /* @__PURE__ */ jsx9(
855
1562
  "div",
856
1563
  {
857
1564
  style: {
@@ -962,10 +1669,10 @@ var ScrollableSVGWrapper = styled("div")({
962
1669
  });
963
1670
 
964
1671
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.overlays.tsx
965
- import { useEffect as useEffect2, useState as useState5 } from "react";
1672
+ import { useEffect as useEffect3, useState as useState6 } from "react";
966
1673
  import { Box as Box2 } from "@mui/material";
967
1674
  import styled2 from "@emotion/styled";
968
- import { jsx as jsx8 } from "react/jsx-runtime";
1675
+ import { jsx as jsx10 } from "react/jsx-runtime";
969
1676
  var baseOverlayStyles = {
970
1677
  tank: {
971
1678
  background: "linear-gradient(135deg, rgba(69, 90, 120, 0.95) 0%, rgba(52, 73, 94, 0.95) 100%)",
@@ -1027,8 +1734,8 @@ var SVGLockedOverlay = ({
1027
1734
  onClick,
1028
1735
  containerSelector = ".tff-svg-container"
1029
1736
  }) => {
1030
- const [svgOffset, setSvgOffset] = useState5({ x: 0, y: 0 });
1031
- useEffect2(() => {
1737
+ const [svgOffset, setSvgOffset] = useState6({ x: 0, y: 0 });
1738
+ useEffect3(() => {
1032
1739
  const updateSvgOffset = () => {
1033
1740
  const containerElement = document.querySelector(containerSelector);
1034
1741
  const svgElement = containerElement?.querySelector("svg");
@@ -1048,7 +1755,7 @@ var SVGLockedOverlay = ({
1048
1755
  const pixelX = svgX + svgOffset.x;
1049
1756
  const pixelY = svgY + svgOffset.y;
1050
1757
  const appliedStyle = theme ? { ...overlayStyles[theme], ...style } : style;
1051
- return /* @__PURE__ */ jsx8(
1758
+ return /* @__PURE__ */ jsx10(
1052
1759
  Box2,
1053
1760
  {
1054
1761
  className: `svg-locked-overlay ${className}`.trim(),
@@ -1112,7 +1819,7 @@ var CardUnit = styled2("div")({
1112
1819
  });
1113
1820
 
1114
1821
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx
1115
- import { Fragment, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1822
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1116
1823
  var TrendLine = ({
1117
1824
  values,
1118
1825
  width = 60,
@@ -1122,7 +1829,7 @@ var TrendLine = ({
1122
1829
  backgroundColor = "rgba(255, 255, 255, 0.1)"
1123
1830
  }) => {
1124
1831
  if (!values || values.length < 2) {
1125
- return /* @__PURE__ */ jsx9(
1832
+ return /* @__PURE__ */ jsx11(
1126
1833
  "div",
1127
1834
  {
1128
1835
  style: {
@@ -1134,7 +1841,7 @@ var TrendLine = ({
1134
1841
  alignItems: "center",
1135
1842
  justifyContent: "center"
1136
1843
  },
1137
- children: /* @__PURE__ */ jsx9("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" })
1844
+ children: /* @__PURE__ */ jsx11("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" })
1138
1845
  }
1139
1846
  );
1140
1847
  }
@@ -1146,7 +1853,7 @@ var TrendLine = ({
1146
1853
  const x = 4 + i / (values.length - 1) * (width - 8);
1147
1854
  return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
1148
1855
  }).join(" ");
1149
- return /* @__PURE__ */ jsx9(
1856
+ return /* @__PURE__ */ jsx11(
1150
1857
  "div",
1151
1858
  {
1152
1859
  style: {
@@ -1157,8 +1864,8 @@ var TrendLine = ({
1157
1864
  position: "relative",
1158
1865
  overflow: "hidden"
1159
1866
  },
1160
- children: /* @__PURE__ */ jsxs6("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
1161
- /* @__PURE__ */ jsx9(
1867
+ children: /* @__PURE__ */ jsxs8("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
1868
+ /* @__PURE__ */ jsx11(
1162
1869
  "path",
1163
1870
  {
1164
1871
  d: pathData,
@@ -1169,7 +1876,7 @@ var TrendLine = ({
1169
1876
  strokeLinejoin: "round"
1170
1877
  }
1171
1878
  ),
1172
- /* @__PURE__ */ jsx9(
1879
+ /* @__PURE__ */ jsx11(
1173
1880
  "circle",
1174
1881
  {
1175
1882
  cx: 4 + (width - 8),
@@ -1207,7 +1914,7 @@ var CircularGauge = ({
1207
1914
  const borderColor = isHex ? hexToRgba(color, 0.9) : color;
1208
1915
  const backgroundColor = isHex ? hexToRgba(color, 0.15) : color;
1209
1916
  const borderStrokeColor = isHex ? hexToRgba(color, 0.4) : color;
1210
- return /* @__PURE__ */ jsxs6(
1917
+ return /* @__PURE__ */ jsxs8(
1211
1918
  "div",
1212
1919
  {
1213
1920
  style: {
@@ -1216,7 +1923,7 @@ var CircularGauge = ({
1216
1923
  alignItems: "center"
1217
1924
  },
1218
1925
  children: [
1219
- /* @__PURE__ */ jsxs6(
1926
+ /* @__PURE__ */ jsxs8(
1220
1927
  "div",
1221
1928
  {
1222
1929
  style: {
@@ -1228,8 +1935,8 @@ var CircularGauge = ({
1228
1935
  border: `1px solid ${borderStrokeColor}`
1229
1936
  },
1230
1937
  children: [
1231
- /* @__PURE__ */ jsxs6("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
1232
- /* @__PURE__ */ jsx9(
1938
+ /* @__PURE__ */ jsxs8("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
1939
+ /* @__PURE__ */ jsx11(
1233
1940
  "circle",
1234
1941
  {
1235
1942
  cx: size / 2,
@@ -1240,7 +1947,7 @@ var CircularGauge = ({
1240
1947
  strokeWidth: 1
1241
1948
  }
1242
1949
  ),
1243
- /* @__PURE__ */ jsx9(
1950
+ /* @__PURE__ */ jsx11(
1244
1951
  "circle",
1245
1952
  {
1246
1953
  cx: size / 2,
@@ -1251,7 +1958,7 @@ var CircularGauge = ({
1251
1958
  fill: "transparent"
1252
1959
  }
1253
1960
  ),
1254
- /* @__PURE__ */ jsx9(
1961
+ /* @__PURE__ */ jsx11(
1255
1962
  "circle",
1256
1963
  {
1257
1964
  cx: size / 2,
@@ -1267,7 +1974,7 @@ var CircularGauge = ({
1267
1974
  }
1268
1975
  )
1269
1976
  ] }),
1270
- /* @__PURE__ */ jsxs6(
1977
+ /* @__PURE__ */ jsxs8(
1271
1978
  "div",
1272
1979
  {
1273
1980
  style: {
@@ -1283,14 +1990,14 @@ var CircularGauge = ({
1283
1990
  },
1284
1991
  children: [
1285
1992
  value.toFixed(1),
1286
- /* @__PURE__ */ jsx9("div", { style: { fontSize: "8px", opacity: 0.9, color: "white" }, children: unit })
1993
+ /* @__PURE__ */ jsx11("div", { style: { fontSize: "8px", opacity: 0.9, color: "white" }, children: unit })
1287
1994
  ]
1288
1995
  }
1289
1996
  )
1290
1997
  ]
1291
1998
  }
1292
1999
  ),
1293
- /* @__PURE__ */ jsx9(
2000
+ /* @__PURE__ */ jsx11(
1294
2001
  "div",
1295
2002
  {
1296
2003
  style: {
@@ -1321,7 +2028,7 @@ var Sparkline = ({ data, width, height, color }) => {
1321
2028
  const y = height - 8 - (value - min) / range * (height - 8);
1322
2029
  return `${x + 4},${y + 4}`;
1323
2030
  }).join(" ");
1324
- return /* @__PURE__ */ jsx9(
2031
+ return /* @__PURE__ */ jsx11(
1325
2032
  "div",
1326
2033
  {
1327
2034
  style: {
@@ -1335,7 +2042,7 @@ var Sparkline = ({ data, width, height, color }) => {
1335
2042
  justifyContent: "center",
1336
2043
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
1337
2044
  },
1338
- children: /* @__PURE__ */ jsx9("svg", { width, height, children: /* @__PURE__ */ jsx9("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) })
2045
+ children: /* @__PURE__ */ jsx11("svg", { width, height, children: /* @__PURE__ */ jsx11("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) })
1339
2046
  }
1340
2047
  );
1341
2048
  };
@@ -1349,8 +2056,8 @@ var EquipmentIndicatorWithSparkline = ({
1349
2056
  sparklineOffset = { x: 40, y: 0 }
1350
2057
  }) => {
1351
2058
  const hasSparkline = sparklineData && sparklineData.length > 1 && Math.max(...sparklineData) !== Math.min(...sparklineData);
1352
- return /* @__PURE__ */ jsxs6(Fragment, { children: [
1353
- /* @__PURE__ */ jsx9(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ jsx9(
2059
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
2060
+ /* @__PURE__ */ jsx11(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ jsx11(
1354
2061
  "div",
1355
2062
  {
1356
2063
  style: {
@@ -1371,7 +2078,7 @@ var EquipmentIndicatorWithSparkline = ({
1371
2078
  children: label
1372
2079
  }
1373
2080
  ) }),
1374
- hasSparkline && /* @__PURE__ */ jsx9(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ jsx9(Sparkline, { data: sparklineData, width: 60, height: 20, color }) })
2081
+ hasSparkline && /* @__PURE__ */ jsx11(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ jsx11(Sparkline, { data: sparklineData, width: 60, height: 20, color }) })
1375
2082
  ] });
1376
2083
  };
1377
2084
  var EquipmentIndicator = ({
@@ -1384,7 +2091,7 @@ var EquipmentIndicator = ({
1384
2091
  className
1385
2092
  }) => {
1386
2093
  const overlayStyle = style !== void 0 ? { transform: "translate(-50%, -50%)", ...style } : { transform: "translate(-50%, -50%)" };
1387
- return /* @__PURE__ */ jsx9(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ jsx9(
2094
+ return /* @__PURE__ */ jsx11(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ jsx11(
1388
2095
  "div",
1389
2096
  {
1390
2097
  style: {
@@ -1408,7 +2115,7 @@ var EquipmentIndicator = ({
1408
2115
  };
1409
2116
 
1410
2117
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
1411
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2118
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1412
2119
  function FullscreenWorkspace({
1413
2120
  svgContent,
1414
2121
  overlays,
@@ -1436,10 +2143,10 @@ function FullscreenWorkspace({
1436
2143
  className = "",
1437
2144
  ...rest
1438
2145
  }) {
1439
- const [leftCollapsedState, setLeftCollapsedState] = React5.useState(defaultLeftCollapsed);
1440
- const [rightCollapsedState, setRightCollapsedState] = React5.useState(defaultRightCollapsed);
1441
- const [internalDisplayMode, setInternalDisplayMode] = React5.useState(displayMode ?? defaultDisplayMode);
1442
- const [internalTab, setInternalTab] = React5.useState(
2146
+ const [leftCollapsedState, setLeftCollapsedState] = React6.useState(defaultLeftCollapsed);
2147
+ const [rightCollapsedState, setRightCollapsedState] = React6.useState(defaultRightCollapsed);
2148
+ const [internalDisplayMode, setInternalDisplayMode] = React6.useState(displayMode ?? defaultDisplayMode);
2149
+ const [internalTab, setInternalTab] = React6.useState(
1443
2150
  tabs && tabs.length > 0 ? tabs[0].value : ""
1444
2151
  );
1445
2152
  const leftCollapsed = leftCollapsedProp ?? leftCollapsedState;
@@ -1447,10 +2154,10 @@ function FullscreenWorkspace({
1447
2154
  const isDisplayModeControlled = displayMode !== void 0;
1448
2155
  const currentDisplayMode = isDisplayModeControlled ? displayMode : internalDisplayMode;
1449
2156
  const isTabControlled = activeTab !== void 0 && activeTab !== null;
1450
- const resolvedTabs = React5.useMemo(() => tabs ?? [], [tabs]);
2157
+ const resolvedTabs = React6.useMemo(() => tabs ?? [], [tabs]);
1451
2158
  const defaultTabValue = resolvedTabs.length > 0 ? resolvedTabs[0].value : "";
1452
2159
  const currentTab = isTabControlled ? activeTab : internalTab || defaultTabValue;
1453
- React5.useEffect(() => {
2160
+ React6.useEffect(() => {
1454
2161
  if (resolvedTabs.length > 0) {
1455
2162
  const values = resolvedTabs.map((tab) => tab.value);
1456
2163
  if (!values.includes(currentTab)) {
@@ -1459,7 +2166,7 @@ function FullscreenWorkspace({
1459
2166
  }
1460
2167
  }
1461
2168
  }, [resolvedTabs, activeTab, currentTab, defaultTabValue]);
1462
- React5.useEffect(() => {
2169
+ React6.useEffect(() => {
1463
2170
  if (displayMode !== void 0) {
1464
2171
  setInternalDisplayMode(displayMode);
1465
2172
  }
@@ -1494,7 +2201,7 @@ function FullscreenWorkspace({
1494
2201
  const currentTabContent = resolvedTabs.find((tab) => tab.value === currentTab)?.content ?? null;
1495
2202
  const showLeftToggle = Boolean(leftPanel);
1496
2203
  const showRightToggle = Boolean(rightPanel);
1497
- return /* @__PURE__ */ jsxs7(
2204
+ return /* @__PURE__ */ jsxs9(
1498
2205
  FullscreenContainer,
1499
2206
  {
1500
2207
  leftCollapsed,
@@ -1502,25 +2209,25 @@ function FullscreenWorkspace({
1502
2209
  className: `tff-svg-container ${className}`.trim(),
1503
2210
  ...rest,
1504
2211
  children: [
1505
- showLeftToggle && /* @__PURE__ */ jsx10(
2212
+ showLeftToggle && /* @__PURE__ */ jsx12(
1506
2213
  LeftToggleButton,
1507
2214
  {
1508
2215
  collapsed: leftCollapsed,
1509
2216
  onClick: handleLeftToggle,
1510
2217
  "aria-label": leftCollapsed ? "Expand left panel" : "Collapse left panel",
1511
- children: leftCollapsed ? /* @__PURE__ */ jsx10(ChevronRight, {}) : /* @__PURE__ */ jsx10(ChevronLeft, {})
2218
+ children: leftCollapsed ? /* @__PURE__ */ jsx12(ChevronRight, {}) : /* @__PURE__ */ jsx12(ChevronLeft, {})
1512
2219
  }
1513
2220
  ),
1514
- showRightToggle && /* @__PURE__ */ jsx10(
2221
+ showRightToggle && /* @__PURE__ */ jsx12(
1515
2222
  RightToggleButton,
1516
2223
  {
1517
2224
  collapsed: rightCollapsed,
1518
2225
  onClick: handleRightToggle,
1519
2226
  "aria-label": rightCollapsed ? "Expand right panel" : "Collapse right panel",
1520
- children: rightCollapsed ? /* @__PURE__ */ jsx10(ChevronLeft, {}) : /* @__PURE__ */ jsx10(ChevronRight, {})
2227
+ children: rightCollapsed ? /* @__PURE__ */ jsx12(ChevronLeft, {}) : /* @__PURE__ */ jsx12(ChevronRight, {})
1521
2228
  }
1522
2229
  ),
1523
- showDisplayModeToggle && /* @__PURE__ */ jsx10(
2230
+ showDisplayModeToggle && /* @__PURE__ */ jsx12(
1524
2231
  DisplayModeToggle,
1525
2232
  {
1526
2233
  mode: currentDisplayMode,
@@ -1529,9 +2236,9 @@ function FullscreenWorkspace({
1529
2236
  children: currentDisplayMode === "dashboard" ? "Dashboard Mode" : "Standard Mode"
1530
2237
  }
1531
2238
  ),
1532
- /* @__PURE__ */ jsxs7(SVGArea, { children: [
1533
- showZoomControls && /* @__PURE__ */ jsxs7(ZoomControls, { children: [
1534
- /* @__PURE__ */ jsx10(
2239
+ /* @__PURE__ */ jsxs9(SVGArea, { children: [
2240
+ showZoomControls && /* @__PURE__ */ jsxs9(ZoomControls, { children: [
2241
+ /* @__PURE__ */ jsx12(
1535
2242
  ZoomButton,
1536
2243
  {
1537
2244
  onClick: onZoomIn,
@@ -1541,7 +2248,7 @@ function FullscreenWorkspace({
1541
2248
  children: "+"
1542
2249
  }
1543
2250
  ),
1544
- /* @__PURE__ */ jsx10(
2251
+ /* @__PURE__ */ jsx12(
1545
2252
  ZoomButton,
1546
2253
  {
1547
2254
  onClick: onZoomOut,
@@ -1551,7 +2258,7 @@ function FullscreenWorkspace({
1551
2258
  children: "-"
1552
2259
  }
1553
2260
  ),
1554
- onResetZoom && /* @__PURE__ */ jsx10(
2261
+ onResetZoom && /* @__PURE__ */ jsx12(
1555
2262
  ZoomButton,
1556
2263
  {
1557
2264
  onClick: onResetZoom,
@@ -1562,33 +2269,33 @@ function FullscreenWorkspace({
1562
2269
  }
1563
2270
  )
1564
2271
  ] }),
1565
- /* @__PURE__ */ jsx10(ScrollableSVGWrapper, { className: "svg-scroll-wrapper", children: /* @__PURE__ */ jsxs7(FixedSVGContainer, { className: "svg-container", children: [
2272
+ /* @__PURE__ */ jsx12(ScrollableSVGWrapper, { className: "svg-scroll-wrapper", children: /* @__PURE__ */ jsxs9(FixedSVGContainer, { className: "svg-container", children: [
1566
2273
  svgContent,
1567
2274
  overlays
1568
2275
  ] }) })
1569
2276
  ] }),
1570
- /* @__PURE__ */ jsx10(LeftPanel, { collapsed: leftCollapsed, children: resolvedTabs.length > 0 ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1571
- /* @__PURE__ */ jsx10(
2277
+ /* @__PURE__ */ jsx12(LeftPanel, { collapsed: leftCollapsed, children: resolvedTabs.length > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
2278
+ /* @__PURE__ */ jsx12(
1572
2279
  PanelTabs,
1573
2280
  {
1574
2281
  value: currentTab,
1575
2282
  onChange: handleTabChangeInternal,
1576
2283
  "aria-label": "Left panel tabs",
1577
- children: resolvedTabs.map((tab) => /* @__PURE__ */ jsx10(Tab, { label: tab.label, value: tab.value }, tab.value))
2284
+ children: resolvedTabs.map((tab) => /* @__PURE__ */ jsx12(Tab, { label: tab.label, value: tab.value }, tab.value))
1578
2285
  }
1579
2286
  ),
1580
- /* @__PURE__ */ jsx10(PanelContent, { children: currentTabContent })
2287
+ /* @__PURE__ */ jsx12(PanelContent, { children: currentTabContent })
1581
2288
  ] }) : leftPanel }),
1582
- /* @__PURE__ */ jsx10(RightPanel, { collapsed: rightCollapsed, children: rightPanel }),
1583
- /* @__PURE__ */ jsx10(FooterPanel, { children: footer })
2289
+ /* @__PURE__ */ jsx12(RightPanel, { collapsed: rightCollapsed, children: rightPanel }),
2290
+ /* @__PURE__ */ jsx12(FooterPanel, { children: footer })
1584
2291
  ]
1585
2292
  }
1586
2293
  );
1587
2294
  }
1588
2295
 
1589
2296
  // src/theme/ThemeContext.tsx
1590
- import { createContext, useContext, useState as useState6, useEffect as useEffect3 } from "react";
1591
- import { jsx as jsx11 } from "react/jsx-runtime";
2297
+ import { createContext, useContext, useState as useState7, useEffect as useEffect4 } from "react";
2298
+ import { jsx as jsx13 } from "react/jsx-runtime";
1592
2299
  var ThemeContext = createContext(void 0);
1593
2300
  var useTheme = () => {
1594
2301
  const context = useContext(ThemeContext);
@@ -1598,8 +2305,8 @@ var useTheme = () => {
1598
2305
  return context;
1599
2306
  };
1600
2307
  var ThemeProvider = ({ children }) => {
1601
- const [theme, setThemeState] = useState6("modern");
1602
- useEffect3(() => {
2308
+ const [theme, setThemeState] = useState7("modern");
2309
+ useEffect4(() => {
1603
2310
  document.body.className = "";
1604
2311
  document.body.classList.add(`theme-${theme}`);
1605
2312
  document.documentElement.className = "";
@@ -1611,11 +2318,11 @@ var ThemeProvider = ({ children }) => {
1611
2318
  const setTheme = (newTheme) => {
1612
2319
  setThemeState(newTheme);
1613
2320
  };
1614
- return /* @__PURE__ */ jsx11(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
2321
+ return /* @__PURE__ */ jsx13(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
1615
2322
  };
1616
2323
 
1617
2324
  // src/theme/ThemeToggle.tsx
1618
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2325
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1619
2326
  var ThemeToggle = ({
1620
2327
  size = "medium",
1621
2328
  position = "top-right",
@@ -1686,15 +2393,15 @@ var ThemeToggle = ({
1686
2393
  transition: "all 0.2s ease",
1687
2394
  boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)"
1688
2395
  };
1689
- return /* @__PURE__ */ jsxs8("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
1690
- /* @__PURE__ */ jsx12("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
1691
- /* @__PURE__ */ jsx12("div", { style: toggleStyle, children: /* @__PURE__ */ jsx12("div", { style: thumbStyle }) }),
1692
- /* @__PURE__ */ jsx12("span", { style: { fontSize: "0.75rem", opacity: 0.8 }, children: theme === "modern" ? "Industrial Design" : "High Performance" })
2396
+ return /* @__PURE__ */ jsxs10("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
2397
+ /* @__PURE__ */ jsx14("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
2398
+ /* @__PURE__ */ jsx14("div", { style: toggleStyle, children: /* @__PURE__ */ jsx14("div", { style: thumbStyle }) }),
2399
+ /* @__PURE__ */ jsx14("span", { style: { fontSize: "0.75rem", opacity: 0.8 }, children: theme === "modern" ? "Industrial Design" : "High Performance" })
1693
2400
  ] });
1694
2401
  };
1695
2402
 
1696
2403
  // src/components/PIDCanvas/PIDCanvas.tsx
1697
- import { useCallback as useCallback7, useState as useState12, useEffect as useEffect10, useRef as useRef5 } from "react";
2404
+ import { useCallback as useCallback7, useState as useState13, useEffect as useEffect11, useRef as useRef5 } from "react";
1698
2405
 
1699
2406
  // src/diagram/symbols/mockSymbolLibrary.ts
1700
2407
  var mockSymbolLibrary = {
@@ -2915,7 +3622,7 @@ function getAvailableSymbols() {
2915
3622
  }
2916
3623
 
2917
3624
  // src/components/PIDCanvas/NodeRenderer.tsx
2918
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3625
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
2919
3626
  function NodeRenderer({
2920
3627
  nodes: nodes2,
2921
3628
  selectedNodeIds,
@@ -2926,13 +3633,13 @@ function NodeRenderer({
2926
3633
  dragOffset = { x: 0, y: 0 },
2927
3634
  enableDrag = false
2928
3635
  }) {
2929
- return /* @__PURE__ */ jsx13("g", { id: "layer-nodes", children: nodes2.filter((node) => node.visible !== false).map((node) => {
3636
+ return /* @__PURE__ */ jsx15("g", { id: "layer-nodes", children: nodes2.filter((node) => node.visible !== false).map((node) => {
2930
3637
  const symbol = getSymbolDefinition(node.symbolId);
2931
3638
  const isSelected = selectedNodeIds?.has(node.id) ?? false;
2932
3639
  const isDragging = draggingNodeId === node.id;
2933
3640
  if (!symbol) {
2934
- return /* @__PURE__ */ jsxs9("g", { children: [
2935
- /* @__PURE__ */ jsx13(
3641
+ return /* @__PURE__ */ jsxs11("g", { children: [
3642
+ /* @__PURE__ */ jsx15(
2936
3643
  "circle",
2937
3644
  {
2938
3645
  cx: node.transform.x,
@@ -2944,7 +3651,7 @@ function NodeRenderer({
2944
3651
  strokeWidth: "2"
2945
3652
  }
2946
3653
  ),
2947
- /* @__PURE__ */ jsx13(
3654
+ /* @__PURE__ */ jsx15(
2948
3655
  "text",
2949
3656
  {
2950
3657
  x: node.transform.x,
@@ -3001,7 +3708,7 @@ function NodeRenderer({
3001
3708
  };
3002
3709
  const fillColor = node.customColors?.fill ?? getStatusColor(node.status);
3003
3710
  const strokeColor = node.customColors?.stroke ?? "rgb(0, 0, 0)";
3004
- return /* @__PURE__ */ jsxs9(
3711
+ return /* @__PURE__ */ jsxs11(
3005
3712
  "g",
3006
3713
  {
3007
3714
  "data-node-id": node.id,
@@ -3034,7 +3741,7 @@ function NodeRenderer({
3034
3741
  }
3035
3742
  },
3036
3743
  children: [
3037
- isSelected && /* @__PURE__ */ jsx13(
3744
+ isSelected && /* @__PURE__ */ jsx15(
3038
3745
  "rect",
3039
3746
  {
3040
3747
  x: "-5",
@@ -3049,7 +3756,7 @@ function NodeRenderer({
3049
3756
  pointerEvents: "none"
3050
3757
  }
3051
3758
  ),
3052
- /* @__PURE__ */ jsx13(
3759
+ /* @__PURE__ */ jsx15(
3053
3760
  "g",
3054
3761
  {
3055
3762
  dangerouslySetInnerHTML: { __html: symbol.svgContent },
@@ -3057,7 +3764,7 @@ function NodeRenderer({
3057
3764
  style: { pointerEvents: "auto" }
3058
3765
  }
3059
3766
  ),
3060
- /* @__PURE__ */ jsx13(
3767
+ /* @__PURE__ */ jsx15(
3061
3768
  "text",
3062
3769
  {
3063
3770
  x: symbol.viewBox.width / 2 + (node.labelOffset?.x ?? 0),
@@ -3078,7 +3785,7 @@ function NodeRenderer({
3078
3785
  }
3079
3786
 
3080
3787
  // src/components/PIDCanvas/PipeRenderer.tsx
3081
- import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
3788
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3082
3789
  function PipeRenderer({
3083
3790
  pipes,
3084
3791
  selectedPipeIds,
@@ -3093,7 +3800,7 @@ function PipeRenderer({
3093
3800
  waypointDragOffset,
3094
3801
  onPipeSegmentClick
3095
3802
  }) {
3096
- return /* @__PURE__ */ jsx14("g", { id: "layer-pipes", children: pipes.filter((pipe) => pipe.visible !== false).map((pipe) => {
3803
+ return /* @__PURE__ */ jsx16("g", { id: "layer-pipes", children: pipes.filter((pipe) => pipe.visible !== false).map((pipe) => {
3097
3804
  const isSelected = selectedPipeIds?.has(pipe.id) ?? false;
3098
3805
  const isEditing = enableWaypointEditing && editingPipeId === pipe.id;
3099
3806
  const showEditingControls = enableWaypointEditing && isSelected;
@@ -3112,7 +3819,7 @@ function PipeRenderer({
3112
3819
  const strokeWidth = isSelected ? (pipeStyle.strokeWidth || 3) + 2 : pipeStyle.strokeWidth || 3;
3113
3820
  const strokeDasharray = pipeStyle.strokeDasharray;
3114
3821
  const opacity = pipeStyle.opacity !== void 0 ? pipeStyle.opacity : 1;
3115
- return /* @__PURE__ */ jsxs10(
3822
+ return /* @__PURE__ */ jsxs12(
3116
3823
  "g",
3117
3824
  {
3118
3825
  "data-pipe-id": pipe.id,
@@ -3125,8 +3832,8 @@ function PipeRenderer({
3125
3832
  cursor: onPipeClick ? "pointer" : "default"
3126
3833
  },
3127
3834
  children: [
3128
- !enableWaypointEditing && /* @__PURE__ */ jsxs10(Fragment3, { children: [
3129
- onPipeClick && /* @__PURE__ */ jsx14(
3835
+ !enableWaypointEditing && /* @__PURE__ */ jsxs12(Fragment5, { children: [
3836
+ onPipeClick && /* @__PURE__ */ jsx16(
3130
3837
  "polyline",
3131
3838
  {
3132
3839
  points: pointsString,
@@ -3138,7 +3845,7 @@ function PipeRenderer({
3138
3845
  pointerEvents: "stroke"
3139
3846
  }
3140
3847
  ),
3141
- /* @__PURE__ */ jsx14(
3848
+ /* @__PURE__ */ jsx16(
3142
3849
  "polyline",
3143
3850
  {
3144
3851
  points: pointsString,
@@ -3162,7 +3869,7 @@ function PipeRenderer({
3162
3869
  const waypointSize = isSelectedWaypoint ? 8 : isDragging ? 8 : showEditingControls ? 6 : isSelected ? 3 : 2;
3163
3870
  const waypointOpacity = isSelectedWaypoint ? 1 : isDragging ? 1 : showEditingControls ? 0.9 : isSelected ? 0.8 : 0.5;
3164
3871
  const waypointColor = isSelectedWaypoint ? "#3b82f6" : isEndpoint && showEditingControls ? "#f59e0b" : isDragging ? "#10b981" : stroke;
3165
- return /* @__PURE__ */ jsx14(
3872
+ return /* @__PURE__ */ jsx16(
3166
3873
  "circle",
3167
3874
  {
3168
3875
  cx: point.x,
@@ -3199,12 +3906,12 @@ function PipeRenderer({
3199
3906
  `${pipe.id}-point-${idx}`
3200
3907
  );
3201
3908
  }),
3202
- showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && /* @__PURE__ */ jsx14(Fragment3, { children: displayPoints.slice(0, -1).map((point, idx) => {
3909
+ showEditingControls && onPipeSegmentClick && displayPoints.length >= 2 && /* @__PURE__ */ jsx16(Fragment5, { children: displayPoints.slice(0, -1).map((point, idx) => {
3203
3910
  const nextPoint = displayPoints[idx + 1];
3204
3911
  const midX = (point.x + nextPoint.x) / 2;
3205
3912
  const midY = (point.y + nextPoint.y) / 2;
3206
- return /* @__PURE__ */ jsxs10("g", { children: [
3207
- /* @__PURE__ */ jsx14(
3913
+ return /* @__PURE__ */ jsxs12("g", { children: [
3914
+ /* @__PURE__ */ jsx16(
3208
3915
  "circle",
3209
3916
  {
3210
3917
  cx: midX,
@@ -3220,7 +3927,7 @@ function PipeRenderer({
3220
3927
  }
3221
3928
  }
3222
3929
  ),
3223
- /* @__PURE__ */ jsx14(
3930
+ /* @__PURE__ */ jsx16(
3224
3931
  "circle",
3225
3932
  {
3226
3933
  cx: midX,
@@ -3236,7 +3943,7 @@ function PipeRenderer({
3236
3943
  )
3237
3944
  ] }, `${pipe.id}-segment-${idx}`);
3238
3945
  }) }),
3239
- pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && /* @__PURE__ */ jsx14(
3946
+ pipe.showLabel !== false && pipe.label && pipe.routePoints.length >= 2 && /* @__PURE__ */ jsx16(
3240
3947
  "text",
3241
3948
  {
3242
3949
  x: pipe.routePoints[Math.floor(pipe.routePoints.length / 2)].x + (pipe.labelOffset?.x ?? 0),
@@ -3258,7 +3965,7 @@ function PipeRenderer({
3258
3965
  }
3259
3966
 
3260
3967
  // src/components/PIDCanvas/DataOverlay.tsx
3261
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3968
+ import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3262
3969
  function DataOverlay({
3263
3970
  nodeId,
3264
3971
  x,
@@ -3278,7 +3985,7 @@ function DataOverlay({
3278
3985
  const offsetX = display?.offsetX ?? 0;
3279
3986
  const offsetY = display?.offsetY ?? 0;
3280
3987
  if (!showValue) {
3281
- return /* @__PURE__ */ jsx15(Fragment4, {});
3988
+ return /* @__PURE__ */ jsx17(Fragment6, {});
3282
3989
  }
3283
3990
  const formatValue = (val) => {
3284
3991
  if (typeof val === "number") {
@@ -3296,9 +4003,14 @@ function DataOverlay({
3296
4003
  fault: { bg: "#dc2626", text: "#ffffff", border: "#b91c1c" },
3297
4004
  off: { bg: "#6b7280", text: "#ffffff", border: "#4b5563" }
3298
4005
  };
3299
- const customColor = telemetry.statusColors?.[value.status];
3300
- const defaultColorSet = defaultStatusColors[value.status] || defaultStatusColors.normal;
3301
- const statusColors = customColor ? { bg: customColor, text: "#ffffff", border: customColor } : defaultColorSet;
4006
+ const getStatusColors = () => {
4007
+ const customColor = telemetry.statusColors?.[value.status];
4008
+ if (customColor) {
4009
+ return { bg: customColor, text: "#ffffff", border: customColor };
4010
+ }
4011
+ return defaultStatusColors[value.status] || defaultStatusColors.normal;
4012
+ };
4013
+ const statusColors = getStatusColors();
3302
4014
  const colors = backgroundColor && backgroundColor !== "status" ? { bg: backgroundColor, text: textColor || "#ffffff", border: backgroundColor } : statusColors;
3303
4015
  const finalTextColor = textColor || colors.text;
3304
4016
  const formattedValue = formatValue(value.value);
@@ -3325,8 +4037,8 @@ function DataOverlay({
3325
4037
  const overlayWidth = 80 * scale;
3326
4038
  const displayX = x + offsetX;
3327
4039
  const displayY = y + offsetY;
3328
- return /* @__PURE__ */ jsxs11("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
3329
- /* @__PURE__ */ jsx15(
4040
+ return /* @__PURE__ */ jsxs13("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
4041
+ /* @__PURE__ */ jsx17(
3330
4042
  "rect",
3331
4043
  {
3332
4044
  x: displayX - overlayWidth / 2,
@@ -3341,7 +4053,7 @@ function DataOverlay({
3341
4053
  filter: "drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
3342
4054
  }
3343
4055
  ),
3344
- label && /* @__PURE__ */ jsx15(
4056
+ label && /* @__PURE__ */ jsx17(
3345
4057
  "text",
3346
4058
  {
3347
4059
  x: displayX,
@@ -3354,7 +4066,7 @@ function DataOverlay({
3354
4066
  children: label
3355
4067
  }
3356
4068
  ),
3357
- /* @__PURE__ */ jsx15(
4069
+ /* @__PURE__ */ jsx17(
3358
4070
  "text",
3359
4071
  {
3360
4072
  x: displayX,
@@ -3367,7 +4079,7 @@ function DataOverlay({
3367
4079
  children: displayText
3368
4080
  }
3369
4081
  ),
3370
- showStatus && value.status !== "normal" && /* @__PURE__ */ jsx15(
4082
+ showStatus && value.status !== "normal" && /* @__PURE__ */ jsx17(
3371
4083
  "circle",
3372
4084
  {
3373
4085
  cx: displayX + 35 * scale,
@@ -3375,7 +4087,7 @@ function DataOverlay({
3375
4087
  r: 3 * scale,
3376
4088
  fill: finalTextColor,
3377
4089
  opacity: "0.9",
3378
- children: /* @__PURE__ */ jsx15(
4090
+ children: /* @__PURE__ */ jsx17(
3379
4091
  "animate",
3380
4092
  {
3381
4093
  attributeName: "opacity",
@@ -3386,7 +4098,7 @@ function DataOverlay({
3386
4098
  )
3387
4099
  }
3388
4100
  ),
3389
- value.quality !== void 0 && value.quality < 100 && /* @__PURE__ */ jsxs11(
4101
+ value.quality !== void 0 && value.quality < 100 && /* @__PURE__ */ jsxs13(
3390
4102
  "text",
3391
4103
  {
3392
4104
  x: displayX,
@@ -3403,7 +4115,7 @@ function DataOverlay({
3403
4115
  ]
3404
4116
  }
3405
4117
  ),
3406
- hasSparkline && /* @__PURE__ */ jsx15("g", { transform: `translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`, children: /* @__PURE__ */ jsx15(
4118
+ hasSparkline && /* @__PURE__ */ jsx17("g", { transform: `translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`, children: /* @__PURE__ */ jsx17(
3407
4119
  "path",
3408
4120
  {
3409
4121
  d: sparklinePath,
@@ -3419,8 +4131,8 @@ function DataOverlay({
3419
4131
  }
3420
4132
 
3421
4133
  // src/components/PIDCanvas/PositionPanel.tsx
3422
- import { useState as useState7, useEffect as useEffect4 } from "react";
3423
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
4134
+ import { useState as useState8, useEffect as useEffect5 } from "react";
4135
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3424
4136
  function PositionPanel({
3425
4137
  selectedNode,
3426
4138
  selectedWaypoint,
@@ -3428,9 +4140,9 @@ function PositionPanel({
3428
4140
  onWaypointPositionChange,
3429
4141
  position = "top-right"
3430
4142
  }) {
3431
- const [x, setX] = useState7("");
3432
- const [y, setY] = useState7("");
3433
- useEffect4(() => {
4143
+ const [x, setX] = useState8("");
4144
+ const [y, setY] = useState8("");
4145
+ useEffect5(() => {
3434
4146
  if (selectedNode) {
3435
4147
  setX(selectedNode.transform.x.toFixed(2));
3436
4148
  setY(selectedNode.transform.y.toFixed(2));
@@ -3489,7 +4201,7 @@ function PositionPanel({
3489
4201
  "bottom-left": { bottom: 10, left: 10 },
3490
4202
  "bottom-right": { bottom: 10, right: 10 }
3491
4203
  };
3492
- return /* @__PURE__ */ jsxs12(
4204
+ return /* @__PURE__ */ jsxs14(
3493
4205
  "div",
3494
4206
  {
3495
4207
  style: {
@@ -3506,11 +4218,11 @@ function PositionPanel({
3506
4218
  fontSize: "13px"
3507
4219
  },
3508
4220
  children: [
3509
- /* @__PURE__ */ jsx16("div", { style: { marginBottom: "8px", fontWeight: 600, color: "#333" }, children: "Position" }),
3510
- /* @__PURE__ */ jsxs12("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
3511
- /* @__PURE__ */ jsxs12("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3512
- /* @__PURE__ */ jsx16("label", { style: { width: "16px", color: "#666" }, children: "X:" }),
3513
- /* @__PURE__ */ jsx16(
4221
+ /* @__PURE__ */ jsx18("div", { style: { marginBottom: "8px", fontWeight: 600, color: "#333" }, children: "Position" }),
4222
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
4223
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
4224
+ /* @__PURE__ */ jsx18("label", { style: { width: "16px", color: "#666" }, children: "X:" }),
4225
+ /* @__PURE__ */ jsx18(
3514
4226
  "input",
3515
4227
  {
3516
4228
  type: "number",
@@ -3528,9 +4240,9 @@ function PositionPanel({
3528
4240
  }
3529
4241
  )
3530
4242
  ] }),
3531
- /* @__PURE__ */ jsxs12("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3532
- /* @__PURE__ */ jsx16("label", { style: { width: "16px", color: "#666" }, children: "Y:" }),
3533
- /* @__PURE__ */ jsx16(
4243
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
4244
+ /* @__PURE__ */ jsx18("label", { style: { width: "16px", color: "#666" }, children: "Y:" }),
4245
+ /* @__PURE__ */ jsx18(
3534
4246
  "input",
3535
4247
  {
3536
4248
  type: "number",
@@ -3549,14 +4261,14 @@ function PositionPanel({
3549
4261
  )
3550
4262
  ] })
3551
4263
  ] }),
3552
- /* @__PURE__ */ jsx16("div", { style: { marginTop: "8px", fontSize: "11px", color: "#888" }, children: "Use arrow keys to nudge (\xB11px)" })
4264
+ /* @__PURE__ */ jsx18("div", { style: { marginTop: "8px", fontSize: "11px", color: "#888" }, children: "Use arrow keys to nudge (\xB11px)" })
3553
4265
  ]
3554
4266
  }
3555
4267
  );
3556
4268
  }
3557
4269
 
3558
4270
  // src/diagram/hooks/useViewBox.ts
3559
- import { useState as useState8, useCallback as useCallback3, useRef as useRef2, useEffect as useEffect5 } from "react";
4271
+ import { useState as useState9, useCallback as useCallback3, useRef as useRef2, useEffect as useEffect6 } from "react";
3560
4272
  function useViewBox(options) {
3561
4273
  const {
3562
4274
  initialViewBox,
@@ -3567,7 +4279,7 @@ function useViewBox(options) {
3567
4279
  zoomSensitivity = 1e-3,
3568
4280
  allowLeftClickPan = true
3569
4281
  } = options;
3570
- const [viewBox, setViewBox] = useState8(initialViewBox);
4282
+ const [viewBox, setViewBox] = useState9(initialViewBox);
3571
4283
  const svgRef = useRef2(null);
3572
4284
  const isPanningRef = useRef2(false);
3573
4285
  const lastMousePosRef = useRef2({ x: 0, y: 0 });
@@ -3638,7 +4350,7 @@ function useViewBox(options) {
3638
4350
  height: newHeight
3639
4351
  });
3640
4352
  }, [initialViewBox]);
3641
- useEffect5(() => {
4353
+ useEffect6(() => {
3642
4354
  if (!enableZoom || !svgRef.current) return;
3643
4355
  const svg = svgRef.current;
3644
4356
  const handleWheel = (e) => {
@@ -3671,7 +4383,7 @@ function useViewBox(options) {
3671
4383
  svg.addEventListener("wheel", handleWheel, { passive: false });
3672
4384
  return () => svg.removeEventListener("wheel", handleWheel);
3673
4385
  }, [enableZoom, zoomSensitivity, minZoom, initialViewBox]);
3674
- useEffect5(() => {
4386
+ useEffect6(() => {
3675
4387
  if (!enablePan || !svgRef.current) return;
3676
4388
  const svg = svgRef.current;
3677
4389
  const handleMouseDown = (e) => {
@@ -3747,7 +4459,7 @@ function useViewBox(options) {
3747
4459
  window.removeEventListener("keyup", handleKeyUp);
3748
4460
  };
3749
4461
  }, [enablePan, allowLeftClickPan]);
3750
- useEffect5(() => {
4462
+ useEffect6(() => {
3751
4463
  if (!svgRef.current) return;
3752
4464
  if (!enablePan && !enableZoom) return;
3753
4465
  const svg = svgRef.current;
@@ -3877,14 +4589,14 @@ function useViewBox(options) {
3877
4589
  }
3878
4590
 
3879
4591
  // src/diagram/hooks/useSelection.ts
3880
- import { useState as useState9, useCallback as useCallback4 } from "react";
4592
+ import { useState as useState10, useCallback as useCallback4 } from "react";
3881
4593
  function useSelection(options = {}) {
3882
4594
  const {
3883
4595
  multiSelect = true,
3884
4596
  initialSelection = { selectedNodeIds: /* @__PURE__ */ new Set(), selectedPipeIds: /* @__PURE__ */ new Set() },
3885
4597
  onSelectionChange
3886
4598
  } = options;
3887
- const [selection, setSelection] = useState9(initialSelection);
4599
+ const [selection, setSelection] = useState10(initialSelection);
3888
4600
  const notifyChange = useCallback4(
3889
4601
  (newSelection) => {
3890
4602
  if (onSelectionChange) {
@@ -3982,14 +4694,14 @@ function useSelection(options = {}) {
3982
4694
  }
3983
4695
 
3984
4696
  // src/diagram/hooks/useTelemetry.ts
3985
- import { useState as useState10, useCallback as useCallback5, useEffect as useEffect6, useRef as useRef3 } from "react";
4697
+ import { useState as useState11, useCallback as useCallback5, useEffect as useEffect7, useRef as useRef3 } from "react";
3986
4698
  function useTelemetry(options = {}) {
3987
4699
  const {
3988
4700
  initialBindings = [],
3989
4701
  onTelemetryChange,
3990
4702
  refreshInterval = 0
3991
4703
  } = options;
3992
- const [telemetry, setTelemetry] = useState10(() => {
4704
+ const [telemetry, setTelemetry] = useState11(() => {
3993
4705
  const map = /* @__PURE__ */ new Map();
3994
4706
  initialBindings.forEach((binding) => {
3995
4707
  map.set(binding.nodeId, binding);
@@ -4110,7 +4822,7 @@ function useTelemetry(options = {}) {
4110
4822
  },
4111
4823
  [notifyChange]
4112
4824
  );
4113
- useEffect6(() => {
4825
+ useEffect7(() => {
4114
4826
  if (refreshInterval <= 0) return;
4115
4827
  const intervalId = setInterval(() => {
4116
4828
  notifyChange(telemetryRef.current);
@@ -4128,10 +4840,10 @@ function useTelemetry(options = {}) {
4128
4840
  }
4129
4841
 
4130
4842
  // src/diagram/hooks/useTelemetryStatus.ts
4131
- import { useEffect as useEffect7 } from "react";
4843
+ import { useEffect as useEffect8 } from "react";
4132
4844
  function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4133
4845
  const { enabled = true, mapStatus } = options;
4134
- useEffect7(() => {
4846
+ useEffect8(() => {
4135
4847
  if (!enabled) return;
4136
4848
  const defaultMapStatus = (telemetryStatus) => {
4137
4849
  switch (telemetryStatus) {
@@ -4170,7 +4882,7 @@ function useTelemetryStatus(telemetry, diagram, setDiagram, options = {}) {
4170
4882
  }
4171
4883
 
4172
4884
  // src/diagram/hooks/useDrag.ts
4173
- import { useState as useState11, useCallback as useCallback6, useRef as useRef4, useEffect as useEffect8 } from "react";
4885
+ import { useState as useState12, useCallback as useCallback6, useRef as useRef4, useEffect as useEffect9 } from "react";
4174
4886
  function useDrag(options = {}) {
4175
4887
  const {
4176
4888
  onDragStart,
@@ -4180,13 +4892,13 @@ function useDrag(options = {}) {
4180
4892
  dragThreshold = 3,
4181
4893
  screenToWorld = (x, y) => ({ x, y })
4182
4894
  } = options;
4183
- const [dragState, setDragState] = useState11({
4895
+ const [dragState, setDragState] = useState12({
4184
4896
  isDragging: false,
4185
4897
  startPosition: null,
4186
4898
  offset: { x: 0, y: 0 },
4187
4899
  draggedId: null
4188
4900
  });
4189
- const [isListening, setIsListening] = useState11(false);
4901
+ const [isListening, setIsListening] = useState12(false);
4190
4902
  const dragStartRef = useRef4(null);
4191
4903
  const hasMovedRef = useRef4(false);
4192
4904
  const applySnap = useCallback6(
@@ -4355,7 +5067,7 @@ function useDrag(options = {}) {
4355
5067
  },
4356
5068
  [screenToWorld, applySnap, onDragEnd]
4357
5069
  );
4358
- useEffect8(() => {
5070
+ useEffect9(() => {
4359
5071
  if (!isListening) return;
4360
5072
  document.addEventListener("mousemove", handleMouseMove);
4361
5073
  document.addEventListener("mouseup", handleMouseUp);
@@ -4424,7 +5136,7 @@ function useDrag(options = {}) {
4424
5136
  }
4425
5137
 
4426
5138
  // src/diagram/hooks/useKeyboardShortcuts.ts
4427
- import { useEffect as useEffect9 } from "react";
5139
+ import { useEffect as useEffect10 } from "react";
4428
5140
  function useKeyboardShortcuts(options) {
4429
5141
  const {
4430
5142
  onDelete,
@@ -4435,7 +5147,7 @@ function useKeyboardShortcuts(options) {
4435
5147
  onSelectAll,
4436
5148
  enabled = true
4437
5149
  } = options;
4438
- useEffect9(() => {
5150
+ useEffect10(() => {
4439
5151
  if (!enabled) return;
4440
5152
  const handleKeyDown = (event) => {
4441
5153
  const target = event.target;
@@ -4544,7 +5256,7 @@ function nodeHasPort(node, portId) {
4544
5256
  }
4545
5257
 
4546
5258
  // src/components/PIDCanvas/PIDCanvas.tsx
4547
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5259
+ import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
4548
5260
  function PIDCanvas({
4549
5261
  diagram,
4550
5262
  className = "",
@@ -4571,21 +5283,21 @@ function PIDCanvas({
4571
5283
  telemetry,
4572
5284
  ...props
4573
5285
  }) {
4574
- const [localDiagram, setLocalDiagram] = useState12(diagram);
4575
- const [dragOverPosition, setDragOverPosition] = useState12(null);
4576
- const [isConnecting, setIsConnecting] = useState12(false);
4577
- const [connectionSource, setConnectionSource] = useState12(null);
4578
- const [connectionSourcePort, setConnectionSourcePort] = useState12(null);
4579
- const [connectionCursor, setConnectionCursor] = useState12(null);
4580
- const [hoveredNode, setHoveredNode] = useState12(null);
4581
- const [hoveredPort, setHoveredPort] = useState12(null);
4582
- const [selectedWaypoint, setSelectedWaypoint] = useState12(null);
5286
+ const [localDiagram, setLocalDiagram] = useState13(diagram);
5287
+ const [dragOverPosition, setDragOverPosition] = useState13(null);
5288
+ const [isConnecting, setIsConnecting] = useState13(false);
5289
+ const [connectionSource, setConnectionSource] = useState13(null);
5290
+ const [connectionSourcePort, setConnectionSourcePort] = useState13(null);
5291
+ const [connectionCursor, setConnectionCursor] = useState13(null);
5292
+ const [hoveredNode, setHoveredNode] = useState13(null);
5293
+ const [hoveredPort, setHoveredPort] = useState13(null);
5294
+ const [selectedWaypoint, setSelectedWaypoint] = useState13(null);
4583
5295
  const historyStack = useRef5([]);
4584
5296
  const redoStack = useRef5([]);
4585
5297
  const isUndoRedoAction = useRef5(false);
4586
5298
  const isInternalChange = useRef5(false);
4587
5299
  const lastInternalDiagram = useRef5(null);
4588
- useEffect10(() => {
5300
+ useEffect11(() => {
4589
5301
  const isExternal = !isInternalChange.current && diagram !== lastInternalDiagram.current;
4590
5302
  if (isExternal) {
4591
5303
  setLocalDiagram(diagram);
@@ -4630,7 +5342,7 @@ function PIDCanvas({
4630
5342
  });
4631
5343
  return { minX, minY, maxX, maxY };
4632
5344
  }, [localDiagram.nodes]);
4633
- useEffect10(() => {
5345
+ useEffect11(() => {
4634
5346
  if (onMounted) {
4635
5347
  const controls = {
4636
5348
  fitToContent: (padding = 50) => {
@@ -4709,7 +5421,7 @@ function PIDCanvas({
4709
5421
  isUndoRedoAction.current = false;
4710
5422
  }, 0);
4711
5423
  }, [localDiagram, onDiagramChange]);
4712
- useEffect10(() => {
5424
+ useEffect11(() => {
4713
5425
  const handleKeyDown = (e) => {
4714
5426
  const isCtrlOrCmd = e.ctrlKey || e.metaKey;
4715
5427
  if (isCtrlOrCmd && e.key === "z" && !e.shiftKey) {
@@ -4726,7 +5438,7 @@ function PIDCanvas({
4726
5438
  window.addEventListener("keydown", handleKeyDown);
4727
5439
  return () => window.removeEventListener("keydown", handleKeyDown);
4728
5440
  }, [undo, redo]);
4729
- useEffect10(() => {
5441
+ useEffect11(() => {
4730
5442
  if (!features.dragNodes) return;
4731
5443
  const handleKeyDown = (e) => {
4732
5444
  if (selection.selectedNodeIds.size === 0 && !selectedWaypoint) return;
@@ -5136,7 +5848,7 @@ function PIDCanvas({
5136
5848
  const viewBox = controlledViewBox;
5137
5849
  const displayDiagram = localDiagram;
5138
5850
  const selectedNode = selectionEnabled && selection.selectedNodeIds.size === 1 ? localDiagram.nodes.find((n) => selection.selectedNodeIds.has(n.id)) || null : null;
5139
- return /* @__PURE__ */ jsxs13(
5851
+ return /* @__PURE__ */ jsxs15(
5140
5852
  "div",
5141
5853
  {
5142
5854
  ref: containerRef,
@@ -5151,7 +5863,7 @@ function PIDCanvas({
5151
5863
  },
5152
5864
  ...props,
5153
5865
  children: [
5154
- /* @__PURE__ */ jsxs13(
5866
+ /* @__PURE__ */ jsxs15(
5155
5867
  "svg",
5156
5868
  {
5157
5869
  ref: svgRef,
@@ -5170,15 +5882,15 @@ function PIDCanvas({
5170
5882
  onDrop: handleDrop,
5171
5883
  onDragLeave: handleDragLeave,
5172
5884
  children: [
5173
- features.showGrid !== false ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
5174
- /* @__PURE__ */ jsxs13("defs", { children: [
5175
- /* @__PURE__ */ jsx17("pattern", { id: "grid-minor", width: "5", height: "5", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx17("path", { d: "M 5 0 L 0 0 0 5", fill: "none", stroke: "#e5e7eb", strokeWidth: "0.5" }) }),
5176
- /* @__PURE__ */ jsxs13("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
5177
- /* @__PURE__ */ jsx17("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
5178
- /* @__PURE__ */ jsx17("path", { d: "M 25 0 L 0 0 0 25", fill: "none", stroke: "#d1d5db", strokeWidth: "1" })
5885
+ features.showGrid !== false ? /* @__PURE__ */ jsxs15(Fragment7, { children: [
5886
+ /* @__PURE__ */ jsxs15("defs", { children: [
5887
+ /* @__PURE__ */ jsx19("pattern", { id: "grid-minor", width: "5", height: "5", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx19("path", { d: "M 5 0 L 0 0 0 5", fill: "none", stroke: "#e5e7eb", strokeWidth: "0.5" }) }),
5888
+ /* @__PURE__ */ jsxs15("pattern", { id: "grid-major", width: "25", height: "25", patternUnits: "userSpaceOnUse", children: [
5889
+ /* @__PURE__ */ jsx19("rect", { width: "25", height: "25", fill: "url(#grid-minor)" }),
5890
+ /* @__PURE__ */ jsx19("path", { d: "M 25 0 L 0 0 0 25", fill: "none", stroke: "#d1d5db", strokeWidth: "1" })
5179
5891
  ] })
5180
5892
  ] }),
5181
- /* @__PURE__ */ jsx17(
5893
+ /* @__PURE__ */ jsx19(
5182
5894
  "rect",
5183
5895
  {
5184
5896
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5188,7 +5900,7 @@ function PIDCanvas({
5188
5900
  fill: "url(#grid-major)"
5189
5901
  }
5190
5902
  )
5191
- ] }) : /* @__PURE__ */ jsx17(
5903
+ ] }) : /* @__PURE__ */ jsx19(
5192
5904
  "rect",
5193
5905
  {
5194
5906
  x: Math.floor(viewBox.x / 25) * 25 - viewBox.width,
@@ -5198,7 +5910,7 @@ function PIDCanvas({
5198
5910
  fill: features.backgroundColor ?? "#ffffff"
5199
5911
  }
5200
5912
  ),
5201
- /* @__PURE__ */ jsx17(
5913
+ /* @__PURE__ */ jsx19(
5202
5914
  PipeRenderer,
5203
5915
  {
5204
5916
  pipes: displayDiagram.pipes,
@@ -5212,12 +5924,12 @@ function PIDCanvas({
5212
5924
  onPipeSegmentClick: void 0
5213
5925
  }
5214
5926
  ),
5215
- /* @__PURE__ */ jsx17(
5927
+ /* @__PURE__ */ jsx19(
5216
5928
  NodeRenderer,
5217
5929
  {
5218
5930
  nodes: displayDiagram.nodes,
5219
5931
  selectedNodeIds: selectionEnabled ? selection.selectedNodeIds : void 0,
5220
- onNodeClick: selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
5932
+ onNodeClick: onNodeClick ? handleNodeClick : selectionEnabled || features.connectionMode ? handleNodeClick : void 0,
5221
5933
  onNodeDoubleClick: selectionEnabled ? handleNodeDoubleClick : void 0,
5222
5934
  enableDrag: dragEnabled && !isConnecting,
5223
5935
  onNodeDragStart: dragEnabled && !isConnecting ? handleNodeDragStart : void 0,
@@ -5225,7 +5937,7 @@ function PIDCanvas({
5225
5937
  dragOffset: dragState.offset
5226
5938
  }
5227
5939
  ),
5228
- (features.editPipeRoutes ?? false) && /* @__PURE__ */ jsx17(
5940
+ (features.editPipeRoutes ?? false) && /* @__PURE__ */ jsx19(
5229
5941
  PipeRenderer,
5230
5942
  {
5231
5943
  pipes: displayDiagram.pipes,
@@ -5242,14 +5954,14 @@ function PIDCanvas({
5242
5954
  onPipeSegmentClick: handlePipeSegmentClick
5243
5955
  }
5244
5956
  ),
5245
- telemetryEnabled && telemetry && /* @__PURE__ */ jsx17("g", { id: "layer-telemetry", children: displayDiagram.nodes.map((node) => {
5957
+ telemetryEnabled && telemetry && /* @__PURE__ */ jsx19("g", { id: "layer-telemetry", children: displayDiagram.nodes.map((node) => {
5246
5958
  const binding = telemetry.get(node.id);
5247
5959
  if (!binding) return null;
5248
5960
  const symbol = getSymbolDefinition(node.symbolId);
5249
5961
  if (!symbol) return null;
5250
5962
  const overlayX = node.transform.x + symbol.viewBox.width / 2;
5251
5963
  const overlayY = node.transform.y + symbol.viewBox.height + 50;
5252
- return /* @__PURE__ */ jsx17(
5964
+ return /* @__PURE__ */ jsx19(
5253
5965
  DataOverlay,
5254
5966
  {
5255
5967
  nodeId: node.id,
@@ -5261,7 +5973,7 @@ function PIDCanvas({
5261
5973
  `telemetry-${node.id}`
5262
5974
  );
5263
5975
  }) }),
5264
- dragOverPosition && /* @__PURE__ */ jsx17(
5976
+ dragOverPosition && /* @__PURE__ */ jsx19(
5265
5977
  "circle",
5266
5978
  {
5267
5979
  cx: dragOverPosition.x,
@@ -5274,7 +5986,7 @@ function PIDCanvas({
5274
5986
  pointerEvents: "none"
5275
5987
  }
5276
5988
  ),
5277
- isConnecting && connectionSource && connectionCursor && /* @__PURE__ */ jsx17("g", { id: "connection-preview", pointerEvents: "none", children: (() => {
5989
+ isConnecting && connectionSource && connectionCursor && /* @__PURE__ */ jsx19("g", { id: "connection-preview", pointerEvents: "none", children: (() => {
5278
5990
  const sourceNode = displayDiagram.nodes.find((n) => n.id === connectionSource);
5279
5991
  if (!sourceNode) return null;
5280
5992
  const symbol = getSymbolDefinition(sourceNode.symbolId);
@@ -5290,8 +6002,8 @@ function PIDCanvas({
5290
6002
  }
5291
6003
  }
5292
6004
  }
5293
- return /* @__PURE__ */ jsxs13(Fragment5, { children: [
5294
- /* @__PURE__ */ jsx17(
6005
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
6006
+ /* @__PURE__ */ jsx19(
5295
6007
  "circle",
5296
6008
  {
5297
6009
  cx: lineStartX,
@@ -5303,7 +6015,7 @@ function PIDCanvas({
5303
6015
  opacity: 0.6
5304
6016
  }
5305
6017
  ),
5306
- /* @__PURE__ */ jsx17(
6018
+ /* @__PURE__ */ jsx19(
5307
6019
  "line",
5308
6020
  {
5309
6021
  x1: lineStartX,
@@ -5316,7 +6028,7 @@ function PIDCanvas({
5316
6028
  opacity: 0.8
5317
6029
  }
5318
6030
  ),
5319
- /* @__PURE__ */ jsx17(
6031
+ /* @__PURE__ */ jsx19(
5320
6032
  "circle",
5321
6033
  {
5322
6034
  cx: connectionCursor.x,
@@ -5328,7 +6040,7 @@ function PIDCanvas({
5328
6040
  )
5329
6041
  ] });
5330
6042
  })() }),
5331
- features.connectionMode && /* @__PURE__ */ jsx17("g", { id: "port-indicators", children: displayDiagram.nodes.map((node) => {
6043
+ features.connectionMode && /* @__PURE__ */ jsx19("g", { id: "port-indicators", children: displayDiagram.nodes.map((node) => {
5332
6044
  const symbol = getSymbolDefinition(node.symbolId);
5333
6045
  if (!symbol?.ports) return null;
5334
6046
  return symbol.ports.map((port) => {
@@ -5336,8 +6048,8 @@ function PIDCanvas({
5336
6048
  if (!portPos) return null;
5337
6049
  const isHovered = hoveredPort === port.id && hoveredNode === node.id;
5338
6050
  const isSourcePort = node.id === connectionSource && port.id === connectionSourcePort;
5339
- return /* @__PURE__ */ jsxs13("g", { children: [
5340
- /* @__PURE__ */ jsx17(
6051
+ return /* @__PURE__ */ jsxs15("g", { children: [
6052
+ /* @__PURE__ */ jsx19(
5341
6053
  "circle",
5342
6054
  {
5343
6055
  cx: portPos.x,
@@ -5370,7 +6082,7 @@ function PIDCanvas({
5370
6082
  }
5371
6083
  }
5372
6084
  ),
5373
- /* @__PURE__ */ jsx17(
6085
+ /* @__PURE__ */ jsx19(
5374
6086
  "circle",
5375
6087
  {
5376
6088
  cx: portPos.x,
@@ -5389,7 +6101,7 @@ function PIDCanvas({
5389
6101
  ]
5390
6102
  }
5391
6103
  ),
5392
- features.dragNodes && /* @__PURE__ */ jsx17(
6104
+ features.dragNodes && /* @__PURE__ */ jsx19(
5393
6105
  PositionPanel,
5394
6106
  {
5395
6107
  selectedNode,
@@ -5409,16 +6121,16 @@ function PIDCanvas({
5409
6121
  }
5410
6122
 
5411
6123
  // src/components/SymbolLibrary/SymbolLibrary.tsx
5412
- import { useState as useState13 } from "react";
5413
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
6124
+ import { useState as useState14 } from "react";
6125
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
5414
6126
  function SymbolLibrary({
5415
6127
  className = "",
5416
6128
  onSymbolDragStart,
5417
6129
  showCategories = true,
5418
6130
  categories = ["Valves", "Pumps", "Tanks", "Instruments", "Displays"]
5419
6131
  }) {
5420
- const [selectedCategory, setSelectedCategory] = useState13("all");
5421
- const [searchQuery, setSearchQuery] = useState13("");
6132
+ const [selectedCategory, setSelectedCategory] = useState14("all");
6133
+ const [searchQuery, setSearchQuery] = useState14("");
5422
6134
  const symbolIds = getAvailableSymbols();
5423
6135
  const allSymbols = symbolIds.map((id) => getSymbolDefinition(id)).filter((symbol) => symbol !== void 0);
5424
6136
  const categoryMap = {
@@ -5439,9 +6151,9 @@ function SymbolLibrary({
5439
6151
  event.dataTransfer.effectAllowed = "copy";
5440
6152
  onSymbolDragStart?.(symbol.id, event);
5441
6153
  };
5442
- return /* @__PURE__ */ jsxs14("div", { className: `symbol-library ${className}`.trim(), style: styles.container, children: [
5443
- /* @__PURE__ */ jsx18("div", { style: styles.header, children: /* @__PURE__ */ jsx18("h3", { style: styles.title, children: "Symbol Library" }) }),
5444
- /* @__PURE__ */ jsx18("div", { style: styles.searchContainer, children: /* @__PURE__ */ jsx18(
6154
+ return /* @__PURE__ */ jsxs16("div", { className: `symbol-library ${className}`.trim(), style: styles.container, children: [
6155
+ /* @__PURE__ */ jsx20("div", { style: styles.header, children: /* @__PURE__ */ jsx20("h3", { style: styles.title, children: "Symbol Library" }) }),
6156
+ /* @__PURE__ */ jsx20("div", { style: styles.searchContainer, children: /* @__PURE__ */ jsx20(
5445
6157
  "input",
5446
6158
  {
5447
6159
  type: "text",
@@ -5451,8 +6163,8 @@ function SymbolLibrary({
5451
6163
  style: styles.searchInput
5452
6164
  }
5453
6165
  ) }),
5454
- showCategories && /* @__PURE__ */ jsxs14("div", { style: styles.categories, children: [
5455
- /* @__PURE__ */ jsx18(
6166
+ showCategories && /* @__PURE__ */ jsxs16("div", { style: styles.categories, children: [
6167
+ /* @__PURE__ */ jsx20(
5456
6168
  "button",
5457
6169
  {
5458
6170
  onClick: () => setSelectedCategory("all"),
@@ -5463,7 +6175,7 @@ function SymbolLibrary({
5463
6175
  children: "All"
5464
6176
  }
5465
6177
  ),
5466
- categories.map((category) => /* @__PURE__ */ jsx18(
6178
+ categories.map((category) => /* @__PURE__ */ jsx20(
5467
6179
  "button",
5468
6180
  {
5469
6181
  onClick: () => setSelectedCategory(category),
@@ -5476,7 +6188,7 @@ function SymbolLibrary({
5476
6188
  category
5477
6189
  ))
5478
6190
  ] }),
5479
- /* @__PURE__ */ jsx18("div", { style: styles.symbolGrid, children: filteredSymbols.length === 0 ? /* @__PURE__ */ jsx18("div", { style: styles.emptyState, children: "No symbols found" }) : filteredSymbols.map((symbol) => /* @__PURE__ */ jsxs14(
6191
+ /* @__PURE__ */ jsx20("div", { style: styles.symbolGrid, children: filteredSymbols.length === 0 ? /* @__PURE__ */ jsx20("div", { style: styles.emptyState, children: "No symbols found" }) : filteredSymbols.map((symbol) => /* @__PURE__ */ jsxs16(
5480
6192
  "div",
5481
6193
  {
5482
6194
  draggable: true,
@@ -5484,15 +6196,15 @@ function SymbolLibrary({
5484
6196
  style: styles.symbolCard,
5485
6197
  title: symbol.metadata?.description || symbol.name,
5486
6198
  children: [
5487
- /* @__PURE__ */ jsx18("div", { style: styles.symbolPreview, children: /* @__PURE__ */ jsx18(
6199
+ /* @__PURE__ */ jsx20("div", { style: styles.symbolPreview, children: /* @__PURE__ */ jsx20(
5488
6200
  "svg",
5489
6201
  {
5490
6202
  viewBox: `${symbol.viewBox.x} ${symbol.viewBox.y} ${symbol.viewBox.width} ${symbol.viewBox.height}`,
5491
6203
  style: styles.symbolSvg,
5492
- children: /* @__PURE__ */ jsx18("g", { dangerouslySetInnerHTML: { __html: symbol.svgContent } })
6204
+ children: /* @__PURE__ */ jsx20("g", { dangerouslySetInnerHTML: { __html: symbol.svgContent } })
5493
6205
  }
5494
6206
  ) }),
5495
- /* @__PURE__ */ jsx18("div", { style: styles.symbolName, children: symbol.name })
6207
+ /* @__PURE__ */ jsx20("div", { style: styles.symbolName, children: symbol.name })
5496
6208
  ]
5497
6209
  },
5498
6210
  symbol.id
@@ -5601,7 +6313,7 @@ var styles = {
5601
6313
  };
5602
6314
 
5603
6315
  // src/components/NodeConfigPanel/NodeConfigPanel.tsx
5604
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
6316
+ import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
5605
6317
  function NodeConfigPanel({
5606
6318
  node,
5607
6319
  binding,
@@ -5620,15 +6332,15 @@ function NodeConfigPanel({
5620
6332
  padding: "20px",
5621
6333
  ...style
5622
6334
  };
5623
- return /* @__PURE__ */ jsxs15("div", { className, style: defaultStyle, children: [
5624
- /* @__PURE__ */ jsxs15("h2", { style: { margin: "0 0 16px 0", fontSize: "1rem", fontWeight: 600 }, children: [
6335
+ return /* @__PURE__ */ jsxs17("div", { className, style: defaultStyle, children: [
6336
+ /* @__PURE__ */ jsxs17("h2", { style: { margin: "0 0 16px 0", fontSize: "1rem", fontWeight: 600 }, children: [
5625
6337
  "Configure: ",
5626
6338
  node.id
5627
6339
  ] }),
5628
- /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "20px" }, children: [
5629
- /* @__PURE__ */ jsx19("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Node Settings" }),
5630
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label:" }),
5631
- /* @__PURE__ */ jsx19(
6340
+ /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "20px" }, children: [
6341
+ /* @__PURE__ */ jsx21("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Node Settings" }),
6342
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label:" }),
6343
+ /* @__PURE__ */ jsx21(
5632
6344
  "input",
5633
6345
  {
5634
6346
  type: "text",
@@ -5644,10 +6356,10 @@ function NodeConfigPanel({
5644
6356
  }
5645
6357
  }
5646
6358
  ),
5647
- /* @__PURE__ */ jsxs15("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5648
- /* @__PURE__ */ jsxs15("div", { children: [
5649
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset X:" }),
5650
- /* @__PURE__ */ jsx19(
6359
+ /* @__PURE__ */ jsxs17("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
6360
+ /* @__PURE__ */ jsxs17("div", { children: [
6361
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset X:" }),
6362
+ /* @__PURE__ */ jsx21(
5651
6363
  "input",
5652
6364
  {
5653
6365
  type: "number",
@@ -5668,9 +6380,9 @@ function NodeConfigPanel({
5668
6380
  }
5669
6381
  )
5670
6382
  ] }),
5671
- /* @__PURE__ */ jsxs15("div", { children: [
5672
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset Y:" }),
5673
- /* @__PURE__ */ jsx19(
6383
+ /* @__PURE__ */ jsxs17("div", { children: [
6384
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Offset Y:" }),
6385
+ /* @__PURE__ */ jsx21(
5674
6386
  "input",
5675
6387
  {
5676
6388
  type: "number",
@@ -5692,9 +6404,9 @@ function NodeConfigPanel({
5692
6404
  )
5693
6405
  ] })
5694
6406
  ] }),
5695
- /* @__PURE__ */ jsxs15("div", { children: [
5696
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Font Size:" }),
5697
- /* @__PURE__ */ jsx19(
6407
+ /* @__PURE__ */ jsxs17("div", { children: [
6408
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Label Font Size:" }),
6409
+ /* @__PURE__ */ jsx21(
5698
6410
  "input",
5699
6411
  {
5700
6412
  type: "number",
@@ -5714,9 +6426,9 @@ function NodeConfigPanel({
5714
6426
  }
5715
6427
  )
5716
6428
  ] }),
5717
- /* @__PURE__ */ jsxs15("div", { children: [
5718
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
5719
- /* @__PURE__ */ jsx19(
6429
+ /* @__PURE__ */ jsxs17("div", { children: [
6430
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Rotation (degrees):" }),
6431
+ /* @__PURE__ */ jsx21(
5720
6432
  "input",
5721
6433
  {
5722
6434
  type: "number",
@@ -5741,9 +6453,9 @@ function NodeConfigPanel({
5741
6453
  )
5742
6454
  ] })
5743
6455
  ] }),
5744
- /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "20px" }, children: [
5745
- /* @__PURE__ */ jsx19("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Telemetry Data" }),
5746
- !binding ? /* @__PURE__ */ jsx19(
6456
+ /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "20px" }, children: [
6457
+ /* @__PURE__ */ jsx21("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Telemetry Data" }),
6458
+ !binding ? /* @__PURE__ */ jsx21(
5747
6459
  "button",
5748
6460
  {
5749
6461
  onClick: () => {
@@ -5786,9 +6498,9 @@ function NodeConfigPanel({
5786
6498
  },
5787
6499
  children: "+ Add Telemetry"
5788
6500
  }
5789
- ) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
5790
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Initial Value:" }),
5791
- /* @__PURE__ */ jsx19(
6501
+ ) : /* @__PURE__ */ jsxs17(Fragment8, { children: [
6502
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Initial Value:" }),
6503
+ /* @__PURE__ */ jsx21(
5792
6504
  "input",
5793
6505
  {
5794
6506
  type: "number",
@@ -5810,8 +6522,8 @@ function NodeConfigPanel({
5810
6522
  }
5811
6523
  }
5812
6524
  ),
5813
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Unit:" }),
5814
- /* @__PURE__ */ jsx19(
6525
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: "Unit:" }),
6526
+ /* @__PURE__ */ jsx21(
5815
6527
  "input",
5816
6528
  {
5817
6529
  type: "text",
@@ -5833,8 +6545,8 @@ function NodeConfigPanel({
5833
6545
  }
5834
6546
  }
5835
6547
  ),
5836
- /* @__PURE__ */ jsxs15("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
5837
- /* @__PURE__ */ jsx19(
6548
+ /* @__PURE__ */ jsxs17("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6549
+ /* @__PURE__ */ jsx21(
5838
6550
  "input",
5839
6551
  {
5840
6552
  type: "checkbox",
@@ -5856,8 +6568,8 @@ function NodeConfigPanel({
5856
6568
  ),
5857
6569
  "Show Sparkline"
5858
6570
  ] }),
5859
- /* @__PURE__ */ jsxs15("div", { style: { marginTop: "12px" }, children: [
5860
- /* @__PURE__ */ jsx19(
6571
+ /* @__PURE__ */ jsxs17("div", { style: { marginTop: "12px" }, children: [
6572
+ /* @__PURE__ */ jsx21(
5861
6573
  "label",
5862
6574
  {
5863
6575
  style: {
@@ -5869,10 +6581,10 @@ function NodeConfigPanel({
5869
6581
  children: "Telemetry Position Offset:"
5870
6582
  }
5871
6583
  ),
5872
- /* @__PURE__ */ jsxs15("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
5873
- /* @__PURE__ */ jsxs15("div", { children: [
5874
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "X Offset:" }),
5875
- /* @__PURE__ */ jsx19(
6584
+ /* @__PURE__ */ jsxs17("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
6585
+ /* @__PURE__ */ jsxs17("div", { children: [
6586
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "X Offset:" }),
6587
+ /* @__PURE__ */ jsx21(
5876
6588
  "input",
5877
6589
  {
5878
6590
  type: "number",
@@ -5899,9 +6611,9 @@ function NodeConfigPanel({
5899
6611
  }
5900
6612
  )
5901
6613
  ] }),
5902
- /* @__PURE__ */ jsxs15("div", { children: [
5903
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Y Offset:" }),
5904
- /* @__PURE__ */ jsx19(
6614
+ /* @__PURE__ */ jsxs17("div", { children: [
6615
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Y Offset:" }),
6616
+ /* @__PURE__ */ jsx21(
5905
6617
  "input",
5906
6618
  {
5907
6619
  type: "number",
@@ -5929,8 +6641,8 @@ function NodeConfigPanel({
5929
6641
  )
5930
6642
  ] })
5931
6643
  ] }),
5932
- /* @__PURE__ */ jsxs15("div", { style: { marginTop: "16px" }, children: [
5933
- /* @__PURE__ */ jsx19(
6644
+ /* @__PURE__ */ jsxs17("div", { style: { marginTop: "16px" }, children: [
6645
+ /* @__PURE__ */ jsx21(
5934
6646
  "label",
5935
6647
  {
5936
6648
  style: {
@@ -5942,10 +6654,10 @@ function NodeConfigPanel({
5942
6654
  children: "Display Colors:"
5943
6655
  }
5944
6656
  ),
5945
- /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "8px" }, children: [
5946
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Text Color:" }),
5947
- /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
5948
- /* @__PURE__ */ jsx19(
6657
+ /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "8px" }, children: [
6658
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Text Color:" }),
6659
+ /* @__PURE__ */ jsxs17("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6660
+ /* @__PURE__ */ jsx21(
5949
6661
  "input",
5950
6662
  {
5951
6663
  type: "color",
@@ -5971,7 +6683,7 @@ function NodeConfigPanel({
5971
6683
  }
5972
6684
  }
5973
6685
  ),
5974
- /* @__PURE__ */ jsx19(
6686
+ /* @__PURE__ */ jsx21(
5975
6687
  "input",
5976
6688
  {
5977
6689
  type: "text",
@@ -6001,10 +6713,10 @@ function NodeConfigPanel({
6001
6713
  )
6002
6714
  ] })
6003
6715
  ] }),
6004
- binding.display?.showSparkline && /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "8px" }, children: [
6005
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Sparkline Color:" }),
6006
- /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6007
- /* @__PURE__ */ jsx19(
6716
+ binding.display?.showSparkline && /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "8px" }, children: [
6717
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Sparkline Color:" }),
6718
+ /* @__PURE__ */ jsxs17("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6719
+ /* @__PURE__ */ jsx21(
6008
6720
  "input",
6009
6721
  {
6010
6722
  type: "color",
@@ -6030,7 +6742,7 @@ function NodeConfigPanel({
6030
6742
  }
6031
6743
  }
6032
6744
  ),
6033
- /* @__PURE__ */ jsx19(
6745
+ /* @__PURE__ */ jsx21(
6034
6746
  "input",
6035
6747
  {
6036
6748
  type: "text",
@@ -6060,9 +6772,9 @@ function NodeConfigPanel({
6060
6772
  )
6061
6773
  ] })
6062
6774
  ] }),
6063
- /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "8px" }, children: [
6064
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Background:" }),
6065
- /* @__PURE__ */ jsx19(
6775
+ /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "8px" }, children: [
6776
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.7rem", display: "block", marginBottom: "4px" }, children: "Background:" }),
6777
+ /* @__PURE__ */ jsx21(
6066
6778
  "input",
6067
6779
  {
6068
6780
  type: "text",
@@ -6090,20 +6802,20 @@ function NodeConfigPanel({
6090
6802
  }
6091
6803
  }
6092
6804
  ),
6093
- /* @__PURE__ */ jsx19("div", { style: { fontSize: "0.65rem", color: "#6b7280", marginTop: "2px" }, children: "Use 'status' for status-based color or hex code" })
6805
+ /* @__PURE__ */ jsx21("div", { style: { fontSize: "0.65rem", color: "#6b7280", marginTop: "2px" }, children: "Use 'status' for status-based color or hex code" })
6094
6806
  ] })
6095
6807
  ] }),
6096
6808
  " "
6097
6809
  ] })
6098
6810
  ] })
6099
6811
  ] }),
6100
- binding && /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "20px" }, children: [
6101
- /* @__PURE__ */ jsx19("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Thresholds" }),
6812
+ binding && /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "20px" }, children: [
6813
+ /* @__PURE__ */ jsx21("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Thresholds" }),
6102
6814
  ["highHigh", "high", "low", "lowLow"].map((key) => {
6103
6815
  const label = key === "highHigh" ? "High-High (\u2265)" : key === "high" ? "High (\u2265)" : key === "low" ? "Low (\u2264)" : "Low-Low (\u2264)";
6104
- return /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "8px" }, children: [
6105
- /* @__PURE__ */ jsx19("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: label }),
6106
- /* @__PURE__ */ jsx19(
6816
+ return /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "8px" }, children: [
6817
+ /* @__PURE__ */ jsx21("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: label }),
6818
+ /* @__PURE__ */ jsx21(
6107
6819
  "input",
6108
6820
  {
6109
6821
  type: "number",
@@ -6127,8 +6839,8 @@ function NodeConfigPanel({
6127
6839
  ] }, key);
6128
6840
  })
6129
6841
  ] }),
6130
- binding && /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "20px" }, children: [
6131
- /* @__PURE__ */ jsx19("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Status Colors" }),
6842
+ binding && /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "20px" }, children: [
6843
+ /* @__PURE__ */ jsx21("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "8px" }, children: "Status Colors" }),
6132
6844
  ["normal", "warning", "alarm", "fault", "off"].map((status) => {
6133
6845
  const defaultColors = {
6134
6846
  normal: "#10b981",
@@ -6138,13 +6850,13 @@ function NodeConfigPanel({
6138
6850
  off: "#6b7280"
6139
6851
  };
6140
6852
  const label = status === "normal" ? "Normal" : status === "warning" ? "Warning" : status === "alarm" ? "Alarm" : status === "fault" ? "Fault" : "Off";
6141
- return /* @__PURE__ */ jsxs15("div", { style: { marginBottom: "8px" }, children: [
6142
- /* @__PURE__ */ jsxs15("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6853
+ return /* @__PURE__ */ jsxs17("div", { style: { marginBottom: "8px" }, children: [
6854
+ /* @__PURE__ */ jsxs17("label", { style: { fontSize: "0.75rem", display: "block", marginBottom: "4px" }, children: [
6143
6855
  label,
6144
6856
  ":"
6145
6857
  ] }),
6146
- /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6147
- /* @__PURE__ */ jsx19(
6858
+ /* @__PURE__ */ jsxs17("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
6859
+ /* @__PURE__ */ jsx21(
6148
6860
  "input",
6149
6861
  {
6150
6862
  type: "color",
@@ -6165,7 +6877,7 @@ function NodeConfigPanel({
6165
6877
  }
6166
6878
  }
6167
6879
  ),
6168
- /* @__PURE__ */ jsx19(
6880
+ /* @__PURE__ */ jsx21(
6169
6881
  "input",
6170
6882
  {
6171
6883
  type: "text",
@@ -6192,7 +6904,7 @@ function NodeConfigPanel({
6192
6904
  ] }, status);
6193
6905
  })
6194
6906
  ] }),
6195
- /* @__PURE__ */ jsx19("div", { style: { marginTop: "20px", paddingTop: "20px", borderTop: "1px solid #cbd5e1" }, children: /* @__PURE__ */ jsx19(
6907
+ /* @__PURE__ */ jsx21("div", { style: { marginTop: "20px", paddingTop: "20px", borderTop: "1px solid #cbd5e1" }, children: /* @__PURE__ */ jsx21(
6196
6908
  "button",
6197
6909
  {
6198
6910
  onClick: onRemove,
@@ -6431,8 +7143,280 @@ var DEFAULT_THRESHOLDS = {
6431
7143
  lowLow: 15
6432
7144
  };
6433
7145
 
7146
+ // src/diagram/schema/DeviceControlBinding.ts
7147
+ function createControlBinding(nodeId, deviceConfig, actions) {
7148
+ const parametersByMode = {};
7149
+ deviceConfig.parameters.forEach((param) => {
7150
+ const modes = param.modes || deviceConfig.modes.map((m) => m.value);
7151
+ modes.forEach((mode) => {
7152
+ if (!parametersByMode[mode]) {
7153
+ parametersByMode[mode] = [];
7154
+ }
7155
+ parametersByMode[mode].push({
7156
+ id: param.id,
7157
+ label: param.label,
7158
+ unit: param.unit,
7159
+ min: param.min,
7160
+ max: param.max,
7161
+ value: param.value,
7162
+ type: typeof param.value === "number" ? "number" : "string"
7163
+ });
7164
+ });
7165
+ });
7166
+ return {
7167
+ nodeId,
7168
+ tag: deviceConfig.tag,
7169
+ deviceName: deviceConfig.name,
7170
+ deviceType: deviceConfig.type,
7171
+ modes: deviceConfig.modes,
7172
+ state: {
7173
+ currentMode: deviceConfig.currentMode,
7174
+ isRunning: deviceConfig.isRunning,
7175
+ status: deviceConfig.status
7176
+ },
7177
+ parameters: parametersByMode,
7178
+ capabilities: deviceConfig.capabilities,
7179
+ actions
7180
+ };
7181
+ }
7182
+
7183
+ // src/hooks/useDeviceControls.ts
7184
+ import { useState as useState15, useCallback as useCallback8 } from "react";
7185
+ function useDeviceControls(initialBindings) {
7186
+ const [controls, setControls] = useState15(() => {
7187
+ const map = /* @__PURE__ */ new Map();
7188
+ if (initialBindings) {
7189
+ initialBindings.forEach((binding) => {
7190
+ map.set(binding.nodeId, binding);
7191
+ });
7192
+ }
7193
+ return map;
7194
+ });
7195
+ const updateControl = useCallback8((nodeId, updates) => {
7196
+ setControls((prev) => {
7197
+ const newMap = new Map(prev);
7198
+ const existing = newMap.get(nodeId);
7199
+ if (existing) {
7200
+ newMap.set(nodeId, { ...existing, ...updates });
7201
+ } else {
7202
+ if ("tag" in updates && "modes" in updates && "state" in updates && "parameters" in updates && "actions" in updates) {
7203
+ newMap.set(nodeId, { nodeId, ...updates });
7204
+ }
7205
+ }
7206
+ return newMap;
7207
+ });
7208
+ }, []);
7209
+ const updateControlBatch = useCallback8((updates) => {
7210
+ setControls((prev) => {
7211
+ const newMap = new Map(prev);
7212
+ Object.entries(updates).forEach(([nodeId, update]) => {
7213
+ const existing = newMap.get(nodeId);
7214
+ if (existing) {
7215
+ newMap.set(nodeId, { ...existing, ...update });
7216
+ }
7217
+ });
7218
+ return newMap;
7219
+ });
7220
+ }, []);
7221
+ const updateDeviceState = useCallback8(
7222
+ (nodeId, stateUpdates) => {
7223
+ setControls((prev) => {
7224
+ const newMap = new Map(prev);
7225
+ const existing = newMap.get(nodeId);
7226
+ if (existing) {
7227
+ newMap.set(nodeId, {
7228
+ ...existing,
7229
+ state: { ...existing.state, ...stateUpdates }
7230
+ });
7231
+ }
7232
+ return newMap;
7233
+ });
7234
+ },
7235
+ []
7236
+ );
7237
+ const updateParameter = useCallback8(
7238
+ (nodeId, parameterId, value) => {
7239
+ setControls((prev) => {
7240
+ const newMap = new Map(prev);
7241
+ const existing = newMap.get(nodeId);
7242
+ if (existing) {
7243
+ const updatedParameters = { ...existing.parameters };
7244
+ Object.keys(updatedParameters).forEach((mode) => {
7245
+ const paramIndex = updatedParameters[mode].findIndex((p) => p.id === parameterId);
7246
+ if (paramIndex !== -1) {
7247
+ updatedParameters[mode] = [...updatedParameters[mode]];
7248
+ updatedParameters[mode][paramIndex] = {
7249
+ ...updatedParameters[mode][paramIndex],
7250
+ value
7251
+ };
7252
+ }
7253
+ });
7254
+ newMap.set(nodeId, {
7255
+ ...existing,
7256
+ parameters: updatedParameters
7257
+ });
7258
+ }
7259
+ return newMap;
7260
+ });
7261
+ },
7262
+ []
7263
+ );
7264
+ const setControlBinding = useCallback8((binding) => {
7265
+ setControls((prev) => {
7266
+ const newMap = new Map(prev);
7267
+ newMap.set(binding.nodeId, binding);
7268
+ return newMap;
7269
+ });
7270
+ }, []);
7271
+ const removeControlBinding = useCallback8((nodeId) => {
7272
+ setControls((prev) => {
7273
+ const newMap = new Map(prev);
7274
+ newMap.delete(nodeId);
7275
+ return newMap;
7276
+ });
7277
+ }, []);
7278
+ const getControlBinding = useCallback8(
7279
+ (nodeId) => {
7280
+ return controls.get(nodeId);
7281
+ },
7282
+ [controls]
7283
+ );
7284
+ const sendModeChange = useCallback8(
7285
+ async (nodeId, mode) => {
7286
+ const binding = controls.get(nodeId);
7287
+ if (!binding) return;
7288
+ updateDeviceState(nodeId, { currentMode: mode });
7289
+ try {
7290
+ await binding.actions.onModeChange?.(mode);
7291
+ updateDeviceState(nodeId, {
7292
+ lastCommandTime: Date.now(),
7293
+ lastCommandResult: { success: true, message: `Mode changed to ${mode}` }
7294
+ });
7295
+ } catch (error) {
7296
+ updateDeviceState(nodeId, {
7297
+ currentMode: binding.state.currentMode,
7298
+ lastCommandResult: {
7299
+ success: false,
7300
+ message: error instanceof Error ? error.message : "Mode change failed"
7301
+ }
7302
+ });
7303
+ }
7304
+ },
7305
+ [controls, updateDeviceState]
7306
+ );
7307
+ const sendParameterChange = useCallback8(
7308
+ async (nodeId, parameterId, value) => {
7309
+ const binding = controls.get(nodeId);
7310
+ if (!binding) return;
7311
+ updateParameter(nodeId, parameterId, value);
7312
+ try {
7313
+ await binding.actions.onParameterChange?.(parameterId, value);
7314
+ updateDeviceState(nodeId, {
7315
+ lastCommandTime: Date.now(),
7316
+ lastCommandResult: { success: true, message: `Parameter ${parameterId} updated` }
7317
+ });
7318
+ } catch (error) {
7319
+ updateDeviceState(nodeId, {
7320
+ lastCommandResult: {
7321
+ success: false,
7322
+ message: error instanceof Error ? error.message : "Parameter change failed"
7323
+ }
7324
+ });
7325
+ }
7326
+ },
7327
+ [controls, updateParameter, updateDeviceState]
7328
+ );
7329
+ const sendStartCommand = useCallback8(
7330
+ async (nodeId) => {
7331
+ const binding = controls.get(nodeId);
7332
+ if (!binding) return;
7333
+ updateDeviceState(nodeId, { isRunning: true, status: "starting" });
7334
+ try {
7335
+ await binding.actions.onStart?.();
7336
+ updateDeviceState(nodeId, {
7337
+ status: "normal",
7338
+ lastCommandTime: Date.now(),
7339
+ lastCommandResult: { success: true, message: "Device started" }
7340
+ });
7341
+ } catch (error) {
7342
+ updateDeviceState(nodeId, {
7343
+ isRunning: false,
7344
+ status: "fault",
7345
+ lastCommandResult: {
7346
+ success: false,
7347
+ message: error instanceof Error ? error.message : "Start failed"
7348
+ }
7349
+ });
7350
+ }
7351
+ },
7352
+ [controls, updateDeviceState]
7353
+ );
7354
+ const sendStopCommand = useCallback8(
7355
+ async (nodeId) => {
7356
+ const binding = controls.get(nodeId);
7357
+ if (!binding) return;
7358
+ updateDeviceState(nodeId, { isRunning: false, status: "stopping" });
7359
+ try {
7360
+ await binding.actions.onStop?.();
7361
+ updateDeviceState(nodeId, {
7362
+ status: "off",
7363
+ lastCommandTime: Date.now(),
7364
+ lastCommandResult: { success: true, message: "Device stopped" }
7365
+ });
7366
+ } catch (error) {
7367
+ updateDeviceState(nodeId, {
7368
+ isRunning: true,
7369
+ status: "fault",
7370
+ lastCommandResult: {
7371
+ success: false,
7372
+ message: error instanceof Error ? error.message : "Stop failed"
7373
+ }
7374
+ });
7375
+ }
7376
+ },
7377
+ [controls, updateDeviceState]
7378
+ );
7379
+ const sendCustomAction = useCallback8(
7380
+ async (nodeId, actionId) => {
7381
+ const binding = controls.get(nodeId);
7382
+ if (!binding) return;
7383
+ try {
7384
+ await binding.actions.onCustomAction?.(actionId);
7385
+ updateDeviceState(nodeId, {
7386
+ lastCommandTime: Date.now(),
7387
+ lastCommandResult: { success: true, message: `Action ${actionId} executed` }
7388
+ });
7389
+ } catch (error) {
7390
+ updateDeviceState(nodeId, {
7391
+ lastCommandResult: {
7392
+ success: false,
7393
+ message: error instanceof Error ? error.message : "Action failed"
7394
+ }
7395
+ });
7396
+ }
7397
+ },
7398
+ [controls, updateDeviceState]
7399
+ );
7400
+ return {
7401
+ controls,
7402
+ updateControl,
7403
+ updateControlBatch,
7404
+ updateDeviceState,
7405
+ updateParameter,
7406
+ setControlBinding,
7407
+ removeControlBinding,
7408
+ getControlBinding,
7409
+ // Command helpers
7410
+ sendModeChange,
7411
+ sendParameterChange,
7412
+ sendStartCommand,
7413
+ sendStopCommand,
7414
+ sendCustomAction
7415
+ };
7416
+ }
7417
+
6434
7418
  // src/diagram/hooks/useSimulation.ts
6435
- import { useEffect as useEffect11 } from "react";
7419
+ import { useEffect as useEffect12 } from "react";
6436
7420
  function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6437
7421
  const {
6438
7422
  interval = 2e3,
@@ -6443,7 +7427,7 @@ function useSimulation(telemetry, updateTelemetryBatch, options = {}) {
6443
7427
  historyLength = 10,
6444
7428
  generateValue
6445
7429
  } = options;
6446
- useEffect11(() => {
7430
+ useEffect12(() => {
6447
7431
  if (!enabled) return;
6448
7432
  const intervalId = setInterval(() => {
6449
7433
  const updates = [];
@@ -6500,6 +7484,7 @@ export {
6500
7484
  ControlPanel,
6501
7485
  DEFAULT_THRESHOLDS,
6502
7486
  DashboardCard,
7487
+ DeviceControlPanel,
6503
7488
  DisplayModeToggle,
6504
7489
  EquipmentIndicator,
6505
7490
  EquipmentIndicatorWithSparkline,
@@ -6511,6 +7496,7 @@ export {
6511
7496
  LeftToggleButton,
6512
7497
  LegacyValueEntry,
6513
7498
  NodeConfigPanel,
7499
+ NodeControlsPanel,
6514
7500
  PIDCanvas,
6515
7501
  PanelContent,
6516
7502
  PanelTabs,
@@ -6530,6 +7516,7 @@ export {
6530
7516
  ZoomButton,
6531
7517
  ZoomControls,
6532
7518
  calculateThresholdStatus,
7519
+ createControlBinding,
6533
7520
  exampleDiagram,
6534
7521
  exportDiagram,
6535
7522
  generateRoutePoints,
@@ -6542,6 +7529,7 @@ export {
6542
7529
  overlayStyles,
6543
7530
  registerSymbol,
6544
7531
  registerSymbols,
7532
+ useDeviceControls,
6545
7533
  useDrag,
6546
7534
  useKeyboardShortcuts,
6547
7535
  useSelection,