@procaaso/alphinity-ui-components 1.9.0 → 1.10.0

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
@@ -174,6 +174,8 @@ var ValueEntry = ({
174
174
  inputType = "numeric",
175
175
  label,
176
176
  readOnly = false,
177
+ forced = false,
178
+ realValue,
177
179
  className = "",
178
180
  disabled,
179
181
  ...props
@@ -224,9 +226,14 @@ var ValueEntry = ({
224
226
  const unitClass = unit ? "ui-value-entry--with-unit" : "";
225
227
  const currentStatus = disabled || readOnly ? "disabled" : status;
226
228
  const statusVariantClass = currentStatus ? `ui-value-entry--${currentStatus}` : "";
229
+ const forcedClass = forced ? "ui-value-entry--forced" : "";
230
+ const hasRealValue = forced && realValue !== void 0;
227
231
  const displayText = isEditing ? displayValue : inputType === "numeric" ? Number(value).toFixed(precision) : String(value);
228
232
  return /* @__PURE__ */ jsxs2("div", { className: "ui-value-entry-container", children: [
229
- label && /* @__PURE__ */ jsx3("label", { className: "ui-value-entry-label", children: label }),
233
+ label && /* @__PURE__ */ jsxs2("label", { className: "ui-value-entry-label", children: [
234
+ label,
235
+ forced && /* @__PURE__ */ jsx3("span", { className: "ui-value-entry-forced-badge", "aria-label": "Forced", children: "F" })
236
+ ] }),
230
237
  /* @__PURE__ */ jsxs2("div", { className: "ui-value-entry-wrapper", children: [
231
238
  /* @__PURE__ */ jsx3(
232
239
  "input",
@@ -239,19 +246,57 @@ var ValueEntry = ({
239
246
  onKeyDown: handleKeyDown,
240
247
  disabled,
241
248
  readOnly,
242
- className: `${baseClass} ${statusVariantClass} ${unitClass} ${className}`.trim(),
249
+ className: `${baseClass} ${statusVariantClass} ${forcedClass} ${unitClass} ${className}`.trim(),
243
250
  ...props
244
251
  }
245
252
  ),
246
253
  unit && /* @__PURE__ */ jsx3("span", { className: "ui-value-entry-unit", children: unit }),
247
254
  status === "alarm" && /* @__PURE__ */ jsx3("div", { className: "ui-value-entry-badge ui-value-entry-badge--alarm" }),
248
- status === "warning" && /* @__PURE__ */ jsx3("div", { className: "ui-value-entry-badge ui-value-entry-badge--warning" })
255
+ status === "warning" && /* @__PURE__ */ jsx3("div", { className: "ui-value-entry-badge ui-value-entry-badge--warning" }),
256
+ !label && forced && /* @__PURE__ */ jsx3("span", { className: "ui-value-entry-forced-badge", "aria-label": "Forced", children: "F" })
257
+ ] }),
258
+ hasRealValue && /* @__PURE__ */ jsxs2("div", { className: "ui-value-entry-real-value", children: [
259
+ "REAL ",
260
+ String(realValue),
261
+ unit ? ` ${unit}` : ""
249
262
  ] })
250
263
  ] });
251
264
  };
252
265
 
253
266
  // src/components/ValueEntry/LegacyValueEntry.tsx
254
267
  import { useState as useState3, useCallback as useCallback2 } from "react";
268
+
269
+ // src/theme/statusColors.ts
270
+ var DEFAULT_STATUS_COLORS = {
271
+ normal: { fill: "#888888", stroke: "#6b6b6b", bg: "#888888", text: "#ffffff", border: "#6b6b6b" },
272
+ warning: { fill: "#f59e0b", stroke: "#d97706", bg: "#FFD700", text: "#000000", border: "#d97706" },
273
+ alarm: { fill: "#ef4444", stroke: "#dc2626", bg: "#FF0000", text: "#ffffff", border: "#dc2626" },
274
+ fault: { fill: "#dc2626", stroke: "#b91c1c", bg: "#FF0000", text: "#ffffff", border: "#b91c1c" },
275
+ off: { fill: "#edeeef", stroke: "#000000", bg: "#666666", text: "#ffffff", border: "#4b5563" },
276
+ starting: { fill: "#90EE90", stroke: "#22c55e", bg: "#90EE90", text: "#000000", border: "#22c55e" },
277
+ stopping: { fill: "#FFD700", stroke: "#d97706", bg: "#FFD700", text: "#000000", border: "#d97706" }
278
+ };
279
+ var FORCED_MARK_COLOR = {
280
+ fill: "#ede9fe",
281
+ stroke: "#5b21b6",
282
+ bg: "#ede9fe",
283
+ text: "#5b21b6",
284
+ border: "#7c3aed"
285
+ };
286
+ var STATUS_ALIASES = {
287
+ running: "normal",
288
+ stopped: "off"
289
+ };
290
+ function resolveStatusColor(status, customMap) {
291
+ const raw = (status || "off").toLowerCase();
292
+ const key = STATUS_ALIASES[raw] ?? raw;
293
+ const defaults = DEFAULT_STATUS_COLORS[key] ?? DEFAULT_STATUS_COLORS.off;
294
+ const overrides = customMap?.[key];
295
+ if (!overrides) return defaults;
296
+ return { ...defaults, ...overrides };
297
+ }
298
+
299
+ // src/components/ValueEntry/LegacyValueEntry.tsx
255
300
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
256
301
  var LegacyValueEntry = ({
257
302
  value,
@@ -264,6 +309,8 @@ var LegacyValueEntry = ({
264
309
  inputType = "numeric",
265
310
  label,
266
311
  readOnly = false,
312
+ forced = false,
313
+ realValue,
267
314
  className = "",
268
315
  disabled,
269
316
  ...props
@@ -344,6 +391,27 @@ var LegacyValueEntry = ({
344
391
  }
345
392
  };
346
393
  const statusStyles = getStatusStyles();
394
+ const forcedBadgeStyle = {
395
+ display: "inline-flex",
396
+ alignItems: "center",
397
+ justifyContent: "center",
398
+ width: "1.1rem",
399
+ height: "1.1rem",
400
+ fontSize: "0.7rem",
401
+ fontWeight: 700,
402
+ color: FORCED_MARK_COLOR.text,
403
+ backgroundColor: FORCED_MARK_COLOR.bg,
404
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
405
+ borderRadius: "50%",
406
+ marginLeft: "8px",
407
+ lineHeight: 1
408
+ };
409
+ const realValueStyle = {
410
+ fontSize: "0.75rem",
411
+ fontWeight: 500,
412
+ color: "#94a3b8",
413
+ letterSpacing: "0.02em"
414
+ };
347
415
  const inputStyle = {
348
416
  width: "12rem",
349
417
  height: "4rem",
@@ -392,17 +460,22 @@ var LegacyValueEntry = ({
392
460
  gap: "0.75rem"
393
461
  },
394
462
  children: [
395
- label && /* @__PURE__ */ jsx4(
463
+ (label || forced) && /* @__PURE__ */ jsxs3(
396
464
  "label",
397
465
  {
398
466
  style: {
467
+ display: "flex",
468
+ alignItems: "center",
399
469
  fontSize: "0.875rem",
400
470
  fontWeight: "600",
401
471
  color: "#cbd5e1",
402
472
  textTransform: "uppercase",
403
473
  letterSpacing: "0.05em"
404
474
  },
405
- children: label
475
+ children: [
476
+ label,
477
+ forced && /* @__PURE__ */ jsx4("span", { style: forcedBadgeStyle, "aria-label": "Forced", children: "F" })
478
+ ]
406
479
  }
407
480
  ),
408
481
  /* @__PURE__ */ jsxs3("div", { style: { position: "relative", display: "inline-flex", alignItems: "center" }, children: [
@@ -459,6 +532,11 @@ var LegacyValueEntry = ({
459
532
  }
460
533
  }
461
534
  )
535
+ ] }),
536
+ forced && realValue !== void 0 && /* @__PURE__ */ jsxs3("div", { style: realValueStyle, children: [
537
+ "REAL ",
538
+ String(realValue),
539
+ unit ? ` ${unit}` : ""
462
540
  ] })
463
541
  ]
464
542
  }
@@ -474,6 +552,9 @@ var ControlPanel = ({
474
552
  onModeChange,
475
553
  setpointValue,
476
554
  onSetpointChange,
555
+ currentSetpointValue,
556
+ currentSetpointForced = false,
557
+ currentSetpointRealValue,
477
558
  secondSetpointValue,
478
559
  onSecondSetpointChange,
479
560
  modeConfigs = {},
@@ -570,6 +651,21 @@ var ControlPanel = ({
570
651
  disabled: status === "disabled"
571
652
  }
572
653
  ) }),
654
+ currentSetpointValue !== void 0 && /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5(
655
+ LegacyValueEntry,
656
+ {
657
+ value: currentSetpointValue,
658
+ onChange: () => {
659
+ },
660
+ label: `${currentLabel} (Current)`,
661
+ unit: currentUnit,
662
+ precision: currentPrecision,
663
+ status,
664
+ readOnly: true,
665
+ forced: currentSetpointForced,
666
+ realValue: currentSetpointRealValue
667
+ }
668
+ ) }),
573
669
  hasSecondSetpoint && onSecondSetpointChange && /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5(
574
670
  LegacyValueEntry,
575
671
  {
@@ -660,6 +756,8 @@ var StatusIndicator = ({
660
756
  labelPosition = "right",
661
757
  shape = "circle",
662
758
  led = true,
759
+ forced = false,
760
+ realValue,
663
761
  className = "",
664
762
  ...props
665
763
  }) => {
@@ -683,28 +781,43 @@ var StatusIndicator = ({
683
781
  return "ui-status-indicator-container ui-status-indicator-label-right";
684
782
  }
685
783
  };
784
+ const forcedClass = forced ? "ui-status-indicator--forced" : "";
785
+ const hasRealValue = forced && realValue !== void 0;
686
786
  return /* @__PURE__ */ jsxs5("span", { className: getContainerLayout(), children: [
687
787
  label && (labelPosition === "left" || labelPosition === "top") && /* @__PURE__ */ jsx6("span", { className: "ui-status-indicator-label", children: label }),
688
- /* @__PURE__ */ jsx6(
689
- "span",
690
- {
691
- role: "status",
692
- "aria-label": label || `Status: ${status}`,
693
- "aria-description": getStatusDescription(status),
694
- "data-status": status,
695
- className: `
696
- ${baseClass}
697
- ${statusClass}
698
- ${sizeClass}
699
- ${shapeClass}
700
- ${animationClass}
701
- ${ledClass}
702
- ${className}
703
- `.replace(/\s+/g, " ").trim(),
704
- ...props
705
- }
706
- ),
707
- label && (labelPosition === "right" || labelPosition === "bottom") && /* @__PURE__ */ jsx6("span", { className: "ui-status-indicator-label", children: label })
788
+ hasRealValue && (labelPosition === "left" || labelPosition === "top") && /* @__PURE__ */ jsxs5("span", { className: "ui-status-indicator-real-value", children: [
789
+ "REAL ",
790
+ realValue
791
+ ] }),
792
+ /* @__PURE__ */ jsxs5("span", { style: { position: "relative", display: "inline-flex" }, children: [
793
+ /* @__PURE__ */ jsx6(
794
+ "span",
795
+ {
796
+ role: "status",
797
+ "aria-label": label || `Status: ${status}`,
798
+ "aria-description": getStatusDescription(status),
799
+ "data-status": status,
800
+ "data-forced": forced,
801
+ className: `
802
+ ${baseClass}
803
+ ${statusClass}
804
+ ${sizeClass}
805
+ ${shapeClass}
806
+ ${animationClass}
807
+ ${ledClass}
808
+ ${forcedClass}
809
+ ${className}
810
+ `.replace(/\s+/g, " ").trim(),
811
+ ...props
812
+ }
813
+ ),
814
+ forced && /* @__PURE__ */ jsx6("span", { className: "ui-status-indicator-forced-badge", "aria-label": "Forced", children: "F" })
815
+ ] }),
816
+ label && (labelPosition === "right" || labelPosition === "bottom") && /* @__PURE__ */ jsx6("span", { className: "ui-status-indicator-label", children: label }),
817
+ hasRealValue && (labelPosition === "right" || labelPosition === "bottom") && /* @__PURE__ */ jsxs5("span", { className: "ui-status-indicator-real-value", children: [
818
+ "REAL ",
819
+ realValue
820
+ ] })
708
821
  ] });
709
822
  };
710
823
 
@@ -832,6 +945,9 @@ var NodeControlsPanel = ({
832
945
  onModeChange: config.onModeChange,
833
946
  setpointValue: config.setpoint,
834
947
  onSetpointChange: config.onSetpointChange,
948
+ currentSetpointValue: config.currentSetpoint,
949
+ currentSetpointForced: config.currentSetpointForced,
950
+ currentSetpointRealValue: config.currentSetpointRealValue,
835
951
  secondSetpointValue: config.secondSetpoint,
836
952
  onSecondSetpointChange: config.onSecondSetpointChange,
837
953
  modeConfigs: config.modeConfigs,
@@ -896,31 +1012,6 @@ var NodeControlsPanel = ({
896
1012
 
897
1013
  // src/components/DeviceControlPanel/DeviceControlPanel.tsx
898
1014
  import React5, { useState as useState5, useEffect as useEffect2 } from "react";
899
-
900
- // src/theme/statusColors.ts
901
- var DEFAULT_STATUS_COLORS = {
902
- normal: { fill: "#888888", stroke: "#6b6b6b", bg: "#888888", text: "#ffffff", border: "#6b6b6b" },
903
- warning: { fill: "#f59e0b", stroke: "#d97706", bg: "#FFD700", text: "#000000", border: "#d97706" },
904
- alarm: { fill: "#ef4444", stroke: "#dc2626", bg: "#FF0000", text: "#ffffff", border: "#dc2626" },
905
- fault: { fill: "#dc2626", stroke: "#b91c1c", bg: "#FF0000", text: "#ffffff", border: "#b91c1c" },
906
- off: { fill: "#edeeef", stroke: "#000000", bg: "#666666", text: "#ffffff", border: "#4b5563" },
907
- starting: { fill: "#90EE90", stroke: "#22c55e", bg: "#90EE90", text: "#000000", border: "#22c55e" },
908
- stopping: { fill: "#FFD700", stroke: "#d97706", bg: "#FFD700", text: "#000000", border: "#d97706" }
909
- };
910
- var STATUS_ALIASES = {
911
- running: "normal",
912
- stopped: "off"
913
- };
914
- function resolveStatusColor(status, customMap) {
915
- const raw = (status || "off").toLowerCase();
916
- const key = STATUS_ALIASES[raw] ?? raw;
917
- const defaults = DEFAULT_STATUS_COLORS[key] ?? DEFAULT_STATUS_COLORS.off;
918
- const overrides = customMap?.[key];
919
- if (!overrides) return defaults;
920
- return { ...defaults, ...overrides };
921
- }
922
-
923
- // src/components/DeviceControlPanel/DeviceControlPanel.tsx
924
1015
  import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
925
1016
  function DeviceControlPanelImpl({
926
1017
  binding,
@@ -1568,10 +1659,13 @@ function DeviceControlPanelImpl({
1568
1659
  marginBottom: spacing.gap
1569
1660
  },
1570
1661
  children: [
1571
- /* @__PURE__ */ jsx8(
1662
+ /* @__PURE__ */ jsxs7(
1572
1663
  "span",
1573
1664
  {
1574
1665
  style: {
1666
+ display: "flex",
1667
+ alignItems: "center",
1668
+ gap: "6px",
1575
1669
  fontSize: `${fontSize.label}px`,
1576
1670
  fontWeight: 600,
1577
1671
  textTransform: "uppercase",
@@ -1579,7 +1673,29 @@ function DeviceControlPanelImpl({
1579
1673
  fontFamily: "Arial, sans-serif",
1580
1674
  color: mutedTextColor
1581
1675
  },
1582
- children: param.label
1676
+ children: [
1677
+ param.label,
1678
+ param.forced?.active && /* @__PURE__ */ jsx8(
1679
+ "span",
1680
+ {
1681
+ "aria-label": "Forced",
1682
+ style: {
1683
+ display: "inline-flex",
1684
+ alignItems: "center",
1685
+ justifyContent: "center",
1686
+ width: "16px",
1687
+ height: "16px",
1688
+ fontSize: "10px",
1689
+ fontWeight: 700,
1690
+ color: FORCED_MARK_COLOR.text,
1691
+ backgroundColor: FORCED_MARK_COLOR.bg,
1692
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
1693
+ borderRadius: "50%"
1694
+ },
1695
+ children: "F"
1696
+ }
1697
+ )
1698
+ ]
1583
1699
  }
1584
1700
  ),
1585
1701
  /* @__PURE__ */ jsxs7(
@@ -1601,6 +1717,27 @@ function DeviceControlPanelImpl({
1601
1717
  ]
1602
1718
  }
1603
1719
  ),
1720
+ param.forced?.active && param.forced.realValue !== void 0 && /* @__PURE__ */ jsxs7(
1721
+ "div",
1722
+ {
1723
+ style: {
1724
+ display: "flex",
1725
+ justifyContent: "flex-end",
1726
+ marginTop: "-4px",
1727
+ marginBottom: spacing.gap,
1728
+ fontSize: "10px",
1729
+ fontWeight: 500,
1730
+ fontFamily: "Arial, sans-serif",
1731
+ color: mutedTextColor
1732
+ },
1733
+ children: [
1734
+ "REAL",
1735
+ " ",
1736
+ typeof param.forced.realValue === "boolean" ? param.forced.realValue ? "ON" : "OFF" : String(param.forced.realValue),
1737
+ param.unit ? ` ${param.unit}` : ""
1738
+ ]
1739
+ }
1740
+ ),
1604
1741
  param.type === "boolean" ? /* @__PURE__ */ jsx8(
1605
1742
  "button",
1606
1743
  {
@@ -2699,19 +2836,38 @@ var CardUnit = styled2("div")({
2699
2836
 
2700
2837
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.visuals.tsx
2701
2838
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2839
+ var forcedTagStyle = {
2840
+ position: "absolute",
2841
+ top: "-6px",
2842
+ right: "-6px",
2843
+ width: "14px",
2844
+ height: "14px",
2845
+ display: "flex",
2846
+ alignItems: "center",
2847
+ justifyContent: "center",
2848
+ fontSize: "9px",
2849
+ fontWeight: 700,
2850
+ color: FORCED_MARK_COLOR.text,
2851
+ backgroundColor: FORCED_MARK_COLOR.bg,
2852
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
2853
+ borderRadius: "50%",
2854
+ pointerEvents: "none"
2855
+ };
2702
2856
  var TrendLine = ({
2703
2857
  values,
2704
2858
  width = 60,
2705
2859
  height = 20,
2706
2860
  color = "#4fc3f7",
2707
2861
  strokeWidth = 1.5,
2708
- backgroundColor = "rgba(255, 255, 255, 0.1)"
2862
+ backgroundColor = "rgba(255, 255, 255, 0.1)",
2863
+ forced = false
2709
2864
  }) => {
2710
2865
  if (!values || values.length < 2) {
2711
- return /* @__PURE__ */ jsx12(
2866
+ return /* @__PURE__ */ jsxs9(
2712
2867
  "div",
2713
2868
  {
2714
2869
  style: {
2870
+ position: "relative",
2715
2871
  width,
2716
2872
  height,
2717
2873
  backgroundColor,
@@ -2720,7 +2876,10 @@ var TrendLine = ({
2720
2876
  alignItems: "center",
2721
2877
  justifyContent: "center"
2722
2878
  },
2723
- children: /* @__PURE__ */ jsx12("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" })
2879
+ children: [
2880
+ /* @__PURE__ */ jsx12("span", { style: { fontSize: "8px", color: "#ccc" }, children: "No data" }),
2881
+ forced && /* @__PURE__ */ jsx12("span", { style: forcedTagStyle, "aria-label": "Forced", children: "F" })
2882
+ ]
2724
2883
  }
2725
2884
  );
2726
2885
  }
@@ -2732,7 +2891,7 @@ var TrendLine = ({
2732
2891
  const x = 4 + i / (values.length - 1) * (width - 8);
2733
2892
  return i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
2734
2893
  }).join(" ");
2735
- return /* @__PURE__ */ jsx12(
2894
+ return /* @__PURE__ */ jsxs9(
2736
2895
  "div",
2737
2896
  {
2738
2897
  style: {
@@ -2741,30 +2900,41 @@ var TrendLine = ({
2741
2900
  backgroundColor,
2742
2901
  borderRadius: "2px",
2743
2902
  position: "relative",
2744
- overflow: "hidden"
2903
+ overflow: "visible"
2745
2904
  },
2746
- children: /* @__PURE__ */ jsxs9("svg", { width, height, style: { position: "absolute", top: 0, left: 0 }, children: [
2747
- /* @__PURE__ */ jsx12(
2748
- "path",
2905
+ children: [
2906
+ /* @__PURE__ */ jsxs9(
2907
+ "svg",
2749
2908
  {
2750
- d: pathData,
2751
- fill: "none",
2752
- stroke: color,
2753
- strokeWidth,
2754
- strokeLinecap: "round",
2755
- strokeLinejoin: "round"
2909
+ width,
2910
+ height,
2911
+ style: { position: "absolute", top: 0, left: 0, overflow: "hidden", borderRadius: "2px" },
2912
+ children: [
2913
+ /* @__PURE__ */ jsx12(
2914
+ "path",
2915
+ {
2916
+ d: pathData,
2917
+ fill: "none",
2918
+ stroke: color,
2919
+ strokeWidth,
2920
+ strokeLinecap: "round",
2921
+ strokeLinejoin: "round"
2922
+ }
2923
+ ),
2924
+ /* @__PURE__ */ jsx12(
2925
+ "circle",
2926
+ {
2927
+ cx: 4 + (width - 8),
2928
+ cy: normalizedValues[normalizedValues.length - 1],
2929
+ r: 1.5,
2930
+ fill: color
2931
+ }
2932
+ )
2933
+ ]
2756
2934
  }
2757
2935
  ),
2758
- /* @__PURE__ */ jsx12(
2759
- "circle",
2760
- {
2761
- cx: 4 + (width - 8),
2762
- cy: normalizedValues[normalizedValues.length - 1],
2763
- r: 1.5,
2764
- fill: color
2765
- }
2766
- )
2767
- ] })
2936
+ forced && /* @__PURE__ */ jsx12("span", { style: forcedTagStyle, "aria-label": "Forced", children: "F" })
2937
+ ]
2768
2938
  }
2769
2939
  );
2770
2940
  };
@@ -2780,7 +2950,9 @@ var CircularGauge = ({
2780
2950
  unit,
2781
2951
  label,
2782
2952
  size = 60,
2783
- color = "rgba(255, 255, 255, 0.9)"
2953
+ color = "rgba(255, 255, 255, 0.9)",
2954
+ forced = false,
2955
+ realValue
2784
2956
  }) => {
2785
2957
  const percentage = Math.min(value / max * 100, 100);
2786
2958
  const strokeWidth = 4;
@@ -2811,7 +2983,7 @@ var CircularGauge = ({
2811
2983
  height: size,
2812
2984
  borderRadius: "50%",
2813
2985
  backgroundColor,
2814
- border: `1px solid ${borderStrokeColor}`
2986
+ border: forced ? `2px solid ${FORCED_MARK_COLOR.border}` : `1px solid ${borderStrokeColor}`
2815
2987
  },
2816
2988
  children: [
2817
2989
  /* @__PURE__ */ jsxs9("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
@@ -2887,12 +3059,39 @@ var CircularGauge = ({
2887
3059
  },
2888
3060
  children: label
2889
3061
  }
2890
- )
3062
+ ),
3063
+ forced && /* @__PURE__ */ jsx12(
3064
+ "span",
3065
+ {
3066
+ "aria-label": "Forced",
3067
+ style: {
3068
+ display: "inline-flex",
3069
+ alignItems: "center",
3070
+ justifyContent: "center",
3071
+ width: "14px",
3072
+ height: "14px",
3073
+ fontSize: "9px",
3074
+ fontWeight: 700,
3075
+ color: FORCED_MARK_COLOR.text,
3076
+ backgroundColor: FORCED_MARK_COLOR.bg,
3077
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
3078
+ borderRadius: "50%",
3079
+ marginTop: "2px"
3080
+ },
3081
+ children: "F"
3082
+ }
3083
+ ),
3084
+ forced && realValue !== void 0 && /* @__PURE__ */ jsxs9("div", { style: { fontSize: "8px", opacity: 0.8, color: "white", marginTop: "1px" }, children: [
3085
+ "REAL ",
3086
+ realValue.toFixed(1),
3087
+ " ",
3088
+ unit
3089
+ ] })
2891
3090
  ]
2892
3091
  }
2893
3092
  );
2894
3093
  };
2895
- var Sparkline = ({ data, width, height, color }) => {
3094
+ var Sparkline = ({ data, width, height, color, forced = false }) => {
2896
3095
  if (!data || data.length < 2) {
2897
3096
  return null;
2898
3097
  }
@@ -2907,24 +3106,52 @@ var Sparkline = ({ data, width, height, color }) => {
2907
3106
  const y = height - 8 - (value - min) / range * (height - 8);
2908
3107
  return `${x + 4},${y + 4}`;
2909
3108
  }).join(" ");
2910
- return /* @__PURE__ */ jsx12(
3109
+ return /* @__PURE__ */ jsxs9(
2911
3110
  "div",
2912
3111
  {
2913
3112
  style: {
3113
+ position: "relative",
2914
3114
  width,
2915
3115
  height,
2916
3116
  backgroundColor: "rgba(0, 0, 0, 0.7)",
2917
- border: "1px solid rgba(255, 255, 255, 0.3)",
3117
+ border: forced ? `1px solid ${FORCED_MARK_COLOR.border}` : "1px solid rgba(255, 255, 255, 0.3)",
2918
3118
  borderRadius: "4px",
2919
3119
  display: "flex",
2920
3120
  alignItems: "center",
2921
3121
  justifyContent: "center",
2922
3122
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
2923
3123
  },
2924
- children: /* @__PURE__ */ jsx12("svg", { width, height, children: /* @__PURE__ */ jsx12("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) })
3124
+ children: [
3125
+ /* @__PURE__ */ jsx12("svg", { width, height, children: /* @__PURE__ */ jsx12("polyline", { points, fill: "none", stroke: color, strokeWidth: "1.5", opacity: "0.9" }) }),
3126
+ forced && /* @__PURE__ */ jsx12("span", { style: forcedTagStyle, "aria-label": "Forced", children: "F" })
3127
+ ]
2925
3128
  }
2926
3129
  );
2927
3130
  };
3131
+ var EquipmentForcedBadge = () => /* @__PURE__ */ jsx12(
3132
+ "span",
3133
+ {
3134
+ "aria-label": "Forced",
3135
+ style: {
3136
+ position: "absolute",
3137
+ top: "-4px",
3138
+ right: "-4px",
3139
+ width: "11px",
3140
+ height: "11px",
3141
+ borderRadius: "50%",
3142
+ backgroundColor: FORCED_MARK_COLOR.bg,
3143
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
3144
+ color: FORCED_MARK_COLOR.text,
3145
+ fontSize: "7px",
3146
+ fontWeight: 700,
3147
+ display: "flex",
3148
+ alignItems: "center",
3149
+ justifyContent: "center",
3150
+ lineHeight: 1
3151
+ },
3152
+ children: "F"
3153
+ }
3154
+ );
2928
3155
  var EquipmentIndicatorWithSparkline = ({
2929
3156
  svgX,
2930
3157
  svgY,
@@ -2932,11 +3159,51 @@ var EquipmentIndicatorWithSparkline = ({
2932
3159
  label,
2933
3160
  size = 30,
2934
3161
  sparklineData,
2935
- sparklineOffset = { x: 40, y: 0 }
3162
+ sparklineOffset = { x: 40, y: 0 },
3163
+ forced = false
2936
3164
  }) => {
2937
3165
  const hasSparkline = sparklineData && sparklineData.length > 1 && Math.max(...sparklineData) !== Math.min(...sparklineData);
2938
3166
  return /* @__PURE__ */ jsxs9(Fragment3, { children: [
2939
- /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ jsx12(
3167
+ /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX, svgY, style: { transform: "translate(-50%, -50%)" }, children: /* @__PURE__ */ jsxs9("div", { style: { position: "relative", width: size, height: size }, children: [
3168
+ /* @__PURE__ */ jsx12(
3169
+ "div",
3170
+ {
3171
+ style: {
3172
+ width: size,
3173
+ height: size,
3174
+ borderRadius: "50%",
3175
+ backgroundColor: color,
3176
+ display: "flex",
3177
+ alignItems: "center",
3178
+ justifyContent: "center",
3179
+ color: "white",
3180
+ fontSize: "7px",
3181
+ fontWeight: "700",
3182
+ textAlign: "center",
3183
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
3184
+ border: forced ? `2px solid ${FORCED_MARK_COLOR.border}` : "2px solid rgba(255, 255, 255, 0.3)"
3185
+ },
3186
+ children: label
3187
+ }
3188
+ ),
3189
+ forced && /* @__PURE__ */ jsx12(EquipmentForcedBadge, {})
3190
+ ] }) }),
3191
+ hasSparkline && /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ jsx12(Sparkline, { data: sparklineData, width: 60, height: 20, color, forced }) })
3192
+ ] });
3193
+ };
3194
+ var EquipmentIndicator = ({
3195
+ svgX,
3196
+ svgY,
3197
+ color,
3198
+ label,
3199
+ size = 30,
3200
+ style,
3201
+ className,
3202
+ forced = false
3203
+ }) => {
3204
+ const overlayStyle = style !== void 0 ? { transform: "translate(-50%, -50%)", ...style } : { transform: "translate(-50%, -50%)" };
3205
+ return /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ jsxs9("div", { style: { position: "relative", width: size, height: size }, children: [
3206
+ /* @__PURE__ */ jsx12(
2940
3207
  "div",
2941
3208
  {
2942
3209
  style: {
@@ -2952,45 +3219,13 @@ var EquipmentIndicatorWithSparkline = ({
2952
3219
  fontWeight: "700",
2953
3220
  textAlign: "center",
2954
3221
  boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
2955
- border: "2px solid rgba(255, 255, 255, 0.3)"
3222
+ border: forced ? `2px solid ${FORCED_MARK_COLOR.border}` : "2px solid rgba(255, 255, 255, 0.3)"
2956
3223
  },
2957
3224
  children: label
2958
3225
  }
2959
- ) }),
2960
- hasSparkline && /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX: svgX + sparklineOffset.x, svgY: svgY + sparklineOffset.y, children: /* @__PURE__ */ jsx12(Sparkline, { data: sparklineData, width: 60, height: 20, color }) })
2961
- ] });
2962
- };
2963
- var EquipmentIndicator = ({
2964
- svgX,
2965
- svgY,
2966
- color,
2967
- label,
2968
- size = 30,
2969
- style,
2970
- className
2971
- }) => {
2972
- const overlayStyle = style !== void 0 ? { transform: "translate(-50%, -50%)", ...style } : { transform: "translate(-50%, -50%)" };
2973
- return /* @__PURE__ */ jsx12(SVGLockedOverlay, { svgX, svgY, style: overlayStyle, className, children: /* @__PURE__ */ jsx12(
2974
- "div",
2975
- {
2976
- style: {
2977
- width: size,
2978
- height: size,
2979
- borderRadius: "50%",
2980
- backgroundColor: color,
2981
- display: "flex",
2982
- alignItems: "center",
2983
- justifyContent: "center",
2984
- color: "white",
2985
- fontSize: "7px",
2986
- fontWeight: "700",
2987
- textAlign: "center",
2988
- boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
2989
- border: "2px solid rgba(255, 255, 255, 0.3)"
2990
- },
2991
- children: label
2992
- }
2993
- ) });
3226
+ ),
3227
+ forced && /* @__PURE__ */ jsx12(EquipmentForcedBadge, {})
3228
+ ] }) });
2994
3229
  };
2995
3230
 
2996
3231
  // src/components/layout/FullscreenWorkspace/FullscreenWorkspace.tsx
@@ -4870,7 +5105,9 @@ function DataOverlayImpl({
4870
5105
  scale = 1,
4871
5106
  statusColorMap
4872
5107
  }) {
4873
- const { value, display, history, scaling } = telemetry;
5108
+ const { value, display, history, scaling, forced } = telemetry;
5109
+ const isForced = forced?.active === true;
5110
+ const hasRealValue = isForced && forced?.realValue !== void 0;
4874
5111
  const showValue = display?.showValue ?? true;
4875
5112
  const showStatus = display?.showStatus ?? true;
4876
5113
  const showSparkline = display?.showSparkline ?? false;
@@ -4919,12 +5156,16 @@ function DataOverlayImpl({
4919
5156
  const finalTextColor = textColor || colors.text;
4920
5157
  const formattedValue = formatValue(value.value);
4921
5158
  const displayText = unit ? `${formattedValue} ${unit}` : formattedValue;
5159
+ const realDisplayText = hasRealValue ? unit ? `REAL ${formatValue(forced.realValue)} ${unit}` : `REAL ${formatValue(forced.realValue)}` : void 0;
5160
+ const borderColor = isForced ? FORCED_MARK_COLOR.border : colors.border;
5161
+ const borderWidth = (isForced ? 2 : 1) * scale;
4922
5162
  const hasSparkline = showSparkline && sparklinePath;
4923
- const overlayHeight = (label ? hasSparkline ? 55 : 30 : hasSparkline ? 47 : 22) * scale;
5163
+ const extraMarkRows = realDisplayText ? 1 : 0;
5164
+ const overlayHeight = (label ? hasSparkline ? 55 : 30 : hasSparkline ? 47 : 22) * scale + extraMarkRows * 9 * scale;
4924
5165
  const overlayWidth = 80 * scale;
4925
5166
  const displayX = x + offsetX;
4926
5167
  const displayY = y + offsetY;
4927
- return /* @__PURE__ */ jsxs14("g", { "data-overlay-node": nodeId, "data-status": value.status, children: [
5168
+ return /* @__PURE__ */ jsxs14("g", { "data-overlay-node": nodeId, "data-status": value.status, "data-forced": isForced, children: [
4928
5169
  /* @__PURE__ */ jsx18(
4929
5170
  "rect",
4930
5171
  {
@@ -4934,8 +5175,8 @@ function DataOverlayImpl({
4934
5175
  height: overlayHeight,
4935
5176
  rx: 4 * scale,
4936
5177
  fill: colors.bg,
4937
- stroke: colors.border,
4938
- strokeWidth: 1 * scale,
5178
+ stroke: borderColor,
5179
+ strokeWidth: borderWidth,
4939
5180
  opacity: "0.95",
4940
5181
  filter: "drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
4941
5182
  }
@@ -4984,6 +5225,48 @@ function DataOverlayImpl({
4984
5225
  ]
4985
5226
  }
4986
5227
  ),
5228
+ isForced && /* @__PURE__ */ jsxs14(
5229
+ "g",
5230
+ {
5231
+ transform: `translate(${displayX + overlayWidth / 2}, ${displayY - 25 * scale})`,
5232
+ "aria-label": "Forced",
5233
+ children: [
5234
+ /* @__PURE__ */ jsx18(
5235
+ "circle",
5236
+ {
5237
+ r: 7 * scale,
5238
+ fill: FORCED_MARK_COLOR.bg,
5239
+ stroke: FORCED_MARK_COLOR.border,
5240
+ strokeWidth: 1.5 * scale
5241
+ }
5242
+ ),
5243
+ /* @__PURE__ */ jsx18(
5244
+ "text",
5245
+ {
5246
+ textAnchor: "middle",
5247
+ dominantBaseline: "central",
5248
+ fontSize: 8 * scale,
5249
+ fontWeight: "700",
5250
+ fill: FORCED_MARK_COLOR.text,
5251
+ children: "F"
5252
+ }
5253
+ )
5254
+ ]
5255
+ }
5256
+ ),
5257
+ realDisplayText && /* @__PURE__ */ jsx18(
5258
+ "text",
5259
+ {
5260
+ x: displayX,
5261
+ y: displayY + (value.quality !== void 0 && value.quality < 100 ? 14 : 5) * scale,
5262
+ textAnchor: "middle",
5263
+ fontSize: 7 * scale,
5264
+ fontWeight: "500",
5265
+ fill: finalTextColor,
5266
+ opacity: "0.85",
5267
+ children: realDisplayText
5268
+ }
5269
+ ),
4987
5270
  hasSparkline && /* @__PURE__ */ jsx18("g", { transform: `translate(${displayX - 35 * scale}, ${displayY + (label ? 5 : 0) * scale})`, children: /* @__PURE__ */ jsx18(
4988
5271
  "path",
4989
5272
  {
@@ -8786,7 +9069,49 @@ function DeviceConfigPanelImpl({
8786
9069
  " (",
8787
9070
  param.unit,
8788
9071
  ")"
8789
- ] })
9072
+ ] }),
9073
+ param.forced?.active && /* @__PURE__ */ jsx23(
9074
+ "span",
9075
+ {
9076
+ "aria-label": "Forced",
9077
+ style: {
9078
+ display: "inline-flex",
9079
+ alignItems: "center",
9080
+ justifyContent: "center",
9081
+ width: "16px",
9082
+ height: "16px",
9083
+ marginLeft: "6px",
9084
+ fontSize: "10px",
9085
+ fontWeight: 700,
9086
+ textTransform: "none",
9087
+ letterSpacing: "normal",
9088
+ color: FORCED_MARK_COLOR.text,
9089
+ backgroundColor: FORCED_MARK_COLOR.bg,
9090
+ border: `1px solid ${FORCED_MARK_COLOR.border}`,
9091
+ borderRadius: "50%",
9092
+ verticalAlign: "middle"
9093
+ },
9094
+ children: "F"
9095
+ }
9096
+ )
9097
+ ]
9098
+ }
9099
+ ),
9100
+ param.forced?.active && param.forced.realValue !== void 0 && /* @__PURE__ */ jsxs19(
9101
+ "div",
9102
+ {
9103
+ style: {
9104
+ fontSize: `${fontSize.description}px`,
9105
+ color: mutedTextColor,
9106
+ marginTop: "-2px",
9107
+ marginBottom: "6px",
9108
+ fontFamily: "Arial, sans-serif"
9109
+ },
9110
+ children: [
9111
+ "REAL",
9112
+ " ",
9113
+ typeof param.forced.realValue === "boolean" ? param.forced.realValue ? "ON" : "OFF" : String(param.forced.realValue),
9114
+ param.unit ? ` ${param.unit}` : ""
8790
9115
  ]
8791
9116
  }
8792
9117
  ),
@@ -9374,6 +9699,7 @@ export {
9374
9699
  DisplayModeToggle,
9375
9700
  EquipmentIndicator,
9376
9701
  EquipmentIndicatorWithSparkline,
9702
+ FORCED_MARK_COLOR,
9377
9703
  FixedSVGContainer,
9378
9704
  FooterPanel,
9379
9705
  FullscreenContainer,