@procaaso/alphinity-ui-components 1.12.0 → 1.14.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.d.cts CHANGED
@@ -528,10 +528,29 @@ interface CustomAction {
528
528
  * Device control capabilities and commands
529
529
  */
530
530
  interface ControlCapabilities {
531
- /** Can the device be started? */
531
+ /**
532
+ * Can the device be started? When `canStart` or `canStop` is true, the
533
+ * panel shows BOTH the START and the STOP button, each in a fixed place
534
+ * (alphinity-tffi #3853). A false `canStart` then shows START disabled.
535
+ * It does not remove START and it does not let STOP take its place.
536
+ */
532
537
  canStart?: boolean;
533
- /** Can the device be stopped? */
538
+ /** Can the device be stopped? See `canStart`. */
534
539
  canStop?: boolean;
540
+ /**
541
+ * Does START apply now? Default: true (enabled). Set false to show START
542
+ * disabled, for example on a pump that runs already. Use this with
543
+ * `canStart: true` to keep "the device has a START" apart from "START
544
+ * applies now". A disabled START does not call `onStart`.
545
+ */
546
+ startEnabled?: boolean;
547
+ /**
548
+ * Does STOP apply now? Default: true (enabled). Set false to show STOP
549
+ * disabled, for example on a pump that is stopped. The panel itself never
550
+ * disables STOP for a pending command. A disabled STOP does not call
551
+ * `onStop`.
552
+ */
553
+ stopEnabled?: boolean;
535
554
  /** Can the device be paused? */
536
555
  canPause?: boolean;
537
556
  /** Can the device be reset? */
@@ -609,8 +628,11 @@ interface ControlState {
609
628
  * Current status. `unknown` marks an untrusted read (OPC UA StatusCode
610
629
  * severity Bad). This library never infers `unknown`; it only displays a
611
630
  * value the backend has already published as `unknown`.
631
+ *
632
+ * `forced` marks a device that a maintenance force holds. It resolves to
633
+ * `FORCED_MARK_COLOR`, the same violet as every other forced mark.
612
634
  */
613
- status?: 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown';
635
+ status?: 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown' | 'forced';
614
636
  /** Status message */
615
637
  statusMessage?: string;
616
638
  /** Last command timestamp */
@@ -775,12 +797,18 @@ interface DeviceControlPanelProps {
775
797
  /** Called when stop button clicked */
776
798
  onStop?: () => void;
777
799
  /**
778
- * Start command is in flight (awaiting ack). Shows a busy affordance on the
779
- * START button and disables it, without locking STOP — wire from the hook's
780
- * `isCommandPending(nodeId, 'start')`.
800
+ * Start command is in flight (awaiting ack). START shows "STARTING..." and
801
+ * is disabled, so a second tap cannot send a second start. STOP stays
802
+ * enabled: a pending start never disables STOP (alphinity-tffi #3853).
803
+ * Wire from the hook's `isCommandPending(nodeId, 'start')`, or from the
804
+ * host's own in-flight state.
781
805
  */
782
806
  startPending?: boolean;
783
- /** Stop command is in flight (awaiting ack). See `startPending`. */
807
+ /**
808
+ * Stop command is in flight (awaiting ack). STOP shows "STOPPING..." and
809
+ * stays enabled, so STOP is always one tap. Only `capabilities.canStop`
810
+ * or `capabilities.stopEnabled` can disable STOP.
811
+ */
784
812
  stopPending?: boolean;
785
813
  /** Called when custom action clicked */
786
814
  onCustomAction?: (actionId: string) => void;
@@ -1166,7 +1194,7 @@ declare const ThemeToggle: React__default.FC<ThemeToggleProps>;
1166
1194
  * from a number or a threshold. The library only displays an `unknown` value
1167
1195
  * that the backend has already published.
1168
1196
  */
1169
- type DeviceStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown';
1197
+ type DeviceStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown' | 'forced';
1170
1198
  /** Color entry covering both SVG node and overlay/panel needs */
1171
1199
  interface StatusColorEntry {
1172
1200
  /** Fill color for SVG node symbols */
@@ -1219,6 +1247,22 @@ declare const ISA_UNKNOWN_DARK: {
1219
1247
  readonly bg: "#23263a";
1220
1248
  readonly border: "#6f74a3";
1221
1249
  };
1250
+ /**
1251
+ * Maintenance-force marking color (#3607, R19). A forced value is not a
1252
+ * health status, and must never share a color with warning/alarm/fault or
1253
+ * read as one of them. Violet is unused elsewhere in this palette.
1254
+ *
1255
+ * `DEFAULT_STATUS_COLORS.forced` points at this same object (alphinity-tffi
1256
+ * #3771). A device that a maintenance force holds then gets the forced
1257
+ * violet on its status dot, and not the `off` grey fallback. The forced
1258
+ * entry is a marker, not a severity: it stays outside the
1259
+ * warning/alarm/fault colours.
1260
+ *
1261
+ * Light fill + dark text, not a solid vivid chip: reads as a calm marker
1262
+ * rather than an alarm sticker, while the vivid `border` shade still
1263
+ * outlines a forced value's box/ring so it stays findable at a glance.
1264
+ */
1265
+ declare const FORCED_MARK_COLOR: StatusColorEntry;
1222
1266
  /**
1223
1267
  * ISA-101 defaults.
1224
1268
  *
@@ -1231,6 +1275,8 @@ declare const ISA_UNKNOWN_DARK: {
1231
1275
  * - stopping: amber (transitional)
1232
1276
  * - unknown: periwinkle (untrusted read; OPC UA severity Bad). Derived
1233
1277
  * from ISA_UNKNOWN above, the shared token.
1278
+ * - forced: violet (a maintenance force holds the device). The same
1279
+ * object as FORCED_MARK_COLOR above.
1234
1280
  *
1235
1281
  * The unknown entry deliberately does NOT rely on its fill. The flat fill
1236
1282
  * sits a CIEDE2000 delta-E of 0.45 from the off fill after greyscale
@@ -1245,17 +1291,6 @@ declare const ISA_UNKNOWN_DARK: {
1245
1291
  * apart. That is why unknown never relies on colour anywhere.
1246
1292
  */
1247
1293
  declare const DEFAULT_STATUS_COLORS: Record<DeviceStatus, StatusColorEntry>;
1248
- /**
1249
- * Maintenance-force marking color (#3607, R19). Deliberately outside
1250
- * DEFAULT_STATUS_COLORS: a forced value is not a health status, and must
1251
- * never share a color with warning/alarm/fault or read as one of them.
1252
- * Violet is unused elsewhere in this palette.
1253
- *
1254
- * Light fill + dark text, not a solid vivid chip: reads as a calm marker
1255
- * rather than an alarm sticker, while the vivid `border` shade still
1256
- * outlines a forced value's box/ring so it stays findable at a glance.
1257
- */
1258
- declare const FORCED_MARK_COLOR: StatusColorEntry;
1259
1294
  /**
1260
1295
  * Normalize a status string to its canonical `DeviceStatus` key.
1261
1296
  *
@@ -1337,6 +1372,54 @@ interface UnknownStatusGlyphProps extends Omit<React__default.SVGProps<SVGSVGEle
1337
1372
  */
1338
1373
  declare function UnknownStatusGlyph({ size, counterRotate, style, ...rest }: UnknownStatusGlyphProps): React__default.ReactElement;
1339
1374
 
1375
+ /**
1376
+ * A corner badge slot on a node (#33).
1377
+ *
1378
+ * A badge marks a condition that is NOT a health severity (`NodeInstance.status`)
1379
+ * and must never replace the node's body color. A device can be tripped AND
1380
+ * blocked at the same time; `status` shows the trip, the badge shows the
1381
+ * block, and both render at once. This is a general slot, not a fixed
1382
+ * prohibition badge: the consumer names the glyph, the colors, the corner,
1383
+ * the size, and the motion. Register additional glyphs with
1384
+ * `registerBadgeGlyph` (`src/diagram/badges`) rather than hardcoding a new
1385
+ * one here.
1386
+ */
1387
+ interface NodeBadgeAppearance {
1388
+ /**
1389
+ * Which glyph to draw. `'prohibit'` (a circle with a slash) ships with the
1390
+ * library. The set is extensible: register more with `registerBadgeGlyph`
1391
+ * / `registerBadgeGlyphs` and reference them by id here, the same way
1392
+ * `symbolId` references a registered symbol.
1393
+ */
1394
+ glyph: 'prohibit' | string;
1395
+ /** Glyph color (stroke/fill). Consumer picks; no status-driven default. */
1396
+ color?: string;
1397
+ /** Disc background color behind the glyph. Consumer picks. */
1398
+ backgroundColor?: string;
1399
+ /** Which corner of the node's bounding box the badge anchors to. Defaults to 'top-right'. */
1400
+ corner?: NodeBadgeCorner;
1401
+ /** Badge disc diameter, in the symbol's local SVG units. Defaults to 14. */
1402
+ size?: number;
1403
+ /** Accessible label read by assistive tech (e.g. "Start blocked"). */
1404
+ label?: string;
1405
+ }
1406
+ type NodeBadgeCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
1407
+ /**
1408
+ * Motion for a `NodeBadge`. Steady by default. A consumer that turns on
1409
+ * `blink` MUST supply `blinkPeriodMs` - the library has no default period.
1410
+ * TFFi drives its status tower, its batch bar, and this badge off one shared
1411
+ * constant so all three beat together; a period fixed in the library would
1412
+ * break that agreement, so there is deliberately no fallback value here.
1413
+ */
1414
+ type NodeBadgeMotion = {
1415
+ blink?: false;
1416
+ } | {
1417
+ blink: true;
1418
+ blinkPeriodMs: number;
1419
+ };
1420
+ /** The full badge: appearance plus motion. This is the type `NodeInstance.badge` carries. */
1421
+ type NodeBadge = NodeBadgeAppearance & NodeBadgeMotion;
1422
+
1340
1423
  interface NodeInstance {
1341
1424
  id: string;
1342
1425
  symbolId: string;
@@ -1361,6 +1444,13 @@ interface NodeInstance {
1361
1444
  * symbol. Default off. Wins over status and customColors.
1362
1445
  */
1363
1446
  positionUnknown?: boolean;
1447
+ /**
1448
+ * A corner badge for a condition that is NOT a health severity (#33),
1449
+ * e.g. a device blocked from starting. Stacks on top of `status` /
1450
+ * `customColors` / `positionUnknown` - it never replaces the node's body
1451
+ * color, so a node can show `status: 'fault'` and a badge at once.
1452
+ */
1453
+ badge?: NodeBadge;
1364
1454
  metadata?: Record<string, unknown>;
1365
1455
  }
1366
1456
  interface Transform {
@@ -2063,6 +2153,36 @@ declare function readAppearanceValue(node: NodeInstance, field: AppearanceField)
2063
2153
  /** Produce the NodeInstance update for a new field value. */
2064
2154
  declare function writeAppearanceValue(node: NodeInstance, field: AppearanceField, value: AppearanceValue): Partial<NodeInstance>;
2065
2155
 
2156
+ /**
2157
+ * Registry of badge glyphs for the `NodeBadge` corner slot (#33).
2158
+ *
2159
+ * Mirrors `diagram/symbols/mockSymbolLibrary`'s registration pattern: a
2160
+ * built-in set plus a consumer-registered set, looked up by id. A consumer
2161
+ * calls `registerBadgeGlyph` from its own app code to add a glyph the
2162
+ * library does not ship - the same escape hatch `registerSymbol` already
2163
+ * gives P&ID symbols. This is deliberately NOT a string resolved only
2164
+ * inside the library (the mistake `Overlay.content.component` made, per
2165
+ * #33): the consumer supplies the real markup.
2166
+ */
2167
+ interface BadgeGlyphDefinition {
2168
+ /** Id a `NodeBadge.glyph` references. */
2169
+ id: string;
2170
+ /**
2171
+ * Raw SVG markup, drawn centered in a 24x24 local box (0,0 to 24,24).
2172
+ * Use `stroke="currentColor"` / `fill="currentColor"` so the glyph picks
2173
+ * up `NodeBadge.color` - the glyph carries no color of its own, only shape.
2174
+ */
2175
+ svgContent: string;
2176
+ }
2177
+ /** Register a single custom badge glyph, or replace a built-in one by reusing its id. */
2178
+ declare function registerBadgeGlyph(glyph: BadgeGlyphDefinition): void;
2179
+ /** Register multiple custom badge glyphs at once. */
2180
+ declare function registerBadgeGlyphs(glyphs: BadgeGlyphDefinition[] | Record<string, BadgeGlyphDefinition>): void;
2181
+ /** Get a badge glyph definition by id (checks custom glyphs before built-ins). */
2182
+ declare function getBadgeGlyph(glyphId: string): BadgeGlyphDefinition | undefined;
2183
+ /** List every registered badge glyph id (built-in plus custom). */
2184
+ declare function getAvailableBadgeGlyphs(): string[];
2185
+
2066
2186
  /**
2067
2187
  * Telemetry status type
2068
2188
  */
@@ -2479,4 +2599,4 @@ interface SimulationOptions {
2479
2599
  */
2480
2600
  declare function useSimulation(telemetry: TelemetryMap, updateTelemetryBatch: (updates: Array<any>) => void, options?: SimulationOptions): void;
2481
2601
 
2482
- export { type AnchorType, type AppearanceField, type AppearanceFieldKind, type AppearanceTarget, type AppearanceValue, Button, type ButtonProps, COLOR_PRESETS, CardHeader, CardUnit, CardValue, CircularGauge, type CircularGaugeProps, type CommandKind, type ConfigActions, type ConfigDisplay, type ConfigParameter, type ConfigSection, type ConfigState, type ConfigTheme, type ControlCapabilities, type ControlMode, ControlPanel, type ControlPanelProps, type ControlParameter, type ControlState, type CustomAction, DEFAULT_STATUS_COLORS, DEFAULT_THRESHOLDS, DashboardCard, type DataBinding, type DeviceConfigBinding, type DeviceConfigMap, DeviceConfigPanel, type DeviceConfigPanelProps, type DeviceControlBinding, type DeviceControlMap, DeviceControlPanel, type DeviceControlPanelProps, type DeviceStatus, type Diagram, type DiagramMetadata, type DisplayMode, DisplayModeToggle, type DragPoint, type DragState, EquipmentIndicator, type EquipmentIndicatorProps, EquipmentIndicatorWithSparkline, type EquipmentIndicatorWithSparklineProps, FORCED_MARK_COLOR, FixedSVGContainer, FooterPanel, type ForcedState, FullscreenContainer, FullscreenWorkspace, type FullscreenWorkspaceProps, type FullscreenWorkspaceTab, ISA_UNKNOWN, ISA_UNKNOWN_DARK, type KeyboardShortcutsOptions, LeftPanel, LeftToggleButton, LegacyValueEntry, type LegacyValueEntryProps, type ModeConfig, NodeConfigPanel, type NodeConfigPanelProps, type NodeControlConfig, NodeControlsPanel, type NodeControlsPanelProps, type NodeInstance, NumpadDialog, type NumpadDialogProps, type Overlay, type OverlayContent, type OverlayTheme, type OverlayType, PIDCanvas, type PIDCanvasControls, type PIDCanvasFeatures, type PIDCanvasProps, PanelContent, PanelTabs, type PipeEdge, type PipeStyle, type Point, type PortDefinition, type PortInstance, type PortType, RightPanel, RightToggleButton, STATUS_LED_APPEARANCE, SVGArea, SVGLockedOverlay, type SVGLockedOverlayProps, type ScalingMetadata, ScrollableSVGWrapper, type SelectionState, Selector, type SelectorOption, type SelectorProps, type SimulationOptions, Sparkline, type SparklineProps, type StatusColorEntry, type StatusColorMap, StatusIndicator, type StatusIndicatorProps, type SymbolCategory, type SymbolDefinition, SymbolLibrary, type SymbolLibraryProps, type SymbolMetadata, type TelemetryBinding, type TelemetryMap, type TelemetryStatus$1 as TelemetryStatus, type TelemetryValue, ThemeProvider, ThemeToggle, type ThemeToggleProps, type Thresholds, type Transform, TrendLine, type TrendLineProps, UNKNOWN_GLYPH_RING_DASH, UNKNOWN_STATUS_GLYPH_COLOR_VAR, type UnknownGlyphSize, UnknownStatusGlyph, type UnknownStatusGlyphProps, type UseBooleanIndicatorsOptions, type UseDeviceControlsOptions, type UseDragOptions, type UseDragResult, type UseSelectionOptions, type UseSelectionResult, type UseTelemetryOptions, type UseTelemetryResult, type UseTelemetryStatusOptions, type UseViewBoxOptions, type UseViewBoxResult, ValueEntry, type ValueEntryProps, type ViewBox, ZoomButton, ZoomControls, calculateThresholdStatus, createConfigBinding, createControlBinding, exampleDiagram, exportDiagram, generateRoutePoints, getAppearanceDefaults, getAppearanceSchema, getAvailableSymbols, getPortWorldPosition, getSymbolDefinition, importDiagram, mockSymbolLibrary, nodeHasPort, normalizeDeviceStatus, overlayStyles, readAppearanceValue, registerSymbol, registerSymbols, resolvePresetColor, resolveStatusColor, useBooleanIndicators, useDeviceConfigs, useDeviceControls, useDrag, useKeyboardShortcuts, useSelection, useSimulation, useTelemetry, useTelemetryStatus, useTheme, useViewBox, validateDiagram, writeAppearanceValue };
2602
+ export { type AnchorType, type AppearanceField, type AppearanceFieldKind, type AppearanceTarget, type AppearanceValue, type BadgeGlyphDefinition, Button, type ButtonProps, COLOR_PRESETS, CardHeader, CardUnit, CardValue, CircularGauge, type CircularGaugeProps, type CommandKind, type ConfigActions, type ConfigDisplay, type ConfigParameter, type ConfigSection, type ConfigState, type ConfigTheme, type ControlCapabilities, type ControlMode, ControlPanel, type ControlPanelProps, type ControlParameter, type ControlState, type CustomAction, DEFAULT_STATUS_COLORS, DEFAULT_THRESHOLDS, DashboardCard, type DataBinding, type DeviceConfigBinding, type DeviceConfigMap, DeviceConfigPanel, type DeviceConfigPanelProps, type DeviceControlBinding, type DeviceControlMap, DeviceControlPanel, type DeviceControlPanelProps, type DeviceStatus, type Diagram, type DiagramMetadata, type DisplayMode, DisplayModeToggle, type DragPoint, type DragState, EquipmentIndicator, type EquipmentIndicatorProps, EquipmentIndicatorWithSparkline, type EquipmentIndicatorWithSparklineProps, FORCED_MARK_COLOR, FixedSVGContainer, FooterPanel, type ForcedState, FullscreenContainer, FullscreenWorkspace, type FullscreenWorkspaceProps, type FullscreenWorkspaceTab, ISA_UNKNOWN, ISA_UNKNOWN_DARK, type KeyboardShortcutsOptions, LeftPanel, LeftToggleButton, LegacyValueEntry, type LegacyValueEntryProps, type ModeConfig, type NodeBadge, type NodeBadgeAppearance, type NodeBadgeCorner, type NodeBadgeMotion, NodeConfigPanel, type NodeConfigPanelProps, type NodeControlConfig, NodeControlsPanel, type NodeControlsPanelProps, type NodeInstance, NumpadDialog, type NumpadDialogProps, type Overlay, type OverlayContent, type OverlayTheme, type OverlayType, PIDCanvas, type PIDCanvasControls, type PIDCanvasFeatures, type PIDCanvasProps, PanelContent, PanelTabs, type PipeEdge, type PipeStyle, type Point, type PortDefinition, type PortInstance, type PortType, RightPanel, RightToggleButton, STATUS_LED_APPEARANCE, SVGArea, SVGLockedOverlay, type SVGLockedOverlayProps, type ScalingMetadata, ScrollableSVGWrapper, type SelectionState, Selector, type SelectorOption, type SelectorProps, type SimulationOptions, Sparkline, type SparklineProps, type StatusColorEntry, type StatusColorMap, StatusIndicator, type StatusIndicatorProps, type SymbolCategory, type SymbolDefinition, SymbolLibrary, type SymbolLibraryProps, type SymbolMetadata, type TelemetryBinding, type TelemetryMap, type TelemetryStatus$1 as TelemetryStatus, type TelemetryValue, ThemeProvider, ThemeToggle, type ThemeToggleProps, type Thresholds, type Transform, TrendLine, type TrendLineProps, UNKNOWN_GLYPH_RING_DASH, UNKNOWN_STATUS_GLYPH_COLOR_VAR, type UnknownGlyphSize, UnknownStatusGlyph, type UnknownStatusGlyphProps, type UseBooleanIndicatorsOptions, type UseDeviceControlsOptions, type UseDragOptions, type UseDragResult, type UseSelectionOptions, type UseSelectionResult, type UseTelemetryOptions, type UseTelemetryResult, type UseTelemetryStatusOptions, type UseViewBoxOptions, type UseViewBoxResult, ValueEntry, type ValueEntryProps, type ViewBox, ZoomButton, ZoomControls, calculateThresholdStatus, createConfigBinding, createControlBinding, exampleDiagram, exportDiagram, generateRoutePoints, getAppearanceDefaults, getAppearanceSchema, getAvailableBadgeGlyphs, getAvailableSymbols, getBadgeGlyph, getPortWorldPosition, getSymbolDefinition, importDiagram, mockSymbolLibrary, nodeHasPort, normalizeDeviceStatus, overlayStyles, readAppearanceValue, registerBadgeGlyph, registerBadgeGlyphs, registerSymbol, registerSymbols, resolvePresetColor, resolveStatusColor, useBooleanIndicators, useDeviceConfigs, useDeviceControls, useDrag, useKeyboardShortcuts, useSelection, useSimulation, useTelemetry, useTelemetryStatus, useTheme, useViewBox, validateDiagram, writeAppearanceValue };
package/dist/index.d.ts CHANGED
@@ -528,10 +528,29 @@ interface CustomAction {
528
528
  * Device control capabilities and commands
529
529
  */
530
530
  interface ControlCapabilities {
531
- /** Can the device be started? */
531
+ /**
532
+ * Can the device be started? When `canStart` or `canStop` is true, the
533
+ * panel shows BOTH the START and the STOP button, each in a fixed place
534
+ * (alphinity-tffi #3853). A false `canStart` then shows START disabled.
535
+ * It does not remove START and it does not let STOP take its place.
536
+ */
532
537
  canStart?: boolean;
533
- /** Can the device be stopped? */
538
+ /** Can the device be stopped? See `canStart`. */
534
539
  canStop?: boolean;
540
+ /**
541
+ * Does START apply now? Default: true (enabled). Set false to show START
542
+ * disabled, for example on a pump that runs already. Use this with
543
+ * `canStart: true` to keep "the device has a START" apart from "START
544
+ * applies now". A disabled START does not call `onStart`.
545
+ */
546
+ startEnabled?: boolean;
547
+ /**
548
+ * Does STOP apply now? Default: true (enabled). Set false to show STOP
549
+ * disabled, for example on a pump that is stopped. The panel itself never
550
+ * disables STOP for a pending command. A disabled STOP does not call
551
+ * `onStop`.
552
+ */
553
+ stopEnabled?: boolean;
535
554
  /** Can the device be paused? */
536
555
  canPause?: boolean;
537
556
  /** Can the device be reset? */
@@ -609,8 +628,11 @@ interface ControlState {
609
628
  * Current status. `unknown` marks an untrusted read (OPC UA StatusCode
610
629
  * severity Bad). This library never infers `unknown`; it only displays a
611
630
  * value the backend has already published as `unknown`.
631
+ *
632
+ * `forced` marks a device that a maintenance force holds. It resolves to
633
+ * `FORCED_MARK_COLOR`, the same violet as every other forced mark.
612
634
  */
613
- status?: 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown';
635
+ status?: 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown' | 'forced';
614
636
  /** Status message */
615
637
  statusMessage?: string;
616
638
  /** Last command timestamp */
@@ -775,12 +797,18 @@ interface DeviceControlPanelProps {
775
797
  /** Called when stop button clicked */
776
798
  onStop?: () => void;
777
799
  /**
778
- * Start command is in flight (awaiting ack). Shows a busy affordance on the
779
- * START button and disables it, without locking STOP — wire from the hook's
780
- * `isCommandPending(nodeId, 'start')`.
800
+ * Start command is in flight (awaiting ack). START shows "STARTING..." and
801
+ * is disabled, so a second tap cannot send a second start. STOP stays
802
+ * enabled: a pending start never disables STOP (alphinity-tffi #3853).
803
+ * Wire from the hook's `isCommandPending(nodeId, 'start')`, or from the
804
+ * host's own in-flight state.
781
805
  */
782
806
  startPending?: boolean;
783
- /** Stop command is in flight (awaiting ack). See `startPending`. */
807
+ /**
808
+ * Stop command is in flight (awaiting ack). STOP shows "STOPPING..." and
809
+ * stays enabled, so STOP is always one tap. Only `capabilities.canStop`
810
+ * or `capabilities.stopEnabled` can disable STOP.
811
+ */
784
812
  stopPending?: boolean;
785
813
  /** Called when custom action clicked */
786
814
  onCustomAction?: (actionId: string) => void;
@@ -1166,7 +1194,7 @@ declare const ThemeToggle: React__default.FC<ThemeToggleProps>;
1166
1194
  * from a number or a threshold. The library only displays an `unknown` value
1167
1195
  * that the backend has already published.
1168
1196
  */
1169
- type DeviceStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown';
1197
+ type DeviceStatus = 'normal' | 'warning' | 'alarm' | 'fault' | 'off' | 'starting' | 'stopping' | 'unknown' | 'forced';
1170
1198
  /** Color entry covering both SVG node and overlay/panel needs */
1171
1199
  interface StatusColorEntry {
1172
1200
  /** Fill color for SVG node symbols */
@@ -1219,6 +1247,22 @@ declare const ISA_UNKNOWN_DARK: {
1219
1247
  readonly bg: "#23263a";
1220
1248
  readonly border: "#6f74a3";
1221
1249
  };
1250
+ /**
1251
+ * Maintenance-force marking color (#3607, R19). A forced value is not a
1252
+ * health status, and must never share a color with warning/alarm/fault or
1253
+ * read as one of them. Violet is unused elsewhere in this palette.
1254
+ *
1255
+ * `DEFAULT_STATUS_COLORS.forced` points at this same object (alphinity-tffi
1256
+ * #3771). A device that a maintenance force holds then gets the forced
1257
+ * violet on its status dot, and not the `off` grey fallback. The forced
1258
+ * entry is a marker, not a severity: it stays outside the
1259
+ * warning/alarm/fault colours.
1260
+ *
1261
+ * Light fill + dark text, not a solid vivid chip: reads as a calm marker
1262
+ * rather than an alarm sticker, while the vivid `border` shade still
1263
+ * outlines a forced value's box/ring so it stays findable at a glance.
1264
+ */
1265
+ declare const FORCED_MARK_COLOR: StatusColorEntry;
1222
1266
  /**
1223
1267
  * ISA-101 defaults.
1224
1268
  *
@@ -1231,6 +1275,8 @@ declare const ISA_UNKNOWN_DARK: {
1231
1275
  * - stopping: amber (transitional)
1232
1276
  * - unknown: periwinkle (untrusted read; OPC UA severity Bad). Derived
1233
1277
  * from ISA_UNKNOWN above, the shared token.
1278
+ * - forced: violet (a maintenance force holds the device). The same
1279
+ * object as FORCED_MARK_COLOR above.
1234
1280
  *
1235
1281
  * The unknown entry deliberately does NOT rely on its fill. The flat fill
1236
1282
  * sits a CIEDE2000 delta-E of 0.45 from the off fill after greyscale
@@ -1245,17 +1291,6 @@ declare const ISA_UNKNOWN_DARK: {
1245
1291
  * apart. That is why unknown never relies on colour anywhere.
1246
1292
  */
1247
1293
  declare const DEFAULT_STATUS_COLORS: Record<DeviceStatus, StatusColorEntry>;
1248
- /**
1249
- * Maintenance-force marking color (#3607, R19). Deliberately outside
1250
- * DEFAULT_STATUS_COLORS: a forced value is not a health status, and must
1251
- * never share a color with warning/alarm/fault or read as one of them.
1252
- * Violet is unused elsewhere in this palette.
1253
- *
1254
- * Light fill + dark text, not a solid vivid chip: reads as a calm marker
1255
- * rather than an alarm sticker, while the vivid `border` shade still
1256
- * outlines a forced value's box/ring so it stays findable at a glance.
1257
- */
1258
- declare const FORCED_MARK_COLOR: StatusColorEntry;
1259
1294
  /**
1260
1295
  * Normalize a status string to its canonical `DeviceStatus` key.
1261
1296
  *
@@ -1337,6 +1372,54 @@ interface UnknownStatusGlyphProps extends Omit<React__default.SVGProps<SVGSVGEle
1337
1372
  */
1338
1373
  declare function UnknownStatusGlyph({ size, counterRotate, style, ...rest }: UnknownStatusGlyphProps): React__default.ReactElement;
1339
1374
 
1375
+ /**
1376
+ * A corner badge slot on a node (#33).
1377
+ *
1378
+ * A badge marks a condition that is NOT a health severity (`NodeInstance.status`)
1379
+ * and must never replace the node's body color. A device can be tripped AND
1380
+ * blocked at the same time; `status` shows the trip, the badge shows the
1381
+ * block, and both render at once. This is a general slot, not a fixed
1382
+ * prohibition badge: the consumer names the glyph, the colors, the corner,
1383
+ * the size, and the motion. Register additional glyphs with
1384
+ * `registerBadgeGlyph` (`src/diagram/badges`) rather than hardcoding a new
1385
+ * one here.
1386
+ */
1387
+ interface NodeBadgeAppearance {
1388
+ /**
1389
+ * Which glyph to draw. `'prohibit'` (a circle with a slash) ships with the
1390
+ * library. The set is extensible: register more with `registerBadgeGlyph`
1391
+ * / `registerBadgeGlyphs` and reference them by id here, the same way
1392
+ * `symbolId` references a registered symbol.
1393
+ */
1394
+ glyph: 'prohibit' | string;
1395
+ /** Glyph color (stroke/fill). Consumer picks; no status-driven default. */
1396
+ color?: string;
1397
+ /** Disc background color behind the glyph. Consumer picks. */
1398
+ backgroundColor?: string;
1399
+ /** Which corner of the node's bounding box the badge anchors to. Defaults to 'top-right'. */
1400
+ corner?: NodeBadgeCorner;
1401
+ /** Badge disc diameter, in the symbol's local SVG units. Defaults to 14. */
1402
+ size?: number;
1403
+ /** Accessible label read by assistive tech (e.g. "Start blocked"). */
1404
+ label?: string;
1405
+ }
1406
+ type NodeBadgeCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
1407
+ /**
1408
+ * Motion for a `NodeBadge`. Steady by default. A consumer that turns on
1409
+ * `blink` MUST supply `blinkPeriodMs` - the library has no default period.
1410
+ * TFFi drives its status tower, its batch bar, and this badge off one shared
1411
+ * constant so all three beat together; a period fixed in the library would
1412
+ * break that agreement, so there is deliberately no fallback value here.
1413
+ */
1414
+ type NodeBadgeMotion = {
1415
+ blink?: false;
1416
+ } | {
1417
+ blink: true;
1418
+ blinkPeriodMs: number;
1419
+ };
1420
+ /** The full badge: appearance plus motion. This is the type `NodeInstance.badge` carries. */
1421
+ type NodeBadge = NodeBadgeAppearance & NodeBadgeMotion;
1422
+
1340
1423
  interface NodeInstance {
1341
1424
  id: string;
1342
1425
  symbolId: string;
@@ -1361,6 +1444,13 @@ interface NodeInstance {
1361
1444
  * symbol. Default off. Wins over status and customColors.
1362
1445
  */
1363
1446
  positionUnknown?: boolean;
1447
+ /**
1448
+ * A corner badge for a condition that is NOT a health severity (#33),
1449
+ * e.g. a device blocked from starting. Stacks on top of `status` /
1450
+ * `customColors` / `positionUnknown` - it never replaces the node's body
1451
+ * color, so a node can show `status: 'fault'` and a badge at once.
1452
+ */
1453
+ badge?: NodeBadge;
1364
1454
  metadata?: Record<string, unknown>;
1365
1455
  }
1366
1456
  interface Transform {
@@ -2063,6 +2153,36 @@ declare function readAppearanceValue(node: NodeInstance, field: AppearanceField)
2063
2153
  /** Produce the NodeInstance update for a new field value. */
2064
2154
  declare function writeAppearanceValue(node: NodeInstance, field: AppearanceField, value: AppearanceValue): Partial<NodeInstance>;
2065
2155
 
2156
+ /**
2157
+ * Registry of badge glyphs for the `NodeBadge` corner slot (#33).
2158
+ *
2159
+ * Mirrors `diagram/symbols/mockSymbolLibrary`'s registration pattern: a
2160
+ * built-in set plus a consumer-registered set, looked up by id. A consumer
2161
+ * calls `registerBadgeGlyph` from its own app code to add a glyph the
2162
+ * library does not ship - the same escape hatch `registerSymbol` already
2163
+ * gives P&ID symbols. This is deliberately NOT a string resolved only
2164
+ * inside the library (the mistake `Overlay.content.component` made, per
2165
+ * #33): the consumer supplies the real markup.
2166
+ */
2167
+ interface BadgeGlyphDefinition {
2168
+ /** Id a `NodeBadge.glyph` references. */
2169
+ id: string;
2170
+ /**
2171
+ * Raw SVG markup, drawn centered in a 24x24 local box (0,0 to 24,24).
2172
+ * Use `stroke="currentColor"` / `fill="currentColor"` so the glyph picks
2173
+ * up `NodeBadge.color` - the glyph carries no color of its own, only shape.
2174
+ */
2175
+ svgContent: string;
2176
+ }
2177
+ /** Register a single custom badge glyph, or replace a built-in one by reusing its id. */
2178
+ declare function registerBadgeGlyph(glyph: BadgeGlyphDefinition): void;
2179
+ /** Register multiple custom badge glyphs at once. */
2180
+ declare function registerBadgeGlyphs(glyphs: BadgeGlyphDefinition[] | Record<string, BadgeGlyphDefinition>): void;
2181
+ /** Get a badge glyph definition by id (checks custom glyphs before built-ins). */
2182
+ declare function getBadgeGlyph(glyphId: string): BadgeGlyphDefinition | undefined;
2183
+ /** List every registered badge glyph id (built-in plus custom). */
2184
+ declare function getAvailableBadgeGlyphs(): string[];
2185
+
2066
2186
  /**
2067
2187
  * Telemetry status type
2068
2188
  */
@@ -2479,4 +2599,4 @@ interface SimulationOptions {
2479
2599
  */
2480
2600
  declare function useSimulation(telemetry: TelemetryMap, updateTelemetryBatch: (updates: Array<any>) => void, options?: SimulationOptions): void;
2481
2601
 
2482
- export { type AnchorType, type AppearanceField, type AppearanceFieldKind, type AppearanceTarget, type AppearanceValue, Button, type ButtonProps, COLOR_PRESETS, CardHeader, CardUnit, CardValue, CircularGauge, type CircularGaugeProps, type CommandKind, type ConfigActions, type ConfigDisplay, type ConfigParameter, type ConfigSection, type ConfigState, type ConfigTheme, type ControlCapabilities, type ControlMode, ControlPanel, type ControlPanelProps, type ControlParameter, type ControlState, type CustomAction, DEFAULT_STATUS_COLORS, DEFAULT_THRESHOLDS, DashboardCard, type DataBinding, type DeviceConfigBinding, type DeviceConfigMap, DeviceConfigPanel, type DeviceConfigPanelProps, type DeviceControlBinding, type DeviceControlMap, DeviceControlPanel, type DeviceControlPanelProps, type DeviceStatus, type Diagram, type DiagramMetadata, type DisplayMode, DisplayModeToggle, type DragPoint, type DragState, EquipmentIndicator, type EquipmentIndicatorProps, EquipmentIndicatorWithSparkline, type EquipmentIndicatorWithSparklineProps, FORCED_MARK_COLOR, FixedSVGContainer, FooterPanel, type ForcedState, FullscreenContainer, FullscreenWorkspace, type FullscreenWorkspaceProps, type FullscreenWorkspaceTab, ISA_UNKNOWN, ISA_UNKNOWN_DARK, type KeyboardShortcutsOptions, LeftPanel, LeftToggleButton, LegacyValueEntry, type LegacyValueEntryProps, type ModeConfig, NodeConfigPanel, type NodeConfigPanelProps, type NodeControlConfig, NodeControlsPanel, type NodeControlsPanelProps, type NodeInstance, NumpadDialog, type NumpadDialogProps, type Overlay, type OverlayContent, type OverlayTheme, type OverlayType, PIDCanvas, type PIDCanvasControls, type PIDCanvasFeatures, type PIDCanvasProps, PanelContent, PanelTabs, type PipeEdge, type PipeStyle, type Point, type PortDefinition, type PortInstance, type PortType, RightPanel, RightToggleButton, STATUS_LED_APPEARANCE, SVGArea, SVGLockedOverlay, type SVGLockedOverlayProps, type ScalingMetadata, ScrollableSVGWrapper, type SelectionState, Selector, type SelectorOption, type SelectorProps, type SimulationOptions, Sparkline, type SparklineProps, type StatusColorEntry, type StatusColorMap, StatusIndicator, type StatusIndicatorProps, type SymbolCategory, type SymbolDefinition, SymbolLibrary, type SymbolLibraryProps, type SymbolMetadata, type TelemetryBinding, type TelemetryMap, type TelemetryStatus$1 as TelemetryStatus, type TelemetryValue, ThemeProvider, ThemeToggle, type ThemeToggleProps, type Thresholds, type Transform, TrendLine, type TrendLineProps, UNKNOWN_GLYPH_RING_DASH, UNKNOWN_STATUS_GLYPH_COLOR_VAR, type UnknownGlyphSize, UnknownStatusGlyph, type UnknownStatusGlyphProps, type UseBooleanIndicatorsOptions, type UseDeviceControlsOptions, type UseDragOptions, type UseDragResult, type UseSelectionOptions, type UseSelectionResult, type UseTelemetryOptions, type UseTelemetryResult, type UseTelemetryStatusOptions, type UseViewBoxOptions, type UseViewBoxResult, ValueEntry, type ValueEntryProps, type ViewBox, ZoomButton, ZoomControls, calculateThresholdStatus, createConfigBinding, createControlBinding, exampleDiagram, exportDiagram, generateRoutePoints, getAppearanceDefaults, getAppearanceSchema, getAvailableSymbols, getPortWorldPosition, getSymbolDefinition, importDiagram, mockSymbolLibrary, nodeHasPort, normalizeDeviceStatus, overlayStyles, readAppearanceValue, registerSymbol, registerSymbols, resolvePresetColor, resolveStatusColor, useBooleanIndicators, useDeviceConfigs, useDeviceControls, useDrag, useKeyboardShortcuts, useSelection, useSimulation, useTelemetry, useTelemetryStatus, useTheme, useViewBox, validateDiagram, writeAppearanceValue };
2602
+ export { type AnchorType, type AppearanceField, type AppearanceFieldKind, type AppearanceTarget, type AppearanceValue, type BadgeGlyphDefinition, Button, type ButtonProps, COLOR_PRESETS, CardHeader, CardUnit, CardValue, CircularGauge, type CircularGaugeProps, type CommandKind, type ConfigActions, type ConfigDisplay, type ConfigParameter, type ConfigSection, type ConfigState, type ConfigTheme, type ControlCapabilities, type ControlMode, ControlPanel, type ControlPanelProps, type ControlParameter, type ControlState, type CustomAction, DEFAULT_STATUS_COLORS, DEFAULT_THRESHOLDS, DashboardCard, type DataBinding, type DeviceConfigBinding, type DeviceConfigMap, DeviceConfigPanel, type DeviceConfigPanelProps, type DeviceControlBinding, type DeviceControlMap, DeviceControlPanel, type DeviceControlPanelProps, type DeviceStatus, type Diagram, type DiagramMetadata, type DisplayMode, DisplayModeToggle, type DragPoint, type DragState, EquipmentIndicator, type EquipmentIndicatorProps, EquipmentIndicatorWithSparkline, type EquipmentIndicatorWithSparklineProps, FORCED_MARK_COLOR, FixedSVGContainer, FooterPanel, type ForcedState, FullscreenContainer, FullscreenWorkspace, type FullscreenWorkspaceProps, type FullscreenWorkspaceTab, ISA_UNKNOWN, ISA_UNKNOWN_DARK, type KeyboardShortcutsOptions, LeftPanel, LeftToggleButton, LegacyValueEntry, type LegacyValueEntryProps, type ModeConfig, type NodeBadge, type NodeBadgeAppearance, type NodeBadgeCorner, type NodeBadgeMotion, NodeConfigPanel, type NodeConfigPanelProps, type NodeControlConfig, NodeControlsPanel, type NodeControlsPanelProps, type NodeInstance, NumpadDialog, type NumpadDialogProps, type Overlay, type OverlayContent, type OverlayTheme, type OverlayType, PIDCanvas, type PIDCanvasControls, type PIDCanvasFeatures, type PIDCanvasProps, PanelContent, PanelTabs, type PipeEdge, type PipeStyle, type Point, type PortDefinition, type PortInstance, type PortType, RightPanel, RightToggleButton, STATUS_LED_APPEARANCE, SVGArea, SVGLockedOverlay, type SVGLockedOverlayProps, type ScalingMetadata, ScrollableSVGWrapper, type SelectionState, Selector, type SelectorOption, type SelectorProps, type SimulationOptions, Sparkline, type SparklineProps, type StatusColorEntry, type StatusColorMap, StatusIndicator, type StatusIndicatorProps, type SymbolCategory, type SymbolDefinition, SymbolLibrary, type SymbolLibraryProps, type SymbolMetadata, type TelemetryBinding, type TelemetryMap, type TelemetryStatus$1 as TelemetryStatus, type TelemetryValue, ThemeProvider, ThemeToggle, type ThemeToggleProps, type Thresholds, type Transform, TrendLine, type TrendLineProps, UNKNOWN_GLYPH_RING_DASH, UNKNOWN_STATUS_GLYPH_COLOR_VAR, type UnknownGlyphSize, UnknownStatusGlyph, type UnknownStatusGlyphProps, type UseBooleanIndicatorsOptions, type UseDeviceControlsOptions, type UseDragOptions, type UseDragResult, type UseSelectionOptions, type UseSelectionResult, type UseTelemetryOptions, type UseTelemetryResult, type UseTelemetryStatusOptions, type UseViewBoxOptions, type UseViewBoxResult, ValueEntry, type ValueEntryProps, type ViewBox, ZoomButton, ZoomControls, calculateThresholdStatus, createConfigBinding, createControlBinding, exampleDiagram, exportDiagram, generateRoutePoints, getAppearanceDefaults, getAppearanceSchema, getAvailableBadgeGlyphs, getAvailableSymbols, getBadgeGlyph, getPortWorldPosition, getSymbolDefinition, importDiagram, mockSymbolLibrary, nodeHasPort, normalizeDeviceStatus, overlayStyles, readAppearanceValue, registerBadgeGlyph, registerBadgeGlyphs, registerSymbol, registerSymbols, resolvePresetColor, resolveStatusColor, useBooleanIndicators, useDeviceConfigs, useDeviceControls, useDrag, useKeyboardShortcuts, useSelection, useSimulation, useTelemetry, useTelemetryStatus, useTheme, useViewBox, validateDiagram, writeAppearanceValue };